From 9b0bbfab903cc495d22fb9ac89d54dc325be5f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 5 Feb 2024 15:58:40 +0100 Subject: [PATCH 001/191] Removes Auth0-related GitHub actions --- .github/actions/invite-user/action.yml | 15 -- .github/actions/invite-user/dist/index.js | 16 -- .github/actions/invite-user/package-lock.json | 217 ------------------ .github/actions/invite-user/package.json | 20 -- .github/actions/invite-user/src/Action.ts | 87 ------- .../actions/invite-user/src/Logger/ILogger.ts | 4 - .../actions/invite-user/src/Logger/Logger.ts | 12 - .../PasswordGenerator/IPasswordGenerator.ts | 3 - .../PasswordGenerator/PasswordGenerator.ts | 14 -- .../src/RoleNameParser/IRoleNameParser.ts | 3 - .../src/RoleNameParser/RoleNameParser.ts | 9 - .../invite-user/src/StateStore/IStateStore.ts | 3 - .../src/StateStore/KeyValueStateStore.ts | 27 --- .../src/UserClient/Auth0UserClient.ts | 119 ---------- .../invite-user/src/UserClient/IUserClient.ts | 17 -- .github/actions/invite-user/src/getOptions.ts | 10 - .github/actions/invite-user/src/index.ts | 42 ---- .github/actions/invite-user/tsconfig.json | 15 -- .github/workflows/build.yml | 10 +- .github/workflows/invite-guest.yml | 57 ----- 20 files changed, 2 insertions(+), 698 deletions(-) delete mode 100644 .github/actions/invite-user/action.yml delete mode 100644 .github/actions/invite-user/dist/index.js delete mode 100644 .github/actions/invite-user/package-lock.json delete mode 100644 .github/actions/invite-user/package.json delete mode 100644 .github/actions/invite-user/src/Action.ts delete mode 100644 .github/actions/invite-user/src/Logger/ILogger.ts delete mode 100644 .github/actions/invite-user/src/Logger/Logger.ts delete mode 100644 .github/actions/invite-user/src/PasswordGenerator/IPasswordGenerator.ts delete mode 100644 .github/actions/invite-user/src/PasswordGenerator/PasswordGenerator.ts delete mode 100644 .github/actions/invite-user/src/RoleNameParser/IRoleNameParser.ts delete mode 100644 .github/actions/invite-user/src/RoleNameParser/RoleNameParser.ts delete mode 100644 .github/actions/invite-user/src/StateStore/IStateStore.ts delete mode 100644 .github/actions/invite-user/src/StateStore/KeyValueStateStore.ts delete mode 100644 .github/actions/invite-user/src/UserClient/Auth0UserClient.ts delete mode 100644 .github/actions/invite-user/src/UserClient/IUserClient.ts delete mode 100644 .github/actions/invite-user/src/getOptions.ts delete mode 100644 .github/actions/invite-user/src/index.ts delete mode 100644 .github/actions/invite-user/tsconfig.json delete mode 100644 .github/workflows/invite-guest.yml diff --git a/.github/actions/invite-user/action.yml b/.github/actions/invite-user/action.yml deleted file mode 100644 index 35bd6095..00000000 --- a/.github/actions/invite-user/action.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Invites User -description: Invites a user to Shape Docs. -inputs: - name: - description: The name of the user. - required: true - email: - description: The e-mail of the user to invite. - required: true - roles: - description: Comma-separated list of roles to assign to the user. - required: true -runs: - using: node20 - main: dist/index.js diff --git a/.github/actions/invite-user/dist/index.js b/.github/actions/invite-user/dist/index.js deleted file mode 100644 index 80c24870..00000000 --- a/.github/actions/invite-user/dist/index.js +++ /dev/null @@ -1,16 +0,0 @@ -(()=>{var __webpack_modules__={3658:function(e,t){"use strict";var n=this&&this.__awaiter||function(e,t,n,s){function adopt(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function fulfilled(e){try{step(s.next(e))}catch(e){i(e)}}function rejected(e){try{step(s["throw"](e))}catch(e){i(e)}}function step(e){e.done?n(e.value):adopt(e.value).then(fulfilled,rejected)}step((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:true});class Action{constructor(e){this.stateStore=e.stateStore;this.logger=e.logger;this.userClient=e.userClient;this.passwordGenerator=e.passwordGenerator;this.roleNameParser=e.roleNameParser}run(e){return n(this,void 0,void 0,(function*(){if(!this.stateStore.isPost){yield this.runMain(e);this.stateStore.isPost=true}}))}runMain(e){return n(this,void 0,void 0,(function*(){if(!e.name||e.name.length==0){throw new Error("No name supplied.")}if(!e.email||e.email.length==0){throw new Error("No e-mail supplied.")}if(!e.roles||e.roles.length==0){throw new Error("No roles supplied.")}const t=this.roleNameParser.parse(e.roles);if(t.length==0){throw new Error("No roles supplied.")}const n=yield this.userClient.getUser({email:e.email});let s=n;if(!n){const t=this.passwordGenerator.generatePassword();const n=yield this.userClient.createUser({name:e.name,email:e.email,password:t});s=n}if(!s){throw new Error("Could not get an existing user or create a new user.")}const i=yield this.userClient.createRoles({roleNames:t});if(i.length==0){throw new Error("Received an empty set of roles.")}const r=i.map((e=>e.id));yield this.userClient.assignRolesToUser({userID:s.id,roleIDs:r});if(!n){yield this.userClient.sendChangePasswordEmail({email:s.email});this.logger.log(`${e.name} (${s.email}) has been invited.`)}else{this.logger.log(`${e.name} (${s.email}) has been updated.`)}}))}}t["default"]=Action},8245:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});const o=r(n(2186));class Logger{log(e){console.log(e)}error(e){o.setFailed(e)}}t["default"]=Logger},2127:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});class PasswordGenerator{generatePassword(){let e="";const t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$";const n=18;for(let s=1;s<=n;s++){const n=Math.floor(Math.random()*t.length+1);e+=t.charAt(n)}return e}}t["default"]=PasswordGenerator},3834:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});class RoleNameParser{parse(e){return e.split(",").map((e=>e.trim().toLowerCase())).filter((e=>e.length>0))}}t["default"]=RoleNameParser},9531:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const n={IS_POST:"isPost"};class KeyValueStateStore{constructor(e){this.writerReader=e;this.isPost=false}get isPost(){return!!this.writerReader.getState(n.IS_POST)}set isPost(e){this.writerReader.saveState(n.IS_POST,e)}}t["default"]=KeyValueStateStore},1485:function(e,t,n){"use strict";var s=this&&this.__awaiter||function(e,t,n,s){function adopt(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function fulfilled(e){try{step(s.next(e))}catch(e){i(e)}}function rejected(e){try{step(s["throw"](e))}catch(e){i(e)}}function step(e){e.done?n(e.value):adopt(e.value).then(fulfilled,rejected)}step((s=s.apply(e,t||[])).next())}))};var i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});const r=i(n(8757));const o=n(4712);class Auth0UserClient{constructor(e){this.config=e;this.managementClient=new o.ManagementClient({domain:e.domain,clientId:e.clientId,clientSecret:e.clientSecret})}getUser(e){return s(this,void 0,void 0,(function*(){const t=yield this.managementClient.usersByEmail.getByEmail({email:e.email});if(t.data.length==0){return null}const n=t.data[0];return{id:n.user_id,email:n.email}}))}createUser(e){return s(this,void 0,void 0,(function*(){const t=yield this.managementClient.users.create({connection:"Username-Password-Authentication",name:e.name,email:e.email,email_verified:true,password:e.password,app_metadata:{has_pending_invitation:true}});return{id:t.data.user_id,email:t.data.email}}))}createRoles(e){return s(this,void 0,void 0,(function*(){const t=yield this.managementClient.roles.getAll();const n=t.data;const s=n.filter((t=>e.roleNames.includes(t.name))).map((e=>({id:e.id,name:e.name})));const i=e.roleNames.filter((e=>{const t=s.find((t=>t.name==e));return t==null}));const r=yield Promise.all(i.map((e=>this.managementClient.roles.create({name:e}))));const o=r.map((e=>({id:e.data.id,name:e.data.name})));return s.concat(o)}))}assignRolesToUser(e){return s(this,void 0,void 0,(function*(){yield this.managementClient.users.assignRoles({id:e.userID},{roles:e.roleIDs})}))}sendChangePasswordEmail(e){return s(this,void 0,void 0,(function*(){const t=yield this.getToken();yield r.default.post(this.getURL("/dbconnections/change_password"),{email:e.email,connection:"Username-Password-Authentication"},{headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"}})}))}getToken(){return s(this,void 0,void 0,(function*(){const e=yield r.default.post(this.getURL("/oauth/token"),{grant_type:"client_credentials",client_id:this.config.clientId,client_secret:this.config.clientSecret,audience:`https://${this.config.domain}/api/v2/`},{headers:{"Content-Type":"application/x-www-form-urlencoded"}});return e.data.access_token}))}getURL(e){return`https://${this.config.domain}${e}`}}t["default"]=Auth0UserClient},8873:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});const o=r(n(2186));function getOptions(){return{name:o.getInput("name"),email:o.getInput("email"),roles:o.getInput("roles")}}t["default"]=getOptions},4822:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});const A=r(n(2186));const a=o(n(3658));const c=o(n(1485));const u=o(n(8873));const l=o(n(8245));const d=o(n(9531));const p=o(n(2127));const g=o(n(3834));const{AUTH0_MANAGEMENT_CLIENT_ID:h,AUTH0_MANAGEMENT_CLIENT_SECRET:f,AUTH0_MANAGEMENT_CLIENT_DOMAIN:E}=process.env;if(!h||h.length==0){throw new Error("AUTH0_MANAGEMENT_CLIENT_ID environment variable not set.")}else if(!f||f.length==0){throw new Error("AUTH0_MANAGEMENT_CLIENT_ID environment variable not set.")}else if(!E||E.length==0){throw new Error("AUTH0_MANAGEMENT_CLIENT_ID environment variable not set.")}const m=new d.default(A);const C=new l.default;const Q=new c.default({clientId:h,clientSecret:f,domain:E});const I=new p.default;const B=new g.default;const y=new a.default({stateStore:m,logger:C,userClient:Q,passwordGenerator:I,roleNameParser:B});y.run((0,u.default)()).catch((e=>{C.error(e.toString())}))},7351:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;Object.defineProperty(e,s,{enumerable:true,get:function(){return t[n]}})}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.issue=t.issueCommand=void 0;const o=r(n(2037));const A=n(5278);function issueCommand(e,t,n){const s=new Command(e,t,n);process.stdout.write(s.toString()+o.EOL)}t.issueCommand=issueCommand;function issue(e,t=""){issueCommand(e,{},t)}t.issue=issue;const a="::";class Command{constructor(e,t,n){if(!e){e="missing.command"}this.command=e;this.properties=t;this.message=n}toString(){let e=a+this.command;if(this.properties&&Object.keys(this.properties).length>0){e+=" ";let t=true;for(const n in this.properties){if(this.properties.hasOwnProperty(n)){const s=this.properties[n];if(s){if(t){t=false}else{e+=","}e+=`${n}=${escapeProperty(s)}`}}}}e+=`${a}${escapeData(this.message)}`;return e}}function escapeData(e){return A.toCommandValue(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A")}function escapeProperty(e){return A.toCommandValue(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A").replace(/:/g,"%3A").replace(/,/g,"%2C")}},2186:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;Object.defineProperty(e,s,{enumerable:true,get:function(){return t[n]}})}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};var o=this&&this.__awaiter||function(e,t,n,s){function adopt(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function fulfilled(e){try{step(s.next(e))}catch(e){i(e)}}function rejected(e){try{step(s["throw"](e))}catch(e){i(e)}}function step(e){e.done?n(e.value):adopt(e.value).then(fulfilled,rejected)}step((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:true});t.getIDToken=t.getState=t.saveState=t.group=t.endGroup=t.startGroup=t.info=t.notice=t.warning=t.error=t.debug=t.isDebug=t.setFailed=t.setCommandEcho=t.setOutput=t.getBooleanInput=t.getMultilineInput=t.getInput=t.addPath=t.setSecret=t.exportVariable=t.ExitCode=void 0;const A=n(7351);const a=n(717);const c=n(5278);const u=r(n(2037));const l=r(n(1017));const d=n(8041);var p;(function(e){e[e["Success"]=0]="Success";e[e["Failure"]=1]="Failure"})(p=t.ExitCode||(t.ExitCode={}));function exportVariable(e,t){const n=c.toCommandValue(t);process.env[e]=n;const s=process.env["GITHUB_ENV"]||"";if(s){return a.issueFileCommand("ENV",a.prepareKeyValueMessage(e,t))}A.issueCommand("set-env",{name:e},n)}t.exportVariable=exportVariable;function setSecret(e){A.issueCommand("add-mask",{},e)}t.setSecret=setSecret;function addPath(e){const t=process.env["GITHUB_PATH"]||"";if(t){a.issueFileCommand("PATH",e)}else{A.issueCommand("add-path",{},e)}process.env["PATH"]=`${e}${l.delimiter}${process.env["PATH"]}`}t.addPath=addPath;function getInput(e,t){const n=process.env[`INPUT_${e.replace(/ /g,"_").toUpperCase()}`]||"";if(t&&t.required&&!n){throw new Error(`Input required and not supplied: ${e}`)}if(t&&t.trimWhitespace===false){return n}return n.trim()}t.getInput=getInput;function getMultilineInput(e,t){const n=getInput(e,t).split("\n").filter((e=>e!==""));if(t&&t.trimWhitespace===false){return n}return n.map((e=>e.trim()))}t.getMultilineInput=getMultilineInput;function getBooleanInput(e,t){const n=["true","True","TRUE"];const s=["false","False","FALSE"];const i=getInput(e,t);if(n.includes(i))return true;if(s.includes(i))return false;throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${e}\n`+`Support boolean input list: \`true | True | TRUE | false | False | FALSE\``)}t.getBooleanInput=getBooleanInput;function setOutput(e,t){const n=process.env["GITHUB_OUTPUT"]||"";if(n){return a.issueFileCommand("OUTPUT",a.prepareKeyValueMessage(e,t))}process.stdout.write(u.EOL);A.issueCommand("set-output",{name:e},c.toCommandValue(t))}t.setOutput=setOutput;function setCommandEcho(e){A.issue("echo",e?"on":"off")}t.setCommandEcho=setCommandEcho;function setFailed(e){process.exitCode=p.Failure;error(e)}t.setFailed=setFailed;function isDebug(){return process.env["RUNNER_DEBUG"]==="1"}t.isDebug=isDebug;function debug(e){A.issueCommand("debug",{},e)}t.debug=debug;function error(e,t={}){A.issueCommand("error",c.toCommandProperties(t),e instanceof Error?e.toString():e)}t.error=error;function warning(e,t={}){A.issueCommand("warning",c.toCommandProperties(t),e instanceof Error?e.toString():e)}t.warning=warning;function notice(e,t={}){A.issueCommand("notice",c.toCommandProperties(t),e instanceof Error?e.toString():e)}t.notice=notice;function info(e){process.stdout.write(e+u.EOL)}t.info=info;function startGroup(e){A.issue("group",e)}t.startGroup=startGroup;function endGroup(){A.issue("endgroup")}t.endGroup=endGroup;function group(e,t){return o(this,void 0,void 0,(function*(){startGroup(e);let n;try{n=yield t()}finally{endGroup()}return n}))}t.group=group;function saveState(e,t){const n=process.env["GITHUB_STATE"]||"";if(n){return a.issueFileCommand("STATE",a.prepareKeyValueMessage(e,t))}A.issueCommand("save-state",{name:e},c.toCommandValue(t))}t.saveState=saveState;function getState(e){return process.env[`STATE_${e}`]||""}t.getState=getState;function getIDToken(e){return o(this,void 0,void 0,(function*(){return yield d.OidcClient.getIDToken(e)}))}t.getIDToken=getIDToken;var g=n(1327);Object.defineProperty(t,"summary",{enumerable:true,get:function(){return g.summary}});var h=n(1327);Object.defineProperty(t,"markdownSummary",{enumerable:true,get:function(){return h.markdownSummary}});var f=n(2981);Object.defineProperty(t,"toPosixPath",{enumerable:true,get:function(){return f.toPosixPath}});Object.defineProperty(t,"toWin32Path",{enumerable:true,get:function(){return f.toWin32Path}});Object.defineProperty(t,"toPlatformPath",{enumerable:true,get:function(){return f.toPlatformPath}})},717:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;Object.defineProperty(e,s,{enumerable:true,get:function(){return t[n]}})}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.prepareKeyValueMessage=t.issueFileCommand=void 0;const o=r(n(7147));const A=r(n(2037));const a=n(5840);const c=n(5278);function issueFileCommand(e,t){const n=process.env[`GITHUB_${e}`];if(!n){throw new Error(`Unable to find environment variable for file command ${e}`)}if(!o.existsSync(n)){throw new Error(`Missing file at path: ${n}`)}o.appendFileSync(n,`${c.toCommandValue(t)}${A.EOL}`,{encoding:"utf8"})}t.issueFileCommand=issueFileCommand;function prepareKeyValueMessage(e,t){const n=`ghadelimiter_${a.v4()}`;const s=c.toCommandValue(t);if(e.includes(n)){throw new Error(`Unexpected input: name should not contain the delimiter "${n}"`)}if(s.includes(n)){throw new Error(`Unexpected input: value should not contain the delimiter "${n}"`)}return`${e}<<${n}${A.EOL}${s}${A.EOL}${n}`}t.prepareKeyValueMessage=prepareKeyValueMessage},8041:function(e,t,n){"use strict";var s=this&&this.__awaiter||function(e,t,n,s){function adopt(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function fulfilled(e){try{step(s.next(e))}catch(e){i(e)}}function rejected(e){try{step(s["throw"](e))}catch(e){i(e)}}function step(e){e.done?n(e.value):adopt(e.value).then(fulfilled,rejected)}step((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:true});t.OidcClient=void 0;const i=n(6255);const r=n(5526);const o=n(2186);class OidcClient{static createHttpClient(e=true,t=10){const n={allowRetries:e,maxRetries:t};return new i.HttpClient("actions/oidc-client",[new r.BearerCredentialHandler(OidcClient.getRequestToken())],n)}static getRequestToken(){const e=process.env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"];if(!e){throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable")}return e}static getIDTokenUrl(){const e=process.env["ACTIONS_ID_TOKEN_REQUEST_URL"];if(!e){throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable")}return e}static getCall(e){var t;return s(this,void 0,void 0,(function*(){const n=OidcClient.createHttpClient();const s=yield n.getJson(e).catch((e=>{throw new Error(`Failed to get ID Token. \n \n Error Code : ${e.statusCode}\n \n Error Message: ${e.message}`)}));const i=(t=s.result)===null||t===void 0?void 0:t.value;if(!i){throw new Error("Response json body do not have ID Token field")}return i}))}static getIDToken(e){return s(this,void 0,void 0,(function*(){try{let t=OidcClient.getIDTokenUrl();if(e){const n=encodeURIComponent(e);t=`${t}&audience=${n}`}o.debug(`ID token url is ${t}`);const n=yield OidcClient.getCall(t);o.setSecret(n);return n}catch(e){throw new Error(`Error message: ${e.message}`)}}))}}t.OidcClient=OidcClient},2981:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;Object.defineProperty(e,s,{enumerable:true,get:function(){return t[n]}})}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.toPlatformPath=t.toWin32Path=t.toPosixPath=void 0;const o=r(n(1017));function toPosixPath(e){return e.replace(/[\\]/g,"/")}t.toPosixPath=toPosixPath;function toWin32Path(e){return e.replace(/[/]/g,"\\")}t.toWin32Path=toWin32Path;function toPlatformPath(e){return e.replace(/[/\\]/g,o.sep)}t.toPlatformPath=toPlatformPath},1327:function(e,t,n){"use strict";var s=this&&this.__awaiter||function(e,t,n,s){function adopt(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function fulfilled(e){try{step(s.next(e))}catch(e){i(e)}}function rejected(e){try{step(s["throw"](e))}catch(e){i(e)}}function step(e){e.done?n(e.value):adopt(e.value).then(fulfilled,rejected)}step((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:true});t.summary=t.markdownSummary=t.SUMMARY_DOCS_URL=t.SUMMARY_ENV_VAR=void 0;const i=n(2037);const r=n(7147);const{access:o,appendFile:A,writeFile:a}=r.promises;t.SUMMARY_ENV_VAR="GITHUB_STEP_SUMMARY";t.SUMMARY_DOCS_URL="https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary";class Summary{constructor(){this._buffer=""}filePath(){return s(this,void 0,void 0,(function*(){if(this._filePath){return this._filePath}const e=process.env[t.SUMMARY_ENV_VAR];if(!e){throw new Error(`Unable to find environment variable for $${t.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`)}try{yield o(e,r.constants.R_OK|r.constants.W_OK)}catch(t){throw new Error(`Unable to access summary file: '${e}'. Check if the file has correct read/write permissions.`)}this._filePath=e;return this._filePath}))}wrap(e,t,n={}){const s=Object.entries(n).map((([e,t])=>` ${e}="${t}"`)).join("");if(!t){return`<${e}${s}>`}return`<${e}${s}>${t}`}write(e){return s(this,void 0,void 0,(function*(){const t=!!(e===null||e===void 0?void 0:e.overwrite);const n=yield this.filePath();const s=t?a:A;yield s(n,this._buffer,{encoding:"utf8"});return this.emptyBuffer()}))}clear(){return s(this,void 0,void 0,(function*(){return this.emptyBuffer().write({overwrite:true})}))}stringify(){return this._buffer}isEmptyBuffer(){return this._buffer.length===0}emptyBuffer(){this._buffer="";return this}addRaw(e,t=false){this._buffer+=e;return t?this.addEOL():this}addEOL(){return this.addRaw(i.EOL)}addCodeBlock(e,t){const n=Object.assign({},t&&{lang:t});const s=this.wrap("pre",this.wrap("code",e),n);return this.addRaw(s).addEOL()}addList(e,t=false){const n=t?"ol":"ul";const s=e.map((e=>this.wrap("li",e))).join("");const i=this.wrap(n,s);return this.addRaw(i).addEOL()}addTable(e){const t=e.map((e=>{const t=e.map((e=>{if(typeof e==="string"){return this.wrap("td",e)}const{header:t,data:n,colspan:s,rowspan:i}=e;const r=t?"th":"td";const o=Object.assign(Object.assign({},s&&{colspan:s}),i&&{rowspan:i});return this.wrap(r,n,o)})).join("");return this.wrap("tr",t)})).join("");const n=this.wrap("table",t);return this.addRaw(n).addEOL()}addDetails(e,t){const n=this.wrap("details",this.wrap("summary",e)+t);return this.addRaw(n).addEOL()}addImage(e,t,n){const{width:s,height:i}=n||{};const r=Object.assign(Object.assign({},s&&{width:s}),i&&{height:i});const o=this.wrap("img",null,Object.assign({src:e,alt:t},r));return this.addRaw(o).addEOL()}addHeading(e,t){const n=`h${t}`;const s=["h1","h2","h3","h4","h5","h6"].includes(n)?n:"h1";const i=this.wrap(s,e);return this.addRaw(i).addEOL()}addSeparator(){const e=this.wrap("hr",null);return this.addRaw(e).addEOL()}addBreak(){const e=this.wrap("br",null);return this.addRaw(e).addEOL()}addQuote(e,t){const n=Object.assign({},t&&{cite:t});const s=this.wrap("blockquote",e,n);return this.addRaw(s).addEOL()}addLink(e,t){const n=this.wrap("a",e,{href:t});return this.addRaw(n).addEOL()}}const c=new Summary;t.markdownSummary=c;t.summary=c},5278:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.toCommandProperties=t.toCommandValue=void 0;function toCommandValue(e){if(e===null||e===undefined){return""}else if(typeof e==="string"||e instanceof String){return e}return JSON.stringify(e)}t.toCommandValue=toCommandValue;function toCommandProperties(e){if(!Object.keys(e).length){return{}}return{title:e.title,file:e.file,line:e.startLine,endLine:e.endLine,col:e.startColumn,endColumn:e.endColumn}}t.toCommandProperties=toCommandProperties},5526:function(e,t){"use strict";var n=this&&this.__awaiter||function(e,t,n,s){function adopt(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function fulfilled(e){try{step(s.next(e))}catch(e){i(e)}}function rejected(e){try{step(s["throw"](e))}catch(e){i(e)}}function step(e){e.done?n(e.value):adopt(e.value).then(fulfilled,rejected)}step((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:true});t.PersonalAccessTokenCredentialHandler=t.BearerCredentialHandler=t.BasicCredentialHandler=void 0;class BasicCredentialHandler{constructor(e,t){this.username=e;this.password=t}prepareRequest(e){if(!e.headers){throw Error("The request has no headers")}e.headers["Authorization"]=`Basic ${Buffer.from(`${this.username}:${this.password}`).toString("base64")}`}canHandleAuthentication(){return false}handleAuthentication(){return n(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}t.BasicCredentialHandler=BasicCredentialHandler;class BearerCredentialHandler{constructor(e){this.token=e}prepareRequest(e){if(!e.headers){throw Error("The request has no headers")}e.headers["Authorization"]=`Bearer ${this.token}`}canHandleAuthentication(){return false}handleAuthentication(){return n(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}t.BearerCredentialHandler=BearerCredentialHandler;class PersonalAccessTokenCredentialHandler{constructor(e){this.token=e}prepareRequest(e){if(!e.headers){throw Error("The request has no headers")}e.headers["Authorization"]=`Basic ${Buffer.from(`PAT:${this.token}`).toString("base64")}`}canHandleAuthentication(){return false}handleAuthentication(){return n(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}t.PersonalAccessTokenCredentialHandler=PersonalAccessTokenCredentialHandler},6255:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};var o=this&&this.__awaiter||function(e,t,n,s){function adopt(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function fulfilled(e){try{step(s.next(e))}catch(e){i(e)}}function rejected(e){try{step(s["throw"](e))}catch(e){i(e)}}function step(e){e.done?n(e.value):adopt(e.value).then(fulfilled,rejected)}step((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:true});t.HttpClient=t.isHttps=t.HttpClientResponse=t.HttpClientError=t.getProxyUrl=t.MediaTypes=t.Headers=t.HttpCodes=void 0;const A=r(n(3685));const a=r(n(5687));const c=r(n(9835));const u=r(n(4294));const l=n(1773);var d;(function(e){e[e["OK"]=200]="OK";e[e["MultipleChoices"]=300]="MultipleChoices";e[e["MovedPermanently"]=301]="MovedPermanently";e[e["ResourceMoved"]=302]="ResourceMoved";e[e["SeeOther"]=303]="SeeOther";e[e["NotModified"]=304]="NotModified";e[e["UseProxy"]=305]="UseProxy";e[e["SwitchProxy"]=306]="SwitchProxy";e[e["TemporaryRedirect"]=307]="TemporaryRedirect";e[e["PermanentRedirect"]=308]="PermanentRedirect";e[e["BadRequest"]=400]="BadRequest";e[e["Unauthorized"]=401]="Unauthorized";e[e["PaymentRequired"]=402]="PaymentRequired";e[e["Forbidden"]=403]="Forbidden";e[e["NotFound"]=404]="NotFound";e[e["MethodNotAllowed"]=405]="MethodNotAllowed";e[e["NotAcceptable"]=406]="NotAcceptable";e[e["ProxyAuthenticationRequired"]=407]="ProxyAuthenticationRequired";e[e["RequestTimeout"]=408]="RequestTimeout";e[e["Conflict"]=409]="Conflict";e[e["Gone"]=410]="Gone";e[e["TooManyRequests"]=429]="TooManyRequests";e[e["InternalServerError"]=500]="InternalServerError";e[e["NotImplemented"]=501]="NotImplemented";e[e["BadGateway"]=502]="BadGateway";e[e["ServiceUnavailable"]=503]="ServiceUnavailable";e[e["GatewayTimeout"]=504]="GatewayTimeout"})(d||(t.HttpCodes=d={}));var p;(function(e){e["Accept"]="accept";e["ContentType"]="content-type"})(p||(t.Headers=p={}));var g;(function(e){e["ApplicationJson"]="application/json"})(g||(t.MediaTypes=g={}));function getProxyUrl(e){const t=c.getProxyUrl(new URL(e));return t?t.href:""}t.getProxyUrl=getProxyUrl;const h=[d.MovedPermanently,d.ResourceMoved,d.SeeOther,d.TemporaryRedirect,d.PermanentRedirect];const f=[d.BadGateway,d.ServiceUnavailable,d.GatewayTimeout];const E=["OPTIONS","GET","DELETE","HEAD"];const m=10;const C=5;class HttpClientError extends Error{constructor(e,t){super(e);this.name="HttpClientError";this.statusCode=t;Object.setPrototypeOf(this,HttpClientError.prototype)}}t.HttpClientError=HttpClientError;class HttpClientResponse{constructor(e){this.message=e}readBody(){return o(this,void 0,void 0,(function*(){return new Promise((e=>o(this,void 0,void 0,(function*(){let t=Buffer.alloc(0);this.message.on("data",(e=>{t=Buffer.concat([t,e])}));this.message.on("end",(()=>{e(t.toString())}))}))))}))}readBodyBuffer(){return o(this,void 0,void 0,(function*(){return new Promise((e=>o(this,void 0,void 0,(function*(){const t=[];this.message.on("data",(e=>{t.push(e)}));this.message.on("end",(()=>{e(Buffer.concat(t))}))}))))}))}}t.HttpClientResponse=HttpClientResponse;function isHttps(e){const t=new URL(e);return t.protocol==="https:"}t.isHttps=isHttps;class HttpClient{constructor(e,t,n){this._ignoreSslError=false;this._allowRedirects=true;this._allowRedirectDowngrade=false;this._maxRedirects=50;this._allowRetries=false;this._maxRetries=1;this._keepAlive=false;this._disposed=false;this.userAgent=e;this.handlers=t||[];this.requestOptions=n;if(n){if(n.ignoreSslError!=null){this._ignoreSslError=n.ignoreSslError}this._socketTimeout=n.socketTimeout;if(n.allowRedirects!=null){this._allowRedirects=n.allowRedirects}if(n.allowRedirectDowngrade!=null){this._allowRedirectDowngrade=n.allowRedirectDowngrade}if(n.maxRedirects!=null){this._maxRedirects=Math.max(n.maxRedirects,0)}if(n.keepAlive!=null){this._keepAlive=n.keepAlive}if(n.allowRetries!=null){this._allowRetries=n.allowRetries}if(n.maxRetries!=null){this._maxRetries=n.maxRetries}}}options(e,t){return o(this,void 0,void 0,(function*(){return this.request("OPTIONS",e,null,t||{})}))}get(e,t){return o(this,void 0,void 0,(function*(){return this.request("GET",e,null,t||{})}))}del(e,t){return o(this,void 0,void 0,(function*(){return this.request("DELETE",e,null,t||{})}))}post(e,t,n){return o(this,void 0,void 0,(function*(){return this.request("POST",e,t,n||{})}))}patch(e,t,n){return o(this,void 0,void 0,(function*(){return this.request("PATCH",e,t,n||{})}))}put(e,t,n){return o(this,void 0,void 0,(function*(){return this.request("PUT",e,t,n||{})}))}head(e,t){return o(this,void 0,void 0,(function*(){return this.request("HEAD",e,null,t||{})}))}sendStream(e,t,n,s){return o(this,void 0,void 0,(function*(){return this.request(e,t,n,s)}))}getJson(e,t={}){return o(this,void 0,void 0,(function*(){t[p.Accept]=this._getExistingOrDefaultHeader(t,p.Accept,g.ApplicationJson);const n=yield this.get(e,t);return this._processResponse(n,this.requestOptions)}))}postJson(e,t,n={}){return o(this,void 0,void 0,(function*(){const s=JSON.stringify(t,null,2);n[p.Accept]=this._getExistingOrDefaultHeader(n,p.Accept,g.ApplicationJson);n[p.ContentType]=this._getExistingOrDefaultHeader(n,p.ContentType,g.ApplicationJson);const i=yield this.post(e,s,n);return this._processResponse(i,this.requestOptions)}))}putJson(e,t,n={}){return o(this,void 0,void 0,(function*(){const s=JSON.stringify(t,null,2);n[p.Accept]=this._getExistingOrDefaultHeader(n,p.Accept,g.ApplicationJson);n[p.ContentType]=this._getExistingOrDefaultHeader(n,p.ContentType,g.ApplicationJson);const i=yield this.put(e,s,n);return this._processResponse(i,this.requestOptions)}))}patchJson(e,t,n={}){return o(this,void 0,void 0,(function*(){const s=JSON.stringify(t,null,2);n[p.Accept]=this._getExistingOrDefaultHeader(n,p.Accept,g.ApplicationJson);n[p.ContentType]=this._getExistingOrDefaultHeader(n,p.ContentType,g.ApplicationJson);const i=yield this.patch(e,s,n);return this._processResponse(i,this.requestOptions)}))}request(e,t,n,s){return o(this,void 0,void 0,(function*(){if(this._disposed){throw new Error("Client has already been disposed.")}const i=new URL(t);let r=this._prepareRequest(e,i,s);const o=this._allowRetries&&E.includes(e)?this._maxRetries+1:1;let A=0;let a;do{a=yield this.requestRaw(r,n);if(a&&a.message&&a.message.statusCode===d.Unauthorized){let e;for(const t of this.handlers){if(t.canHandleAuthentication(a)){e=t;break}}if(e){return e.handleAuthentication(this,r,n)}else{return a}}let t=this._maxRedirects;while(a.message.statusCode&&h.includes(a.message.statusCode)&&this._allowRedirects&&t>0){const o=a.message.headers["location"];if(!o){break}const A=new URL(o);if(i.protocol==="https:"&&i.protocol!==A.protocol&&!this._allowRedirectDowngrade){throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.")}yield a.readBody();if(A.hostname!==i.hostname){for(const e in s){if(e.toLowerCase()==="authorization"){delete s[e]}}}r=this._prepareRequest(e,A,s);a=yield this.requestRaw(r,n);t--}if(!a.message.statusCode||!f.includes(a.message.statusCode)){return a}A+=1;if(A{function callbackForResult(e,t){if(e){s(e)}else if(!t){s(new Error("Unknown error"))}else{n(t)}}this.requestRawWithCallback(e,t,callbackForResult)}))}))}requestRawWithCallback(e,t,n){if(typeof t==="string"){if(!e.options.headers){e.options.headers={}}e.options.headers["Content-Length"]=Buffer.byteLength(t,"utf8")}let s=false;function handleResult(e,t){if(!s){s=true;n(e,t)}}const i=e.httpModule.request(e.options,(e=>{const t=new HttpClientResponse(e);handleResult(undefined,t)}));let r;i.on("socket",(e=>{r=e}));i.setTimeout(this._socketTimeout||3*6e4,(()=>{if(r){r.end()}handleResult(new Error(`Request timeout: ${e.options.path}`))}));i.on("error",(function(e){handleResult(e)}));if(t&&typeof t==="string"){i.write(t,"utf8")}if(t&&typeof t!=="string"){t.on("close",(function(){i.end()}));t.pipe(i)}else{i.end()}}getAgent(e){const t=new URL(e);return this._getAgent(t)}getAgentDispatcher(e){const t=new URL(e);const n=c.getProxyUrl(t);const s=n&&n.hostname;if(!s){return}return this._getProxyAgentDispatcher(t,n)}_prepareRequest(e,t,n){const s={};s.parsedUrl=t;const i=s.parsedUrl.protocol==="https:";s.httpModule=i?a:A;const r=i?443:80;s.options={};s.options.host=s.parsedUrl.hostname;s.options.port=s.parsedUrl.port?parseInt(s.parsedUrl.port):r;s.options.path=(s.parsedUrl.pathname||"")+(s.parsedUrl.search||"");s.options.method=e;s.options.headers=this._mergeHeaders(n);if(this.userAgent!=null){s.options.headers["user-agent"]=this.userAgent}s.options.agent=this._getAgent(s.parsedUrl);if(this.handlers){for(const e of this.handlers){e.prepareRequest(s.options)}}return s}_mergeHeaders(e){if(this.requestOptions&&this.requestOptions.headers){return Object.assign({},lowercaseKeys(this.requestOptions.headers),lowercaseKeys(e||{}))}return lowercaseKeys(e||{})}_getExistingOrDefaultHeader(e,t,n){let s;if(this.requestOptions&&this.requestOptions.headers){s=lowercaseKeys(this.requestOptions.headers)[t]}return e[t]||s||n}_getAgent(e){let t;const n=c.getProxyUrl(e);const s=n&&n.hostname;if(this._keepAlive&&s){t=this._proxyAgent}if(this._keepAlive&&!s){t=this._agent}if(t){return t}const i=e.protocol==="https:";let r=100;if(this.requestOptions){r=this.requestOptions.maxSockets||A.globalAgent.maxSockets}if(n&&n.hostname){const e={maxSockets:r,keepAlive:this._keepAlive,proxy:Object.assign(Object.assign({},(n.username||n.password)&&{proxyAuth:`${n.username}:${n.password}`}),{host:n.hostname,port:n.port})};let s;const o=n.protocol==="https:";if(i){s=o?u.httpsOverHttps:u.httpsOverHttp}else{s=o?u.httpOverHttps:u.httpOverHttp}t=s(e);this._proxyAgent=t}if(this._keepAlive&&!t){const e={keepAlive:this._keepAlive,maxSockets:r};t=i?new a.Agent(e):new A.Agent(e);this._agent=t}if(!t){t=i?a.globalAgent:A.globalAgent}if(i&&this._ignoreSslError){t.options=Object.assign(t.options||{},{rejectUnauthorized:false})}return t}_getProxyAgentDispatcher(e,t){let n;if(this._keepAlive){n=this._proxyAgentDispatcher}if(n){return n}const s=e.protocol==="https:";n=new l.ProxyAgent(Object.assign({uri:t.href,pipelining:!this._keepAlive?0:1},(t.username||t.password)&&{token:`${t.username}:${t.password}`}));this._proxyAgentDispatcher=n;if(s&&this._ignoreSslError){n.options=Object.assign(n.options.requestTls||{},{rejectUnauthorized:false})}return n}_performExponentialBackoff(e){return o(this,void 0,void 0,(function*(){e=Math.min(m,e);const t=C*Math.pow(2,e);return new Promise((e=>setTimeout((()=>e()),t)))}))}_processResponse(e,t){return o(this,void 0,void 0,(function*(){return new Promise(((n,s)=>o(this,void 0,void 0,(function*(){const i=e.message.statusCode||0;const r={statusCode:i,result:null,headers:{}};if(i===d.NotFound){n(r)}function dateTimeDeserializer(e,t){if(typeof t==="string"){const e=new Date(t);if(!isNaN(e.valueOf())){return e}}return t}let o;let A;try{A=yield e.readBody();if(A&&A.length>0){if(t&&t.deserializeDates){o=JSON.parse(A,dateTimeDeserializer)}else{o=JSON.parse(A)}r.result=o}r.headers=e.message.headers}catch(e){}if(i>299){let e;if(o&&o.message){e=o.message}else if(A&&A.length>0){e=A}else{e=`Failed request: (${i})`}const t=new HttpClientError(e,i);t.result=r.result;s(t)}else{n(r)}}))))}))}}t.HttpClient=HttpClient;const lowercaseKeys=e=>Object.keys(e).reduce(((t,n)=>(t[n.toLowerCase()]=e[n],t)),{})},9835:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.checkBypass=t.getProxyUrl=void 0;function getProxyUrl(e){const t=e.protocol==="https:";if(checkBypass(e)){return undefined}const n=(()=>{if(t){return process.env["https_proxy"]||process.env["HTTPS_PROXY"]}else{return process.env["http_proxy"]||process.env["HTTP_PROXY"]}})();if(n){try{return new URL(n)}catch(e){if(!n.startsWith("http://")&&!n.startsWith("https://"))return new URL(`http://${n}`)}}else{return undefined}}t.getProxyUrl=getProxyUrl;function checkBypass(e){if(!e.hostname){return false}const t=e.hostname;if(isLoopbackAddress(t)){return true}const n=process.env["no_proxy"]||process.env["NO_PROXY"]||"";if(!n){return false}let s;if(e.port){s=Number(e.port)}else if(e.protocol==="http:"){s=80}else if(e.protocol==="https:"){s=443}const i=[e.hostname.toUpperCase()];if(typeof s==="number"){i.push(`${i[0]}:${s}`)}for(const e of n.split(",").map((e=>e.trim().toUpperCase())).filter((e=>e))){if(e==="*"||i.some((t=>t===e||t.endsWith(`.${e}`)||e.startsWith(".")&&t.endsWith(`${e}`)))){return true}}return false}t.checkBypass=checkBypass;function isLoopbackAddress(e){const t=e.toLowerCase();return t==="localhost"||t.startsWith("127.")||t.startsWith("[::1]")||t.startsWith("[0:0:0:0:0:0:0:1]")}},2856:(e,t,n)=>{"use strict";const s=n(4492).Writable;const i=n(7261).inherits;const r=n(8534);const o=n(8710);const A=n(333);const a=45;const c=Buffer.from("-");const u=Buffer.from("\r\n");const EMPTY_FN=function(){};function Dicer(e){if(!(this instanceof Dicer)){return new Dicer(e)}s.call(this,e);if(!e||!e.headerFirst&&typeof e.boundary!=="string"){throw new TypeError("Boundary required")}if(typeof e.boundary==="string"){this.setBoundary(e.boundary)}else{this._bparser=undefined}this._headerFirst=e.headerFirst;this._dashes=0;this._parts=0;this._finished=false;this._realFinish=false;this._isPreamble=true;this._justMatched=false;this._firstWrite=true;this._inHeader=true;this._part=undefined;this._cb=undefined;this._ignoreData=false;this._partOpts={highWaterMark:e.partHwm};this._pause=false;const t=this;this._hparser=new A(e);this._hparser.on("header",(function(e){t._inHeader=false;t._part.emit("header",e)}))}i(Dicer,s);Dicer.prototype.emit=function(e){if(e==="finish"&&!this._realFinish){if(!this._finished){const e=this;process.nextTick((function(){e.emit("error",new Error("Unexpected end of multipart data"));if(e._part&&!e._ignoreData){const t=e._isPreamble?"Preamble":"Part";e._part.emit("error",new Error(t+" terminated early due to unexpected end of multipart data"));e._part.push(null);process.nextTick((function(){e._realFinish=true;e.emit("finish");e._realFinish=false}));return}e._realFinish=true;e.emit("finish");e._realFinish=false}))}}else{s.prototype.emit.apply(this,arguments)}};Dicer.prototype._write=function(e,t,n){if(!this._hparser&&!this._bparser){return n()}if(this._headerFirst&&this._isPreamble){if(!this._part){this._part=new o(this._partOpts);if(this._events.preamble){this.emit("preamble",this._part)}else{this._ignore()}}const t=this._hparser.push(e);if(!this._inHeader&&t!==undefined&&t{"use strict";const s=n(5673).EventEmitter;const i=n(7261).inherits;const r=n(9692);const o=n(8534);const A=Buffer.from("\r\n\r\n");const a=/\r\n/g;const c=/^([^:]+):[ \t]?([\x00-\xFF]+)?$/;function HeaderParser(e){s.call(this);e=e||{};const t=this;this.nread=0;this.maxed=false;this.npairs=0;this.maxHeaderPairs=r(e,"maxHeaderPairs",2e3);this.maxHeaderSize=r(e,"maxHeaderSize",80*1024);this.buffer="";this.header={};this.finished=false;this.ss=new o(A);this.ss.on("info",(function(e,n,s,i){if(n&&!t.maxed){if(t.nread+i-s>=t.maxHeaderSize){i=t.maxHeaderSize-t.nread+s;t.nread=t.maxHeaderSize;t.maxed=true}else{t.nread+=i-s}t.buffer+=n.toString("binary",s,i)}if(e){t._finish()}}))}i(HeaderParser,s);HeaderParser.prototype.push=function(e){const t=this.ss.push(e);if(this.finished){return t}};HeaderParser.prototype.reset=function(){this.finished=false;this.buffer="";this.header={};this.ss.reset()};HeaderParser.prototype._finish=function(){if(this.buffer){this._parseHeader()}this.ss.matches=this.ss.maxMatches;const e=this.header;this.header={};this.buffer="";this.finished=true;this.nread=this.npairs=0;this.maxed=false;this.emit("header",e)};HeaderParser.prototype._parseHeader=function(){if(this.npairs===this.maxHeaderPairs){return}const e=this.buffer.split(a);const t=e.length;let n,s;for(var i=0;i{"use strict";const s=n(7261).inherits;const i=n(4492).Readable;function PartStream(e){i.call(this,e)}s(PartStream,i);PartStream.prototype._read=function(e){};e.exports=PartStream},8534:(e,t,n)=>{"use strict";const s=n(5673).EventEmitter;const i=n(7261).inherits;function SBMH(e){if(typeof e==="string"){e=Buffer.from(e)}if(!Buffer.isBuffer(e)){throw new TypeError("The needle has to be a String or a Buffer.")}const t=e.length;if(t===0){throw new Error("The needle cannot be an empty String/Buffer.")}if(t>256){throw new Error("The needle cannot have a length bigger than 256.")}this.maxMatches=Infinity;this.matches=0;this._occ=new Array(256).fill(t);this._lookbehind_size=0;this._needle=e;this._bufpos=0;this._lookbehind=Buffer.alloc(t);for(var n=0;n=0){this.emit("info",false,this._lookbehind,0,this._lookbehind_size);this._lookbehind_size=0}else{const n=this._lookbehind_size+r;if(n>0){this.emit("info",false,this._lookbehind,0,n)}this._lookbehind.copy(this._lookbehind,0,n,this._lookbehind_size-n);this._lookbehind_size-=n;e.copy(this._lookbehind,this._lookbehind_size);this._lookbehind_size+=t;this._bufpos=t;return t}}r+=(r>=0)*this._bufpos;if(e.indexOf(n,r)!==-1){r=e.indexOf(n,r);++this.matches;if(r>0){this.emit("info",true,e,this._bufpos,r)}else{this.emit("info",true)}return this._bufpos=r+s}else{r=t-s}while(r0){this.emit("info",false,e,this._bufpos,r{"use strict";const s=n(4492).Writable;const{inherits:i}=n(7261);const r=n(2856);const o=n(415);const A=n(6780);const a=n(4426);function Busboy(e){if(!(this instanceof Busboy)){return new Busboy(e)}if(typeof e!=="object"){throw new TypeError("Busboy expected an options-Object.")}if(typeof e.headers!=="object"){throw new TypeError("Busboy expected an options-Object with headers-attribute.")}if(typeof e.headers["content-type"]!=="string"){throw new TypeError("Missing Content-Type-header.")}const{headers:t,...n}=e;this.opts={autoDestroy:false,...n};s.call(this,this.opts);this._done=false;this._parser=this.getParserByHeaders(t);this._finished=false}i(Busboy,s);Busboy.prototype.emit=function(e){if(e==="finish"){if(!this._done){this._parser?.end();return}else if(this._finished){return}this._finished=true}s.prototype.emit.apply(this,arguments)};Busboy.prototype.getParserByHeaders=function(e){const t=a(e["content-type"]);const n={defCharset:this.opts.defCharset,fileHwm:this.opts.fileHwm,headers:e,highWaterMark:this.opts.highWaterMark,isPartAFile:this.opts.isPartAFile,limits:this.opts.limits,parsedConType:t,preservePath:this.opts.preservePath};if(o.detect.test(t[0])){return new o(this,n)}if(A.detect.test(t[0])){return new A(this,n)}throw new Error("Unsupported Content-Type.")};Busboy.prototype._write=function(e,t,n){this._parser.write(e,n)};e.exports=Busboy;e.exports["default"]=Busboy;e.exports.Busboy=Busboy;e.exports.Dicer=r},415:(e,t,n)=>{"use strict";const{Readable:s}=n(4492);const{inherits:i}=n(7261);const r=n(2856);const o=n(4426);const A=n(9136);const a=n(496);const c=n(9692);const u=/^boundary$/i;const l=/^form-data$/i;const d=/^charset$/i;const p=/^filename$/i;const g=/^name$/i;Multipart.detect=/^multipart\/form-data/i;function Multipart(e,t){let n;let s;const i=this;let h;const f=t.limits;const E=t.isPartAFile||((e,t,n)=>t==="application/octet-stream"||n!==undefined);const m=t.parsedConType||[];const C=t.defCharset||"utf8";const Q=t.preservePath;const I={highWaterMark:t.fileHwm};for(n=0,s=m.length;nR){i.parser.removeListener("part",onPart);i.parser.on("part",skipPart);e.hitPartsLimit=true;e.emit("partsLimit");return skipPart(t)}if(N){const e=N;e.emit("end");e.removeAllListeners("end")}t.on("header",(function(r){let c;let u;let h;let f;let m;let R;let v=0;if(r["content-type"]){h=o(r["content-type"][0]);if(h[0]){c=h[0].toLowerCase();for(n=0,s=h.length;ny){const s=y-v+e.length;if(s>0){n.push(e.slice(0,s))}n.truncated=true;n.bytesRead=y;t.removeAllListeners("data");n.emit("limit");return}else if(!n.push(e)){i._pause=true}n.bytesRead=v};F=function(){_=undefined;n.push(null)}}else{if(x===w){if(!e.hitFieldsLimit){e.hitFieldsLimit=true;e.emit("fieldsLimit")}return skipPart(t)}++x;++D;let n="";let s=false;N=t;k=function(e){if((v+=e.length)>B){const i=B-(v-e.length);n+=e.toString("binary",0,i);s=true;t.removeAllListeners("data")}else{n+=e.toString("binary")}};F=function(){N=undefined;if(n.length){n=A(n,"binary",f)}e.emit("field",u,n,false,s,m,c);--D;checkFinished()}}t._readableState.sync=false;t.on("data",k);t.on("end",F)})).on("error",(function(e){if(_){_.emit("error",e)}}))})).on("error",(function(t){e.emit("error",t)})).on("finish",(function(){F=true;checkFinished()}))}Multipart.prototype.write=function(e,t){const n=this.parser.write(e);if(n&&!this._pause){t()}else{this._needDrain=!n;this._cb=t}};Multipart.prototype.end=function(){const e=this;if(e.parser.writable){e.parser.end()}else if(!e._boy._done){process.nextTick((function(){e._boy._done=true;e._boy.emit("finish")}))}};function skipPart(e){e.resume()}function FileStream(e){s.call(this,e);this.bytesRead=0;this.truncated=false}i(FileStream,s);FileStream.prototype._read=function(e){};e.exports=Multipart},6780:(e,t,n)=>{"use strict";const s=n(9730);const i=n(9136);const r=n(9692);const o=/^charset$/i;UrlEncoded.detect=/^application\/x-www-form-urlencoded/i;function UrlEncoded(e,t){const n=t.limits;const i=t.parsedConType;this.boy=e;this.fieldSizeLimit=r(n,"fieldSize",1*1024*1024);this.fieldNameSizeLimit=r(n,"fieldNameSize",100);this.fieldsLimit=r(n,"fields",Infinity);let A;for(var a=0,c=i.length;ao){this._key+=this.decoder.write(e.toString("binary",o,n))}this._state="val";this._hitLimit=false;this._checkingBytes=true;this._val="";this._bytesVal=0;this._valTrunc=false;this.decoder.reset();o=n+1}else if(s!==undefined){++this._fields;let n;const r=this._keyTrunc;if(s>o){n=this._key+=this.decoder.write(e.toString("binary",o,s))}else{n=this._key}this._hitLimit=false;this._checkingBytes=true;this._key="";this._bytesKey=0;this._keyTrunc=false;this.decoder.reset();if(n.length){this.boy.emit("field",i(n,"binary",this.charset),"",r,false)}o=s+1;if(this._fields===this.fieldsLimit){return t()}}else if(this._hitLimit){if(r>o){this._key+=this.decoder.write(e.toString("binary",o,r))}o=r;if((this._bytesKey=this._key.length)===this.fieldNameSizeLimit){this._checkingBytes=false;this._keyTrunc=true}}else{if(oo){this._val+=this.decoder.write(e.toString("binary",o,s))}this.boy.emit("field",i(this._key,"binary",this.charset),i(this._val,"binary",this.charset),this._keyTrunc,this._valTrunc);this._state="key";this._hitLimit=false;this._checkingBytes=true;this._key="";this._bytesKey=0;this._keyTrunc=false;this.decoder.reset();o=s+1;if(this._fields===this.fieldsLimit){return t()}}else if(this._hitLimit){if(r>o){this._val+=this.decoder.write(e.toString("binary",o,r))}o=r;if(this._val===""&&this.fieldSizeLimit===0||(this._bytesVal=this._val.length)===this.fieldSizeLimit){this._checkingBytes=false;this._valTrunc=true}}else{if(o0){this.boy.emit("field",i(this._key,"binary",this.charset),"",this._keyTrunc,false)}else if(this._state==="val"){this.boy.emit("field",i(this._key,"binary",this.charset),i(this._val,"binary",this.charset),this._keyTrunc,this._valTrunc)}this.boy._done=true;this.boy.emit("finish")};e.exports=UrlEncoded},9730:e=>{"use strict";const t=/\+/g;const n=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];function Decoder(){this.buffer=undefined}Decoder.prototype.write=function(e){e=e.replace(t," ");let s="";let i=0;let r=0;const o=e.length;for(;ir){s+=e.substring(r,i);r=i}this.buffer="";++r}}if(r{"use strict";e.exports=function basename(e){if(typeof e!=="string"){return""}for(var t=e.length-1;t>=0;--t){switch(e.charCodeAt(t)){case 47:case 92:e=e.slice(t+1);return e===".."||e==="."?"":e}}return e===".."||e==="."?"":e}},9136:e=>{"use strict";const t=new TextDecoder("utf-8");const n=new Map([["utf-8",t],["utf8",t]]);function decodeText(e,t,s){if(e){if(n.has(s)){try{return n.get(s).decode(Buffer.from(e,t))}catch(e){}}else{try{n.set(s,new TextDecoder(s));return n.get(s).decode(Buffer.from(e,t))}catch(e){}}}return e}e.exports=decodeText},9692:e=>{"use strict";e.exports=function getLimit(e,t,n){if(!e||e[t]===undefined||e[t]===null){return n}if(typeof e[t]!=="number"||isNaN(e[t])){throw new TypeError("Limit "+t+" is not a valid number")}return e[t]}},4426:(e,t,n)=>{"use strict";const s=n(9136);const i=/%([a-fA-F0-9]{2})/g;function encodedReplacer(e,t){return String.fromCharCode(parseInt(t,16))}function parseParams(e){const t=[];let n="key";let r="";let o=false;let A=false;let a=0;let c="";for(var u=0,l=e.length;u{e.exports={parallel:n(8210),serial:n(445),serialOrdered:n(3578)}},1700:e=>{e.exports=abort;function abort(e){Object.keys(e.jobs).forEach(clean.bind(e));e.jobs={}}function clean(e){if(typeof this.jobs[e]=="function"){this.jobs[e]()}}},2794:(e,t,n)=>{var s=n(5295);e.exports=async;function async(e){var t=false;s((function(){t=true}));return function async_callback(n,i){if(t){e(n,i)}else{s((function nextTick_callback(){e(n,i)}))}}}},5295:e=>{e.exports=defer;function defer(e){var t=typeof setImmediate=="function"?setImmediate:typeof process=="object"&&typeof process.nextTick=="function"?process.nextTick:null;if(t){t(e)}else{setTimeout(e,0)}}},9023:(e,t,n)=>{var s=n(2794),i=n(1700);e.exports=iterate;function iterate(e,t,n,s){var r=n["keyedList"]?n["keyedList"][n.index]:n.index;n.jobs[r]=runJob(t,r,e[r],(function(e,t){if(!(r in n.jobs)){return}delete n.jobs[r];if(e){i(n)}else{n.results[r]=t}s(e,n.results)}))}function runJob(e,t,n,i){var r;if(e.length==2){r=e(n,s(i))}else{r=e(n,t,s(i))}return r}},2474:e=>{e.exports=state;function state(e,t){var n=!Array.isArray(e),s={index:0,keyedList:n||t?Object.keys(e):null,jobs:{},results:n?{}:[],size:n?Object.keys(e).length:e.length};if(t){s.keyedList.sort(n?t:function(n,s){return t(e[n],e[s])})}return s}},7942:(e,t,n)=>{var s=n(1700),i=n(2794);e.exports=terminator;function terminator(e){if(!Object.keys(this.jobs).length){return}this.index=this.size;s(this);i(e)(null,this.results)}},8210:(e,t,n)=>{var s=n(9023),i=n(2474),r=n(7942);e.exports=parallel;function parallel(e,t,n){var o=i(e);while(o.index<(o["keyedList"]||e).length){s(e,t,o,(function(e,t){if(e){n(e,t);return}if(Object.keys(o.jobs).length===0){n(null,o.results);return}}));o.index++}return r.bind(o,n)}},445:(e,t,n)=>{var s=n(3578);e.exports=serial;function serial(e,t,n){return s(e,t,null,n)}},3578:(e,t,n)=>{var s=n(9023),i=n(2474),r=n(7942);e.exports=serialOrdered;e.exports.ascending=ascending;e.exports.descending=descending;function serialOrdered(e,t,n,o){var A=i(e,n);s(e,t,A,(function iteratorHandler(n,i){if(n){o(n,i);return}A.index++;if(A.index<(A["keyedList"]||e).length){s(e,t,A,iteratorHandler);return}o(null,A.results)}));return r.bind(A,o)}function ascending(e,t){return et?1:0}function descending(e,t){return-1*ascending(e,t)}},6008:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});Object.defineProperty(t,"NIL",{enumerable:true,get:function(){return A.default}});Object.defineProperty(t,"parse",{enumerable:true,get:function(){return l.default}});Object.defineProperty(t,"stringify",{enumerable:true,get:function(){return u.default}});Object.defineProperty(t,"v1",{enumerable:true,get:function(){return s.default}});Object.defineProperty(t,"v3",{enumerable:true,get:function(){return i.default}});Object.defineProperty(t,"v4",{enumerable:true,get:function(){return r.default}});Object.defineProperty(t,"v5",{enumerable:true,get:function(){return o.default}});Object.defineProperty(t,"validate",{enumerable:true,get:function(){return c.default}});Object.defineProperty(t,"version",{enumerable:true,get:function(){return a.default}});var s=_interopRequireDefault(n(272));var i=_interopRequireDefault(n(4867));var r=_interopRequireDefault(n(1537));var o=_interopRequireDefault(n(1453));var A=_interopRequireDefault(n(588));var a=_interopRequireDefault(n(5440));var c=_interopRequireDefault(n(2092));var u=_interopRequireDefault(n(61));var l=_interopRequireDefault(n(1855));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}},1774:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6113));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function md5(e){if(Array.isArray(e)){e=Buffer.from(e)}else if(typeof e==="string"){e=Buffer.from(e,"utf8")}return s.default.createHash("md5").update(e).digest()}var i=md5;t["default"]=i},5056:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6113));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var i={randomUUID:s.default.randomUUID};t["default"]=i},588:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var n="00000000-0000-0000-0000-000000000000";t["default"]=n},1855:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(2092));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function parse(e){if(!(0,s.default)(e)){throw TypeError("Invalid UUID")}let t;const n=new Uint8Array(16);n[0]=(t=parseInt(e.slice(0,8),16))>>>24;n[1]=t>>>16&255;n[2]=t>>>8&255;n[3]=t&255;n[4]=(t=parseInt(e.slice(9,13),16))>>>8;n[5]=t&255;n[6]=(t=parseInt(e.slice(14,18),16))>>>8;n[7]=t&255;n[8]=(t=parseInt(e.slice(19,23),16))>>>8;n[9]=t&255;n[10]=(t=parseInt(e.slice(24,36),16))/1099511627776&255;n[11]=t/4294967296&255;n[12]=t>>>24&255;n[13]=t>>>16&255;n[14]=t>>>8&255;n[15]=t&255;return n}var i=parse;t["default"]=i},2822:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var n=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;t["default"]=n},2378:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=rng;var s=_interopRequireDefault(n(6113));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const i=new Uint8Array(256);let r=i.length;function rng(){if(r>i.length-16){s.default.randomFillSync(i);r=0}return i.slice(r,r+=16)}},2732:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6113));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function sha1(e){if(Array.isArray(e)){e=Buffer.from(e)}else if(typeof e==="string"){e=Buffer.from(e,"utf8")}return s.default.createHash("sha1").update(e).digest()}var i=sha1;t["default"]=i},61:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;t.unsafeStringify=unsafeStringify;var s=_interopRequireDefault(n(2092));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const i=[];for(let e=0;e<256;++e){i.push((e+256).toString(16).slice(1))}function unsafeStringify(e,t=0){return i[e[t+0]]+i[e[t+1]]+i[e[t+2]]+i[e[t+3]]+"-"+i[e[t+4]]+i[e[t+5]]+"-"+i[e[t+6]]+i[e[t+7]]+"-"+i[e[t+8]]+i[e[t+9]]+"-"+i[e[t+10]]+i[e[t+11]]+i[e[t+12]]+i[e[t+13]]+i[e[t+14]]+i[e[t+15]]}function stringify(e,t=0){const n=unsafeStringify(e,t);if(!(0,s.default)(n)){throw TypeError("Stringified UUID is invalid")}return n}var r=stringify;t["default"]=r},272:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(2378));var i=n(61);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}let r;let o;let A=0;let a=0;function v1(e,t,n){let c=t&&n||0;const u=t||new Array(16);e=e||{};let l=e.node||r;let d=e.clockseq!==undefined?e.clockseq:o;if(l==null||d==null){const t=e.random||(e.rng||s.default)();if(l==null){l=r=[t[0]|1,t[1],t[2],t[3],t[4],t[5]]}if(d==null){d=o=(t[6]<<8|t[7])&16383}}let p=e.msecs!==undefined?e.msecs:Date.now();let g=e.nsecs!==undefined?e.nsecs:a+1;const h=p-A+(g-a)/1e4;if(h<0&&e.clockseq===undefined){d=d+1&16383}if((h<0||p>A)&&e.nsecs===undefined){g=0}if(g>=1e4){throw new Error("uuid.v1(): Can't create more than 10M uuids/sec")}A=p;a=g;o=d;p+=122192928e5;const f=((p&268435455)*1e4+g)%4294967296;u[c++]=f>>>24&255;u[c++]=f>>>16&255;u[c++]=f>>>8&255;u[c++]=f&255;const E=p/4294967296*1e4&268435455;u[c++]=E>>>8&255;u[c++]=E&255;u[c++]=E>>>24&15|16;u[c++]=E>>>16&255;u[c++]=d>>>8|128;u[c++]=d&255;for(let e=0;e<6;++e){u[c+e]=l[e]}return t||(0,i.unsafeStringify)(u)}var c=v1;t["default"]=c},4867:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6222));var i=_interopRequireDefault(n(1774));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const r=(0,s.default)("v3",48,i.default);var o=r;t["default"]=o},6222:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.URL=t.DNS=void 0;t["default"]=v35;var s=n(61);var i=_interopRequireDefault(n(1855));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function stringToBytes(e){e=unescape(encodeURIComponent(e));const t=[];for(let n=0;n{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(5056));var i=_interopRequireDefault(n(2378));var r=n(61);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function v4(e,t,n){if(s.default.randomUUID&&!t&&!e){return s.default.randomUUID()}e=e||{};const o=e.random||(e.rng||i.default)();o[6]=o[6]&15|64;o[8]=o[8]&63|128;if(t){n=n||0;for(let e=0;e<16;++e){t[n+e]=o[e]}return t}return(0,r.unsafeStringify)(o)}var o=v4;t["default"]=o},1453:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6222));var i=_interopRequireDefault(n(2732));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const r=(0,s.default)("v5",80,i.default);var o=r;t["default"]=o},2092:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(2822));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function validate(e){return typeof e==="string"&&s.default.test(e)}var i=validate;t["default"]=i},5440:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(2092));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function version(e){if(!(0,s.default)(e)){throw TypeError("Invalid UUID")}return parseInt(e.slice(14,15),16)}var i=version;t["default"]=i},5443:(e,t,n)=>{var s=n(3837);var i=n(2781).Stream;var r=n(8611);e.exports=CombinedStream;function CombinedStream(){this.writable=false;this.readable=true;this.dataSize=0;this.maxDataSize=2*1024*1024;this.pauseStreams=true;this._released=false;this._streams=[];this._currentStream=null;this._insideLoop=false;this._pendingNext=false}s.inherits(CombinedStream,i);CombinedStream.create=function(e){var t=new this;e=e||{};for(var n in e){t[n]=e[n]}return t};CombinedStream.isStreamLike=function(e){return typeof e!=="function"&&typeof e!=="string"&&typeof e!=="boolean"&&typeof e!=="number"&&!Buffer.isBuffer(e)};CombinedStream.prototype.append=function(e){var t=CombinedStream.isStreamLike(e);if(t){if(!(e instanceof r)){var n=r.create(e,{maxDataSize:Infinity,pauseStream:this.pauseStreams});e.on("data",this._checkDataSize.bind(this));e=n}this._handleErrors(e);if(this.pauseStreams){e.pause()}}this._streams.push(e);return this};CombinedStream.prototype.pipe=function(e,t){i.prototype.pipe.call(this,e,t);this.resume();return e};CombinedStream.prototype._getNext=function(){this._currentStream=null;if(this._insideLoop){this._pendingNext=true;return}this._insideLoop=true;try{do{this._pendingNext=false;this._realGetNext()}while(this._pendingNext)}finally{this._insideLoop=false}};CombinedStream.prototype._realGetNext=function(){var e=this._streams.shift();if(typeof e=="undefined"){this.end();return}if(typeof e!=="function"){this._pipeNext(e);return}var t=e;t(function(e){var t=CombinedStream.isStreamLike(e);if(t){e.on("data",this._checkDataSize.bind(this));this._handleErrors(e)}this._pipeNext(e)}.bind(this))};CombinedStream.prototype._pipeNext=function(e){this._currentStream=e;var t=CombinedStream.isStreamLike(e);if(t){e.on("end",this._getNext.bind(this));e.pipe(this,{end:false});return}var n=e;this.write(n);this._getNext()};CombinedStream.prototype._handleErrors=function(e){var t=this;e.on("error",(function(e){t._emitError(e)}))};CombinedStream.prototype.write=function(e){this.emit("data",e)};CombinedStream.prototype.pause=function(){if(!this.pauseStreams){return}if(this.pauseStreams&&this._currentStream&&typeof this._currentStream.pause=="function")this._currentStream.pause();this.emit("pause")};CombinedStream.prototype.resume=function(){if(!this._released){this._released=true;this.writable=true;this._getNext()}if(this.pauseStreams&&this._currentStream&&typeof this._currentStream.resume=="function")this._currentStream.resume();this.emit("resume")};CombinedStream.prototype.end=function(){this._reset();this.emit("end")};CombinedStream.prototype.destroy=function(){this._reset();this.emit("close")};CombinedStream.prototype._reset=function(){this.writable=false;this._streams=[];this._currentStream=null};CombinedStream.prototype._checkDataSize=function(){this._updateDataSize();if(this.dataSize<=this.maxDataSize){return}var e="DelayedStream#maxDataSize of "+this.maxDataSize+" bytes exceeded.";this._emitError(new Error(e))};CombinedStream.prototype._updateDataSize=function(){this.dataSize=0;var e=this;this._streams.forEach((function(t){if(!t.dataSize){return}e.dataSize+=t.dataSize}));if(this._currentStream&&this._currentStream.dataSize){this.dataSize+=this._currentStream.dataSize}};CombinedStream.prototype._emitError=function(e){this._reset();this.emit("error",e)}},8611:(e,t,n)=>{var s=n(2781).Stream;var i=n(3837);e.exports=DelayedStream;function DelayedStream(){this.source=null;this.dataSize=0;this.maxDataSize=1024*1024;this.pauseStream=true;this._maxDataSizeExceeded=false;this._released=false;this._bufferedEvents=[]}i.inherits(DelayedStream,s);DelayedStream.create=function(e,t){var n=new this;t=t||{};for(var s in t){n[s]=t[s]}n.source=e;var i=e.emit;e.emit=function(){n._handleEmit(arguments);return i.apply(e,arguments)};e.on("error",(function(){}));if(n.pauseStream){e.pause()}return n};Object.defineProperty(DelayedStream.prototype,"readable",{configurable:true,enumerable:true,get:function(){return this.source.readable}});DelayedStream.prototype.setEncoding=function(){return this.source.setEncoding.apply(this.source,arguments)};DelayedStream.prototype.resume=function(){if(!this._released){this.release()}this.source.resume()};DelayedStream.prototype.pause=function(){this.source.pause()};DelayedStream.prototype.release=function(){this._released=true;this._bufferedEvents.forEach(function(e){this.emit.apply(this,e)}.bind(this));this._bufferedEvents=[]};DelayedStream.prototype.pipe=function(){var e=s.prototype.pipe.apply(this,arguments);this.resume();return e};DelayedStream.prototype._handleEmit=function(e){if(this._released){this.emit.apply(this,e);return}if(e[0]==="data"){this.dataSize+=e[1].length;this._checkIfMaxDataSizeExceeded()}this._bufferedEvents.push(e)};DelayedStream.prototype._checkIfMaxDataSizeExceeded=function(){if(this._maxDataSizeExceeded){return}if(this.dataSize<=this.maxDataSize){return}this._maxDataSizeExceeded=true;var e="DelayedStream#maxDataSize of "+this.maxDataSize+" bytes exceeded.";this.emit("error",new Error(e))}},1133:(e,t,n)=>{var s;e.exports=function(){if(!s){try{s=n(6167)("follow-redirects")}catch(e){}if(typeof s!=="function"){s=function(){}}}s.apply(null,arguments)}},7707:(e,t,n)=>{var s=n(7310);var i=s.URL;var r=n(3685);var o=n(5687);var A=n(2781).Writable;var a=n(9491);var c=n(1133);var u=["abort","aborted","connect","error","socket","timeout"];var l=Object.create(null);u.forEach((function(e){l[e]=function(t,n,s){this._redirectable.emit(e,t,n,s)}}));var d=createErrorType("ERR_INVALID_URL","Invalid URL",TypeError);var p=createErrorType("ERR_FR_REDIRECTION_FAILURE","Redirected request failed");var g=createErrorType("ERR_FR_TOO_MANY_REDIRECTS","Maximum number of redirects exceeded");var h=createErrorType("ERR_FR_MAX_BODY_LENGTH_EXCEEDED","Request body larger than maxBodyLength limit");var f=createErrorType("ERR_STREAM_WRITE_AFTER_END","write after end");var E=A.prototype.destroy||noop;function RedirectableRequest(e,t){A.call(this);this._sanitizeOptions(e);this._options=e;this._ended=false;this._ending=false;this._redirectCount=0;this._redirects=[];this._requestBodyLength=0;this._requestBodyBuffers=[];if(t){this.on("response",t)}var n=this;this._onNativeResponse=function(e){n._processResponse(e)};this._performRequest()}RedirectableRequest.prototype=Object.create(A.prototype);RedirectableRequest.prototype.abort=function(){destroyRequest(this._currentRequest);this._currentRequest.abort();this.emit("abort")};RedirectableRequest.prototype.destroy=function(e){destroyRequest(this._currentRequest,e);E.call(this,e);return this};RedirectableRequest.prototype.write=function(e,t,n){if(this._ending){throw new f}if(!isString(e)&&!isBuffer(e)){throw new TypeError("data should be a string, Buffer or Uint8Array")}if(isFunction(t)){n=t;t=null}if(e.length===0){if(n){n()}return}if(this._requestBodyLength+e.length<=this._options.maxBodyLength){this._requestBodyLength+=e.length;this._requestBodyBuffers.push({data:e,encoding:t});this._currentRequest.write(e,t,n)}else{this.emit("error",new h);this.abort()}};RedirectableRequest.prototype.end=function(e,t,n){if(isFunction(e)){n=e;e=t=null}else if(isFunction(t)){n=t;t=null}if(!e){this._ended=this._ending=true;this._currentRequest.end(null,null,n)}else{var s=this;var i=this._currentRequest;this.write(e,t,(function(){s._ended=true;i.end(null,null,n)}));this._ending=true}};RedirectableRequest.prototype.setHeader=function(e,t){this._options.headers[e]=t;this._currentRequest.setHeader(e,t)};RedirectableRequest.prototype.removeHeader=function(e){delete this._options.headers[e];this._currentRequest.removeHeader(e)};RedirectableRequest.prototype.setTimeout=function(e,t){var n=this;function destroyOnTimeout(t){t.setTimeout(e);t.removeListener("timeout",t.destroy);t.addListener("timeout",t.destroy)}function startTimer(t){if(n._timeout){clearTimeout(n._timeout)}n._timeout=setTimeout((function(){n.emit("timeout");clearTimer()}),e);destroyOnTimeout(t)}function clearTimer(){if(n._timeout){clearTimeout(n._timeout);n._timeout=null}n.removeListener("abort",clearTimer);n.removeListener("error",clearTimer);n.removeListener("response",clearTimer);n.removeListener("close",clearTimer);if(t){n.removeListener("timeout",t)}if(!n.socket){n._currentRequest.removeListener("socket",startTimer)}}if(t){this.on("timeout",t)}if(this.socket){startTimer(this.socket)}else{this._currentRequest.once("socket",startTimer)}this.on("socket",destroyOnTimeout);this.on("abort",clearTimer);this.on("error",clearTimer);this.on("response",clearTimer);this.on("close",clearTimer);return this};["flushHeaders","getHeader","setNoDelay","setSocketKeepAlive"].forEach((function(e){RedirectableRequest.prototype[e]=function(t,n){return this._currentRequest[e](t,n)}}));["aborted","connection","socket"].forEach((function(e){Object.defineProperty(RedirectableRequest.prototype,e,{get:function(){return this._currentRequest[e]}})}));RedirectableRequest.prototype._sanitizeOptions=function(e){if(!e.headers){e.headers={}}if(e.host){if(!e.hostname){e.hostname=e.host}delete e.host}if(!e.pathname&&e.path){var t=e.path.indexOf("?");if(t<0){e.pathname=e.path}else{e.pathname=e.path.substring(0,t);e.search=e.path.substring(t)}}};RedirectableRequest.prototype._performRequest=function(){var e=this._options.protocol;var t=this._options.nativeProtocols[e];if(!t){this.emit("error",new TypeError("Unsupported protocol "+e));return}if(this._options.agents){var n=e.slice(0,-1);this._options.agent=this._options.agents[n]}var i=this._currentRequest=t.request(this._options,this._onNativeResponse);i._redirectable=this;for(var r of u){i.on(r,l[r])}this._currentUrl=/^\//.test(this._options.path)?s.format(this._options):this._options.path;if(this._isRedirect){var o=0;var A=this;var a=this._requestBodyBuffers;(function writeNext(e){if(i===A._currentRequest){if(e){A.emit("error",e)}else if(o=400){e.responseUrl=this._currentUrl;e.redirects=this._redirects;this.emit("response",e);this._requestBodyBuffers=[];return}destroyRequest(this._currentRequest);e.destroy();if(++this._redirectCount>this._options.maxRedirects){this.emit("error",new g);return}var i;var r=this._options.beforeRedirect;if(r){i=Object.assign({Host:e.req.getHeader("host")},this._options.headers)}var o=this._options.method;if((t===301||t===302)&&this._options.method==="POST"||t===303&&!/^(?:GET|HEAD)$/.test(this._options.method)){this._options.method="GET";this._requestBodyBuffers=[];removeMatchingHeaders(/^content-/i,this._options.headers)}var A=removeMatchingHeaders(/^host$/i,this._options.headers);var a=s.parse(this._currentUrl);var u=A||a.host;var l=/^\w+:/.test(n)?this._currentUrl:s.format(Object.assign(a,{host:u}));var d;try{d=s.resolve(l,n)}catch(e){this.emit("error",new p({cause:e}));return}c("redirecting to",d);this._isRedirect=true;var h=s.parse(d);Object.assign(this._options,h);if(h.protocol!==a.protocol&&h.protocol!=="https:"||h.host!==u&&!isSubdomain(h.host,u)){removeMatchingHeaders(/^(?:authorization|cookie)$/i,this._options.headers)}if(isFunction(r)){var f={headers:e.headers,statusCode:t};var E={url:l,method:o,headers:i};try{r(this._options,f,E)}catch(e){this.emit("error",e);return}this._sanitizeOptions(this._options)}try{this._performRequest()}catch(e){this.emit("error",new p({cause:e}))}};function wrap(e){var t={maxRedirects:21,maxBodyLength:10*1024*1024};var n={};Object.keys(e).forEach((function(r){var o=r+":";var A=n[o]=e[r];var u=t[r]=Object.create(A);function request(e,r,A){if(isString(e)){var u;try{u=urlToOptions(new i(e))}catch(t){u=s.parse(e)}if(!isString(u.protocol)){throw new d({input:e})}e=u}else if(i&&e instanceof i){e=urlToOptions(e)}else{A=r;r=e;e={protocol:o}}if(isFunction(r)){A=r;r=null}r=Object.assign({maxRedirects:t.maxRedirects,maxBodyLength:t.maxBodyLength},e,r);r.nativeProtocols=n;if(!isString(r.host)&&!isString(r.hostname)){r.hostname="::1"}a.equal(r.protocol,o,"protocol mismatch");c("options",r);return new RedirectableRequest(r,A)}function get(e,t,n){var s=u.request(e,t,n);s.end();return s}Object.defineProperties(u,{request:{value:request,configurable:true,enumerable:true,writable:true},get:{value:get,configurable:true,enumerable:true,writable:true}})}));return t}function noop(){}function urlToOptions(e){var t={protocol:e.protocol,hostname:e.hostname.startsWith("[")?e.hostname.slice(1,-1):e.hostname,hash:e.hash,search:e.search,pathname:e.pathname,path:e.pathname+e.search,href:e.href};if(e.port!==""){t.port=Number(e.port)}return t}function removeMatchingHeaders(e,t){var n;for(var s in t){if(e.test(s)){n=t[s];delete t[s]}}return n===null||typeof n==="undefined"?undefined:String(n).trim()}function createErrorType(e,t,n){function CustomError(n){Error.captureStackTrace(this,this.constructor);Object.assign(this,n||{});this.code=e;this.message=this.cause?t+": "+this.cause.message:t}CustomError.prototype=new(n||Error);CustomError.prototype.constructor=CustomError;CustomError.prototype.name="Error ["+e+"]";return CustomError}function destroyRequest(e,t){for(var n of u){e.removeListener(n,l[n])}e.on("error",noop);e.destroy(t)}function isSubdomain(e,t){a(isString(e)&&isString(t));var n=e.length-t.length-1;return n>0&&e[n]==="."&&e.endsWith(t)}function isString(e){return typeof e==="string"||e instanceof String}function isFunction(e){return typeof e==="function"}function isBuffer(e){return typeof e==="object"&&"length"in e}e.exports=wrap({http:r,https:o});e.exports.wrap=wrap},4334:(e,t,n)=>{var s=n(5443);var i=n(3837);var r=n(1017);var o=n(3685);var A=n(5687);var a=n(7310).parse;var c=n(7147);var u=n(2781).Stream;var l=n(3583);var d=n(4812);var p=n(7142);e.exports=FormData;i.inherits(FormData,s);function FormData(e){if(!(this instanceof FormData)){return new FormData(e)}this._overheadLength=0;this._valueLength=0;this._valuesToMeasure=[];s.call(this);e=e||{};for(var t in e){this[t]=e[t]}}FormData.LINE_BREAK="\r\n";FormData.DEFAULT_CONTENT_TYPE="application/octet-stream";FormData.prototype.append=function(e,t,n){n=n||{};if(typeof n=="string"){n={filename:n}}var r=s.prototype.append.bind(this);if(typeof t=="number"){t=""+t}if(i.isArray(t)){this._error(new Error("Arrays are not supported."));return}var o=this._multiPartHeader(e,t,n);var A=this._multiPartFooter();r(o);r(t);r(A);this._trackLength(o,t,n)};FormData.prototype._trackLength=function(e,t,n){var s=0;if(n.knownLength!=null){s+=+n.knownLength}else if(Buffer.isBuffer(t)){s=t.length}else if(typeof t==="string"){s=Buffer.byteLength(t)}this._valueLength+=s;this._overheadLength+=Buffer.byteLength(e)+FormData.LINE_BREAK.length;if(!t||!t.path&&!(t.readable&&t.hasOwnProperty("httpVersion"))&&!(t instanceof u)){return}if(!n.knownLength){this._valuesToMeasure.push(t)}};FormData.prototype._lengthRetriever=function(e,t){if(e.hasOwnProperty("fd")){if(e.end!=undefined&&e.end!=Infinity&&e.start!=undefined){t(null,e.end+1-(e.start?e.start:0))}else{c.stat(e.path,(function(n,s){var i;if(n){t(n);return}i=s.size-(e.start?e.start:0);t(null,i)}))}}else if(e.hasOwnProperty("httpVersion")){t(null,+e.headers["content-length"])}else if(e.hasOwnProperty("httpModule")){e.on("response",(function(n){e.pause();t(null,+n.headers["content-length"])}));e.resume()}else{t("Unknown stream")}};FormData.prototype._multiPartHeader=function(e,t,n){if(typeof n.header=="string"){return n.header}var s=this._getContentDisposition(t,n);var i=this._getContentType(t,n);var r="";var o={"Content-Disposition":["form-data",'name="'+e+'"'].concat(s||[]),"Content-Type":[].concat(i||[])};if(typeof n.header=="object"){p(o,n.header)}var A;for(var a in o){if(!o.hasOwnProperty(a))continue;A=o[a];if(A==null){continue}if(!Array.isArray(A)){A=[A]}if(A.length){r+=a+": "+A.join("; ")+FormData.LINE_BREAK}}return"--"+this.getBoundary()+FormData.LINE_BREAK+r+FormData.LINE_BREAK};FormData.prototype._getContentDisposition=function(e,t){var n,s;if(typeof t.filepath==="string"){n=r.normalize(t.filepath).replace(/\\/g,"/")}else if(t.filename||e.name||e.path){n=r.basename(t.filename||e.name||e.path)}else if(e.readable&&e.hasOwnProperty("httpVersion")){n=r.basename(e.client._httpMessage.path||"")}if(n){s='filename="'+n+'"'}return s};FormData.prototype._getContentType=function(e,t){var n=t.contentType;if(!n&&e.name){n=l.lookup(e.name)}if(!n&&e.path){n=l.lookup(e.path)}if(!n&&e.readable&&e.hasOwnProperty("httpVersion")){n=e.headers["content-type"]}if(!n&&(t.filepath||t.filename)){n=l.lookup(t.filepath||t.filename)}if(!n&&typeof e=="object"){n=FormData.DEFAULT_CONTENT_TYPE}return n};FormData.prototype._multiPartFooter=function(){return function(e){var t=FormData.LINE_BREAK;var n=this._streams.length===0;if(n){t+=this._lastBoundary()}e(t)}.bind(this)};FormData.prototype._lastBoundary=function(){return"--"+this.getBoundary()+"--"+FormData.LINE_BREAK};FormData.prototype.getHeaders=function(e){var t;var n={"content-type":"multipart/form-data; boundary="+this.getBoundary()};for(t in e){if(e.hasOwnProperty(t)){n[t.toLowerCase()]=e[t]}}return n};FormData.prototype.setBoundary=function(e){this._boundary=e};FormData.prototype.getBoundary=function(){if(!this._boundary){this._generateBoundary()}return this._boundary};FormData.prototype.getBuffer=function(){var e=new Buffer.alloc(0);var t=this.getBoundary();for(var n=0,s=this._streams.length;n{e.exports=function(e,t){Object.keys(t).forEach((function(n){e[n]=e[n]||t[n]}));return e}},4061:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.cryptoRuntime=t.base64url=t.generateSecret=t.generateKeyPair=t.errors=t.decodeJwt=t.decodeProtectedHeader=t.importJWK=t.importX509=t.importPKCS8=t.importSPKI=t.exportJWK=t.exportSPKI=t.exportPKCS8=t.UnsecuredJWT=t.createRemoteJWKSet=t.createLocalJWKSet=t.EmbeddedJWK=t.calculateJwkThumbprintUri=t.calculateJwkThumbprint=t.EncryptJWT=t.SignJWT=t.GeneralSign=t.FlattenedSign=t.CompactSign=t.FlattenedEncrypt=t.CompactEncrypt=t.jwtDecrypt=t.jwtVerify=t.generalVerify=t.flattenedVerify=t.compactVerify=t.GeneralEncrypt=t.generalDecrypt=t.flattenedDecrypt=t.compactDecrypt=void 0;var s=n(7651);Object.defineProperty(t,"compactDecrypt",{enumerable:true,get:function(){return s.compactDecrypt}});var i=n(7566);Object.defineProperty(t,"flattenedDecrypt",{enumerable:true,get:function(){return i.flattenedDecrypt}});var r=n(5684);Object.defineProperty(t,"generalDecrypt",{enumerable:true,get:function(){return r.generalDecrypt}});var o=n(3992);Object.defineProperty(t,"GeneralEncrypt",{enumerable:true,get:function(){return o.GeneralEncrypt}});var A=n(5212);Object.defineProperty(t,"compactVerify",{enumerable:true,get:function(){return A.compactVerify}});var a=n(2095);Object.defineProperty(t,"flattenedVerify",{enumerable:true,get:function(){return a.flattenedVerify}});var c=n(4975);Object.defineProperty(t,"generalVerify",{enumerable:true,get:function(){return c.generalVerify}});var u=n(9887);Object.defineProperty(t,"jwtVerify",{enumerable:true,get:function(){return u.jwtVerify}});var l=n(3378);Object.defineProperty(t,"jwtDecrypt",{enumerable:true,get:function(){return l.jwtDecrypt}});var d=n(6203);Object.defineProperty(t,"CompactEncrypt",{enumerable:true,get:function(){return d.CompactEncrypt}});var p=n(1555);Object.defineProperty(t,"FlattenedEncrypt",{enumerable:true,get:function(){return p.FlattenedEncrypt}});var g=n(8257);Object.defineProperty(t,"CompactSign",{enumerable:true,get:function(){return g.CompactSign}});var h=n(4825);Object.defineProperty(t,"FlattenedSign",{enumerable:true,get:function(){return h.FlattenedSign}});var f=n(4268);Object.defineProperty(t,"GeneralSign",{enumerable:true,get:function(){return f.GeneralSign}});var E=n(8882);Object.defineProperty(t,"SignJWT",{enumerable:true,get:function(){return E.SignJWT}});var m=n(960);Object.defineProperty(t,"EncryptJWT",{enumerable:true,get:function(){return m.EncryptJWT}});var C=n(3494);Object.defineProperty(t,"calculateJwkThumbprint",{enumerable:true,get:function(){return C.calculateJwkThumbprint}});Object.defineProperty(t,"calculateJwkThumbprintUri",{enumerable:true,get:function(){return C.calculateJwkThumbprintUri}});var Q=n(1751);Object.defineProperty(t,"EmbeddedJWK",{enumerable:true,get:function(){return Q.EmbeddedJWK}});var I=n(9970);Object.defineProperty(t,"createLocalJWKSet",{enumerable:true,get:function(){return I.createLocalJWKSet}});var B=n(9035);Object.defineProperty(t,"createRemoteJWKSet",{enumerable:true,get:function(){return B.createRemoteJWKSet}});var y=n(8568);Object.defineProperty(t,"UnsecuredJWT",{enumerable:true,get:function(){return y.UnsecuredJWT}});var b=n(465);Object.defineProperty(t,"exportPKCS8",{enumerable:true,get:function(){return b.exportPKCS8}});Object.defineProperty(t,"exportSPKI",{enumerable:true,get:function(){return b.exportSPKI}});Object.defineProperty(t,"exportJWK",{enumerable:true,get:function(){return b.exportJWK}});var w=n(4230);Object.defineProperty(t,"importSPKI",{enumerable:true,get:function(){return w.importSPKI}});Object.defineProperty(t,"importPKCS8",{enumerable:true,get:function(){return w.importPKCS8}});Object.defineProperty(t,"importX509",{enumerable:true,get:function(){return w.importX509}});Object.defineProperty(t,"importJWK",{enumerable:true,get:function(){return w.importJWK}});var R=n(3991);Object.defineProperty(t,"decodeProtectedHeader",{enumerable:true,get:function(){return R.decodeProtectedHeader}});var v=n(5611);Object.defineProperty(t,"decodeJwt",{enumerable:true,get:function(){return v.decodeJwt}});t.errors=n(4419);var k=n(1036);Object.defineProperty(t,"generateKeyPair",{enumerable:true,get:function(){return k.generateKeyPair}});var S=n(6617);Object.defineProperty(t,"generateSecret",{enumerable:true,get:function(){return S.generateSecret}});t.base64url=n(3238);var x=n(1173);Object.defineProperty(t,"cryptoRuntime",{enumerable:true,get:function(){return x.default}})},7651:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.compactDecrypt=void 0;const s=n(7566);const i=n(4419);const r=n(1691);async function compactDecrypt(e,t,n){if(e instanceof Uint8Array){e=r.decoder.decode(e)}if(typeof e!=="string"){throw new i.JWEInvalid("Compact JWE must be a string or Uint8Array")}const{0:o,1:A,2:a,3:c,4:u,length:l}=e.split(".");if(l!==5){throw new i.JWEInvalid("Invalid Compact JWE")}const d=await(0,s.flattenedDecrypt)({ciphertext:c,iv:a||undefined,protected:o||undefined,tag:u||undefined,encrypted_key:A||undefined},t,n);const p={plaintext:d.plaintext,protectedHeader:d.protectedHeader};if(typeof t==="function"){return{...p,key:d.key}}return p}t.compactDecrypt=compactDecrypt},6203:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.CompactEncrypt=void 0;const s=n(1555);class CompactEncrypt{constructor(e){this._flattened=new s.FlattenedEncrypt(e)}setContentEncryptionKey(e){this._flattened.setContentEncryptionKey(e);return this}setInitializationVector(e){this._flattened.setInitializationVector(e);return this}setProtectedHeader(e){this._flattened.setProtectedHeader(e);return this}setKeyManagementParameters(e){this._flattened.setKeyManagementParameters(e);return this}async encrypt(e,t){const n=await this._flattened.encrypt(e,t);return[n.protected,n.encrypted_key,n.iv,n.ciphertext,n.tag].join(".")}}t.CompactEncrypt=CompactEncrypt},7566:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.flattenedDecrypt=void 0;const s=n(518);const i=n(6137);const r=n(7022);const o=n(4419);const A=n(6063);const a=n(9127);const c=n(6127);const u=n(1691);const l=n(3987);const d=n(863);const p=n(5148);async function flattenedDecrypt(e,t,n){var g;if(!(0,a.default)(e)){throw new o.JWEInvalid("Flattened JWE must be an object")}if(e.protected===undefined&&e.header===undefined&&e.unprotected===undefined){throw new o.JWEInvalid("JOSE Header missing")}if(typeof e.iv!=="string"){throw new o.JWEInvalid("JWE Initialization Vector missing or incorrect type")}if(typeof e.ciphertext!=="string"){throw new o.JWEInvalid("JWE Ciphertext missing or incorrect type")}if(typeof e.tag!=="string"){throw new o.JWEInvalid("JWE Authentication Tag missing or incorrect type")}if(e.protected!==undefined&&typeof e.protected!=="string"){throw new o.JWEInvalid("JWE Protected Header incorrect type")}if(e.encrypted_key!==undefined&&typeof e.encrypted_key!=="string"){throw new o.JWEInvalid("JWE Encrypted Key incorrect type")}if(e.aad!==undefined&&typeof e.aad!=="string"){throw new o.JWEInvalid("JWE AAD incorrect type")}if(e.header!==undefined&&!(0,a.default)(e.header)){throw new o.JWEInvalid("JWE Shared Unprotected Header incorrect type")}if(e.unprotected!==undefined&&!(0,a.default)(e.unprotected)){throw new o.JWEInvalid("JWE Per-Recipient Unprotected Header incorrect type")}let h;if(e.protected){try{const t=(0,s.decode)(e.protected);h=JSON.parse(u.decoder.decode(t))}catch{throw new o.JWEInvalid("JWE Protected Header is invalid")}}if(!(0,A.default)(h,e.header,e.unprotected)){throw new o.JWEInvalid("JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint")}const f={...h,...e.header,...e.unprotected};(0,d.default)(o.JWEInvalid,new Map,n===null||n===void 0?void 0:n.crit,h,f);if(f.zip!==undefined){if(!h||!h.zip){throw new o.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected')}if(f.zip!=="DEF"){throw new o.JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value')}}const{alg:E,enc:m}=f;if(typeof E!=="string"||!E){throw new o.JWEInvalid("missing JWE Algorithm (alg) in JWE Header")}if(typeof m!=="string"||!m){throw new o.JWEInvalid("missing JWE Encryption Algorithm (enc) in JWE Header")}const C=n&&(0,p.default)("keyManagementAlgorithms",n.keyManagementAlgorithms);const Q=n&&(0,p.default)("contentEncryptionAlgorithms",n.contentEncryptionAlgorithms);if(C&&!C.has(E)){throw new o.JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed')}if(Q&&!Q.has(m)){throw new o.JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter not allowed')}let I;if(e.encrypted_key!==undefined){try{I=(0,s.decode)(e.encrypted_key)}catch{throw new o.JWEInvalid("Failed to base64url decode the encrypted_key")}}let B=false;if(typeof t==="function"){t=await t(h,e);B=true}let y;try{y=await(0,c.default)(E,t,I,f,n)}catch(e){if(e instanceof TypeError||e instanceof o.JWEInvalid||e instanceof o.JOSENotSupported){throw e}y=(0,l.default)(m)}let b;let w;try{b=(0,s.decode)(e.iv)}catch{throw new o.JWEInvalid("Failed to base64url decode the iv")}try{w=(0,s.decode)(e.tag)}catch{throw new o.JWEInvalid("Failed to base64url decode the tag")}const R=u.encoder.encode((g=e.protected)!==null&&g!==void 0?g:"");let v;if(e.aad!==undefined){v=(0,u.concat)(R,u.encoder.encode("."),u.encoder.encode(e.aad))}else{v=R}let k;try{k=(0,s.decode)(e.ciphertext)}catch{throw new o.JWEInvalid("Failed to base64url decode the ciphertext")}let S=await(0,i.default)(m,y,k,b,w,v);if(f.zip==="DEF"){S=await((n===null||n===void 0?void 0:n.inflateRaw)||r.inflate)(S)}const x={plaintext:S};if(e.protected!==undefined){x.protectedHeader=h}if(e.aad!==undefined){try{x.additionalAuthenticatedData=(0,s.decode)(e.aad)}catch{throw new o.JWEInvalid("Failed to base64url decode the aad")}}if(e.unprotected!==undefined){x.sharedUnprotectedHeader=e.unprotected}if(e.header!==undefined){x.unprotectedHeader=e.header}if(B){return{...x,key:t}}return x}t.flattenedDecrypt=flattenedDecrypt},1555:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.FlattenedEncrypt=t.unprotected=void 0;const s=n(518);const i=n(6476);const r=n(7022);const o=n(4630);const A=n(3286);const a=n(4419);const c=n(6063);const u=n(1691);const l=n(863);t.unprotected=Symbol();class FlattenedEncrypt{constructor(e){if(!(e instanceof Uint8Array)){throw new TypeError("plaintext must be an instance of Uint8Array")}this._plaintext=e}setKeyManagementParameters(e){if(this._keyManagementParameters){throw new TypeError("setKeyManagementParameters can only be called once")}this._keyManagementParameters=e;return this}setProtectedHeader(e){if(this._protectedHeader){throw new TypeError("setProtectedHeader can only be called once")}this._protectedHeader=e;return this}setSharedUnprotectedHeader(e){if(this._sharedUnprotectedHeader){throw new TypeError("setSharedUnprotectedHeader can only be called once")}this._sharedUnprotectedHeader=e;return this}setUnprotectedHeader(e){if(this._unprotectedHeader){throw new TypeError("setUnprotectedHeader can only be called once")}this._unprotectedHeader=e;return this}setAdditionalAuthenticatedData(e){this._aad=e;return this}setContentEncryptionKey(e){if(this._cek){throw new TypeError("setContentEncryptionKey can only be called once")}this._cek=e;return this}setInitializationVector(e){if(this._iv){throw new TypeError("setInitializationVector can only be called once")}this._iv=e;return this}async encrypt(e,n){if(!this._protectedHeader&&!this._unprotectedHeader&&!this._sharedUnprotectedHeader){throw new a.JWEInvalid("either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()")}if(!(0,c.default)(this._protectedHeader,this._unprotectedHeader,this._sharedUnprotectedHeader)){throw new a.JWEInvalid("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint")}const d={...this._protectedHeader,...this._unprotectedHeader,...this._sharedUnprotectedHeader};(0,l.default)(a.JWEInvalid,new Map,n===null||n===void 0?void 0:n.crit,this._protectedHeader,d);if(d.zip!==undefined){if(!this._protectedHeader||!this._protectedHeader.zip){throw new a.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected')}if(d.zip!=="DEF"){throw new a.JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value')}}const{alg:p,enc:g}=d;if(typeof p!=="string"||!p){throw new a.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid')}if(typeof g!=="string"||!g){throw new a.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid')}let h;if(p==="dir"){if(this._cek){throw new TypeError("setContentEncryptionKey cannot be called when using Direct Encryption")}}else if(p==="ECDH-ES"){if(this._cek){throw new TypeError("setContentEncryptionKey cannot be called when using Direct Key Agreement")}}let f;{let s;({cek:f,encryptedKey:h,parameters:s}=await(0,A.default)(p,g,e,this._cek,this._keyManagementParameters));if(s){if(n&&t.unprotected in n){if(!this._unprotectedHeader){this.setUnprotectedHeader(s)}else{this._unprotectedHeader={...this._unprotectedHeader,...s}}}else{if(!this._protectedHeader){this.setProtectedHeader(s)}else{this._protectedHeader={...this._protectedHeader,...s}}}}}this._iv||(this._iv=(0,o.default)(g));let E;let m;let C;if(this._protectedHeader){m=u.encoder.encode((0,s.encode)(JSON.stringify(this._protectedHeader)))}else{m=u.encoder.encode("")}if(this._aad){C=(0,s.encode)(this._aad);E=(0,u.concat)(m,u.encoder.encode("."),u.encoder.encode(C))}else{E=m}let Q;let I;if(d.zip==="DEF"){const e=await((n===null||n===void 0?void 0:n.deflateRaw)||r.deflate)(this._plaintext);({ciphertext:Q,tag:I}=await(0,i.default)(g,e,f,this._iv,E))}else{({ciphertext:Q,tag:I}=await(0,i.default)(g,this._plaintext,f,this._iv,E))}const B={ciphertext:(0,s.encode)(Q),iv:(0,s.encode)(this._iv),tag:(0,s.encode)(I)};if(h){B.encrypted_key=(0,s.encode)(h)}if(C){B.aad=C}if(this._protectedHeader){B.protected=u.decoder.decode(m)}if(this._sharedUnprotectedHeader){B.unprotected=this._sharedUnprotectedHeader}if(this._unprotectedHeader){B.header=this._unprotectedHeader}return B}}t.FlattenedEncrypt=FlattenedEncrypt},5684:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.generalDecrypt=void 0;const s=n(7566);const i=n(4419);const r=n(9127);async function generalDecrypt(e,t,n){if(!(0,r.default)(e)){throw new i.JWEInvalid("General JWE must be an object")}if(!Array.isArray(e.recipients)||!e.recipients.every(r.default)){throw new i.JWEInvalid("JWE Recipients missing or incorrect type")}if(!e.recipients.length){throw new i.JWEInvalid("JWE Recipients has no members")}for(const i of e.recipients){try{return await(0,s.flattenedDecrypt)({aad:e.aad,ciphertext:e.ciphertext,encrypted_key:i.encrypted_key,header:i.header,iv:e.iv,protected:e.protected,tag:e.tag,unprotected:e.unprotected},t,n)}catch{}}throw new i.JWEDecryptionFailed}t.generalDecrypt=generalDecrypt},3992:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.GeneralEncrypt=void 0;const s=n(1555);const i=n(4419);const r=n(3987);const o=n(6063);const A=n(3286);const a=n(518);const c=n(863);class IndividualRecipient{constructor(e,t,n){this.parent=e;this.key=t;this.options=n}setUnprotectedHeader(e){if(this.unprotectedHeader){throw new TypeError("setUnprotectedHeader can only be called once")}this.unprotectedHeader=e;return this}addRecipient(...e){return this.parent.addRecipient(...e)}encrypt(...e){return this.parent.encrypt(...e)}done(){return this.parent}}class GeneralEncrypt{constructor(e){this._recipients=[];this._plaintext=e}addRecipient(e,t){const n=new IndividualRecipient(this,e,{crit:t===null||t===void 0?void 0:t.crit});this._recipients.push(n);return n}setProtectedHeader(e){if(this._protectedHeader){throw new TypeError("setProtectedHeader can only be called once")}this._protectedHeader=e;return this}setSharedUnprotectedHeader(e){if(this._unprotectedHeader){throw new TypeError("setSharedUnprotectedHeader can only be called once")}this._unprotectedHeader=e;return this}setAdditionalAuthenticatedData(e){this._aad=e;return this}async encrypt(e){var t,n,u;if(!this._recipients.length){throw new i.JWEInvalid("at least one recipient must be added")}e={deflateRaw:e===null||e===void 0?void 0:e.deflateRaw};if(this._recipients.length===1){const[t]=this._recipients;const n=await new s.FlattenedEncrypt(this._plaintext).setAdditionalAuthenticatedData(this._aad).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(t.unprotectedHeader).encrypt(t.key,{...t.options,...e});let i={ciphertext:n.ciphertext,iv:n.iv,recipients:[{}],tag:n.tag};if(n.aad)i.aad=n.aad;if(n.protected)i.protected=n.protected;if(n.unprotected)i.unprotected=n.unprotected;if(n.encrypted_key)i.recipients[0].encrypted_key=n.encrypted_key;if(n.header)i.recipients[0].header=n.header;return i}let l;for(let e=0;e{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.EmbeddedJWK=void 0;const s=n(4230);const i=n(9127);const r=n(4419);async function EmbeddedJWK(e,t){const n={...e,...t===null||t===void 0?void 0:t.header};if(!(0,i.default)(n.jwk)){throw new r.JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object')}const o=await(0,s.importJWK)({...n.jwk,ext:true},n.alg,true);if(o instanceof Uint8Array||o.type!=="public"){throw new r.JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key')}return o}t.EmbeddedJWK=EmbeddedJWK},3494:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.calculateJwkThumbprintUri=t.calculateJwkThumbprint=void 0;const s=n(2355);const i=n(518);const r=n(4419);const o=n(1691);const A=n(9127);const check=(e,t)=>{if(typeof e!=="string"||!e){throw new r.JWKInvalid(`${t} missing or invalid`)}};async function calculateJwkThumbprint(e,t){if(!(0,A.default)(e)){throw new TypeError("JWK must be an object")}t!==null&&t!==void 0?t:t="sha256";if(t!=="sha256"&&t!=="sha384"&&t!=="sha512"){throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"')}let n;switch(e.kty){case"EC":check(e.crv,'"crv" (Curve) Parameter');check(e.x,'"x" (X Coordinate) Parameter');check(e.y,'"y" (Y Coordinate) Parameter');n={crv:e.crv,kty:e.kty,x:e.x,y:e.y};break;case"OKP":check(e.crv,'"crv" (Subtype of Key Pair) Parameter');check(e.x,'"x" (Public Key) Parameter');n={crv:e.crv,kty:e.kty,x:e.x};break;case"RSA":check(e.e,'"e" (Exponent) Parameter');check(e.n,'"n" (Modulus) Parameter');n={e:e.e,kty:e.kty,n:e.n};break;case"oct":check(e.k,'"k" (Key Value) Parameter');n={k:e.k,kty:e.kty};break;default:throw new r.JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported')}const a=o.encoder.encode(JSON.stringify(n));return(0,i.encode)(await(0,s.default)(t,a))}t.calculateJwkThumbprint=calculateJwkThumbprint;async function calculateJwkThumbprintUri(e,t){t!==null&&t!==void 0?t:t="sha256";const n=await calculateJwkThumbprint(e,t);return`urn:ietf:params:oauth:jwk-thumbprint:sha-${t.slice(-3)}:${n}`}t.calculateJwkThumbprintUri=calculateJwkThumbprintUri},9970:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.createLocalJWKSet=t.LocalJWKSet=t.isJWKSLike=void 0;const s=n(4230);const i=n(4419);const r=n(9127);function getKtyFromAlg(e){switch(typeof e==="string"&&e.slice(0,2)){case"RS":case"PS":return"RSA";case"ES":return"EC";case"Ed":return"OKP";default:throw new i.JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set')}}function isJWKSLike(e){return e&&typeof e==="object"&&Array.isArray(e.keys)&&e.keys.every(isJWKLike)}t.isJWKSLike=isJWKSLike;function isJWKLike(e){return(0,r.default)(e)}function clone(e){if(typeof structuredClone==="function"){return structuredClone(e)}return JSON.parse(JSON.stringify(e))}class LocalJWKSet{constructor(e){this._cached=new WeakMap;if(!isJWKSLike(e)){throw new i.JWKSInvalid("JSON Web Key Set malformed")}this._jwks=clone(e)}async getKey(e,t){const{alg:n,kid:s}={...e,...t===null||t===void 0?void 0:t.header};const r=getKtyFromAlg(n);const o=this._jwks.keys.filter((e=>{let t=r===e.kty;if(t&&typeof s==="string"){t=s===e.kid}if(t&&typeof e.alg==="string"){t=n===e.alg}if(t&&typeof e.use==="string"){t=e.use==="sig"}if(t&&Array.isArray(e.key_ops)){t=e.key_ops.includes("verify")}if(t&&n==="EdDSA"){t=e.crv==="Ed25519"||e.crv==="Ed448"}if(t){switch(n){case"ES256":t=e.crv==="P-256";break;case"ES256K":t=e.crv==="secp256k1";break;case"ES384":t=e.crv==="P-384";break;case"ES512":t=e.crv==="P-521";break}}return t}));const{0:A,length:a}=o;if(a===0){throw new i.JWKSNoMatchingKey}else if(a!==1){const e=new i.JWKSMultipleMatchingKeys;const{_cached:t}=this;e[Symbol.asyncIterator]=async function*(){for(const e of o){try{yield await importWithAlgCache(t,e,n)}catch{continue}}};throw e}return importWithAlgCache(this._cached,A,n)}}t.LocalJWKSet=LocalJWKSet;async function importWithAlgCache(e,t,n){const r=e.get(t)||e.set(t,{}).get(t);if(r[n]===undefined){const e=await(0,s.importJWK)({...t,ext:true},n);if(e instanceof Uint8Array||e.type!=="public"){throw new i.JWKSInvalid("JSON Web Key Set members must be public keys")}r[n]=e}return r[n]}function createLocalJWKSet(e){const t=new LocalJWKSet(e);return async function(e,n){return t.getKey(e,n)}}t.createLocalJWKSet=createLocalJWKSet},9035:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.createRemoteJWKSet=void 0;const s=n(3650);const i=n(4419);const r=n(9970);function isCloudflareWorkers(){return typeof WebSocketPair!=="undefined"||typeof navigator!=="undefined"&&navigator.userAgent==="Cloudflare-Workers"||typeof EdgeRuntime!=="undefined"&&EdgeRuntime==="vercel"}class RemoteJWKSet extends r.LocalJWKSet{constructor(e,t){super({keys:[]});this._jwks=undefined;if(!(e instanceof URL)){throw new TypeError("url must be an instance of URL")}this._url=new URL(e.href);this._options={agent:t===null||t===void 0?void 0:t.agent,headers:t===null||t===void 0?void 0:t.headers};this._timeoutDuration=typeof(t===null||t===void 0?void 0:t.timeoutDuration)==="number"?t===null||t===void 0?void 0:t.timeoutDuration:5e3;this._cooldownDuration=typeof(t===null||t===void 0?void 0:t.cooldownDuration)==="number"?t===null||t===void 0?void 0:t.cooldownDuration:3e4;this._cacheMaxAge=typeof(t===null||t===void 0?void 0:t.cacheMaxAge)==="number"?t===null||t===void 0?void 0:t.cacheMaxAge:6e5}coolingDown(){return typeof this._jwksTimestamp==="number"?Date.now(){if(!(0,r.isJWKSLike)(e)){throw new i.JWKSInvalid("JSON Web Key Set malformed")}this._jwks={keys:e.keys};this._jwksTimestamp=Date.now();this._pendingFetch=undefined})).catch((e=>{this._pendingFetch=undefined;throw e})));await this._pendingFetch}}function createRemoteJWKSet(e,t){const n=new RemoteJWKSet(e,t);return async function(e,t){return n.getKey(e,t)}}t.createRemoteJWKSet=createRemoteJWKSet},8257:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.CompactSign=void 0;const s=n(4825);class CompactSign{constructor(e){this._flattened=new s.FlattenedSign(e)}setProtectedHeader(e){this._flattened.setProtectedHeader(e);return this}async sign(e,t){const n=await this._flattened.sign(e,t);if(n.payload===undefined){throw new TypeError("use the flattened module for creating JWS with b64: false")}return`${n.protected}.${n.payload}.${n.signature}`}}t.CompactSign=CompactSign},5212:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.compactVerify=void 0;const s=n(2095);const i=n(4419);const r=n(1691);async function compactVerify(e,t,n){if(e instanceof Uint8Array){e=r.decoder.decode(e)}if(typeof e!=="string"){throw new i.JWSInvalid("Compact JWS must be a string or Uint8Array")}const{0:o,1:A,2:a,length:c}=e.split(".");if(c!==3){throw new i.JWSInvalid("Invalid Compact JWS")}const u=await(0,s.flattenedVerify)({payload:A,protected:o,signature:a},t,n);const l={payload:u.payload,protectedHeader:u.protectedHeader};if(typeof t==="function"){return{...l,key:u.key}}return l}t.compactVerify=compactVerify},4825:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.FlattenedSign=void 0;const s=n(518);const i=n(9935);const r=n(6063);const o=n(4419);const A=n(1691);const a=n(6241);const c=n(863);class FlattenedSign{constructor(e){if(!(e instanceof Uint8Array)){throw new TypeError("payload must be an instance of Uint8Array")}this._payload=e}setProtectedHeader(e){if(this._protectedHeader){throw new TypeError("setProtectedHeader can only be called once")}this._protectedHeader=e;return this}setUnprotectedHeader(e){if(this._unprotectedHeader){throw new TypeError("setUnprotectedHeader can only be called once")}this._unprotectedHeader=e;return this}async sign(e,t){if(!this._protectedHeader&&!this._unprotectedHeader){throw new o.JWSInvalid("either setProtectedHeader or setUnprotectedHeader must be called before #sign()")}if(!(0,r.default)(this._protectedHeader,this._unprotectedHeader)){throw new o.JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint")}const n={...this._protectedHeader,...this._unprotectedHeader};const u=(0,c.default)(o.JWSInvalid,new Map([["b64",true]]),t===null||t===void 0?void 0:t.crit,this._protectedHeader,n);let l=true;if(u.has("b64")){l=this._protectedHeader.b64;if(typeof l!=="boolean"){throw new o.JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean')}}const{alg:d}=n;if(typeof d!=="string"||!d){throw new o.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid')}(0,a.default)(d,e,"sign");let p=this._payload;if(l){p=A.encoder.encode((0,s.encode)(p))}let g;if(this._protectedHeader){g=A.encoder.encode((0,s.encode)(JSON.stringify(this._protectedHeader)))}else{g=A.encoder.encode("")}const h=(0,A.concat)(g,A.encoder.encode("."),p);const f=await(0,i.default)(d,e,h);const E={signature:(0,s.encode)(f),payload:""};if(l){E.payload=A.decoder.decode(p)}if(this._unprotectedHeader){E.header=this._unprotectedHeader}if(this._protectedHeader){E.protected=A.decoder.decode(g)}return E}}t.FlattenedSign=FlattenedSign},2095:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.flattenedVerify=void 0;const s=n(518);const i=n(3569);const r=n(4419);const o=n(1691);const A=n(6063);const a=n(9127);const c=n(6241);const u=n(863);const l=n(5148);async function flattenedVerify(e,t,n){var d;if(!(0,a.default)(e)){throw new r.JWSInvalid("Flattened JWS must be an object")}if(e.protected===undefined&&e.header===undefined){throw new r.JWSInvalid('Flattened JWS must have either of the "protected" or "header" members')}if(e.protected!==undefined&&typeof e.protected!=="string"){throw new r.JWSInvalid("JWS Protected Header incorrect type")}if(e.payload===undefined){throw new r.JWSInvalid("JWS Payload missing")}if(typeof e.signature!=="string"){throw new r.JWSInvalid("JWS Signature missing or incorrect type")}if(e.header!==undefined&&!(0,a.default)(e.header)){throw new r.JWSInvalid("JWS Unprotected Header incorrect type")}let p={};if(e.protected){try{const t=(0,s.decode)(e.protected);p=JSON.parse(o.decoder.decode(t))}catch{throw new r.JWSInvalid("JWS Protected Header is invalid")}}if(!(0,A.default)(p,e.header)){throw new r.JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint")}const g={...p,...e.header};const h=(0,u.default)(r.JWSInvalid,new Map([["b64",true]]),n===null||n===void 0?void 0:n.crit,p,g);let f=true;if(h.has("b64")){f=p.b64;if(typeof f!=="boolean"){throw new r.JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean')}}const{alg:E}=g;if(typeof E!=="string"||!E){throw new r.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid')}const m=n&&(0,l.default)("algorithms",n.algorithms);if(m&&!m.has(E)){throw new r.JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter not allowed')}if(f){if(typeof e.payload!=="string"){throw new r.JWSInvalid("JWS Payload must be a string")}}else if(typeof e.payload!=="string"&&!(e.payload instanceof Uint8Array)){throw new r.JWSInvalid("JWS Payload must be a string or an Uint8Array instance")}let C=false;if(typeof t==="function"){t=await t(p,e);C=true}(0,c.default)(E,t,"verify");const Q=(0,o.concat)(o.encoder.encode((d=e.protected)!==null&&d!==void 0?d:""),o.encoder.encode("."),typeof e.payload==="string"?o.encoder.encode(e.payload):e.payload);let I;try{I=(0,s.decode)(e.signature)}catch{throw new r.JWSInvalid("Failed to base64url decode the signature")}const B=await(0,i.default)(E,t,I,Q);if(!B){throw new r.JWSSignatureVerificationFailed}let y;if(f){try{y=(0,s.decode)(e.payload)}catch{throw new r.JWSInvalid("Failed to base64url decode the payload")}}else if(typeof e.payload==="string"){y=o.encoder.encode(e.payload)}else{y=e.payload}const b={payload:y};if(e.protected!==undefined){b.protectedHeader=p}if(e.header!==undefined){b.unprotectedHeader=e.header}if(C){return{...b,key:t}}return b}t.flattenedVerify=flattenedVerify},4268:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.GeneralSign=void 0;const s=n(4825);const i=n(4419);class IndividualSignature{constructor(e,t,n){this.parent=e;this.key=t;this.options=n}setProtectedHeader(e){if(this.protectedHeader){throw new TypeError("setProtectedHeader can only be called once")}this.protectedHeader=e;return this}setUnprotectedHeader(e){if(this.unprotectedHeader){throw new TypeError("setUnprotectedHeader can only be called once")}this.unprotectedHeader=e;return this}addSignature(...e){return this.parent.addSignature(...e)}sign(...e){return this.parent.sign(...e)}done(){return this.parent}}class GeneralSign{constructor(e){this._signatures=[];this._payload=e}addSignature(e,t){const n=new IndividualSignature(this,e,t);this._signatures.push(n);return n}async sign(){if(!this._signatures.length){throw new i.JWSInvalid("at least one signature must be added")}const e={signatures:[],payload:""};for(let t=0;t{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.generalVerify=void 0;const s=n(2095);const i=n(4419);const r=n(9127);async function generalVerify(e,t,n){if(!(0,r.default)(e)){throw new i.JWSInvalid("General JWS must be an object")}if(!Array.isArray(e.signatures)||!e.signatures.every(r.default)){throw new i.JWSInvalid("JWS Signatures missing or incorrect type")}for(const i of e.signatures){try{return await(0,s.flattenedVerify)({header:i.header,payload:e.payload,protected:i.protected,signature:i.signature},t,n)}catch{}}throw new i.JWSSignatureVerificationFailed}t.generalVerify=generalVerify},3378:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.jwtDecrypt=void 0;const s=n(7651);const i=n(7274);const r=n(4419);async function jwtDecrypt(e,t,n){const o=await(0,s.compactDecrypt)(e,t,n);const A=(0,i.default)(o.protectedHeader,o.plaintext,n);const{protectedHeader:a}=o;if(a.iss!==undefined&&a.iss!==A.iss){throw new r.JWTClaimValidationFailed('replicated "iss" claim header parameter mismatch',"iss","mismatch")}if(a.sub!==undefined&&a.sub!==A.sub){throw new r.JWTClaimValidationFailed('replicated "sub" claim header parameter mismatch',"sub","mismatch")}if(a.aud!==undefined&&JSON.stringify(a.aud)!==JSON.stringify(A.aud)){throw new r.JWTClaimValidationFailed('replicated "aud" claim header parameter mismatch',"aud","mismatch")}const c={payload:A,protectedHeader:a};if(typeof t==="function"){return{...c,key:o.key}}return c}t.jwtDecrypt=jwtDecrypt},960:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.EncryptJWT=void 0;const s=n(6203);const i=n(1691);const r=n(1908);class EncryptJWT extends r.ProduceJWT{setProtectedHeader(e){if(this._protectedHeader){throw new TypeError("setProtectedHeader can only be called once")}this._protectedHeader=e;return this}setKeyManagementParameters(e){if(this._keyManagementParameters){throw new TypeError("setKeyManagementParameters can only be called once")}this._keyManagementParameters=e;return this}setContentEncryptionKey(e){if(this._cek){throw new TypeError("setContentEncryptionKey can only be called once")}this._cek=e;return this}setInitializationVector(e){if(this._iv){throw new TypeError("setInitializationVector can only be called once")}this._iv=e;return this}replicateIssuerAsHeader(){this._replicateIssuerAsHeader=true;return this}replicateSubjectAsHeader(){this._replicateSubjectAsHeader=true;return this}replicateAudienceAsHeader(){this._replicateAudienceAsHeader=true;return this}async encrypt(e,t){const n=new s.CompactEncrypt(i.encoder.encode(JSON.stringify(this._payload)));if(this._replicateIssuerAsHeader){this._protectedHeader={...this._protectedHeader,iss:this._payload.iss}}if(this._replicateSubjectAsHeader){this._protectedHeader={...this._protectedHeader,sub:this._payload.sub}}if(this._replicateAudienceAsHeader){this._protectedHeader={...this._protectedHeader,aud:this._payload.aud}}n.setProtectedHeader(this._protectedHeader);if(this._iv){n.setInitializationVector(this._iv)}if(this._cek){n.setContentEncryptionKey(this._cek)}if(this._keyManagementParameters){n.setKeyManagementParameters(this._keyManagementParameters)}return n.encrypt(e,t)}}t.EncryptJWT=EncryptJWT},1908:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.ProduceJWT=void 0;const s=n(4476);const i=n(9127);const r=n(7810);class ProduceJWT{constructor(e){if(!(0,i.default)(e)){throw new TypeError("JWT Claims Set MUST be an object")}this._payload=e}setIssuer(e){this._payload={...this._payload,iss:e};return this}setSubject(e){this._payload={...this._payload,sub:e};return this}setAudience(e){this._payload={...this._payload,aud:e};return this}setJti(e){this._payload={...this._payload,jti:e};return this}setNotBefore(e){if(typeof e==="number"){this._payload={...this._payload,nbf:e}}else{this._payload={...this._payload,nbf:(0,s.default)(new Date)+(0,r.default)(e)}}return this}setExpirationTime(e){if(typeof e==="number"){this._payload={...this._payload,exp:e}}else{this._payload={...this._payload,exp:(0,s.default)(new Date)+(0,r.default)(e)}}return this}setIssuedAt(e){if(typeof e==="undefined"){this._payload={...this._payload,iat:(0,s.default)(new Date)}}else{this._payload={...this._payload,iat:e}}return this}}t.ProduceJWT=ProduceJWT},8882:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.SignJWT=void 0;const s=n(8257);const i=n(4419);const r=n(1691);const o=n(1908);class SignJWT extends o.ProduceJWT{setProtectedHeader(e){this._protectedHeader=e;return this}async sign(e,t){var n;const o=new s.CompactSign(r.encoder.encode(JSON.stringify(this._payload)));o.setProtectedHeader(this._protectedHeader);if(Array.isArray((n=this._protectedHeader)===null||n===void 0?void 0:n.crit)&&this._protectedHeader.crit.includes("b64")&&this._protectedHeader.b64===false){throw new i.JWTInvalid("JWTs MUST NOT use unencoded payload")}return o.sign(e,t)}}t.SignJWT=SignJWT},8568:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.UnsecuredJWT=void 0;const s=n(518);const i=n(1691);const r=n(4419);const o=n(7274);const A=n(1908);class UnsecuredJWT extends A.ProduceJWT{encode(){const e=s.encode(JSON.stringify({alg:"none"}));const t=s.encode(JSON.stringify(this._payload));return`${e}.${t}.`}static decode(e,t){if(typeof e!=="string"){throw new r.JWTInvalid("Unsecured JWT must be a string")}const{0:n,1:A,2:a,length:c}=e.split(".");if(c!==3||a!==""){throw new r.JWTInvalid("Invalid Unsecured JWT")}let u;try{u=JSON.parse(i.decoder.decode(s.decode(n)));if(u.alg!=="none")throw new Error}catch{throw new r.JWTInvalid("Invalid Unsecured JWT")}const l=(0,o.default)(u,s.decode(A),t);return{payload:l,header:u}}}t.UnsecuredJWT=UnsecuredJWT},9887:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.jwtVerify=void 0;const s=n(5212);const i=n(7274);const r=n(4419);async function jwtVerify(e,t,n){var o;const A=await(0,s.compactVerify)(e,t,n);if(((o=A.protectedHeader.crit)===null||o===void 0?void 0:o.includes("b64"))&&A.protectedHeader.b64===false){throw new r.JWTInvalid("JWTs MUST NOT use unencoded payload")}const a=(0,i.default)(A.protectedHeader,A.payload,n);const c={payload:a,protectedHeader:A.protectedHeader};if(typeof t==="function"){return{...c,key:A.key}}return c}t.jwtVerify=jwtVerify},465:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.exportJWK=t.exportPKCS8=t.exportSPKI=void 0;const s=n(858);const i=n(858);const r=n(997);async function exportSPKI(e){return(0,s.toSPKI)(e)}t.exportSPKI=exportSPKI;async function exportPKCS8(e){return(0,i.toPKCS8)(e)}t.exportPKCS8=exportPKCS8;async function exportJWK(e){return(0,r.default)(e)}t.exportJWK=exportJWK},1036:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.generateKeyPair=void 0;const s=n(9378);async function generateKeyPair(e,t){return(0,s.generateKeyPair)(e,t)}t.generateKeyPair=generateKeyPair},6617:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.generateSecret=void 0;const s=n(9378);async function generateSecret(e,t){return(0,s.generateSecret)(e,t)}t.generateSecret=generateSecret},4230:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.importJWK=t.importPKCS8=t.importX509=t.importSPKI=void 0;const s=n(518);const i=n(858);const r=n(2659);const o=n(4419);const A=n(9127);async function importSPKI(e,t,n){if(typeof e!=="string"||e.indexOf("-----BEGIN PUBLIC KEY-----")!==0){throw new TypeError('"spki" must be SPKI formatted string')}return(0,i.fromSPKI)(e,t,n)}t.importSPKI=importSPKI;async function importX509(e,t,n){if(typeof e!=="string"||e.indexOf("-----BEGIN CERTIFICATE-----")!==0){throw new TypeError('"x509" must be X.509 formatted string')}return(0,i.fromX509)(e,t,n)}t.importX509=importX509;async function importPKCS8(e,t,n){if(typeof e!=="string"||e.indexOf("-----BEGIN PRIVATE KEY-----")!==0){throw new TypeError('"pkcs8" must be PKCS#8 formatted string')}return(0,i.fromPKCS8)(e,t,n)}t.importPKCS8=importPKCS8;async function importJWK(e,t,n){var i;if(!(0,A.default)(e)){throw new TypeError("JWK must be an object")}t||(t=e.alg);switch(e.kty){case"oct":if(typeof e.k!=="string"||!e.k){throw new TypeError('missing "k" (Key Value) Parameter value')}n!==null&&n!==void 0?n:n=e.ext!==true;if(n){return(0,r.default)({...e,alg:t,ext:(i=e.ext)!==null&&i!==void 0?i:false})}return(0,s.decode)(e.k);case"RSA":if(e.oth!==undefined){throw new o.JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported')}case"EC":case"OKP":return(0,r.default)({...e,alg:t});default:throw new o.JOSENotSupported('Unsupported "kty" (Key Type) Parameter value')}}t.importJWK=importJWK},233:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.unwrap=t.wrap=void 0;const s=n(6476);const i=n(6137);const r=n(4630);const o=n(518);async function wrap(e,t,n,i){const A=e.slice(0,7);i||(i=(0,r.default)(A));const{ciphertext:a,tag:c}=await(0,s.default)(A,n,t,i,new Uint8Array(0));return{encryptedKey:a,iv:(0,o.encode)(i),tag:(0,o.encode)(c)}}t.wrap=wrap;async function unwrap(e,t,n,s,r){const o=e.slice(0,7);return(0,i.default)(o,t,n,s,r,new Uint8Array(0))}t.unwrap=unwrap},1691:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.concatKdf=t.lengthAndInput=t.uint32be=t.uint64be=t.p2s=t.concat=t.decoder=t.encoder=void 0;const s=n(2355);t.encoder=new TextEncoder;t.decoder=new TextDecoder;const i=2**32;function concat(...e){const t=e.reduce(((e,{length:t})=>e+t),0);const n=new Uint8Array(t);let s=0;e.forEach((e=>{n.set(e,s);s+=e.length}));return n}t.concat=concat;function p2s(e,n){return concat(t.encoder.encode(e),new Uint8Array([0]),n)}t.p2s=p2s;function writeUInt32BE(e,t,n){if(t<0||t>=i){throw new RangeError(`value must be >= 0 and <= ${i-1}. Received ${t}`)}e.set([t>>>24,t>>>16,t>>>8,t&255],n)}function uint64be(e){const t=Math.floor(e/i);const n=e%i;const s=new Uint8Array(8);writeUInt32BE(s,t,0);writeUInt32BE(s,n,4);return s}t.uint64be=uint64be;function uint32be(e){const t=new Uint8Array(4);writeUInt32BE(t,e);return t}t.uint32be=uint32be;function lengthAndInput(e){return concat(uint32be(e.length),e)}t.lengthAndInput=lengthAndInput;async function concatKdf(e,t,n){const i=Math.ceil((t>>3)/32);const r=new Uint8Array(i*32);for(let t=0;t>3)}t.concatKdf=concatKdf},3987:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.bitLength=void 0;const s=n(4419);const i=n(5770);function bitLength(e){switch(e){case"A128GCM":return 128;case"A192GCM":return 192;case"A256GCM":case"A128CBC-HS256":return 256;case"A192CBC-HS384":return 384;case"A256CBC-HS512":return 512;default:throw new s.JOSENotSupported(`Unsupported JWE Algorithm: ${e}`)}}t.bitLength=bitLength;t["default"]=e=>(0,i.default)(new Uint8Array(bitLength(e)>>3))},1120:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4419);const i=n(4630);const checkIvLength=(e,t)=>{if(t.length<<3!==(0,i.bitLength)(e)){throw new s.JWEInvalid("Invalid Initialization Vector length")}};t["default"]=checkIvLength},6241:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(1146);const i=n(7947);const symmetricTypeCheck=(e,t)=>{if(t instanceof Uint8Array)return;if(!(0,i.default)(t)){throw new TypeError((0,s.withAlg)(e,t,...i.types,"Uint8Array"))}if(t.type!=="secret"){throw new TypeError(`${i.types.join(" or ")} instances for symmetric algorithms must be of type "secret"`)}};const asymmetricTypeCheck=(e,t,n)=>{if(!(0,i.default)(t)){throw new TypeError((0,s.withAlg)(e,t,...i.types))}if(t.type==="secret"){throw new TypeError(`${i.types.join(" or ")} instances for asymmetric algorithms must not be of type "secret"`)}if(n==="sign"&&t.type==="public"){throw new TypeError(`${i.types.join(" or ")} instances for asymmetric algorithm signing must be of type "private"`)}if(n==="decrypt"&&t.type==="public"){throw new TypeError(`${i.types.join(" or ")} instances for asymmetric algorithm decryption must be of type "private"`)}if(t.algorithm&&n==="verify"&&t.type==="private"){throw new TypeError(`${i.types.join(" or ")} instances for asymmetric algorithm verifying must be of type "public"`)}if(t.algorithm&&n==="encrypt"&&t.type==="private"){throw new TypeError(`${i.types.join(" or ")} instances for asymmetric algorithm encryption must be of type "public"`)}};const checkKeyType=(e,t,n)=>{const s=e.startsWith("HS")||e==="dir"||e.startsWith("PBES2")||/^A\d{3}(?:GCM)?KW$/.test(e);if(s){symmetricTypeCheck(e,t)}else{asymmetricTypeCheck(e,t,n)}};t["default"]=checkKeyType},3499:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4419);function checkP2s(e){if(!(e instanceof Uint8Array)||e.length<8){throw new s.JWEInvalid("PBES2 Salt Input must be 8 or more octets")}}t["default"]=checkP2s},3386:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.checkEncCryptoKey=t.checkSigCryptoKey=void 0;function unusable(e,t="algorithm.name"){return new TypeError(`CryptoKey does not support this operation, its ${t} must be ${e}`)}function isAlgorithm(e,t){return e.name===t}function getHashLength(e){return parseInt(e.name.slice(4),10)}function getNamedCurve(e){switch(e){case"ES256":return"P-256";case"ES384":return"P-384";case"ES512":return"P-521";default:throw new Error("unreachable")}}function checkUsage(e,t){if(t.length&&!t.some((t=>e.usages.includes(t)))){let e="CryptoKey does not support this operation, its usages must include ";if(t.length>2){const n=t.pop();e+=`one of ${t.join(", ")}, or ${n}.`}else if(t.length===2){e+=`one of ${t[0]} or ${t[1]}.`}else{e+=`${t[0]}.`}throw new TypeError(e)}}function checkSigCryptoKey(e,t,...n){switch(t){case"HS256":case"HS384":case"HS512":{if(!isAlgorithm(e.algorithm,"HMAC"))throw unusable("HMAC");const n=parseInt(t.slice(2),10);const s=getHashLength(e.algorithm.hash);if(s!==n)throw unusable(`SHA-${n}`,"algorithm.hash");break}case"RS256":case"RS384":case"RS512":{if(!isAlgorithm(e.algorithm,"RSASSA-PKCS1-v1_5"))throw unusable("RSASSA-PKCS1-v1_5");const n=parseInt(t.slice(2),10);const s=getHashLength(e.algorithm.hash);if(s!==n)throw unusable(`SHA-${n}`,"algorithm.hash");break}case"PS256":case"PS384":case"PS512":{if(!isAlgorithm(e.algorithm,"RSA-PSS"))throw unusable("RSA-PSS");const n=parseInt(t.slice(2),10);const s=getHashLength(e.algorithm.hash);if(s!==n)throw unusable(`SHA-${n}`,"algorithm.hash");break}case"EdDSA":{if(e.algorithm.name!=="Ed25519"&&e.algorithm.name!=="Ed448"){throw unusable("Ed25519 or Ed448")}break}case"ES256":case"ES384":case"ES512":{if(!isAlgorithm(e.algorithm,"ECDSA"))throw unusable("ECDSA");const n=getNamedCurve(t);const s=e.algorithm.namedCurve;if(s!==n)throw unusable(n,"algorithm.namedCurve");break}default:throw new TypeError("CryptoKey does not support this operation")}checkUsage(e,n)}t.checkSigCryptoKey=checkSigCryptoKey;function checkEncCryptoKey(e,t,...n){switch(t){case"A128GCM":case"A192GCM":case"A256GCM":{if(!isAlgorithm(e.algorithm,"AES-GCM"))throw unusable("AES-GCM");const n=parseInt(t.slice(1,4),10);const s=e.algorithm.length;if(s!==n)throw unusable(n,"algorithm.length");break}case"A128KW":case"A192KW":case"A256KW":{if(!isAlgorithm(e.algorithm,"AES-KW"))throw unusable("AES-KW");const n=parseInt(t.slice(1,4),10);const s=e.algorithm.length;if(s!==n)throw unusable(n,"algorithm.length");break}case"ECDH":{switch(e.algorithm.name){case"ECDH":case"X25519":case"X448":break;default:throw unusable("ECDH, X25519, or X448")}break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":if(!isAlgorithm(e.algorithm,"PBKDF2"))throw unusable("PBKDF2");break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(!isAlgorithm(e.algorithm,"RSA-OAEP"))throw unusable("RSA-OAEP");const n=parseInt(t.slice(9),10)||1;const s=getHashLength(e.algorithm.hash);if(s!==n)throw unusable(`SHA-${n}`,"algorithm.hash");break}default:throw new TypeError("CryptoKey does not support this operation")}checkUsage(e,n)}t.checkEncCryptoKey=checkEncCryptoKey},6127:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6083);const i=n(3706);const r=n(6898);const o=n(9526);const A=n(518);const a=n(4419);const c=n(3987);const u=n(4230);const l=n(6241);const d=n(9127);const p=n(233);async function decryptKeyManagement(e,t,n,g,h){(0,l.default)(e,t,"decrypt");switch(e){case"dir":{if(n!==undefined)throw new a.JWEInvalid("Encountered unexpected JWE Encrypted Key");return t}case"ECDH-ES":if(n!==undefined)throw new a.JWEInvalid("Encountered unexpected JWE Encrypted Key");case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!(0,d.default)(g.epk))throw new a.JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`);if(!i.ecdhAllowed(t))throw new a.JOSENotSupported("ECDH with the provided key is not allowed or not supported by your javascript runtime");const r=await(0,u.importJWK)(g.epk,e);let o;let l;if(g.apu!==undefined){if(typeof g.apu!=="string")throw new a.JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`);try{o=(0,A.decode)(g.apu)}catch{throw new a.JWEInvalid("Failed to base64url decode the apu")}}if(g.apv!==undefined){if(typeof g.apv!=="string")throw new a.JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`);try{l=(0,A.decode)(g.apv)}catch{throw new a.JWEInvalid("Failed to base64url decode the apv")}}const p=await i.deriveKey(r,t,e==="ECDH-ES"?g.enc:e,e==="ECDH-ES"?(0,c.bitLength)(g.enc):parseInt(e.slice(-5,-2),10),o,l);if(e==="ECDH-ES")return p;if(n===undefined)throw new a.JWEInvalid("JWE Encrypted Key missing");return(0,s.unwrap)(e.slice(-6),p,n)}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(n===undefined)throw new a.JWEInvalid("JWE Encrypted Key missing");return(0,o.decrypt)(e,t,n)}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{if(n===undefined)throw new a.JWEInvalid("JWE Encrypted Key missing");if(typeof g.p2c!=="number")throw new a.JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`);const s=(h===null||h===void 0?void 0:h.maxPBES2Count)||1e4;if(g.p2c>s)throw new a.JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`);if(typeof g.p2s!=="string")throw new a.JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`);let i;try{i=(0,A.decode)(g.p2s)}catch{throw new a.JWEInvalid("Failed to base64url decode the p2s")}return(0,r.decrypt)(e,t,n,g.p2c,i)}case"A128KW":case"A192KW":case"A256KW":{if(n===undefined)throw new a.JWEInvalid("JWE Encrypted Key missing");return(0,s.unwrap)(e,t,n)}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{if(n===undefined)throw new a.JWEInvalid("JWE Encrypted Key missing");if(typeof g.iv!=="string")throw new a.JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`);if(typeof g.tag!=="string")throw new a.JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`);let s;try{s=(0,A.decode)(g.iv)}catch{throw new a.JWEInvalid("Failed to base64url decode the iv")}let i;try{i=(0,A.decode)(g.tag)}catch{throw new a.JWEInvalid("Failed to base64url decode the tag")}return(0,p.unwrap)(e,t,n,s,i)}default:{throw new a.JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value')}}}t["default"]=decryptKeyManagement},3286:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6083);const i=n(3706);const r=n(6898);const o=n(9526);const A=n(518);const a=n(3987);const c=n(4419);const u=n(465);const l=n(6241);const d=n(233);async function encryptKeyManagement(e,t,n,p,g={}){let h;let f;let E;(0,l.default)(e,n,"encrypt");switch(e){case"dir":{E=n;break}case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!i.ecdhAllowed(n)){throw new c.JOSENotSupported("ECDH with the provided key is not allowed or not supported by your javascript runtime")}const{apu:r,apv:o}=g;let{epk:l}=g;l||(l=(await i.generateEpk(n)).privateKey);const{x:d,y:m,crv:C,kty:Q}=await(0,u.exportJWK)(l);const I=await i.deriveKey(n,l,e==="ECDH-ES"?t:e,e==="ECDH-ES"?(0,a.bitLength)(t):parseInt(e.slice(-5,-2),10),r,o);f={epk:{x:d,crv:C,kty:Q}};if(Q==="EC")f.epk.y=m;if(r)f.apu=(0,A.encode)(r);if(o)f.apv=(0,A.encode)(o);if(e==="ECDH-ES"){E=I;break}E=p||(0,a.default)(t);const B=e.slice(-6);h=await(0,s.wrap)(B,I,E);break}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{E=p||(0,a.default)(t);h=await(0,o.encrypt)(e,n,E);break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{E=p||(0,a.default)(t);const{p2c:s,p2s:i}=g;({encryptedKey:h,...f}=await(0,r.encrypt)(e,n,E,s,i));break}case"A128KW":case"A192KW":case"A256KW":{E=p||(0,a.default)(t);h=await(0,s.wrap)(e,n,E);break}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{E=p||(0,a.default)(t);const{iv:s}=g;({encryptedKey:h,...f}=await(0,d.wrap)(e,n,E,s));break}default:{throw new c.JOSENotSupported('Invalid or unsupported "alg" (JWE Algorithm) header value')}}return{cek:E,encryptedKey:h,parameters:f}}t["default"]=encryptKeyManagement},4476:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=e=>Math.floor(e.getTime()/1e3)},1146:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.withAlg=void 0;function message(e,t,...n){if(n.length>2){const t=n.pop();e+=`one of type ${n.join(", ")}, or ${t}.`}else if(n.length===2){e+=`one of type ${n[0]} or ${n[1]}.`}else{e+=`of type ${n[0]}.`}if(t==null){e+=` Received ${t}`}else if(typeof t==="function"&&t.name){e+=` Received function ${t.name}`}else if(typeof t==="object"&&t!=null){if(t.constructor&&t.constructor.name){e+=` Received an instance of ${t.constructor.name}`}}return e}t["default"]=(e,...t)=>message("Key must be ",e,...t);function withAlg(e,t,...n){return message(`Key for the ${e} algorithm must be `,t,...n)}t.withAlg=withAlg},6063:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const isDisjoint=(...e)=>{const t=e.filter(Boolean);if(t.length===0||t.length===1){return true}let n;for(const e of t){const t=Object.keys(e);if(!n||n.size===0){n=new Set(t);continue}for(const e of t){if(n.has(e)){return false}n.add(e)}}return true};t["default"]=isDisjoint},9127:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});function isObjectLike(e){return typeof e==="object"&&e!==null}function isObject(e){if(!isObjectLike(e)||Object.prototype.toString.call(e)!=="[object Object]"){return false}if(Object.getPrototypeOf(e)===null){return true}let t=e;while(Object.getPrototypeOf(t)!==null){t=Object.getPrototypeOf(t)}return Object.getPrototypeOf(e)===t}t["default"]=isObject},4630:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.bitLength=void 0;const s=n(4419);const i=n(5770);function bitLength(e){switch(e){case"A128GCM":case"A128GCMKW":case"A192GCM":case"A192GCMKW":case"A256GCM":case"A256GCMKW":return 96;case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return 128;default:throw new s.JOSENotSupported(`Unsupported JWE Algorithm: ${e}`)}}t.bitLength=bitLength;t["default"]=e=>(0,i.default)(new Uint8Array(bitLength(e)>>3))},7274:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4419);const i=n(1691);const r=n(4476);const o=n(7810);const A=n(9127);const normalizeTyp=e=>e.toLowerCase().replace(/^application\//,"");const checkAudiencePresence=(e,t)=>{if(typeof e==="string"){return t.includes(e)}if(Array.isArray(e)){return t.some(Set.prototype.has.bind(new Set(e)))}return false};t["default"]=(e,t,n={})=>{const{typ:a}=n;if(a&&(typeof e.typ!=="string"||normalizeTyp(e.typ)!==normalizeTyp(a))){throw new s.JWTClaimValidationFailed('unexpected "typ" JWT header value',"typ","check_failed")}let c;try{c=JSON.parse(i.decoder.decode(t))}catch{}if(!(0,A.default)(c)){throw new s.JWTInvalid("JWT Claims Set must be a top-level JSON object")}const{requiredClaims:u=[],issuer:l,subject:d,audience:p,maxTokenAge:g}=n;if(g!==undefined)u.push("iat");if(p!==undefined)u.push("aud");if(d!==undefined)u.push("sub");if(l!==undefined)u.push("iss");for(const e of new Set(u.reverse())){if(!(e in c)){throw new s.JWTClaimValidationFailed(`missing required "${e}" claim`,e,"missing")}}if(l&&!(Array.isArray(l)?l:[l]).includes(c.iss)){throw new s.JWTClaimValidationFailed('unexpected "iss" claim value',"iss","check_failed")}if(d&&c.sub!==d){throw new s.JWTClaimValidationFailed('unexpected "sub" claim value',"sub","check_failed")}if(p&&!checkAudiencePresence(c.aud,typeof p==="string"?[p]:p)){throw new s.JWTClaimValidationFailed('unexpected "aud" claim value',"aud","check_failed")}let h;switch(typeof n.clockTolerance){case"string":h=(0,o.default)(n.clockTolerance);break;case"number":h=n.clockTolerance;break;case"undefined":h=0;break;default:throw new TypeError("Invalid clockTolerance option type")}const{currentDate:f}=n;const E=(0,r.default)(f||new Date);if((c.iat!==undefined||g)&&typeof c.iat!=="number"){throw new s.JWTClaimValidationFailed('"iat" claim must be a number',"iat","invalid")}if(c.nbf!==undefined){if(typeof c.nbf!=="number"){throw new s.JWTClaimValidationFailed('"nbf" claim must be a number',"nbf","invalid")}if(c.nbf>E+h){throw new s.JWTClaimValidationFailed('"nbf" claim timestamp check failed',"nbf","check_failed")}}if(c.exp!==undefined){if(typeof c.exp!=="number"){throw new s.JWTClaimValidationFailed('"exp" claim must be a number',"exp","invalid")}if(c.exp<=E-h){throw new s.JWTExpired('"exp" claim timestamp check failed',"exp","check_failed")}}if(g){const e=E-c.iat;const t=typeof g==="number"?g:(0,o.default)(g);if(e-h>t){throw new s.JWTExpired('"iat" claim timestamp check failed (too far in the past)',"iat","check_failed")}if(e<0-h){throw new s.JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)',"iat","check_failed")}}return c}},7810:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const n=60;const s=n*60;const i=s*24;const r=i*7;const o=i*365.25;const A=/^(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)$/i;t["default"]=e=>{const t=A.exec(e);if(!t){throw new TypeError("Invalid time period format")}const a=parseFloat(t[1]);const c=t[2].toLowerCase();switch(c){case"sec":case"secs":case"second":case"seconds":case"s":return Math.round(a);case"minute":case"minutes":case"min":case"mins":case"m":return Math.round(a*n);case"hour":case"hours":case"hr":case"hrs":case"h":return Math.round(a*s);case"day":case"days":case"d":return Math.round(a*i);case"week":case"weeks":case"w":return Math.round(a*r);default:return Math.round(a*o)}}},5148:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const validateAlgorithms=(e,t)=>{if(t!==undefined&&(!Array.isArray(t)||t.some((e=>typeof e!=="string")))){throw new TypeError(`"${e}" option must be an array of strings`)}if(!t){return undefined}return new Set(t)};t["default"]=validateAlgorithms},863:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4419);function validateCrit(e,t,n,i,r){if(r.crit!==undefined&&i.crit===undefined){throw new e('"crit" (Critical) Header Parameter MUST be integrity protected')}if(!i||i.crit===undefined){return new Set}if(!Array.isArray(i.crit)||i.crit.length===0||i.crit.some((e=>typeof e!=="string"||e.length===0))){throw new e('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present')}let o;if(n!==undefined){o=new Map([...Object.entries(n),...t.entries()])}else{o=t}for(const t of i.crit){if(!o.has(t)){throw new s.JOSENotSupported(`Extension Header Parameter "${t}" is not recognized`)}if(r[t]===undefined){throw new e(`Extension Header Parameter "${t}" is missing`)}else if(o.get(t)&&i[t]===undefined){throw new e(`Extension Header Parameter "${t}" MUST be integrity protected`)}}return new Set(i.crit)}t["default"]=validateCrit},6083:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.unwrap=t.wrap=void 0;const s=n(4300);const i=n(6113);const r=n(4419);const o=n(1691);const A=n(6852);const a=n(3386);const c=n(2768);const u=n(1146);const l=n(4618);const d=n(7947);function checkKeySize(e,t){if(e.symmetricKeySize<<3!==parseInt(t.slice(1,4),10)){throw new TypeError(`Invalid key size for alg: ${t}`)}}function ensureKeyObject(e,t,n){if((0,c.default)(e)){return e}if(e instanceof Uint8Array){return(0,i.createSecretKey)(e)}if((0,A.isCryptoKey)(e)){(0,a.checkEncCryptoKey)(e,t,n);return i.KeyObject.from(e)}throw new TypeError((0,u.default)(e,...d.types,"Uint8Array"))}const wrap=(e,t,n)=>{const A=parseInt(e.slice(1,4),10);const a=`aes${A}-wrap`;if(!(0,l.default)(a)){throw new r.JOSENotSupported(`alg ${e} is not supported either by JOSE or your javascript runtime`)}const c=ensureKeyObject(t,e,"wrapKey");checkKeySize(c,e);const u=(0,i.createCipheriv)(a,c,s.Buffer.alloc(8,166));return(0,o.concat)(u.update(n),u.final())};t.wrap=wrap;const unwrap=(e,t,n)=>{const A=parseInt(e.slice(1,4),10);const a=`aes${A}-wrap`;if(!(0,l.default)(a)){throw new r.JOSENotSupported(`alg ${e} is not supported either by JOSE or your javascript runtime`)}const c=ensureKeyObject(t,e,"unwrapKey");checkKeySize(c,e);const u=(0,i.createDecipheriv)(a,c,s.Buffer.alloc(8,166));return(0,o.concat)(u.update(n),u.final())};t.unwrap=unwrap},858:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.fromX509=t.fromSPKI=t.fromPKCS8=t.toPKCS8=t.toSPKI=void 0;const s=n(6113);const i=n(4300);const r=n(6852);const o=n(2768);const A=n(1146);const a=n(7947);const genericExport=(e,t,n)=>{let i;if((0,r.isCryptoKey)(n)){if(!n.extractable){throw new TypeError("CryptoKey is not extractable")}i=s.KeyObject.from(n)}else if((0,o.default)(n)){i=n}else{throw new TypeError((0,A.default)(n,...a.types))}if(i.type!==e){throw new TypeError(`key is not a ${e} key`)}return i.export({format:"pem",type:t})};const toSPKI=e=>genericExport("public","spki",e);t.toSPKI=toSPKI;const toPKCS8=e=>genericExport("private","pkcs8",e);t.toPKCS8=toPKCS8;const fromPKCS8=e=>(0,s.createPrivateKey)({key:i.Buffer.from(e.replace(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g,""),"base64"),type:"pkcs8",format:"der"});t.fromPKCS8=fromPKCS8;const fromSPKI=e=>(0,s.createPublicKey)({key:i.Buffer.from(e.replace(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g,""),"base64"),type:"spki",format:"der"});t.fromSPKI=fromSPKI;const fromX509=e=>(0,s.createPublicKey)({key:e,type:"spki",format:"pem"});t.fromX509=fromX509},3888:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const n=2;const s=48;class Asn1SequenceDecoder{constructor(e){if(e[0]!==s){throw new TypeError}this.buffer=e;this.offset=1;const t=this.decodeLength();if(t!==e.length-this.offset){throw new TypeError}}decodeLength(){let e=this.buffer[this.offset++];if(e&128){const t=e&~128;e=0;for(let n=0;n{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4300);const i=n(4419);const r=2;const o=3;const A=4;const a=48;const c=s.Buffer.from([0]);const u=s.Buffer.from([r]);const l=s.Buffer.from([o]);const d=s.Buffer.from([a]);const p=s.Buffer.from([A]);const encodeLength=e=>{if(e<128)return s.Buffer.from([e]);const t=s.Buffer.alloc(5);t.writeUInt32BE(e,1);let n=1;while(t[n]===0)n++;t[n-1]=128|5-n;return t.slice(n-1)};const g=new Map([["P-256",s.Buffer.from("06 08 2A 86 48 CE 3D 03 01 07".replace(/ /g,""),"hex")],["secp256k1",s.Buffer.from("06 05 2B 81 04 00 0A".replace(/ /g,""),"hex")],["P-384",s.Buffer.from("06 05 2B 81 04 00 22".replace(/ /g,""),"hex")],["P-521",s.Buffer.from("06 05 2B 81 04 00 23".replace(/ /g,""),"hex")],["ecPublicKey",s.Buffer.from("06 07 2A 86 48 CE 3D 02 01".replace(/ /g,""),"hex")],["X25519",s.Buffer.from("06 03 2B 65 6E".replace(/ /g,""),"hex")],["X448",s.Buffer.from("06 03 2B 65 6F".replace(/ /g,""),"hex")],["Ed25519",s.Buffer.from("06 03 2B 65 70".replace(/ /g,""),"hex")],["Ed448",s.Buffer.from("06 03 2B 65 71".replace(/ /g,""),"hex")]]);class DumbAsn1Encoder{constructor(){this.length=0;this.elements=[]}oidFor(e){const t=g.get(e);if(!t){throw new i.JOSENotSupported("Invalid or unsupported OID")}this.elements.push(t);this.length+=t.length}zero(){this.elements.push(u,s.Buffer.from([1]),c);this.length+=3}one(){this.elements.push(u,s.Buffer.from([1]),s.Buffer.from([1]));this.length+=3}unsignedInteger(e){if(e[0]&128){const t=encodeLength(e.length+1);this.elements.push(u,t,c,e);this.length+=2+t.length+e.length}else{let t=0;while(e[t]===0&&(e[t+1]&128)===0)t++;const n=encodeLength(e.length-t);this.elements.push(u,encodeLength(e.length-t),e.slice(t));this.length+=1+n.length+e.length-t}}octStr(e){const t=encodeLength(e.length);this.elements.push(p,encodeLength(e.length),e);this.length+=1+t.length+e.length}bitStr(e){const t=encodeLength(e.length+1);this.elements.push(l,encodeLength(e.length+1),c,e);this.length+=1+t.length+e.length+1}add(e){this.elements.push(e);this.length+=e.length}end(e=d){const t=encodeLength(this.length);return s.Buffer.concat([e,t,...this.elements],1+t.length+this.length)}}t["default"]=DumbAsn1Encoder},518:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.decode=t.encode=t.encodeBase64=t.decodeBase64=void 0;const s=n(4300);const i=n(1691);let r;function normalize(e){let t=e;if(t instanceof Uint8Array){t=i.decoder.decode(t)}return t}if(s.Buffer.isEncoding("base64url")){t.encode=r=e=>s.Buffer.from(e).toString("base64url")}else{t.encode=r=e=>s.Buffer.from(e).toString("base64").replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}const decodeBase64=e=>s.Buffer.from(e,"base64");t.decodeBase64=decodeBase64;const encodeBase64=e=>s.Buffer.from(e).toString("base64");t.encodeBase64=encodeBase64;const decode=e=>s.Buffer.from(normalize(e),"base64");t.decode=decode},4519:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(1691);function cbcTag(e,t,n,r,o,A){const a=(0,i.concat)(e,t,n,(0,i.uint64be)(e.length<<3));const c=(0,s.createHmac)(`sha${r}`,o);c.update(a);return c.digest().slice(0,A>>3)}t["default"]=cbcTag},4047:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4419);const i=n(2768);const checkCekLength=(e,t)=>{let n;switch(e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":n=parseInt(e.slice(-3),10);break;case"A128GCM":case"A192GCM":case"A256GCM":n=parseInt(e.slice(1,4),10);break;default:throw new s.JOSENotSupported(`Content Encryption Algorithm ${e} is not supported either by JOSE or your javascript runtime`)}if(t instanceof Uint8Array){const e=t.byteLength<<3;if(e!==n){throw new s.JWEInvalid(`Invalid Content Encryption Key length. Expected ${n} bits, got ${e} bits`)}return}if((0,i.default)(t)&&t.type==="secret"){const e=t.symmetricKeySize<<3;if(e!==n){throw new s.JWEInvalid(`Invalid Content Encryption Key length. Expected ${n} bits, got ${e} bits`)}return}throw new TypeError("Invalid Content Encryption Key type")};t["default"]=checkCekLength},122:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.setModulusLength=t.weakMap=void 0;t.weakMap=new WeakMap;const getLength=(e,t)=>{let n=e.readUInt8(1);if((n&128)===0){if(t===0){return n}return getLength(e.subarray(2+n),t-1)}const s=n&127;n=0;for(let t=0;t{const n=e.readUInt8(1);if((n&128)===0){return getLength(e.subarray(2),t)}const s=n&127;return getLength(e.subarray(2+s),t)};const getModulusLength=e=>{var n,s;if(t.weakMap.has(e)){return t.weakMap.get(e)}const i=(s=(n=e.asymmetricKeyDetails)===null||n===void 0?void 0:n.modulusLength)!==null&&s!==void 0?s:getLengthOfSeqIndex(e.export({format:"der",type:"pkcs1"}),e.type==="private"?1:0)-1<<3;t.weakMap.set(e,i);return i};const setModulusLength=(e,n)=>{t.weakMap.set(e,n)};t.setModulusLength=setModulusLength;t["default"]=(e,t)=>{if(getModulusLength(e)<2048){throw new TypeError(`${t} requires key modulusLength to be 2048 bits or larger`)}}},4618:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);let i;t["default"]=e=>{i||(i=new Set((0,s.getCiphers)()));return i.has(e)}},6137:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(1120);const r=n(4047);const o=n(1691);const A=n(4419);const a=n(5390);const c=n(4519);const u=n(6852);const l=n(3386);const d=n(2768);const p=n(1146);const g=n(4618);const h=n(7947);function cbcDecrypt(e,t,n,i,r,u){const l=parseInt(e.slice(1,4),10);if((0,d.default)(t)){t=t.export()}const p=t.subarray(l>>3);const h=t.subarray(0,l>>3);const f=parseInt(e.slice(-3),10);const E=`aes-${l}-cbc`;if(!(0,g.default)(E)){throw new A.JOSENotSupported(`alg ${e} is not supported by your javascript runtime`)}const m=(0,c.default)(u,i,n,f,h,l);let C;try{C=(0,a.default)(r,m)}catch{}if(!C){throw new A.JWEDecryptionFailed}let Q;try{const e=(0,s.createDecipheriv)(E,p,i);Q=(0,o.concat)(e.update(n),e.final())}catch{}if(!Q){throw new A.JWEDecryptionFailed}return Q}function gcmDecrypt(e,t,n,i,r,o){const a=parseInt(e.slice(1,4),10);const c=`aes-${a}-gcm`;if(!(0,g.default)(c)){throw new A.JOSENotSupported(`alg ${e} is not supported by your javascript runtime`)}try{const e=(0,s.createDecipheriv)(c,t,i,{authTagLength:16});e.setAuthTag(r);if(o.byteLength){e.setAAD(o,{plaintextLength:n.length})}const A=e.update(n);e.final();return A}catch{throw new A.JWEDecryptionFailed}}const decrypt=(e,t,n,o,a,c)=>{let g;if((0,u.isCryptoKey)(t)){(0,l.checkEncCryptoKey)(t,e,"decrypt");g=s.KeyObject.from(t)}else if(t instanceof Uint8Array||(0,d.default)(t)){g=t}else{throw new TypeError((0,p.default)(t,...h.types,"Uint8Array"))}(0,r.default)(e,g);(0,i.default)(e,o);switch(e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return cbcDecrypt(e,g,n,o,a,c);case"A128GCM":case"A192GCM":case"A256GCM":return gcmDecrypt(e,g,n,o,a,c);default:throw new A.JOSENotSupported("Unsupported JWE Content Encryption Algorithm")}};t["default"]=decrypt},2355:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const digest=(e,t)=>(0,s.createHash)(e).update(t).digest();t["default"]=digest},4965:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4419);function dsaDigest(e){switch(e){case"PS256":case"RS256":case"ES256":case"ES256K":return"sha256";case"PS384":case"RS384":case"ES384":return"sha384";case"PS512":case"RS512":case"ES512":return"sha512";case"EdDSA":return undefined;default:throw new s.JOSENotSupported(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}t["default"]=dsaDigest},3706:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.ecdhAllowed=t.generateEpk=t.deriveKey=void 0;const s=n(6113);const i=n(3837);const r=n(9302);const o=n(1691);const A=n(4419);const a=n(6852);const c=n(3386);const u=n(2768);const l=n(1146);const d=n(7947);const p=(0,i.promisify)(s.generateKeyPair);async function deriveKey(e,t,n,i,r=new Uint8Array(0),A=new Uint8Array(0)){let p;if((0,a.isCryptoKey)(e)){(0,c.checkEncCryptoKey)(e,"ECDH");p=s.KeyObject.from(e)}else if((0,u.default)(e)){p=e}else{throw new TypeError((0,l.default)(e,...d.types))}let g;if((0,a.isCryptoKey)(t)){(0,c.checkEncCryptoKey)(t,"ECDH","deriveBits");g=s.KeyObject.from(t)}else if((0,u.default)(t)){g=t}else{throw new TypeError((0,l.default)(t,...d.types))}const h=(0,o.concat)((0,o.lengthAndInput)(o.encoder.encode(n)),(0,o.lengthAndInput)(r),(0,o.lengthAndInput)(A),(0,o.uint32be)(i));const f=(0,s.diffieHellman)({privateKey:g,publicKey:p});return(0,o.concatKdf)(f,i,h)}t.deriveKey=deriveKey;async function generateEpk(e){let t;if((0,a.isCryptoKey)(e)){t=s.KeyObject.from(e)}else if((0,u.default)(e)){t=e}else{throw new TypeError((0,l.default)(e,...d.types))}switch(t.asymmetricKeyType){case"x25519":return p("x25519");case"x448":{return p("x448")}case"ec":{const e=(0,r.default)(t);return p("ec",{namedCurve:e})}default:throw new A.JOSENotSupported("Invalid or unsupported EPK")}}t.generateEpk=generateEpk;const ecdhAllowed=e=>["P-256","P-384","P-521","X25519","X448"].includes((0,r.default)(e));t.ecdhAllowed=ecdhAllowed},6476:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(1120);const r=n(4047);const o=n(1691);const A=n(4519);const a=n(6852);const c=n(3386);const u=n(2768);const l=n(1146);const d=n(4419);const p=n(4618);const g=n(7947);function cbcEncrypt(e,t,n,i,r){const a=parseInt(e.slice(1,4),10);if((0,u.default)(n)){n=n.export()}const c=n.subarray(a>>3);const l=n.subarray(0,a>>3);const g=`aes-${a}-cbc`;if(!(0,p.default)(g)){throw new d.JOSENotSupported(`alg ${e} is not supported by your javascript runtime`)}const h=(0,s.createCipheriv)(g,c,i);const f=(0,o.concat)(h.update(t),h.final());const E=parseInt(e.slice(-3),10);const m=(0,A.default)(r,i,f,E,l,a);return{ciphertext:f,tag:m}}function gcmEncrypt(e,t,n,i,r){const o=parseInt(e.slice(1,4),10);const A=`aes-${o}-gcm`;if(!(0,p.default)(A)){throw new d.JOSENotSupported(`alg ${e} is not supported by your javascript runtime`)}const a=(0,s.createCipheriv)(A,n,i,{authTagLength:16});if(r.byteLength){a.setAAD(r,{plaintextLength:t.length})}const c=a.update(t);a.final();const u=a.getAuthTag();return{ciphertext:c,tag:u}}const encrypt=(e,t,n,o,A)=>{let p;if((0,a.isCryptoKey)(n)){(0,c.checkEncCryptoKey)(n,e,"encrypt");p=s.KeyObject.from(n)}else if(n instanceof Uint8Array||(0,u.default)(n)){p=n}else{throw new TypeError((0,l.default)(n,...g.types,"Uint8Array"))}(0,r.default)(e,p);(0,i.default)(e,o);switch(e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return cbcEncrypt(e,t,p,o,A);case"A128GCM":case"A192GCM":case"A256GCM":return gcmEncrypt(e,t,p,o,A);default:throw new d.JOSENotSupported("Unsupported JWE Content Encryption Algorithm")}};t["default"]=encrypt},3650:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(3685);const i=n(5687);const r=n(2361);const o=n(4419);const A=n(1691);const fetchJwks=async(e,t,n)=>{let a;switch(e.protocol){case"https:":a=i.get;break;case"http:":a=s.get;break;default:throw new TypeError("Unsupported URL protocol.")}const{agent:c,headers:u}=n;const l=a(e.href,{agent:c,timeout:t,headers:u});const[d]=await Promise.race([(0,r.once)(l,"response"),(0,r.once)(l,"timeout")]);if(!d){l.destroy();throw new o.JWKSTimeout}if(d.statusCode!==200){throw new o.JOSEError("Expected 200 OK from the JSON Web Key Set HTTP response")}const p=[];for await(const e of d){p.push(e)}try{return JSON.parse(A.decoder.decode((0,A.concat)(...p)))}catch{throw new o.JOSEError("Failed to parse the JSON Web Key Set HTTP response as JSON")}};t["default"]=fetchJwks},9737:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.jwkImport=t.jwkExport=t.rsaPssParams=t.oneShotCallback=void 0;const[n,s]=process.versions.node.split(".").map((e=>parseInt(e,10)));t.oneShotCallback=n>=16||n===15&&s>=13;t.rsaPssParams=!("electron"in process.versions)&&(n>=17||n===16&&s>=9);t.jwkExport=n>=16||n===15&&s>=9;t.jwkImport=n>=16||n===15&&s>=12},9378:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.generateKeyPair=t.generateSecret=void 0;const s=n(6113);const i=n(3837);const r=n(5770);const o=n(122);const A=n(4419);const a=(0,i.promisify)(s.generateKeyPair);async function generateSecret(e,t){let n;switch(e){case"HS256":case"HS384":case"HS512":case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":n=parseInt(e.slice(-3),10);break;case"A128KW":case"A192KW":case"A256KW":case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":case"A128GCM":case"A192GCM":case"A256GCM":n=parseInt(e.slice(1,4),10);break;default:throw new A.JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}return(0,s.createSecretKey)((0,r.default)(new Uint8Array(n>>3)))}t.generateSecret=generateSecret;async function generateKeyPair(e,t){var n,s;switch(e){case"RS256":case"RS384":case"RS512":case"PS256":case"PS384":case"PS512":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":case"RSA1_5":{const e=(n=t===null||t===void 0?void 0:t.modulusLength)!==null&&n!==void 0?n:2048;if(typeof e!=="number"||e<2048){throw new A.JOSENotSupported("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used")}const s=await a("rsa",{modulusLength:e,publicExponent:65537});(0,o.setModulusLength)(s.privateKey,e);(0,o.setModulusLength)(s.publicKey,e);return s}case"ES256":return a("ec",{namedCurve:"P-256"});case"ES256K":return a("ec",{namedCurve:"secp256k1"});case"ES384":return a("ec",{namedCurve:"P-384"});case"ES512":return a("ec",{namedCurve:"P-521"});case"EdDSA":{switch(t===null||t===void 0?void 0:t.crv){case undefined:case"Ed25519":return a("ed25519");case"Ed448":return a("ed448");default:throw new A.JOSENotSupported("Invalid or unsupported crv option provided, supported values are Ed25519 and Ed448")}}case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":const e=(s=t===null||t===void 0?void 0:t.crv)!==null&&s!==void 0?s:"P-256";switch(e){case undefined:case"P-256":case"P-384":case"P-521":return a("ec",{namedCurve:e});case"X25519":return a("x25519");case"X448":return a("x448");default:throw new A.JOSENotSupported("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448")}default:throw new A.JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}}t.generateKeyPair=generateKeyPair},9302:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.setCurve=t.weakMap=void 0;const s=n(4300);const i=n(6113);const r=n(4419);const o=n(6852);const A=n(2768);const a=n(1146);const c=n(7947);const u=s.Buffer.from([42,134,72,206,61,3,1,7]);const l=s.Buffer.from([43,129,4,0,34]);const d=s.Buffer.from([43,129,4,0,35]);const p=s.Buffer.from([43,129,4,0,10]);t.weakMap=new WeakMap;const namedCurveToJOSE=e=>{switch(e){case"prime256v1":return"P-256";case"secp384r1":return"P-384";case"secp521r1":return"P-521";case"secp256k1":return"secp256k1";default:throw new r.JOSENotSupported("Unsupported key curve for this operation")}};const getNamedCurve=(e,n)=>{var s;let g;if((0,o.isCryptoKey)(e)){g=i.KeyObject.from(e)}else if((0,A.default)(e)){g=e}else{throw new TypeError((0,a.default)(e,...c.types))}if(g.type==="secret"){throw new TypeError('only "private" or "public" type keys can be used for this operation')}switch(g.asymmetricKeyType){case"ed25519":case"ed448":return`Ed${g.asymmetricKeyType.slice(2)}`;case"x25519":case"x448":return`X${g.asymmetricKeyType.slice(1)}`;case"ec":{if(t.weakMap.has(g)){return t.weakMap.get(g)}let e=(s=g.asymmetricKeyDetails)===null||s===void 0?void 0:s.namedCurve;if(!e&&g.type==="private"){e=getNamedCurve((0,i.createPublicKey)(g),true)}else if(!e){const t=g.export({format:"der",type:"spki"});const n=t[1]<128?14:15;const s=t[n];const i=t.slice(n+1,n+1+s);if(i.equals(u)){e="prime256v1"}else if(i.equals(l)){e="secp384r1"}else if(i.equals(d)){e="secp521r1"}else if(i.equals(p)){e="secp256k1"}else{throw new r.JOSENotSupported("Unsupported key curve for this operation")}}if(n)return e;const o=namedCurveToJOSE(e);t.weakMap.set(g,o);return o}default:throw new TypeError("Invalid asymmetric key type for this operation")}};function setCurve(e,n){t.weakMap.set(e,n)}t.setCurve=setCurve;t["default"]=getNamedCurve},3170:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(6852);const r=n(3386);const o=n(1146);const A=n(7947);function getSignVerifyKey(e,t,n){if(t instanceof Uint8Array){if(!e.startsWith("HS")){throw new TypeError((0,o.default)(t,...A.types))}return(0,s.createSecretKey)(t)}if(t instanceof s.KeyObject){return t}if((0,i.isCryptoKey)(t)){(0,r.checkSigCryptoKey)(t,e,n);return s.KeyObject.from(t)}throw new TypeError((0,o.default)(t,...A.types,"Uint8Array"))}t["default"]=getSignVerifyKey},3811:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4419);function hmacDigest(e){switch(e){case"HS256":return"sha256";case"HS384":return"sha384";case"HS512":return"sha512";default:throw new s.JOSENotSupported(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}t["default"]=hmacDigest},7947:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.types=void 0;const s=n(6852);const i=n(2768);t["default"]=e=>(0,i.default)(e)||(0,s.isCryptoKey)(e);const r=["KeyObject"];t.types=r;if(globalThis.CryptoKey||(s.default===null||s.default===void 0?void 0:s.default.CryptoKey)){r.push("CryptoKey")}},2768:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(3837);t["default"]=i.types.isKeyObject?e=>i.types.isKeyObject(e):e=>e!=null&&e instanceof s.KeyObject},2659:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(4300);const i=n(6113);const r=n(518);const o=n(4419);const A=n(9302);const a=n(122);const c=n(3341);const u=n(9737);const parse=e=>{if(u.jwkImport&&e.kty!=="oct"){return e.d?(0,i.createPrivateKey)({format:"jwk",key:e}):(0,i.createPublicKey)({format:"jwk",key:e})}switch(e.kty){case"oct":{return(0,i.createSecretKey)((0,r.decode)(e.k))}case"RSA":{const t=new c.default;const n=e.d!==undefined;const r=s.Buffer.from(e.n,"base64");const o=s.Buffer.from(e.e,"base64");if(n){t.zero();t.unsignedInteger(r);t.unsignedInteger(o);t.unsignedInteger(s.Buffer.from(e.d,"base64"));t.unsignedInteger(s.Buffer.from(e.p,"base64"));t.unsignedInteger(s.Buffer.from(e.q,"base64"));t.unsignedInteger(s.Buffer.from(e.dp,"base64"));t.unsignedInteger(s.Buffer.from(e.dq,"base64"));t.unsignedInteger(s.Buffer.from(e.qi,"base64"))}else{t.unsignedInteger(r);t.unsignedInteger(o)}const A=t.end();const u={key:A,format:"der",type:"pkcs1"};const l=n?(0,i.createPrivateKey)(u):(0,i.createPublicKey)(u);(0,a.setModulusLength)(l,r.length<<3);return l}case"EC":{const t=new c.default;const n=e.d!==undefined;const r=s.Buffer.concat([s.Buffer.alloc(1,4),s.Buffer.from(e.x,"base64"),s.Buffer.from(e.y,"base64")]);if(n){t.zero();const n=new c.default;n.oidFor("ecPublicKey");n.oidFor(e.crv);t.add(n.end());const o=new c.default;o.one();o.octStr(s.Buffer.from(e.d,"base64"));const a=new c.default;a.bitStr(r);const u=a.end(s.Buffer.from([161]));o.add(u);const l=o.end();const d=new c.default;d.add(l);const p=d.end(s.Buffer.from([4]));t.add(p);const g=t.end();const h=(0,i.createPrivateKey)({key:g,format:"der",type:"pkcs8"});(0,A.setCurve)(h,e.crv);return h}const o=new c.default;o.oidFor("ecPublicKey");o.oidFor(e.crv);t.add(o.end());t.bitStr(r);const a=t.end();const u=(0,i.createPublicKey)({key:a,format:"der",type:"spki"});(0,A.setCurve)(u,e.crv);return u}case"OKP":{const t=new c.default;const n=e.d!==undefined;if(n){t.zero();const n=new c.default;n.oidFor(e.crv);t.add(n.end());const r=new c.default;r.octStr(s.Buffer.from(e.d,"base64"));const o=r.end(s.Buffer.from([4]));t.add(o);const A=t.end();return(0,i.createPrivateKey)({key:A,format:"der",type:"pkcs8"})}const r=new c.default;r.oidFor(e.crv);t.add(r.end());t.bitStr(s.Buffer.from(e.x,"base64"));const o=t.end();return(0,i.createPublicKey)({key:o,format:"der",type:"spki"})}default:throw new o.JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value')}};t["default"]=parse},997:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(518);const r=n(3888);const o=n(4419);const A=n(9302);const a=n(6852);const c=n(2768);const u=n(1146);const l=n(7947);const d=n(9737);const keyToJWK=e=>{let t;if((0,a.isCryptoKey)(e)){if(!e.extractable){throw new TypeError("CryptoKey is not extractable")}t=s.KeyObject.from(e)}else if((0,c.default)(e)){t=e}else if(e instanceof Uint8Array){return{kty:"oct",k:(0,i.encode)(e)}}else{throw new TypeError((0,u.default)(e,...l.types,"Uint8Array"))}if(d.jwkExport){if(t.type!=="secret"&&!["rsa","ec","ed25519","x25519","ed448","x448"].includes(t.asymmetricKeyType)){throw new o.JOSENotSupported("Unsupported key asymmetricKeyType")}return t.export({format:"jwk"})}switch(t.type){case"secret":return{kty:"oct",k:(0,i.encode)(t.export())};case"private":case"public":{switch(t.asymmetricKeyType){case"rsa":{const e=t.export({format:"der",type:"pkcs1"});const n=new r.default(e);if(t.type==="private"){n.unsignedInteger()}const s=(0,i.encode)(n.unsignedInteger());const o=(0,i.encode)(n.unsignedInteger());let A;if(t.type==="private"){A={d:(0,i.encode)(n.unsignedInteger()),p:(0,i.encode)(n.unsignedInteger()),q:(0,i.encode)(n.unsignedInteger()),dp:(0,i.encode)(n.unsignedInteger()),dq:(0,i.encode)(n.unsignedInteger()),qi:(0,i.encode)(n.unsignedInteger())}}n.end();return{kty:"RSA",n:s,e:o,...A}}case"ec":{const e=(0,A.default)(t);let n;let r;let a;switch(e){case"secp256k1":n=64;r=31+2;a=-1;break;case"P-256":n=64;r=34+2;a=-1;break;case"P-384":n=96;r=33+2;a=-3;break;case"P-521":n=132;r=33+2;a=-3;break;default:throw new o.JOSENotSupported("Unsupported curve")}if(t.type==="public"){const s=t.export({type:"spki",format:"der"});return{kty:"EC",crv:e,x:(0,i.encode)(s.subarray(-n,-n/2)),y:(0,i.encode)(s.subarray(-n/2))}}const c=t.export({type:"pkcs8",format:"der"});if(c.length<100){r+=a}return{...keyToJWK((0,s.createPublicKey)(t)),d:(0,i.encode)(c.subarray(r,r+n/2))}}case"ed25519":case"x25519":{const e=(0,A.default)(t);if(t.type==="public"){const n=t.export({type:"spki",format:"der"});return{kty:"OKP",crv:e,x:(0,i.encode)(n.subarray(-32))}}const n=t.export({type:"pkcs8",format:"der"});return{...keyToJWK((0,s.createPublicKey)(t)),d:(0,i.encode)(n.subarray(-32))}}case"ed448":case"x448":{const e=(0,A.default)(t);if(t.type==="public"){const n=t.export({type:"spki",format:"der"});return{kty:"OKP",crv:e,x:(0,i.encode)(n.subarray(e==="Ed448"?-57:-56))}}const n=t.export({type:"pkcs8",format:"der"});return{...keyToJWK((0,s.createPublicKey)(t)),d:(0,i.encode)(n.subarray(e==="Ed448"?-57:-56))}}default:throw new o.JOSENotSupported("Unsupported key asymmetricKeyType")}}default:throw new o.JOSENotSupported("Unsupported key type")}};t["default"]=keyToJWK},2413:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(9302);const r=n(4419);const o=n(122);const A=n(9737);const a={padding:s.constants.RSA_PKCS1_PSS_PADDING,saltLength:s.constants.RSA_PSS_SALTLEN_DIGEST};const c=new Map([["ES256","P-256"],["ES256K","secp256k1"],["ES384","P-384"],["ES512","P-521"]]);function keyForCrypto(e,t){switch(e){case"EdDSA":if(!["ed25519","ed448"].includes(t.asymmetricKeyType)){throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be ed25519 or ed448")}return t;case"RS256":case"RS384":case"RS512":if(t.asymmetricKeyType!=="rsa"){throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be rsa")}(0,o.default)(t,e);return t;case A.rsaPssParams&&"PS256":case A.rsaPssParams&&"PS384":case A.rsaPssParams&&"PS512":if(t.asymmetricKeyType==="rsa-pss"){const{hashAlgorithm:n,mgf1HashAlgorithm:s,saltLength:i}=t.asymmetricKeyDetails;const r=parseInt(e.slice(-3),10);if(n!==undefined&&(n!==`sha${r}`||s!==n)){throw new TypeError(`Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${e}`)}if(i!==undefined&&i>r>>3){throw new TypeError(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${e}`)}}else if(t.asymmetricKeyType!=="rsa"){throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss")}(0,o.default)(t,e);return{key:t,...a};case!A.rsaPssParams&&"PS256":case!A.rsaPssParams&&"PS384":case!A.rsaPssParams&&"PS512":if(t.asymmetricKeyType!=="rsa"){throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be rsa")}(0,o.default)(t,e);return{key:t,...a};case"ES256":case"ES256K":case"ES384":case"ES512":{if(t.asymmetricKeyType!=="ec"){throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be ec")}const n=(0,i.default)(t);const s=c.get(e);if(n!==s){throw new TypeError(`Invalid key curve for the algorithm, its curve must be ${s}, got ${n}`)}return{dsaEncoding:"ieee-p1363",key:t}}default:throw new r.JOSENotSupported(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}t["default"]=keyForCrypto},6898:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.decrypt=t.encrypt=void 0;const s=n(3837);const i=n(6113);const r=n(5770);const o=n(1691);const A=n(518);const a=n(6083);const c=n(3499);const u=n(6852);const l=n(3386);const d=n(2768);const p=n(1146);const g=n(7947);const h=(0,s.promisify)(i.pbkdf2);function getPassword(e,t){if((0,d.default)(e)){return e.export()}if(e instanceof Uint8Array){return e}if((0,u.isCryptoKey)(e)){(0,l.checkEncCryptoKey)(e,t,"deriveBits","deriveKey");return i.KeyObject.from(e).export()}throw new TypeError((0,p.default)(e,...g.types,"Uint8Array"))}const encrypt=async(e,t,n,s=2048,i=(0,r.default)(new Uint8Array(16)))=>{(0,c.default)(i);const u=(0,o.p2s)(e,i);const l=parseInt(e.slice(13,16),10)>>3;const d=getPassword(t,e);const p=await h(d,u,s,l,`sha${e.slice(8,11)}`);const g=await(0,a.wrap)(e.slice(-6),p,n);return{encryptedKey:g,p2c:s,p2s:(0,A.encode)(i)}};t.encrypt=encrypt;const decrypt=async(e,t,n,s,i)=>{(0,c.default)(i);const r=(0,o.p2s)(e,i);const A=parseInt(e.slice(13,16),10)>>3;const u=getPassword(t,e);const l=await h(u,r,s,A,`sha${e.slice(8,11)}`);return(0,a.unwrap)(e.slice(-6),l,n)};t.decrypt=decrypt},5770:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=n(6113);Object.defineProperty(t,"default",{enumerable:true,get:function(){return s.randomFillSync}})},9526:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.decrypt=t.encrypt=void 0;const s=n(6113);const i=n(122);const r=n(6852);const o=n(3386);const A=n(2768);const a=n(1146);const c=n(7947);const checkKey=(e,t)=>{if(e.asymmetricKeyType!=="rsa"){throw new TypeError("Invalid key for this operation, its asymmetricKeyType must be rsa")}(0,i.default)(e,t)};const resolvePadding=e=>{switch(e){case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":return s.constants.RSA_PKCS1_OAEP_PADDING;case"RSA1_5":return s.constants.RSA_PKCS1_PADDING;default:return undefined}};const resolveOaepHash=e=>{switch(e){case"RSA-OAEP":return"sha1";case"RSA-OAEP-256":return"sha256";case"RSA-OAEP-384":return"sha384";case"RSA-OAEP-512":return"sha512";default:return undefined}};function ensureKeyObject(e,t,...n){if((0,A.default)(e)){return e}if((0,r.isCryptoKey)(e)){(0,o.checkEncCryptoKey)(e,t,...n);return s.KeyObject.from(e)}throw new TypeError((0,a.default)(e,...c.types))}const encrypt=(e,t,n)=>{const i=resolvePadding(e);const r=resolveOaepHash(e);const o=ensureKeyObject(t,e,"wrapKey","encrypt");checkKey(o,e);return(0,s.publicEncrypt)({key:o,oaepHash:r,padding:i},n)};t.encrypt=encrypt;const decrypt=(e,t,n)=>{const i=resolvePadding(e);const r=resolveOaepHash(e);const o=ensureKeyObject(t,e,"unwrapKey","decrypt");checkKey(o,e);return(0,s.privateDecrypt)({key:o,oaepHash:r,padding:i},n)};t.decrypt=decrypt},1622:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]="node:crypto"},9935:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(3837);const r=n(4965);const o=n(3811);const A=n(2413);const a=n(3170);let c;if(s.sign.length>3){c=(0,i.promisify)(s.sign)}else{c=s.sign}const sign=async(e,t,n)=>{const i=(0,a.default)(e,t,"sign");if(e.startsWith("HS")){const t=s.createHmac((0,o.default)(e),i);t.update(n);return t.digest()}return c((0,r.default)(e),n,(0,A.default)(e,i))};t["default"]=sign},5390:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=s.timingSafeEqual;t["default"]=i},3569:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(6113);const i=n(3837);const r=n(4965);const o=n(2413);const A=n(9935);const a=n(3170);const c=n(9737);let u;if(s.verify.length>4&&c.oneShotCallback){u=(0,i.promisify)(s.verify)}else{u=s.verify}const verify=async(e,t,n,i)=>{const c=(0,a.default)(e,t,"verify");if(e.startsWith("HS")){const t=await(0,A.default)(e,c,i);const r=n;try{return s.timingSafeEqual(r,t)}catch{return false}}const l=(0,r.default)(e);const d=(0,o.default)(e,c);try{return await u(l,i,d,n)}catch{return false}};t["default"]=verify},6852:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.isCryptoKey=void 0;const s=n(6113);const i=n(3837);const r=s.webcrypto;t["default"]=r;t.isCryptoKey=i.types.isCryptoKey?e=>i.types.isCryptoKey(e):e=>false},7022:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.deflate=t.inflate=void 0;const s=n(3837);const i=n(9796);const r=(0,s.promisify)(i.inflateRaw);const o=(0,s.promisify)(i.deflateRaw);const inflate=e=>r(e);t.inflate=inflate;const deflate=e=>o(e);t.deflate=deflate},3238:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.decode=t.encode=void 0;const s=n(518);t.encode=s.encode;t.decode=s.decode},5611:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.decodeJwt=void 0;const s=n(3238);const i=n(1691);const r=n(9127);const o=n(4419);function decodeJwt(e){if(typeof e!=="string")throw new o.JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");const{1:t,length:n}=e.split(".");if(n===5)throw new o.JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");if(n!==3)throw new o.JWTInvalid("Invalid JWT");if(!t)throw new o.JWTInvalid("JWTs must contain a payload");let A;try{A=(0,s.decode)(t)}catch{throw new o.JWTInvalid("Failed to base64url decode the payload")}let a;try{a=JSON.parse(i.decoder.decode(A))}catch{throw new o.JWTInvalid("Failed to parse the decoded payload as JSON")}if(!(0,r.default)(a))throw new o.JWTInvalid("Invalid JWT Claims Set");return a}t.decodeJwt=decodeJwt},3991:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.decodeProtectedHeader=void 0;const s=n(3238);const i=n(1691);const r=n(9127);function decodeProtectedHeader(e){let t;if(typeof e==="string"){const n=e.split(".");if(n.length===3||n.length===5){[t]=n}}else if(typeof e==="object"&&e){if("protected"in e){t=e.protected}else{throw new TypeError("Token does not contain a Protected Header")}}try{if(typeof t!=="string"||!t){throw new Error}const e=JSON.parse(i.decoder.decode((0,s.decode)(t)));if(!(0,r.default)(e)){throw new Error}return e}catch{throw new TypeError("Invalid Token or Protected Header formatting")}}t.decodeProtectedHeader=decodeProtectedHeader},4419:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.JWSSignatureVerificationFailed=t.JWKSTimeout=t.JWKSMultipleMatchingKeys=t.JWKSNoMatchingKey=t.JWKSInvalid=t.JWKInvalid=t.JWTInvalid=t.JWSInvalid=t.JWEInvalid=t.JWEDecryptionFailed=t.JOSENotSupported=t.JOSEAlgNotAllowed=t.JWTExpired=t.JWTClaimValidationFailed=t.JOSEError=void 0;class JOSEError extends Error{static get code(){return"ERR_JOSE_GENERIC"}constructor(e){var t;super(e);this.code="ERR_JOSE_GENERIC";this.name=this.constructor.name;(t=Error.captureStackTrace)===null||t===void 0?void 0:t.call(Error,this,this.constructor)}}t.JOSEError=JOSEError;class JWTClaimValidationFailed extends JOSEError{static get code(){return"ERR_JWT_CLAIM_VALIDATION_FAILED"}constructor(e,t="unspecified",n="unspecified"){super(e);this.code="ERR_JWT_CLAIM_VALIDATION_FAILED";this.claim=t;this.reason=n}}t.JWTClaimValidationFailed=JWTClaimValidationFailed;class JWTExpired extends JOSEError{static get code(){return"ERR_JWT_EXPIRED"}constructor(e,t="unspecified",n="unspecified"){super(e);this.code="ERR_JWT_EXPIRED";this.claim=t;this.reason=n}}t.JWTExpired=JWTExpired;class JOSEAlgNotAllowed extends JOSEError{constructor(){super(...arguments);this.code="ERR_JOSE_ALG_NOT_ALLOWED"}static get code(){return"ERR_JOSE_ALG_NOT_ALLOWED"}}t.JOSEAlgNotAllowed=JOSEAlgNotAllowed;class JOSENotSupported extends JOSEError{constructor(){super(...arguments);this.code="ERR_JOSE_NOT_SUPPORTED"}static get code(){return"ERR_JOSE_NOT_SUPPORTED"}}t.JOSENotSupported=JOSENotSupported;class JWEDecryptionFailed extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWE_DECRYPTION_FAILED";this.message="decryption operation failed"}static get code(){return"ERR_JWE_DECRYPTION_FAILED"}}t.JWEDecryptionFailed=JWEDecryptionFailed;class JWEInvalid extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWE_INVALID"}static get code(){return"ERR_JWE_INVALID"}}t.JWEInvalid=JWEInvalid;class JWSInvalid extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWS_INVALID"}static get code(){return"ERR_JWS_INVALID"}}t.JWSInvalid=JWSInvalid;class JWTInvalid extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWT_INVALID"}static get code(){return"ERR_JWT_INVALID"}}t.JWTInvalid=JWTInvalid;class JWKInvalid extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWK_INVALID"}static get code(){return"ERR_JWK_INVALID"}}t.JWKInvalid=JWKInvalid;class JWKSInvalid extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWKS_INVALID"}static get code(){return"ERR_JWKS_INVALID"}}t.JWKSInvalid=JWKSInvalid;class JWKSNoMatchingKey extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWKS_NO_MATCHING_KEY";this.message="no applicable key found in the JSON Web Key Set"}static get code(){return"ERR_JWKS_NO_MATCHING_KEY"}}t.JWKSNoMatchingKey=JWKSNoMatchingKey;class JWKSMultipleMatchingKeys extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWKS_MULTIPLE_MATCHING_KEYS";this.message="multiple matching keys found in the JSON Web Key Set"}static get code(){return"ERR_JWKS_MULTIPLE_MATCHING_KEYS"}}t.JWKSMultipleMatchingKeys=JWKSMultipleMatchingKeys;Symbol.asyncIterator;class JWKSTimeout extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWKS_TIMEOUT";this.message="request timed out"}static get code(){return"ERR_JWKS_TIMEOUT"}}t.JWKSTimeout=JWKSTimeout;class JWSSignatureVerificationFailed extends JOSEError{constructor(){super(...arguments);this.code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED";this.message="signature verification failed"}static get code(){return"ERR_JWS_SIGNATURE_VERIFICATION_FAILED"}}t.JWSSignatureVerificationFailed=JWSSignatureVerificationFailed},1173:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});const s=n(1622);t["default"]=s.default},7426:(e,t,n)=>{ -/*! - * mime-db - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2015-2022 Douglas Christopher Wilson - * MIT Licensed - */ -e.exports=n(3765)},3583:(e,t,n)=>{"use strict"; -/*! - * mime-types - * Copyright(c) 2014 Jonathan Ong - * Copyright(c) 2015 Douglas Christopher Wilson - * MIT Licensed - */var s=n(7426);var i=n(1017).extname;var r=/^\s*([^;\s]*)(?:;|\s|$)/;var o=/^text\//i;t.charset=charset;t.charsets={lookup:charset};t.contentType=contentType;t.extension=extension;t.extensions=Object.create(null);t.lookup=lookup;t.types=Object.create(null);populateMaps(t.extensions,t.types);function charset(e){if(!e||typeof e!=="string"){return false}var t=r.exec(e);var n=t&&s[t[1].toLowerCase()];if(n&&n.charset){return n.charset}if(t&&o.test(t[1])){return"UTF-8"}return false}function contentType(e){if(!e||typeof e!=="string"){return false}var n=e.indexOf("/")===-1?t.lookup(e):e;if(!n){return false}if(n.indexOf("charset")===-1){var s=t.charset(n);if(s)n+="; charset="+s.toLowerCase()}return n}function extension(e){if(!e||typeof e!=="string"){return false}var n=r.exec(e);var s=n&&t.extensions[n[1].toLowerCase()];if(!s||!s.length){return false}return s[0]}function lookup(e){if(!e||typeof e!=="string"){return false}var n=i("x."+e).toLowerCase().substr(1);if(!n){return false}return t.types[n]||false}function populateMaps(e,t){var n=["nginx","apache",undefined,"iana"];Object.keys(s).forEach((function forEachMimeType(i){var r=s[i];var o=r.extensions;if(!o||!o.length){return}e[i]=o;for(var A=0;Au||c===u&&t[a].substr(0,12)==="application/")){continue}}t[a]=i}}))}},3329:(e,t,n)=>{"use strict";var s=n(7310).parse;var i={ftp:21,gopher:70,http:80,https:443,ws:80,wss:443};var r=String.prototype.endsWith||function(e){return e.length<=this.length&&this.indexOf(e,this.length-e.length)!==-1};function getProxyForUrl(e){var t=typeof e==="string"?s(e):e||{};var n=t.protocol;var r=t.host;var o=t.port;if(typeof r!=="string"||!r||typeof n!=="string"){return""}n=n.split(":",1)[0];r=r.replace(/:\d*$/,"");o=parseInt(o)||i[n]||0;if(!shouldProxy(r,o)){return""}var A=getEnv("npm_config_"+n+"_proxy")||getEnv(n+"_proxy")||getEnv("npm_config_proxy")||getEnv("all_proxy");if(A&&A.indexOf("://")===-1){A=n+"://"+A}return A}function shouldProxy(e,t){var n=(getEnv("npm_config_no_proxy")||getEnv("no_proxy")).toLowerCase();if(!n){return true}if(n==="*"){return false}return n.split(/[,\s]/).every((function(n){if(!n){return true}var s=n.match(/^(.+):(\d+)$/);var i=s?s[1]:n;var o=s?parseInt(s[2]):0;if(o&&o!==t){return true}if(!/^[.*]/.test(i)){return e!==i}if(i.charAt(0)==="*"){i=i.slice(1)}return!r.call(e,i)}))}function getEnv(e){return process.env[e.toLowerCase()]||process.env[e.toUpperCase()]||""}t.getProxyForUrl=getProxyForUrl},4294:(e,t,n)=>{e.exports=n(4219)},4219:(e,t,n)=>{"use strict";var s=n(1808);var i=n(4404);var r=n(3685);var o=n(5687);var A=n(2361);var a=n(9491);var c=n(3837);t.httpOverHttp=httpOverHttp;t.httpsOverHttp=httpsOverHttp;t.httpOverHttps=httpOverHttps;t.httpsOverHttps=httpsOverHttps;function httpOverHttp(e){var t=new TunnelingAgent(e);t.request=r.request;return t}function httpsOverHttp(e){var t=new TunnelingAgent(e);t.request=r.request;t.createSocket=createSecureSocket;t.defaultPort=443;return t}function httpOverHttps(e){var t=new TunnelingAgent(e);t.request=o.request;return t}function httpsOverHttps(e){var t=new TunnelingAgent(e);t.request=o.request;t.createSocket=createSecureSocket;t.defaultPort=443;return t}function TunnelingAgent(e){var t=this;t.options=e||{};t.proxyOptions=t.options.proxy||{};t.maxSockets=t.options.maxSockets||r.Agent.defaultMaxSockets;t.requests=[];t.sockets=[];t.on("free",(function onFree(e,n,s,i){var r=toOptions(n,s,i);for(var o=0,A=t.requests.length;o=this.maxSockets){i.requests.push(r);return}i.createSocket(r,(function(t){t.on("free",onFree);t.on("close",onCloseOrRemove);t.on("agentRemove",onCloseOrRemove);e.onSocket(t);function onFree(){i.emit("free",t,r)}function onCloseOrRemove(e){i.removeSocket(t);t.removeListener("free",onFree);t.removeListener("close",onCloseOrRemove);t.removeListener("agentRemove",onCloseOrRemove)}}))};TunnelingAgent.prototype.createSocket=function createSocket(e,t){var n=this;var s={};n.sockets.push(s);var i=mergeOptions({},n.proxyOptions,{method:"CONNECT",path:e.host+":"+e.port,agent:false,headers:{host:e.host+":"+e.port}});if(e.localAddress){i.localAddress=e.localAddress}if(i.proxyAuth){i.headers=i.headers||{};i.headers["Proxy-Authorization"]="Basic "+new Buffer(i.proxyAuth).toString("base64")}u("making CONNECT request");var r=n.request(i);r.useChunkedEncodingByDefault=false;r.once("response",onResponse);r.once("upgrade",onUpgrade);r.once("connect",onConnect);r.once("error",onError);r.end();function onResponse(e){e.upgrade=true}function onUpgrade(e,t,n){process.nextTick((function(){onConnect(e,t,n)}))}function onConnect(i,o,A){r.removeAllListeners();o.removeAllListeners();if(i.statusCode!==200){u("tunneling socket could not be established, statusCode=%d",i.statusCode);o.destroy();var a=new Error("tunneling socket could not be established, "+"statusCode="+i.statusCode);a.code="ECONNRESET";e.request.emit("error",a);n.removeSocket(s);return}if(A.length>0){u("got illegal response body from proxy");o.destroy();var a=new Error("got illegal response body from proxy");a.code="ECONNRESET";e.request.emit("error",a);n.removeSocket(s);return}u("tunneling connection has established");n.sockets[n.sockets.indexOf(s)]=o;return t(o)}function onError(t){r.removeAllListeners();u("tunneling socket could not be established, cause=%s\n",t.message,t.stack);var i=new Error("tunneling socket could not be established, "+"cause="+t.message);i.code="ECONNRESET";e.request.emit("error",i);n.removeSocket(s)}};TunnelingAgent.prototype.removeSocket=function removeSocket(e){var t=this.sockets.indexOf(e);if(t===-1){return}this.sockets.splice(t,1);var n=this.requests.shift();if(n){this.createSocket(n,(function(e){n.request.onSocket(e)}))}};function createSecureSocket(e,t){var n=this;TunnelingAgent.prototype.createSocket.call(n,e,(function(s){var r=e.request.getHeader("host");var o=mergeOptions({},n.options,{socket:s,servername:r?r.replace(/:.*$/,""):e.host});var A=i.connect(0,o);n.sockets[n.sockets.indexOf(s)]=A;t(A)}))}function toOptions(e,t,n){if(typeof e==="string"){return{host:e,port:t,localAddress:n}}return e}function mergeOptions(e){for(var t=1,n=arguments.length;t{"use strict";const s=n(3598);const i=n(412);const r=n(8045);const o=n(4634);const A=n(7931);const a=n(7890);const c=n(3983);const{InvalidArgumentError:u}=r;const l=n(4059);const d=n(2067);const p=n(8687);const g=n(6771);const h=n(6193);const f=n(888);const E=n(7858);const{getGlobalDispatcher:m,setGlobalDispatcher:C}=n(1892);const Q=n(6930);const I=n(2860);const B=n(8861);let y;try{n(6113);y=true}catch{y=false}Object.assign(i.prototype,l);e.exports.Dispatcher=i;e.exports.Client=s;e.exports.Pool=o;e.exports.BalancedPool=A;e.exports.Agent=a;e.exports.ProxyAgent=E;e.exports.DecoratorHandler=Q;e.exports.RedirectHandler=I;e.exports.createRedirectInterceptor=B;e.exports.buildConnector=d;e.exports.errors=r;function makeDispatcher(e){return(t,n,s)=>{if(typeof n==="function"){s=n;n=null}if(!t||typeof t!=="string"&&typeof t!=="object"&&!(t instanceof URL)){throw new u("invalid url")}if(n!=null&&typeof n!=="object"){throw new u("invalid opts")}if(n&&n.path!=null){if(typeof n.path!=="string"){throw new u("invalid opts.path")}let e=n.path;if(!n.path.startsWith("/")){e=`/${e}`}t=new URL(c.parseOrigin(t).origin+e)}else{if(!n){n=typeof t==="object"?t:{}}t=c.parseURL(t)}const{agent:i,dispatcher:r=m()}=n;if(i){throw new u("unsupported opts.agent. Did you mean opts.client?")}return e.call(r,{...n,origin:t.origin,path:t.search?`${t.pathname}${t.search}`:t.pathname,method:n.method||(n.body?"PUT":"GET")},s)}}e.exports.setGlobalDispatcher=C;e.exports.getGlobalDispatcher=m;if(c.nodeMajor>16||c.nodeMajor===16&&c.nodeMinor>=8){let t=null;e.exports.fetch=async function fetch(e){if(!t){t=n(4881).fetch}try{return await t(...arguments)}catch(e){if(typeof e==="object"){Error.captureStackTrace(e,this)}throw e}};e.exports.Headers=n(554).Headers;e.exports.Response=n(7823).Response;e.exports.Request=n(8359).Request;e.exports.FormData=n(2015).FormData;e.exports.File=n(8511).File;e.exports.FileReader=n(1446).FileReader;const{setGlobalOrigin:s,getGlobalOrigin:i}=n(1246);e.exports.setGlobalOrigin=s;e.exports.getGlobalOrigin=i;const{CacheStorage:r}=n(7907);const{kConstruct:o}=n(9174);e.exports.caches=new r(o)}if(c.nodeMajor>=16){const{deleteCookie:t,getCookies:s,getSetCookies:i,setCookie:r}=n(1724);e.exports.deleteCookie=t;e.exports.getCookies=s;e.exports.getSetCookies=i;e.exports.setCookie=r;const{parseMIMEType:o,serializeAMimeType:A}=n(685);e.exports.parseMIMEType=o;e.exports.serializeAMimeType=A}if(c.nodeMajor>=18&&y){const{WebSocket:t}=n(4284);e.exports.WebSocket=t}e.exports.request=makeDispatcher(l.request);e.exports.stream=makeDispatcher(l.stream);e.exports.pipeline=makeDispatcher(l.pipeline);e.exports.connect=makeDispatcher(l.connect);e.exports.upgrade=makeDispatcher(l.upgrade);e.exports.MockClient=p;e.exports.MockPool=h;e.exports.MockAgent=g;e.exports.mockErrors=f},7890:(e,t,n)=>{"use strict";const{InvalidArgumentError:s}=n(8045);const{kClients:i,kRunning:r,kClose:o,kDestroy:A,kDispatch:a,kInterceptors:c}=n(2785);const u=n(4839);const l=n(4634);const d=n(3598);const p=n(3983);const g=n(8861);const{WeakRef:h,FinalizationRegistry:f}=n(6436)();const E=Symbol("onConnect");const m=Symbol("onDisconnect");const C=Symbol("onConnectionError");const Q=Symbol("maxRedirections");const I=Symbol("onDrain");const B=Symbol("factory");const y=Symbol("finalizer");const b=Symbol("options");function defaultFactory(e,t){return t&&t.connections===1?new d(e,t):new l(e,t)}class Agent extends u{constructor({factory:e=defaultFactory,maxRedirections:t=0,connect:n,...r}={}){super();if(typeof e!=="function"){throw new s("factory must be a function.")}if(n!=null&&typeof n!=="function"&&typeof n!=="object"){throw new s("connect must be a function or an object")}if(!Number.isInteger(t)||t<0){throw new s("maxRedirections must be a positive number")}if(n&&typeof n!=="function"){n={...n}}this[c]=r.interceptors&&r.interceptors.Agent&&Array.isArray(r.interceptors.Agent)?r.interceptors.Agent:[g({maxRedirections:t})];this[b]={...p.deepClone(r),connect:n};this[b].interceptors=r.interceptors?{...r.interceptors}:undefined;this[Q]=t;this[B]=e;this[i]=new Map;this[y]=new f((e=>{const t=this[i].get(e);if(t!==undefined&&t.deref()===undefined){this[i].delete(e)}}));const o=this;this[I]=(e,t)=>{o.emit("drain",e,[o,...t])};this[E]=(e,t)=>{o.emit("connect",e,[o,...t])};this[m]=(e,t,n)=>{o.emit("disconnect",e,[o,...t],n)};this[C]=(e,t,n)=>{o.emit("connectionError",e,[o,...t],n)}}get[r](){let e=0;for(const t of this[i].values()){const n=t.deref();if(n){e+=n[r]}}return e}[a](e,t){let n;if(e.origin&&(typeof e.origin==="string"||e.origin instanceof URL)){n=String(e.origin)}else{throw new s("opts.origin must be a non-empty string or URL.")}const r=this[i].get(n);let o=r?r.deref():null;if(!o){o=this[B](e.origin,this[b]).on("drain",this[I]).on("connect",this[E]).on("disconnect",this[m]).on("connectionError",this[C]);this[i].set(n,new h(o));this[y].register(o,n)}return o.dispatch(e,t)}async[o](){const e=[];for(const t of this[i].values()){const n=t.deref();if(n){e.push(n.close())}}await Promise.all(e)}async[A](e){const t=[];for(const n of this[i].values()){const s=n.deref();if(s){t.push(s.destroy(e))}}await Promise.all(t)}}e.exports=Agent},7032:(e,t,n)=>{const{addAbortListener:s}=n(3983);const{RequestAbortedError:i}=n(8045);const r=Symbol("kListener");const o=Symbol("kSignal");function abort(e){if(e.abort){e.abort()}else{e.onError(new i)}}function addSignal(e,t){e[o]=null;e[r]=null;if(!t){return}if(t.aborted){abort(e);return}e[o]=t;e[r]=()=>{abort(e)};s(e[o],e[r])}function removeSignal(e){if(!e[o]){return}if("removeEventListener"in e[o]){e[o].removeEventListener("abort",e[r])}else{e[o].removeListener("abort",e[r])}e[o]=null;e[r]=null}e.exports={addSignal:addSignal,removeSignal:removeSignal}},9744:(e,t,n)=>{"use strict";const{AsyncResource:s}=n(852);const{InvalidArgumentError:i,RequestAbortedError:r,SocketError:o}=n(8045);const A=n(3983);const{addSignal:a,removeSignal:c}=n(7032);class ConnectHandler extends s{constructor(e,t){if(!e||typeof e!=="object"){throw new i("invalid opts")}if(typeof t!=="function"){throw new i("invalid callback")}const{signal:n,opaque:s,responseHeaders:r}=e;if(n&&typeof n.on!=="function"&&typeof n.addEventListener!=="function"){throw new i("signal must be an EventEmitter or EventTarget")}super("UNDICI_CONNECT");this.opaque=s||null;this.responseHeaders=r||null;this.callback=t;this.abort=null;a(this,n)}onConnect(e,t){if(!this.callback){throw new r}this.abort=e;this.context=t}onHeaders(){throw new o("bad connect",null)}onUpgrade(e,t,n){const{callback:s,opaque:i,context:r}=this;c(this);this.callback=null;let o=t;if(o!=null){o=this.responseHeaders==="raw"?A.parseRawHeaders(t):A.parseHeaders(t)}this.runInAsyncScope(s,null,null,{statusCode:e,headers:o,socket:n,opaque:i,context:r})}onError(e){const{callback:t,opaque:n}=this;c(this);if(t){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(t,null,e,{opaque:n})}))}}}function connect(e,t){if(t===undefined){return new Promise(((t,n)=>{connect.call(this,e,((e,s)=>e?n(e):t(s)))}))}try{const n=new ConnectHandler(e,t);this.dispatch({...e,method:"CONNECT"},n)}catch(n){if(typeof t!=="function"){throw n}const s=e&&e.opaque;queueMicrotask((()=>t(n,{opaque:s})))}}e.exports=connect},8752:(e,t,n)=>{"use strict";const{Readable:s,Duplex:i,PassThrough:r}=n(2781);const{InvalidArgumentError:o,InvalidReturnValueError:A,RequestAbortedError:a}=n(8045);const c=n(3983);const{AsyncResource:u}=n(852);const{addSignal:l,removeSignal:d}=n(7032);const p=n(9491);const g=Symbol("resume");class PipelineRequest extends s{constructor(){super({autoDestroy:true});this[g]=null}_read(){const{[g]:e}=this;if(e){this[g]=null;e()}}_destroy(e,t){this._read();t(e)}}class PipelineResponse extends s{constructor(e){super({autoDestroy:true});this[g]=e}_read(){this[g]()}_destroy(e,t){if(!e&&!this._readableState.endEmitted){e=new a}t(e)}}class PipelineHandler extends u{constructor(e,t){if(!e||typeof e!=="object"){throw new o("invalid opts")}if(typeof t!=="function"){throw new o("invalid handler")}const{signal:n,method:s,opaque:r,onInfo:A,responseHeaders:u}=e;if(n&&typeof n.on!=="function"&&typeof n.addEventListener!=="function"){throw new o("signal must be an EventEmitter or EventTarget")}if(s==="CONNECT"){throw new o("invalid method")}if(A&&typeof A!=="function"){throw new o("invalid onInfo callback")}super("UNDICI_PIPELINE");this.opaque=r||null;this.responseHeaders=u||null;this.handler=t;this.abort=null;this.context=null;this.onInfo=A||null;this.req=(new PipelineRequest).on("error",c.nop);this.ret=new i({readableObjectMode:e.objectMode,autoDestroy:true,read:()=>{const{body:e}=this;if(e&&e.resume){e.resume()}},write:(e,t,n)=>{const{req:s}=this;if(s.push(e,t)||s._readableState.destroyed){n()}else{s[g]=n}},destroy:(e,t)=>{const{body:n,req:s,res:i,ret:r,abort:o}=this;if(!e&&!r._readableState.endEmitted){e=new a}if(o&&e){o()}c.destroy(n,e);c.destroy(s,e);c.destroy(i,e);d(this);t(e)}}).on("prefinish",(()=>{const{req:e}=this;e.push(null)}));this.res=null;l(this,n)}onConnect(e,t){const{ret:n,res:s}=this;p(!s,"pipeline cannot be retried");if(n.destroyed){throw new a}this.abort=e;this.context=t}onHeaders(e,t,n){const{opaque:s,handler:i,context:r}=this;if(e<200){if(this.onInfo){const n=this.responseHeaders==="raw"?c.parseRawHeaders(t):c.parseHeaders(t);this.onInfo({statusCode:e,headers:n})}return}this.res=new PipelineResponse(n);let o;try{this.handler=null;const n=this.responseHeaders==="raw"?c.parseRawHeaders(t):c.parseHeaders(t);o=this.runInAsyncScope(i,null,{statusCode:e,headers:n,opaque:s,body:this.res,context:r})}catch(e){this.res.on("error",c.nop);throw e}if(!o||typeof o.on!=="function"){throw new A("expected Readable")}o.on("data",(e=>{const{ret:t,body:n}=this;if(!t.push(e)&&n.pause){n.pause()}})).on("error",(e=>{const{ret:t}=this;c.destroy(t,e)})).on("end",(()=>{const{ret:e}=this;e.push(null)})).on("close",(()=>{const{ret:e}=this;if(!e._readableState.ended){c.destroy(e,new a)}}));this.body=o}onData(e){const{res:t}=this;return t.push(e)}onComplete(e){const{res:t}=this;t.push(null)}onError(e){const{ret:t}=this;this.handler=null;c.destroy(t,e)}}function pipeline(e,t){try{const n=new PipelineHandler(e,t);this.dispatch({...e,body:n.req},n);return n.ret}catch(e){return(new r).destroy(e)}}e.exports=pipeline},5448:(e,t,n)=>{"use strict";const s=n(3858);const{InvalidArgumentError:i,RequestAbortedError:r}=n(8045);const o=n(3983);const{getResolveErrorBodyCallback:A}=n(7474);const{AsyncResource:a}=n(852);const{addSignal:c,removeSignal:u}=n(7032);class RequestHandler extends a{constructor(e,t){if(!e||typeof e!=="object"){throw new i("invalid opts")}const{signal:n,method:s,opaque:r,body:A,onInfo:a,responseHeaders:u,throwOnError:l,highWaterMark:d}=e;try{if(typeof t!=="function"){throw new i("invalid callback")}if(d&&(typeof d!=="number"||d<0)){throw new i("invalid highWaterMark")}if(n&&typeof n.on!=="function"&&typeof n.addEventListener!=="function"){throw new i("signal must be an EventEmitter or EventTarget")}if(s==="CONNECT"){throw new i("invalid method")}if(a&&typeof a!=="function"){throw new i("invalid onInfo callback")}super("UNDICI_REQUEST")}catch(e){if(o.isStream(A)){o.destroy(A.on("error",o.nop),e)}throw e}this.responseHeaders=u||null;this.opaque=r||null;this.callback=t;this.res=null;this.abort=null;this.body=A;this.trailers={};this.context=null;this.onInfo=a||null;this.throwOnError=l;this.highWaterMark=d;if(o.isStream(A)){A.on("error",(e=>{this.onError(e)}))}c(this,n)}onConnect(e,t){if(!this.callback){throw new r}this.abort=e;this.context=t}onHeaders(e,t,n,i){const{callback:r,opaque:a,abort:c,context:u,responseHeaders:l,highWaterMark:d}=this;const p=l==="raw"?o.parseRawHeaders(t):o.parseHeaders(t);if(e<200){if(this.onInfo){this.onInfo({statusCode:e,headers:p})}return}const g=l==="raw"?o.parseHeaders(t):p;const h=g["content-type"];const f=new s({resume:n,abort:c,contentType:h,highWaterMark:d});this.callback=null;this.res=f;if(r!==null){if(this.throwOnError&&e>=400){this.runInAsyncScope(A,null,{callback:r,body:f,contentType:h,statusCode:e,statusMessage:i,headers:p})}else{this.runInAsyncScope(r,null,null,{statusCode:e,headers:p,trailers:this.trailers,opaque:a,body:f,context:u})}}}onData(e){const{res:t}=this;return t.push(e)}onComplete(e){const{res:t}=this;u(this);o.parseHeaders(e,this.trailers);t.push(null)}onError(e){const{res:t,callback:n,body:s,opaque:i}=this;u(this);if(n){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(n,null,e,{opaque:i})}))}if(t){this.res=null;queueMicrotask((()=>{o.destroy(t,e)}))}if(s){this.body=null;o.destroy(s,e)}}}function request(e,t){if(t===undefined){return new Promise(((t,n)=>{request.call(this,e,((e,s)=>e?n(e):t(s)))}))}try{this.dispatch(e,new RequestHandler(e,t))}catch(n){if(typeof t!=="function"){throw n}const s=e&&e.opaque;queueMicrotask((()=>t(n,{opaque:s})))}}e.exports=request},5395:(e,t,n)=>{"use strict";const{finished:s,PassThrough:i}=n(2781);const{InvalidArgumentError:r,InvalidReturnValueError:o,RequestAbortedError:A}=n(8045);const a=n(3983);const{getResolveErrorBodyCallback:c}=n(7474);const{AsyncResource:u}=n(852);const{addSignal:l,removeSignal:d}=n(7032);class StreamHandler extends u{constructor(e,t,n){if(!e||typeof e!=="object"){throw new r("invalid opts")}const{signal:s,method:i,opaque:o,body:A,onInfo:c,responseHeaders:u,throwOnError:d}=e;try{if(typeof n!=="function"){throw new r("invalid callback")}if(typeof t!=="function"){throw new r("invalid factory")}if(s&&typeof s.on!=="function"&&typeof s.addEventListener!=="function"){throw new r("signal must be an EventEmitter or EventTarget")}if(i==="CONNECT"){throw new r("invalid method")}if(c&&typeof c!=="function"){throw new r("invalid onInfo callback")}super("UNDICI_STREAM")}catch(e){if(a.isStream(A)){a.destroy(A.on("error",a.nop),e)}throw e}this.responseHeaders=u||null;this.opaque=o||null;this.factory=t;this.callback=n;this.res=null;this.abort=null;this.context=null;this.trailers=null;this.body=A;this.onInfo=c||null;this.throwOnError=d||false;if(a.isStream(A)){A.on("error",(e=>{this.onError(e)}))}l(this,s)}onConnect(e,t){if(!this.callback){throw new A}this.abort=e;this.context=t}onHeaders(e,t,n,r){const{factory:A,opaque:u,context:l,callback:d,responseHeaders:p}=this;const g=p==="raw"?a.parseRawHeaders(t):a.parseHeaders(t);if(e<200){if(this.onInfo){this.onInfo({statusCode:e,headers:g})}return}this.factory=null;let h;if(this.throwOnError&&e>=400){const n=p==="raw"?a.parseHeaders(t):g;const s=n["content-type"];h=new i;this.callback=null;this.runInAsyncScope(c,null,{callback:d,body:h,contentType:s,statusCode:e,statusMessage:r,headers:g})}else{if(A===null){return}h=this.runInAsyncScope(A,null,{statusCode:e,headers:g,opaque:u,context:l});if(!h||typeof h.write!=="function"||typeof h.end!=="function"||typeof h.on!=="function"){throw new o("expected Writable")}s(h,{readable:false},(e=>{const{callback:t,res:n,opaque:s,trailers:i,abort:r}=this;this.res=null;if(e||!n.readable){a.destroy(n,e)}this.callback=null;this.runInAsyncScope(t,null,e||null,{opaque:s,trailers:i});if(e){r()}}))}h.on("drain",n);this.res=h;const f=h.writableNeedDrain!==undefined?h.writableNeedDrain:h._writableState&&h._writableState.needDrain;return f!==true}onData(e){const{res:t}=this;return t?t.write(e):true}onComplete(e){const{res:t}=this;d(this);if(!t){return}this.trailers=a.parseHeaders(e);t.end()}onError(e){const{res:t,callback:n,opaque:s,body:i}=this;d(this);this.factory=null;if(t){this.res=null;a.destroy(t,e)}else if(n){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(n,null,e,{opaque:s})}))}if(i){this.body=null;a.destroy(i,e)}}}function stream(e,t,n){if(n===undefined){return new Promise(((n,s)=>{stream.call(this,e,t,((e,t)=>e?s(e):n(t)))}))}try{this.dispatch(e,new StreamHandler(e,t,n))}catch(t){if(typeof n!=="function"){throw t}const s=e&&e.opaque;queueMicrotask((()=>n(t,{opaque:s})))}}e.exports=stream},6923:(e,t,n)=>{"use strict";const{InvalidArgumentError:s,RequestAbortedError:i,SocketError:r}=n(8045);const{AsyncResource:o}=n(852);const A=n(3983);const{addSignal:a,removeSignal:c}=n(7032);const u=n(9491);class UpgradeHandler extends o{constructor(e,t){if(!e||typeof e!=="object"){throw new s("invalid opts")}if(typeof t!=="function"){throw new s("invalid callback")}const{signal:n,opaque:i,responseHeaders:r}=e;if(n&&typeof n.on!=="function"&&typeof n.addEventListener!=="function"){throw new s("signal must be an EventEmitter or EventTarget")}super("UNDICI_UPGRADE");this.responseHeaders=r||null;this.opaque=i||null;this.callback=t;this.abort=null;this.context=null;a(this,n)}onConnect(e,t){if(!this.callback){throw new i}this.abort=e;this.context=null}onHeaders(){throw new r("bad upgrade",null)}onUpgrade(e,t,n){const{callback:s,opaque:i,context:r}=this;u.strictEqual(e,101);c(this);this.callback=null;const o=this.responseHeaders==="raw"?A.parseRawHeaders(t):A.parseHeaders(t);this.runInAsyncScope(s,null,null,{headers:o,socket:n,opaque:i,context:r})}onError(e){const{callback:t,opaque:n}=this;c(this);if(t){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(t,null,e,{opaque:n})}))}}}function upgrade(e,t){if(t===undefined){return new Promise(((t,n)=>{upgrade.call(this,e,((e,s)=>e?n(e):t(s)))}))}try{const n=new UpgradeHandler(e,t);this.dispatch({...e,method:e.method||"GET",upgrade:e.protocol||"Websocket"},n)}catch(n){if(typeof t!=="function"){throw n}const s=e&&e.opaque;queueMicrotask((()=>t(n,{opaque:s})))}}e.exports=upgrade},4059:(e,t,n)=>{"use strict";e.exports.request=n(5448);e.exports.stream=n(5395);e.exports.pipeline=n(8752);e.exports.upgrade=n(6923);e.exports.connect=n(9744)},3858:(e,t,n)=>{"use strict";const s=n(9491);const{Readable:i}=n(2781);const{RequestAbortedError:r,NotSupportedError:o,InvalidArgumentError:A}=n(8045);const a=n(3983);const{ReadableStreamFrom:c,toUSVString:u}=n(3983);let l;const d=Symbol("kConsume");const p=Symbol("kReading");const g=Symbol("kBody");const h=Symbol("abort");const f=Symbol("kContentType");e.exports=class BodyReadable extends i{constructor({resume:e,abort:t,contentType:n="",highWaterMark:s=64*1024}){super({autoDestroy:true,read:e,highWaterMark:s});this._readableState.dataEmitted=false;this[h]=t;this[d]=null;this[g]=null;this[f]=n;this[p]=false}destroy(e){if(this.destroyed){return this}if(!e&&!this._readableState.endEmitted){e=new r}if(e){this[h]()}return super.destroy(e)}emit(e,...t){if(e==="data"){this._readableState.dataEmitted=true}else if(e==="error"){this._readableState.errorEmitted=true}return super.emit(e,...t)}on(e,...t){if(e==="data"||e==="readable"){this[p]=true}return super.on(e,...t)}addListener(e,...t){return this.on(e,...t)}off(e,...t){const n=super.off(e,...t);if(e==="data"||e==="readable"){this[p]=this.listenerCount("data")>0||this.listenerCount("readable")>0}return n}removeListener(e,...t){return this.off(e,...t)}push(e){if(this[d]&&e!==null&&this.readableLength===0){consumePush(this[d],e);return this[p]?super.push(e):true}return super.push(e)}async text(){return consume(this,"text")}async json(){return consume(this,"json")}async blob(){return consume(this,"blob")}async arrayBuffer(){return consume(this,"arrayBuffer")}async formData(){throw new o}get bodyUsed(){return a.isDisturbed(this)}get body(){if(!this[g]){this[g]=c(this);if(this[d]){this[g].getReader();s(this[g].locked)}}return this[g]}async dump(e){let t=e&&Number.isFinite(e.limit)?e.limit:262144;const n=e&&e.signal;const abortFn=()=>{this.destroy()};let s;if(n){if(typeof n!=="object"||!("aborted"in n)){throw new A("signal must be an AbortSignal")}a.throwIfAborted(n);s=a.addAbortListener(n,abortFn)}try{for await(const e of this){a.throwIfAborted(n);t-=Buffer.byteLength(e);if(t<0){return}}}catch{a.throwIfAborted(n)}finally{if(typeof s==="function"){s()}else if(s){s[Symbol.dispose]()}}}};function isLocked(e){return e[g]&&e[g].locked===true||e[d]}function isUnusable(e){return a.isDisturbed(e)||isLocked(e)}async function consume(e,t){if(isUnusable(e)){throw new TypeError("unusable")}s(!e[d]);return new Promise(((n,s)=>{e[d]={type:t,stream:e,resolve:n,reject:s,length:0,body:[]};e.on("error",(function(e){consumeFinish(this[d],e)})).on("close",(function(){if(this[d].body!==null){consumeFinish(this[d],new r)}}));process.nextTick(consumeStart,e[d])}))}function consumeStart(e){if(e.body===null){return}const{_readableState:t}=e.stream;for(const n of t.buffer){consumePush(e,n)}if(t.endEmitted){consumeEnd(this[d])}else{e.stream.on("end",(function(){consumeEnd(this[d])}))}e.stream.resume();while(e.stream.read()!=null){}}function consumeEnd(e){const{type:t,body:s,resolve:i,stream:r,length:o}=e;try{if(t==="text"){i(u(Buffer.concat(s)))}else if(t==="json"){i(JSON.parse(Buffer.concat(s)))}else if(t==="arrayBuffer"){const e=new Uint8Array(o);let t=0;for(const n of s){e.set(n,t);t+=n.byteLength}i(e.buffer)}else if(t==="blob"){if(!l){l=n(4300).Blob}i(new l(s,{type:r[f]}))}consumeFinish(e)}catch(e){r.destroy(e)}}function consumePush(e,t){e.length+=t.length;e.body.push(t)}function consumeFinish(e,t){if(e.body===null){return}if(t){e.reject(t)}else{e.resolve()}e.type=null;e.stream=null;e.resolve=null;e.reject=null;e.length=0;e.body=null}},7474:(e,t,n)=>{const s=n(9491);const{ResponseStatusCodeError:i}=n(8045);const{toUSVString:r}=n(3983);async function getResolveErrorBodyCallback({callback:e,body:t,contentType:n,statusCode:o,statusMessage:A,headers:a}){s(t);let c=[];let u=0;for await(const e of t){c.push(e);u+=e.length;if(u>128*1024){c=null;break}}if(o===204||!n||!c){process.nextTick(e,new i(`Response status code ${o}${A?`: ${A}`:""}`,o,a));return}try{if(n.startsWith("application/json")){const t=JSON.parse(r(Buffer.concat(c)));process.nextTick(e,new i(`Response status code ${o}${A?`: ${A}`:""}`,o,a,t));return}if(n.startsWith("text/")){const t=r(Buffer.concat(c));process.nextTick(e,new i(`Response status code ${o}${A?`: ${A}`:""}`,o,a,t));return}}catch(e){}process.nextTick(e,new i(`Response status code ${o}${A?`: ${A}`:""}`,o,a))}e.exports={getResolveErrorBodyCallback:getResolveErrorBodyCallback}},7931:(e,t,n)=>{"use strict";const{BalancedPoolMissingUpstreamError:s,InvalidArgumentError:i}=n(8045);const{PoolBase:r,kClients:o,kNeedDrain:A,kAddClient:a,kRemoveClient:c,kGetDispatcher:u}=n(3198);const l=n(4634);const{kUrl:d,kInterceptors:p}=n(2785);const{parseOrigin:g}=n(3983);const h=Symbol("factory");const f=Symbol("options");const E=Symbol("kGreatestCommonDivisor");const m=Symbol("kCurrentWeight");const C=Symbol("kIndex");const Q=Symbol("kWeight");const I=Symbol("kMaxWeightPerServer");const B=Symbol("kErrorPenalty");function getGreatestCommonDivisor(e,t){if(t===0)return e;return getGreatestCommonDivisor(t,e%t)}function defaultFactory(e,t){return new l(e,t)}class BalancedPool extends r{constructor(e=[],{factory:t=defaultFactory,...n}={}){super();this[f]=n;this[C]=-1;this[m]=0;this[I]=this[f].maxWeightPerServer||100;this[B]=this[f].errorPenalty||15;if(!Array.isArray(e)){e=[e]}if(typeof t!=="function"){throw new i("factory must be a function.")}this[p]=n.interceptors&&n.interceptors.BalancedPool&&Array.isArray(n.interceptors.BalancedPool)?n.interceptors.BalancedPool:[];this[h]=t;for(const t of e){this.addUpstream(t)}this._updateBalancedPoolStats()}addUpstream(e){const t=g(e).origin;if(this[o].find((e=>e[d].origin===t&&e.closed!==true&&e.destroyed!==true))){return this}const n=this[h](t,Object.assign({},this[f]));this[a](n);n.on("connect",(()=>{n[Q]=Math.min(this[I],n[Q]+this[B])}));n.on("connectionError",(()=>{n[Q]=Math.max(1,n[Q]-this[B]);this._updateBalancedPoolStats()}));n.on("disconnect",((...e)=>{const t=e[2];if(t&&t.code==="UND_ERR_SOCKET"){n[Q]=Math.max(1,n[Q]-this[B]);this._updateBalancedPoolStats()}}));for(const e of this[o]){e[Q]=this[I]}this._updateBalancedPoolStats();return this}_updateBalancedPoolStats(){this[E]=this[o].map((e=>e[Q])).reduce(getGreatestCommonDivisor,0)}removeUpstream(e){const t=g(e).origin;const n=this[o].find((e=>e[d].origin===t&&e.closed!==true&&e.destroyed!==true));if(n){this[c](n)}return this}get upstreams(){return this[o].filter((e=>e.closed!==true&&e.destroyed!==true)).map((e=>e[d].origin))}[u](){if(this[o].length===0){throw new s}const e=this[o].find((e=>!e[A]&&e.closed!==true&&e.destroyed!==true));if(!e){return}const t=this[o].map((e=>e[A])).reduce(((e,t)=>e&&t),true);if(t){return}let n=0;let i=this[o].findIndex((e=>!e[A]));while(n++this[o][i][Q]&&!e[A]){i=this[C]}if(this[C]===0){this[m]=this[m]-this[E];if(this[m]<=0){this[m]=this[I]}}if(e[Q]>=this[m]&&!e[A]){return e}}this[m]=this[o][i][Q];this[C]=i;return this[o][i]}}e.exports=BalancedPool},6101:(e,t,n)=>{"use strict";const{kConstruct:s}=n(9174);const{urlEquals:i,fieldValues:r}=n(2396);const{kEnumerableProperty:o,isDisturbed:A}=n(3983);const{kHeadersList:a}=n(2785);const{webidl:c}=n(1744);const{Response:u,cloneResponse:l}=n(7823);const{Request:d}=n(8359);const{kState:p,kHeaders:g,kGuard:h,kRealm:f}=n(5861);const{fetching:E}=n(4881);const{urlIsHttpHttpsScheme:m,createDeferredPromise:C,readAllBytes:Q}=n(2538);const I=n(9491);const{getGlobalDispatcher:B}=n(1892);class Cache{#e;constructor(){if(arguments[0]!==s){c.illegalConstructor()}this.#e=arguments[1]}async match(e,t={}){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.match"});e=c.converters.RequestInfo(e);t=c.converters.CacheQueryOptions(t);const n=await this.matchAll(e,t);if(n.length===0){return}return n[0]}async matchAll(e=undefined,t={}){c.brandCheck(this,Cache);if(e!==undefined)e=c.converters.RequestInfo(e);t=c.converters.CacheQueryOptions(t);let n=null;if(e!==undefined){if(e instanceof d){n=e[p];if(n.method!=="GET"&&!t.ignoreMethod){return[]}}else if(typeof e==="string"){n=new d(e)[p]}}const s=[];if(e===undefined){for(const e of this.#e){s.push(e[1])}}else{const e=this.#t(n,t);for(const t of e){s.push(t[1])}}const i=[];for(const e of s){const t=new u(e.body?.source??null);const n=t[p].body;t[p]=e;t[p].body=n;t[g][a]=e.headersList;t[g][h]="immutable";i.push(t)}return Object.freeze(i)}async add(e){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.add"});e=c.converters.RequestInfo(e);const t=[e];const n=this.addAll(t);return await n}async addAll(e){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.addAll"});e=c.converters["sequence"](e);const t=[];const n=[];for(const t of e){if(typeof t==="string"){continue}const e=t[p];if(!m(e.url)||e.method!=="GET"){throw c.errors.exception({header:"Cache.addAll",message:"Expected http/s scheme when method is not GET."})}}const s=[];for(const i of e){const e=new d(i)[p];if(!m(e.url)){throw c.errors.exception({header:"Cache.addAll",message:"Expected http/s scheme."})}e.initiator="fetch";e.destination="subresource";n.push(e);const o=C();s.push(E({request:e,dispatcher:B(),processResponse(e){if(e.type==="error"||e.status===206||e.status<200||e.status>299){o.reject(c.errors.exception({header:"Cache.addAll",message:"Received an invalid status code or the request failed."}))}else if(e.headersList.contains("vary")){const t=r(e.headersList.get("vary"));for(const e of t){if(e==="*"){o.reject(c.errors.exception({header:"Cache.addAll",message:"invalid vary field value"}));for(const e of s){e.abort()}return}}}},processResponseEndOfBody(e){if(e.aborted){o.reject(new DOMException("aborted","AbortError"));return}o.resolve(e)}}));t.push(o.promise)}const i=Promise.all(t);const o=await i;const A=[];let a=0;for(const e of o){const t={type:"put",request:n[a],response:e};A.push(t);a++}const u=C();let l=null;try{this.#n(A)}catch(e){l=e}queueMicrotask((()=>{if(l===null){u.resolve(undefined)}else{u.reject(l)}}));return u.promise}async put(e,t){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,2,{header:"Cache.put"});e=c.converters.RequestInfo(e);t=c.converters.Response(t);let n=null;if(e instanceof d){n=e[p]}else{n=new d(e)[p]}if(!m(n.url)||n.method!=="GET"){throw c.errors.exception({header:"Cache.put",message:"Expected an http/s scheme when method is not GET"})}const s=t[p];if(s.status===206){throw c.errors.exception({header:"Cache.put",message:"Got 206 status"})}if(s.headersList.contains("vary")){const e=r(s.headersList.get("vary"));for(const t of e){if(t==="*"){throw c.errors.exception({header:"Cache.put",message:"Got * vary field value"})}}}if(s.body&&(A(s.body.stream)||s.body.stream.locked)){throw c.errors.exception({header:"Cache.put",message:"Response body is locked or disturbed"})}const i=l(s);const o=C();if(s.body!=null){const e=s.body.stream;const t=e.getReader();Q(t).then(o.resolve,o.reject)}else{o.resolve(undefined)}const a=[];const u={type:"put",request:n,response:i};a.push(u);const g=await o.promise;if(i.body!=null){i.body.source=g}const h=C();let f=null;try{this.#n(a)}catch(e){f=e}queueMicrotask((()=>{if(f===null){h.resolve()}else{h.reject(f)}}));return h.promise}async delete(e,t={}){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.delete"});e=c.converters.RequestInfo(e);t=c.converters.CacheQueryOptions(t);let n=null;if(e instanceof d){n=e[p];if(n.method!=="GET"&&!t.ignoreMethod){return false}}else{I(typeof e==="string");n=new d(e)[p]}const s=[];const i={type:"delete",request:n,options:t};s.push(i);const r=C();let o=null;let A;try{A=this.#n(s)}catch(e){o=e}queueMicrotask((()=>{if(o===null){r.resolve(!!A?.length)}else{r.reject(o)}}));return r.promise}async keys(e=undefined,t={}){c.brandCheck(this,Cache);if(e!==undefined)e=c.converters.RequestInfo(e);t=c.converters.CacheQueryOptions(t);let n=null;if(e!==undefined){if(e instanceof d){n=e[p];if(n.method!=="GET"&&!t.ignoreMethod){return[]}}else if(typeof e==="string"){n=new d(e)[p]}}const s=C();const i=[];if(e===undefined){for(const e of this.#e){i.push(e[0])}}else{const e=this.#t(n,t);for(const t of e){i.push(t[0])}}queueMicrotask((()=>{const e=[];for(const t of i){const n=new d("https://a");n[p]=t;n[g][a]=t.headersList;n[g][h]="immutable";n[f]=t.client;e.push(n)}s.resolve(Object.freeze(e))}));return s.promise}#n(e){const t=this.#e;const n=[...t];const s=[];const i=[];try{for(const n of e){if(n.type!=="delete"&&n.type!=="put"){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:'operation type does not match "delete" or "put"'})}if(n.type==="delete"&&n.response!=null){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"delete operation should not have an associated response"})}if(this.#t(n.request,n.options,s).length){throw new DOMException("???","InvalidStateError")}let e;if(n.type==="delete"){e=this.#t(n.request,n.options);if(e.length===0){return[]}for(const n of e){const e=t.indexOf(n);I(e!==-1);t.splice(e,1)}}else if(n.type==="put"){if(n.response==null){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"put operation should have an associated response"})}const i=n.request;if(!m(i.url)){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"expected http or https scheme"})}if(i.method!=="GET"){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"not get method"})}if(n.options!=null){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"options must not be defined"})}e=this.#t(n.request);for(const n of e){const e=t.indexOf(n);I(e!==-1);t.splice(e,1)}t.push([n.request,n.response]);s.push([n.request,n.response])}i.push([n.request,n.response])}return i}catch(e){this.#e.length=0;this.#e=n;throw e}}#t(e,t,n){const s=[];const i=n??this.#e;for(const n of i){const[i,r]=n;if(this.#s(e,i,r,t)){s.push(n)}}return s}#s(e,t,n=null,s){const o=new URL(e.url);const A=new URL(t.url);if(s?.ignoreSearch){A.search="";o.search=""}if(!i(o,A,true)){return false}if(n==null||s?.ignoreVary||!n.headersList.contains("vary")){return true}const a=r(n.headersList.get("vary"));for(const n of a){if(n==="*"){return false}const s=t.headersList.get(n);const i=e.headersList.get(n);if(s!==i){return false}}return true}}Object.defineProperties(Cache.prototype,{[Symbol.toStringTag]:{value:"Cache",configurable:true},match:o,matchAll:o,add:o,addAll:o,put:o,delete:o,keys:o});const y=[{key:"ignoreSearch",converter:c.converters.boolean,defaultValue:false},{key:"ignoreMethod",converter:c.converters.boolean,defaultValue:false},{key:"ignoreVary",converter:c.converters.boolean,defaultValue:false}];c.converters.CacheQueryOptions=c.dictionaryConverter(y);c.converters.MultiCacheQueryOptions=c.dictionaryConverter([...y,{key:"cacheName",converter:c.converters.DOMString}]);c.converters.Response=c.interfaceConverter(u);c.converters["sequence"]=c.sequenceConverter(c.converters.RequestInfo);e.exports={Cache:Cache}},7907:(e,t,n)=>{"use strict";const{kConstruct:s}=n(9174);const{Cache:i}=n(6101);const{webidl:r}=n(1744);const{kEnumerableProperty:o}=n(3983);class CacheStorage{#i=new Map;constructor(){if(arguments[0]!==s){r.illegalConstructor()}}async match(e,t={}){r.brandCheck(this,CacheStorage);r.argumentLengthCheck(arguments,1,{header:"CacheStorage.match"});e=r.converters.RequestInfo(e);t=r.converters.MultiCacheQueryOptions(t);if(t.cacheName!=null){if(this.#i.has(t.cacheName)){const n=this.#i.get(t.cacheName);const r=new i(s,n);return await r.match(e,t)}}else{for(const n of this.#i.values()){const r=new i(s,n);const o=await r.match(e,t);if(o!==undefined){return o}}}}async has(e){r.brandCheck(this,CacheStorage);r.argumentLengthCheck(arguments,1,{header:"CacheStorage.has"});e=r.converters.DOMString(e);return this.#i.has(e)}async open(e){r.brandCheck(this,CacheStorage);r.argumentLengthCheck(arguments,1,{header:"CacheStorage.open"});e=r.converters.DOMString(e);if(this.#i.has(e)){const t=this.#i.get(e);return new i(s,t)}const t=[];this.#i.set(e,t);return new i(s,t)}async delete(e){r.brandCheck(this,CacheStorage);r.argumentLengthCheck(arguments,1,{header:"CacheStorage.delete"});e=r.converters.DOMString(e);return this.#i.delete(e)}async keys(){r.brandCheck(this,CacheStorage);const e=this.#i.keys();return[...e]}}Object.defineProperties(CacheStorage.prototype,{[Symbol.toStringTag]:{value:"CacheStorage",configurable:true},match:o,has:o,open:o,delete:o,keys:o});e.exports={CacheStorage:CacheStorage}},9174:e=>{"use strict";e.exports={kConstruct:Symbol("constructable")}},2396:(e,t,n)=>{"use strict";const s=n(9491);const{URLSerializer:i}=n(685);const{isValidHeaderName:r}=n(2538);function urlEquals(e,t,n=false){const s=i(e,n);const r=i(t,n);return s===r}function fieldValues(e){s(e!==null);const t=[];for(let n of e.split(",")){n=n.trim();if(!n.length){continue}else if(!r(n)){continue}t.push(n)}return t}e.exports={urlEquals:urlEquals,fieldValues:fieldValues}},3598:(e,t,n)=>{"use strict";const s=n(9491);const i=n(1808);const r=n(3685);const{pipeline:o}=n(2781);const A=n(3983);const a=n(9459);const c=n(2905);const u=n(4839);const{RequestContentLengthMismatchError:l,ResponseContentLengthMismatchError:d,InvalidArgumentError:p,RequestAbortedError:g,HeadersTimeoutError:h,HeadersOverflowError:f,SocketError:E,InformationalError:m,BodyTimeoutError:C,HTTPParserError:Q,ResponseExceededMaxSizeError:I,ClientDestroyedError:B}=n(8045);const y=n(2067);const{kUrl:b,kReset:w,kServerName:R,kClient:v,kBusy:k,kParser:S,kConnect:x,kBlocking:D,kResuming:_,kRunning:N,kPending:F,kSize:U,kWriting:T,kQueue:M,kConnected:L,kConnecting:O,kNeedDrain:P,kNoRef:J,kKeepAliveDefaultTimeout:H,kHostHeader:q,kPendingIdx:G,kRunningIdx:Y,kError:V,kPipelining:j,kSocket:W,kKeepAliveTimeoutValue:K,kMaxHeadersSize:z,kKeepAliveMaxTimeout:Z,kKeepAliveTimeoutThreshold:X,kHeadersTimeout:$,kBodyTimeout:ee,kStrictContentLength:te,kConnector:ne,kMaxRedirections:se,kMaxRequests:ie,kCounter:re,kClose:oe,kDestroy:Ae,kDispatch:ae,kInterceptors:ce,kLocalAddress:ue,kMaxResponseSize:le,kHTTPConnVersion:de,kHost:pe,kHTTP2Session:ge,kHTTP2SessionState:he,kHTTP2BuildRequest:fe,kHTTP2CopyHeaders:Ee,kHTTP1BuildRequest:me}=n(2785);let Ce;try{Ce=n(5158)}catch{Ce={constants:{}}}const{constants:{HTTP2_HEADER_AUTHORITY:Qe,HTTP2_HEADER_METHOD:Ie,HTTP2_HEADER_PATH:Be,HTTP2_HEADER_SCHEME:ye,HTTP2_HEADER_CONTENT_LENGTH:be,HTTP2_HEADER_EXPECT:we,HTTP2_HEADER_STATUS:Re}}=Ce;let ve=false;const ke=Buffer[Symbol.species];const Se=Symbol("kClosedResolve");const xe={};try{const e=n(7643);xe.sendHeaders=e.channel("undici:client:sendHeaders");xe.beforeConnect=e.channel("undici:client:beforeConnect");xe.connectError=e.channel("undici:client:connectError");xe.connected=e.channel("undici:client:connected")}catch{xe.sendHeaders={hasSubscribers:false};xe.beforeConnect={hasSubscribers:false};xe.connectError={hasSubscribers:false};xe.connected={hasSubscribers:false}}class Client extends u{constructor(e,{interceptors:t,maxHeaderSize:n,headersTimeout:s,socketTimeout:o,requestTimeout:a,connectTimeout:c,bodyTimeout:u,idleTimeout:l,keepAlive:d,keepAliveTimeout:g,maxKeepAliveTimeout:h,keepAliveMaxTimeout:f,keepAliveTimeoutThreshold:E,socketPath:m,pipelining:C,tls:Q,strictContentLength:I,maxCachedSessions:B,maxRedirections:w,connect:v,maxRequestsPerClient:k,localAddress:S,maxResponseSize:x,autoSelectFamily:D,autoSelectFamilyAttemptTimeout:N,allowH2:F,maxConcurrentStreams:U}={}){super();if(d!==undefined){throw new p("unsupported keepAlive, use pipelining=0 instead")}if(o!==undefined){throw new p("unsupported socketTimeout, use headersTimeout & bodyTimeout instead")}if(a!==undefined){throw new p("unsupported requestTimeout, use headersTimeout & bodyTimeout instead")}if(l!==undefined){throw new p("unsupported idleTimeout, use keepAliveTimeout instead")}if(h!==undefined){throw new p("unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead")}if(n!=null&&!Number.isFinite(n)){throw new p("invalid maxHeaderSize")}if(m!=null&&typeof m!=="string"){throw new p("invalid socketPath")}if(c!=null&&(!Number.isFinite(c)||c<0)){throw new p("invalid connectTimeout")}if(g!=null&&(!Number.isFinite(g)||g<=0)){throw new p("invalid keepAliveTimeout")}if(f!=null&&(!Number.isFinite(f)||f<=0)){throw new p("invalid keepAliveMaxTimeout")}if(E!=null&&!Number.isFinite(E)){throw new p("invalid keepAliveTimeoutThreshold")}if(s!=null&&(!Number.isInteger(s)||s<0)){throw new p("headersTimeout must be a positive integer or zero")}if(u!=null&&(!Number.isInteger(u)||u<0)){throw new p("bodyTimeout must be a positive integer or zero")}if(v!=null&&typeof v!=="function"&&typeof v!=="object"){throw new p("connect must be a function or an object")}if(w!=null&&(!Number.isInteger(w)||w<0)){throw new p("maxRedirections must be a positive number")}if(k!=null&&(!Number.isInteger(k)||k<0)){throw new p("maxRequestsPerClient must be a positive number")}if(S!=null&&(typeof S!=="string"||i.isIP(S)===0)){throw new p("localAddress must be valid string IP address")}if(x!=null&&(!Number.isInteger(x)||x<-1)){throw new p("maxResponseSize must be a positive number")}if(N!=null&&(!Number.isInteger(N)||N<-1)){throw new p("autoSelectFamilyAttemptTimeout must be a positive number")}if(F!=null&&typeof F!=="boolean"){throw new p("allowH2 must be a valid boolean value")}if(U!=null&&(typeof U!=="number"||U<1)){throw new p("maxConcurrentStreams must be a possitive integer, greater than 0")}if(typeof v!=="function"){v=y({...Q,maxCachedSessions:B,allowH2:F,socketPath:m,timeout:c,...A.nodeHasAutoSelectFamily&&D?{autoSelectFamily:D,autoSelectFamilyAttemptTimeout:N}:undefined,...v})}this[ce]=t&&t.Client&&Array.isArray(t.Client)?t.Client:[_e({maxRedirections:w})];this[b]=A.parseOrigin(e);this[ne]=v;this[W]=null;this[j]=C!=null?C:1;this[z]=n||r.maxHeaderSize;this[H]=g==null?4e3:g;this[Z]=f==null?6e5:f;this[X]=E==null?1e3:E;this[K]=this[H];this[R]=null;this[ue]=S!=null?S:null;this[_]=0;this[P]=0;this[q]=`host: ${this[b].hostname}${this[b].port?`:${this[b].port}`:""}\r\n`;this[ee]=u!=null?u:3e5;this[$]=s!=null?s:3e5;this[te]=I==null?true:I;this[se]=w;this[ie]=k;this[Se]=null;this[le]=x>-1?x:-1;this[de]="h1";this[ge]=null;this[he]=!F?null:{openStreams:0,maxConcurrentStreams:U!=null?U:100};this[pe]=`${this[b].hostname}${this[b].port?`:${this[b].port}`:""}`;this[M]=[];this[Y]=0;this[G]=0}get pipelining(){return this[j]}set pipelining(e){this[j]=e;resume(this,true)}get[F](){return this[M].length-this[G]}get[N](){return this[G]-this[Y]}get[U](){return this[M].length-this[Y]}get[L](){return!!this[W]&&!this[O]&&!this[W].destroyed}get[k](){const e=this[W];return e&&(e[w]||e[T]||e[D])||this[U]>=(this[j]||1)||this[F]>0}[x](e){connect(this);this.once("connect",e)}[ae](e,t){const n=e.origin||this[b].origin;const s=this[de]==="h2"?c[fe](n,e,t):c[me](n,e,t);this[M].push(s);if(this[_]){}else if(A.bodyLength(s.body)==null&&A.isIterable(s.body)){this[_]=1;process.nextTick(resume,this)}else{resume(this,true)}if(this[_]&&this[P]!==2&&this[k]){this[P]=2}return this[P]<2}async[oe](){return new Promise((e=>{if(!this[U]){e(null)}else{this[Se]=e}}))}async[Ae](e){return new Promise((t=>{const n=this[M].splice(this[G]);for(let t=0;t{if(this[Se]){this[Se]();this[Se]=null}t()};if(this[ge]!=null){A.destroy(this[ge],e);this[ge]=null;this[he]=null}if(!this[W]){queueMicrotask(callback)}else{A.destroy(this[W].on("close",callback),e)}resume(this)}))}}function onHttp2SessionError(e){s(e.code!=="ERR_TLS_CERT_ALTNAME_INVALID");this[W][V]=e;onError(this[v],e)}function onHttp2FrameError(e,t,n){const s=new m(`HTTP/2: "frameError" received - type ${e}, code ${t}`);if(n===0){this[W][V]=s;onError(this[v],s)}}function onHttp2SessionEnd(){A.destroy(this,new E("other side closed"));A.destroy(this[W],new E("other side closed"))}function onHTTP2GoAway(e){const t=this[v];const n=new m(`HTTP/2: "GOAWAY" frame received with code ${e}`);t[W]=null;t[ge]=null;if(t.destroyed){s(this[F]===0);const e=t[M].splice(t[Y]);for(let t=0;t0){const e=t[M][t[Y]];t[M][t[Y]++]=null;errorRequest(t,e,n)}t[G]=t[Y];s(t[N]===0);t.emit("disconnect",t[b],[t],n);resume(t)}const De=n(953);const _e=n(8861);const Ne=Buffer.alloc(0);async function lazyllhttp(){const e=process.env.JEST_WORKER_ID?n(1145):undefined;let t;try{t=await WebAssembly.compile(Buffer.from(n(5627),"base64"))}catch(s){t=await WebAssembly.compile(Buffer.from(e||n(1145),"base64"))}return await WebAssembly.instantiate(t,{env:{wasm_on_url:(e,t,n)=>0,wasm_on_status:(e,t,n)=>{s.strictEqual(Te.ptr,e);const i=t-Oe+Me.byteOffset;return Te.onStatus(new ke(Me.buffer,i,n))||0},wasm_on_message_begin:e=>{s.strictEqual(Te.ptr,e);return Te.onMessageBegin()||0},wasm_on_header_field:(e,t,n)=>{s.strictEqual(Te.ptr,e);const i=t-Oe+Me.byteOffset;return Te.onHeaderField(new ke(Me.buffer,i,n))||0},wasm_on_header_value:(e,t,n)=>{s.strictEqual(Te.ptr,e);const i=t-Oe+Me.byteOffset;return Te.onHeaderValue(new ke(Me.buffer,i,n))||0},wasm_on_headers_complete:(e,t,n,i)=>{s.strictEqual(Te.ptr,e);return Te.onHeadersComplete(t,Boolean(n),Boolean(i))||0},wasm_on_body:(e,t,n)=>{s.strictEqual(Te.ptr,e);const i=t-Oe+Me.byteOffset;return Te.onBody(new ke(Me.buffer,i,n))||0},wasm_on_message_complete:e=>{s.strictEqual(Te.ptr,e);return Te.onMessageComplete()||0}}})}let Fe=null;let Ue=lazyllhttp();Ue.catch();let Te=null;let Me=null;let Le=0;let Oe=null;const Pe=1;const Je=2;const He=3;class Parser{constructor(e,t,{exports:n}){s(Number.isFinite(e[z])&&e[z]>0);this.llhttp=n;this.ptr=this.llhttp.llhttp_alloc(De.TYPE.RESPONSE);this.client=e;this.socket=t;this.timeout=null;this.timeoutValue=null;this.timeoutType=null;this.statusCode=null;this.statusText="";this.upgrade=false;this.headers=[];this.headersSize=0;this.headersMaxSize=e[z];this.shouldKeepAlive=false;this.paused=false;this.resume=this.resume.bind(this);this.bytesRead=0;this.keepAlive="";this.contentLength="";this.connection="";this.maxResponseSize=e[le]}setTimeout(e,t){this.timeoutType=t;if(e!==this.timeoutValue){a.clearTimeout(this.timeout);if(e){this.timeout=a.setTimeout(onParserTimeout,e,this);if(this.timeout.unref){this.timeout.unref()}}else{this.timeout=null}this.timeoutValue=e}else if(this.timeout){if(this.timeout.refresh){this.timeout.refresh()}}}resume(){if(this.socket.destroyed||!this.paused){return}s(this.ptr!=null);s(Te==null);this.llhttp.llhttp_resume(this.ptr);s(this.timeoutType===Je);if(this.timeout){if(this.timeout.refresh){this.timeout.refresh()}}this.paused=false;this.execute(this.socket.read()||Ne);this.readMore()}readMore(){while(!this.paused&&this.ptr){const e=this.socket.read();if(e===null){break}this.execute(e)}}execute(e){s(this.ptr!=null);s(Te==null);s(!this.paused);const{socket:t,llhttp:n}=this;if(e.length>Le){if(Oe){n.free(Oe)}Le=Math.ceil(e.length/4096)*4096;Oe=n.malloc(Le)}new Uint8Array(n.memory.buffer,Oe,Le).set(e);try{let s;try{Me=e;Te=this;s=n.llhttp_execute(this.ptr,Oe,e.length)}catch(e){throw e}finally{Te=null;Me=null}const i=n.llhttp_get_error_pos(this.ptr)-Oe;if(s===De.ERROR.PAUSED_UPGRADE){this.onUpgrade(e.slice(i))}else if(s===De.ERROR.PAUSED){this.paused=true;t.unshift(e.slice(i))}else if(s!==De.ERROR.OK){const t=n.llhttp_get_error_reason(this.ptr);let r="";if(t){const e=new Uint8Array(n.memory.buffer,t).indexOf(0);r="Response does not match the HTTP/1.1 protocol ("+Buffer.from(n.memory.buffer,t,e).toString()+")"}throw new Q(r,De.ERROR[s],e.slice(i))}}catch(e){A.destroy(t,e)}}destroy(){s(this.ptr!=null);s(Te==null);this.llhttp.llhttp_free(this.ptr);this.ptr=null;a.clearTimeout(this.timeout);this.timeout=null;this.timeoutValue=null;this.timeoutType=null;this.paused=false}onStatus(e){this.statusText=e.toString()}onMessageBegin(){const{socket:e,client:t}=this;if(e.destroyed){return-1}const n=t[M][t[Y]];if(!n){return-1}}onHeaderField(e){const t=this.headers.length;if((t&1)===0){this.headers.push(e)}else{this.headers[t-1]=Buffer.concat([this.headers[t-1],e])}this.trackHeader(e.length)}onHeaderValue(e){let t=this.headers.length;if((t&1)===1){this.headers.push(e);t+=1}else{this.headers[t-1]=Buffer.concat([this.headers[t-1],e])}const n=this.headers[t-2];if(n.length===10&&n.toString().toLowerCase()==="keep-alive"){this.keepAlive+=e.toString()}else if(n.length===10&&n.toString().toLowerCase()==="connection"){this.connection+=e.toString()}else if(n.length===14&&n.toString().toLowerCase()==="content-length"){this.contentLength+=e.toString()}this.trackHeader(e.length)}trackHeader(e){this.headersSize+=e;if(this.headersSize>=this.headersMaxSize){A.destroy(this.socket,new f)}}onUpgrade(e){const{upgrade:t,client:n,socket:i,headers:r,statusCode:o}=this;s(t);const a=n[M][n[Y]];s(a);s(!i.destroyed);s(i===n[W]);s(!this.paused);s(a.upgrade||a.method==="CONNECT");this.statusCode=null;this.statusText="";this.shouldKeepAlive=null;s(this.headers.length%2===0);this.headers=[];this.headersSize=0;i.unshift(e);i[S].destroy();i[S]=null;i[v]=null;i[V]=null;i.removeListener("error",onSocketError).removeListener("readable",onSocketReadable).removeListener("end",onSocketEnd).removeListener("close",onSocketClose);n[W]=null;n[M][n[Y]++]=null;n.emit("disconnect",n[b],[n],new m("upgrade"));try{a.onUpgrade(o,r,i)}catch(e){A.destroy(i,e)}resume(n)}onHeadersComplete(e,t,n){const{client:i,socket:r,headers:o,statusText:a}=this;if(r.destroyed){return-1}const c=i[M][i[Y]];if(!c){return-1}s(!this.upgrade);s(this.statusCode<200);if(e===100){A.destroy(r,new E("bad response",A.getSocketInfo(r)));return-1}if(t&&!c.upgrade){A.destroy(r,new E("bad upgrade",A.getSocketInfo(r)));return-1}s.strictEqual(this.timeoutType,Pe);this.statusCode=e;this.shouldKeepAlive=n||c.method==="HEAD"&&!r[w]&&this.connection.toLowerCase()==="keep-alive";if(this.statusCode>=200){const e=c.bodyTimeout!=null?c.bodyTimeout:i[ee];this.setTimeout(e,Je)}else if(this.timeout){if(this.timeout.refresh){this.timeout.refresh()}}if(c.method==="CONNECT"){s(i[N]===1);this.upgrade=true;return 2}if(t){s(i[N]===1);this.upgrade=true;return 2}s(this.headers.length%2===0);this.headers=[];this.headersSize=0;if(this.shouldKeepAlive&&i[j]){const e=this.keepAlive?A.parseKeepAliveTimeout(this.keepAlive):null;if(e!=null){const t=Math.min(e-i[X],i[Z]);if(t<=0){r[w]=true}else{i[K]=t}}else{i[K]=i[H]}}else{r[w]=true}let u;try{u=c.onHeaders(e,o,this.resume,a)===false}catch(e){A.destroy(r,e);return-1}if(c.method==="HEAD"){return 1}if(e<200){return 1}if(r[D]){r[D]=false;resume(i)}return u?De.ERROR.PAUSED:0}onBody(e){const{client:t,socket:n,statusCode:i,maxResponseSize:r}=this;if(n.destroyed){return-1}const o=t[M][t[Y]];s(o);s.strictEqual(this.timeoutType,Je);if(this.timeout){if(this.timeout.refresh){this.timeout.refresh()}}s(i>=200);if(r>-1&&this.bytesRead+e.length>r){A.destroy(n,new I);return-1}this.bytesRead+=e.length;try{if(o.onData(e)===false){return De.ERROR.PAUSED}}catch(e){A.destroy(n,e);return-1}}onMessageComplete(){const{client:e,socket:t,statusCode:n,upgrade:i,headers:r,contentLength:o,bytesRead:a,shouldKeepAlive:c}=this;if(t.destroyed&&(!n||c)){return-1}if(i){return}const u=e[M][e[Y]];s(u);s(n>=100);this.statusCode=null;this.statusText="";this.bytesRead=0;this.contentLength="";this.keepAlive="";this.connection="";s(this.headers.length%2===0);this.headers=[];this.headersSize=0;if(n<200){return}if(u.method!=="HEAD"&&o&&a!==parseInt(o,10)){A.destroy(t,new d);return-1}try{u.onComplete(r)}catch(t){errorRequest(e,u,t)}e[M][e[Y]++]=null;if(t[T]){s.strictEqual(e[N],0);A.destroy(t,new m("reset"));return De.ERROR.PAUSED}else if(!c){A.destroy(t,new m("reset"));return De.ERROR.PAUSED}else if(t[w]&&e[N]===0){A.destroy(t,new m("reset"));return De.ERROR.PAUSED}else if(e[j]===1){setImmediate(resume,e)}else{resume(e)}}}function onParserTimeout(e){const{socket:t,timeoutType:n,client:i}=e;if(n===Pe){if(!t[T]||t.writableNeedDrain||i[N]>1){s(!e.paused,"cannot be paused while waiting for headers");A.destroy(t,new h)}}else if(n===Je){if(!e.paused){A.destroy(t,new C)}}else if(n===He){s(i[N]===0&&i[K]);A.destroy(t,new m("socket idle timeout"))}}function onSocketReadable(){const{[S]:e}=this;if(e){e.readMore()}}function onSocketError(e){const{[v]:t,[S]:n}=this;s(e.code!=="ERR_TLS_CERT_ALTNAME_INVALID");if(t[de]!=="h2"){if(e.code==="ECONNRESET"&&n.statusCode&&!n.shouldKeepAlive){n.onMessageComplete();return}}this[V]=e;onError(this[v],e)}function onError(e,t){if(e[N]===0&&t.code!=="UND_ERR_INFO"&&t.code!=="UND_ERR_SOCKET"){s(e[G]===e[Y]);const n=e[M].splice(e[Y]);for(let s=0;s0&&n.code!=="UND_ERR_INFO"){const t=e[M][e[Y]];e[M][e[Y]++]=null;errorRequest(e,t,n)}e[G]=e[Y];s(e[N]===0);e.emit("disconnect",e[b],[e],n);resume(e)}async function connect(e){s(!e[O]);s(!e[W]);let{host:t,hostname:n,protocol:r,port:o}=e[b];if(n[0]==="["){const e=n.indexOf("]");s(e!==-1);const t=n.substr(1,e-1);s(i.isIP(t));n=t}e[O]=true;if(xe.beforeConnect.hasSubscribers){xe.beforeConnect.publish({connectParams:{host:t,hostname:n,protocol:r,port:o,servername:e[R],localAddress:e[ue]},connector:e[ne]})}try{const i=await new Promise(((s,i)=>{e[ne]({host:t,hostname:n,protocol:r,port:o,servername:e[R],localAddress:e[ue]},((e,t)=>{if(e){i(e)}else{s(t)}}))}));if(e.destroyed){A.destroy(i.on("error",(()=>{})),new B);return}e[O]=false;s(i);const a=i.alpnProtocol==="h2";if(a){if(!ve){ve=true;process.emitWarning("H2 support is experimental, expect them to change at any time.",{code:"UNDICI-H2"})}const t=Ce.connect(e[b],{createConnection:()=>i,peerMaxConcurrentStreams:e[he].maxConcurrentStreams});e[de]="h2";t[v]=e;t[W]=i;t.on("error",onHttp2SessionError);t.on("frameError",onHttp2FrameError);t.on("end",onHttp2SessionEnd);t.on("goaway",onHTTP2GoAway);t.on("close",onSocketClose);t.unref();e[ge]=t;i[ge]=t}else{if(!Fe){Fe=await Ue;Ue=null}i[J]=false;i[T]=false;i[w]=false;i[D]=false;i[S]=new Parser(e,i,Fe)}i[re]=0;i[ie]=e[ie];i[v]=e;i[V]=null;i.on("error",onSocketError).on("readable",onSocketReadable).on("end",onSocketEnd).on("close",onSocketClose);e[W]=i;if(xe.connected.hasSubscribers){xe.connected.publish({connectParams:{host:t,hostname:n,protocol:r,port:o,servername:e[R],localAddress:e[ue]},connector:e[ne],socket:i})}e.emit("connect",e[b],[e])}catch(i){if(e.destroyed){return}e[O]=false;if(xe.connectError.hasSubscribers){xe.connectError.publish({connectParams:{host:t,hostname:n,protocol:r,port:o,servername:e[R],localAddress:e[ue]},connector:e[ne],error:i})}if(i.code==="ERR_TLS_CERT_ALTNAME_INVALID"){s(e[N]===0);while(e[F]>0&&e[M][e[G]].servername===e[R]){const t=e[M][e[G]++];errorRequest(e,t,i)}}else{onError(e,i)}e.emit("connectionError",e[b],[e],i)}resume(e)}function emitDrain(e){e[P]=0;e.emit("drain",e[b],[e])}function resume(e,t){if(e[_]===2){return}e[_]=2;_resume(e,t);e[_]=0;if(e[Y]>256){e[M].splice(0,e[Y]);e[G]-=e[Y];e[Y]=0}}function _resume(e,t){while(true){if(e.destroyed){s(e[F]===0);return}if(e[Se]&&!e[U]){e[Se]();e[Se]=null;return}const n=e[W];if(n&&!n.destroyed&&n.alpnProtocol!=="h2"){if(e[U]===0){if(!n[J]&&n.unref){n.unref();n[J]=true}}else if(n[J]&&n.ref){n.ref();n[J]=false}if(e[U]===0){if(n[S].timeoutType!==He){n[S].setTimeout(e[K],He)}}else if(e[N]>0&&n[S].statusCode<200){if(n[S].timeoutType!==Pe){const t=e[M][e[Y]];const s=t.headersTimeout!=null?t.headersTimeout:e[$];n[S].setTimeout(s,Pe)}}}if(e[k]){e[P]=2}else if(e[P]===2){if(t){e[P]=1;process.nextTick(emitDrain,e)}else{emitDrain(e)}continue}if(e[F]===0){return}if(e[N]>=(e[j]||1)){return}const i=e[M][e[G]];if(e[b].protocol==="https:"&&e[R]!==i.servername){if(e[N]>0){return}e[R]=i.servername;if(n&&n.servername!==i.servername){A.destroy(n,new m("servername changed"));return}}if(e[O]){return}if(!n&&!e[ge]){connect(e);return}if(n.destroyed||n[T]||n[w]||n[D]){return}if(e[N]>0&&!i.idempotent){return}if(e[N]>0&&(i.upgrade||i.method==="CONNECT")){return}if(A.isStream(i.body)&&A.bodyLength(i.body)===0){i.body.on("data",(function(){s(false)})).on("error",(function(t){errorRequest(e,i,t)})).on("end",(function(){A.destroy(this)}));i.body=null}if(e[N]>0&&(A.isStream(i.body)||A.isAsyncIterable(i.body))){return}if(!i.aborted&&write(e,i)){e[G]++}else{e[M].splice(e[G],1)}}}function write(e,t){if(e[de]==="h2"){writeH2(e,e[ge],t);return}const{body:n,method:i,path:r,host:o,upgrade:a,headers:c,blocking:u,reset:d}=t;const p=i==="PUT"||i==="POST"||i==="PATCH";if(n&&typeof n.read==="function"){n.read(0)}let h=A.bodyLength(n);if(h===null){h=t.contentLength}if(h===0&&!p){h=null}if(t.contentLength!==null&&t.contentLength!==h){if(e[te]){errorRequest(e,t,new l);return false}process.emitWarning(new l)}const f=e[W];try{t.onConnect((n=>{if(t.aborted||t.completed){return}errorRequest(e,t,n||new g);A.destroy(f,new m("aborted"))}))}catch(n){errorRequest(e,t,n)}if(t.aborted){return false}if(i==="HEAD"){f[w]=true}if(a||i==="CONNECT"){f[w]=true}if(d!=null){f[w]=d}if(e[ie]&&f[re]++>=e[ie]){f[w]=true}if(u){f[D]=true}let E=`${i} ${r} HTTP/1.1\r\n`;if(typeof o==="string"){E+=`host: ${o}\r\n`}else{E+=e[q]}if(a){E+=`connection: upgrade\r\nupgrade: ${a}\r\n`}else if(e[j]&&!f[w]){E+="connection: keep-alive\r\n"}else{E+="connection: close\r\n"}if(c){E+=c}if(xe.sendHeaders.hasSubscribers){xe.sendHeaders.publish({request:t,headers:E,socket:f})}if(!n){if(h===0){f.write(`${E}content-length: 0\r\n\r\n`,"latin1")}else{s(h===null,"no body must not have content length");f.write(`${E}\r\n`,"latin1")}t.onRequestSent()}else if(A.isBuffer(n)){s(h===n.byteLength,"buffer body must have content length");f.cork();f.write(`${E}content-length: ${h}\r\n\r\n`,"latin1");f.write(n);f.uncork();t.onBodySent(n);t.onRequestSent();if(!p){f[w]=true}}else if(A.isBlobLike(n)){if(typeof n.stream==="function"){writeIterable({body:n.stream(),client:e,request:t,socket:f,contentLength:h,header:E,expectsPayload:p})}else{writeBlob({body:n,client:e,request:t,socket:f,contentLength:h,header:E,expectsPayload:p})}}else if(A.isStream(n)){writeStream({body:n,client:e,request:t,socket:f,contentLength:h,header:E,expectsPayload:p})}else if(A.isIterable(n)){writeIterable({body:n,client:e,request:t,socket:f,contentLength:h,header:E,expectsPayload:p})}else{s(false)}return true}function writeH2(e,t,n){const{body:i,method:r,path:o,host:a,upgrade:u,expectContinue:d,signal:p,headers:h}=n;let f;if(typeof h==="string")f=c[Ee](h.trim());else f=h;if(u){errorRequest(e,n,new Error("Upgrade not supported for H2"));return false}try{n.onConnect((t=>{if(n.aborted||n.completed){return}errorRequest(e,n,t||new g)}))}catch(t){errorRequest(e,n,t)}if(n.aborted){return false}let E;const C=e[he];f[Qe]=a||e[pe];f[Ie]=r;if(r==="CONNECT"){t.ref();E=t.request(f,{endStream:false,signal:p});if(E.id&&!E.pending){n.onUpgrade(null,null,E);++C.openStreams}else{E.once("ready",(()=>{n.onUpgrade(null,null,E);++C.openStreams}))}E.once("close",(()=>{C.openStreams-=1;if(C.openStreams===0)t.unref()}));return true}f[Be]=o;f[ye]="https";const Q=r==="PUT"||r==="POST"||r==="PATCH";if(i&&typeof i.read==="function"){i.read(0)}let I=A.bodyLength(i);if(I==null){I=n.contentLength}if(I===0||!Q){I=null}if(n.contentLength!=null&&n.contentLength!==I){if(e[te]){errorRequest(e,n,new l);return false}process.emitWarning(new l)}if(I!=null){s(i,"no body must not have content length");f[be]=`${I}`}t.ref();const B=r==="GET"||r==="HEAD";if(d){f[we]="100-continue";E=t.request(f,{endStream:B,signal:p});E.once("continue",writeBodyH2)}else{E=t.request(f,{endStream:B,signal:p});writeBodyH2()}++C.openStreams;E.once("response",(e=>{if(n.onHeaders(Number(e[Re]),e,E.resume.bind(E),"")===false){E.pause()}}));E.once("end",(()=>{n.onComplete([])}));E.on("data",(e=>{if(n.onData(e)===false)E.pause()}));E.once("close",(()=>{C.openStreams-=1;if(C.openStreams===0)t.unref()}));E.once("error",(function(t){if(e[ge]&&!e[ge].destroyed&&!this.closed&&!this.destroyed){C.streams-=1;A.destroy(E,t)}}));E.once("frameError",((t,s)=>{const i=new m(`HTTP/2: "frameError" received - type ${t}, code ${s}`);errorRequest(e,n,i);if(e[ge]&&!e[ge].destroyed&&!this.closed&&!this.destroyed){C.streams-=1;A.destroy(E,i)}}));return true;function writeBodyH2(){if(!i){n.onRequestSent()}else if(A.isBuffer(i)){s(I===i.byteLength,"buffer body must have content length");E.cork();E.write(i);E.uncork();E.end();n.onBodySent(i);n.onRequestSent()}else if(A.isBlobLike(i)){if(typeof i.stream==="function"){writeIterable({client:e,request:n,contentLength:I,h2stream:E,expectsPayload:Q,body:i.stream(),socket:e[W],header:""})}else{writeBlob({body:i,client:e,request:n,contentLength:I,expectsPayload:Q,h2stream:E,header:"",socket:e[W]})}}else if(A.isStream(i)){writeStream({body:i,client:e,request:n,contentLength:I,expectsPayload:Q,socket:e[W],h2stream:E,header:""})}else if(A.isIterable(i)){writeIterable({body:i,client:e,request:n,contentLength:I,expectsPayload:Q,header:"",h2stream:E,socket:e[W]})}else{s(false)}}}function writeStream({h2stream:e,body:t,client:n,request:i,socket:r,contentLength:a,header:c,expectsPayload:u}){s(a!==0||n[N]===0,"stream body cannot be pipelined");if(n[de]==="h2"){const n=o(t,e,(n=>{if(n){A.destroy(t,n);A.destroy(e,n)}else{i.onRequestSent()}}));n.on("data",onPipeData);n.once("end",(()=>{n.removeListener("data",onPipeData);A.destroy(n)}));function onPipeData(e){i.onBodySent(e)}return}let l=false;const d=new AsyncWriter({socket:r,request:i,contentLength:a,client:n,expectsPayload:u,header:c});const onData=function(e){if(l){return}try{if(!d.write(e)&&this.pause){this.pause()}}catch(e){A.destroy(this,e)}};const onDrain=function(){if(l){return}if(t.resume){t.resume()}};const onAbort=function(){onFinished(new g)};const onFinished=function(e){if(l){return}l=true;s(r.destroyed||r[T]&&n[N]<=1);r.off("drain",onDrain).off("error",onFinished);t.removeListener("data",onData).removeListener("end",onFinished).removeListener("error",onFinished).removeListener("close",onAbort);if(!e){try{d.end()}catch(t){e=t}}d.destroy(e);if(e&&(e.code!=="UND_ERR_INFO"||e.message!=="reset")){A.destroy(t,e)}else{A.destroy(t)}};t.on("data",onData).on("end",onFinished).on("error",onFinished).on("close",onAbort);if(t.resume){t.resume()}r.on("drain",onDrain).on("error",onFinished)}async function writeBlob({h2stream:e,body:t,client:n,request:i,socket:r,contentLength:o,header:a,expectsPayload:c}){s(o===t.size,"blob body must have content length");const u=n[de]==="h2";try{if(o!=null&&o!==t.size){throw new l}const s=Buffer.from(await t.arrayBuffer());if(u){e.cork();e.write(s);e.uncork()}else{r.cork();r.write(`${a}content-length: ${o}\r\n\r\n`,"latin1");r.write(s);r.uncork()}i.onBodySent(s);i.onRequestSent();if(!c){r[w]=true}resume(n)}catch(t){A.destroy(u?e:r,t)}}async function writeIterable({h2stream:e,body:t,client:n,request:i,socket:r,contentLength:o,header:A,expectsPayload:a}){s(o!==0||n[N]===0,"iterator body cannot be pipelined");let c=null;function onDrain(){if(c){const e=c;c=null;e()}}const waitForDrain=()=>new Promise(((e,t)=>{s(c===null);if(r[V]){t(r[V])}else{c=e}}));if(n[de]==="h2"){e.on("close",onDrain).on("drain",onDrain);try{for await(const n of t){if(r[V]){throw r[V]}const t=e.write(n);i.onBodySent(n);if(!t){await waitForDrain()}}}catch(t){e.destroy(t)}finally{i.onRequestSent();e.end();e.off("close",onDrain).off("drain",onDrain)}return}r.on("close",onDrain).on("drain",onDrain);const u=new AsyncWriter({socket:r,request:i,contentLength:o,client:n,expectsPayload:a,header:A});try{for await(const e of t){if(r[V]){throw r[V]}if(!u.write(e)){await waitForDrain()}}u.end()}catch(e){u.destroy(e)}finally{r.off("close",onDrain).off("drain",onDrain)}}class AsyncWriter{constructor({socket:e,request:t,contentLength:n,client:s,expectsPayload:i,header:r}){this.socket=e;this.request=t;this.contentLength=n;this.client=s;this.bytesWritten=0;this.expectsPayload=i;this.header=r;e[T]=true}write(e){const{socket:t,request:n,contentLength:s,client:i,bytesWritten:r,expectsPayload:o,header:A}=this;if(t[V]){throw t[V]}if(t.destroyed){return false}const a=Buffer.byteLength(e);if(!a){return true}if(s!==null&&r+a>s){if(i[te]){throw new l}process.emitWarning(new l)}t.cork();if(r===0){if(!o){t[w]=true}if(s===null){t.write(`${A}transfer-encoding: chunked\r\n`,"latin1")}else{t.write(`${A}content-length: ${s}\r\n\r\n`,"latin1")}}if(s===null){t.write(`\r\n${a.toString(16)}\r\n`,"latin1")}this.bytesWritten+=a;const c=t.write(e);t.uncork();n.onBodySent(e);if(!c){if(t[S].timeout&&t[S].timeoutType===Pe){if(t[S].timeout.refresh){t[S].timeout.refresh()}}}return c}end(){const{socket:e,contentLength:t,client:n,bytesWritten:s,expectsPayload:i,header:r,request:o}=this;o.onRequestSent();e[T]=false;if(e[V]){throw e[V]}if(e.destroyed){return}if(s===0){if(i){e.write(`${r}content-length: 0\r\n\r\n`,"latin1")}else{e.write(`${r}\r\n`,"latin1")}}else if(t===null){e.write("\r\n0\r\n\r\n","latin1")}if(t!==null&&s!==t){if(n[te]){throw new l}else{process.emitWarning(new l)}}if(e[S].timeout&&e[S].timeoutType===Pe){if(e[S].timeout.refresh){e[S].timeout.refresh()}}resume(n)}destroy(e){const{socket:t,client:n}=this;t[T]=false;if(e){s(n[N]<=1,"pipeline should only contain this request");A.destroy(t,e)}}}function errorRequest(e,t,n){try{t.onError(n);s(t.aborted)}catch(n){e.emit("error",n)}}e.exports=Client},6436:(e,t,n)=>{"use strict";const{kConnected:s,kSize:i}=n(2785);class CompatWeakRef{constructor(e){this.value=e}deref(){return this.value[s]===0&&this.value[i]===0?undefined:this.value}}class CompatFinalizer{constructor(e){this.finalizer=e}register(e,t){if(e.on){e.on("disconnect",(()=>{if(e[s]===0&&e[i]===0){this.finalizer(t)}}))}}}e.exports=function(){if(process.env.NODE_V8_COVERAGE){return{WeakRef:CompatWeakRef,FinalizationRegistry:CompatFinalizer}}return{WeakRef:global.WeakRef||CompatWeakRef,FinalizationRegistry:global.FinalizationRegistry||CompatFinalizer}}},663:e=>{"use strict";const t=1024;const n=4096;e.exports={maxAttributeValueSize:t,maxNameValuePairSize:n}},1724:(e,t,n)=>{"use strict";const{parseSetCookie:s}=n(4408);const{stringify:i,getHeadersList:r}=n(3121);const{webidl:o}=n(1744);const{Headers:A}=n(554);function getCookies(e){o.argumentLengthCheck(arguments,1,{header:"getCookies"});o.brandCheck(e,A,{strict:false});const t=e.get("cookie");const n={};if(!t){return n}for(const e of t.split(";")){const[t,...s]=e.split("=");n[t.trim()]=s.join("=")}return n}function deleteCookie(e,t,n){o.argumentLengthCheck(arguments,2,{header:"deleteCookie"});o.brandCheck(e,A,{strict:false});t=o.converters.DOMString(t);n=o.converters.DeleteCookieAttributes(n);setCookie(e,{name:t,value:"",expires:new Date(0),...n})}function getSetCookies(e){o.argumentLengthCheck(arguments,1,{header:"getSetCookies"});o.brandCheck(e,A,{strict:false});const t=r(e).cookies;if(!t){return[]}return t.map((e=>s(Array.isArray(e)?e[1]:e)))}function setCookie(e,t){o.argumentLengthCheck(arguments,2,{header:"setCookie"});o.brandCheck(e,A,{strict:false});t=o.converters.Cookie(t);const n=i(t);if(n){e.append("Set-Cookie",i(t))}}o.converters.DeleteCookieAttributes=o.dictionaryConverter([{converter:o.nullableConverter(o.converters.DOMString),key:"path",defaultValue:null},{converter:o.nullableConverter(o.converters.DOMString),key:"domain",defaultValue:null}]);o.converters.Cookie=o.dictionaryConverter([{converter:o.converters.DOMString,key:"name"},{converter:o.converters.DOMString,key:"value"},{converter:o.nullableConverter((e=>{if(typeof e==="number"){return o.converters["unsigned long long"](e)}return new Date(e)})),key:"expires",defaultValue:null},{converter:o.nullableConverter(o.converters["long long"]),key:"maxAge",defaultValue:null},{converter:o.nullableConverter(o.converters.DOMString),key:"domain",defaultValue:null},{converter:o.nullableConverter(o.converters.DOMString),key:"path",defaultValue:null},{converter:o.nullableConverter(o.converters.boolean),key:"secure",defaultValue:null},{converter:o.nullableConverter(o.converters.boolean),key:"httpOnly",defaultValue:null},{converter:o.converters.USVString,key:"sameSite",allowedValues:["Strict","Lax","None"]},{converter:o.sequenceConverter(o.converters.DOMString),key:"unparsed",defaultValue:[]}]);e.exports={getCookies:getCookies,deleteCookie:deleteCookie,getSetCookies:getSetCookies,setCookie:setCookie}},4408:(e,t,n)=>{"use strict";const{maxNameValuePairSize:s,maxAttributeValueSize:i}=n(663);const{isCTLExcludingHtab:r}=n(3121);const{collectASequenceOfCodePointsFast:o}=n(685);const A=n(9491);function parseSetCookie(e){if(r(e)){return null}let t="";let n="";let i="";let A="";if(e.includes(";")){const s={position:0};t=o(";",e,s);n=e.slice(s.position)}else{t=e}if(!t.includes("=")){A=t}else{const e={position:0};i=o("=",t,e);A=t.slice(e.position+1)}i=i.trim();A=A.trim();if(i.length+A.length>s){return null}return{name:i,value:A,...parseUnparsedAttributes(n)}}function parseUnparsedAttributes(e,t={}){if(e.length===0){return t}A(e[0]===";");e=e.slice(1);let n="";if(e.includes(";")){n=o(";",e,{position:0});e=e.slice(n.length)}else{n=e;e=""}let s="";let r="";if(n.includes("=")){const e={position:0};s=o("=",n,e);r=n.slice(e.position+1)}else{s=n}s=s.trim();r=r.trim();if(r.length>i){return parseUnparsedAttributes(e,t)}const a=s.toLowerCase();if(a==="expires"){const e=new Date(r);t.expires=e}else if(a==="max-age"){const n=r.charCodeAt(0);if((n<48||n>57)&&r[0]!=="-"){return parseUnparsedAttributes(e,t)}if(!/^\d+$/.test(r)){return parseUnparsedAttributes(e,t)}const s=Number(r);t.maxAge=s}else if(a==="domain"){let e=r;if(e[0]==="."){e=e.slice(1)}e=e.toLowerCase();t.domain=e}else if(a==="path"){let e="";if(r.length===0||r[0]!=="/"){e="/"}else{e=r}t.path=e}else if(a==="secure"){t.secure=true}else if(a==="httponly"){t.httpOnly=true}else if(a==="samesite"){let e="Default";const n=r.toLowerCase();if(n.includes("none")){e="None"}if(n.includes("strict")){e="Strict"}if(n.includes("lax")){e="Lax"}t.sameSite=e}else{t.unparsed??=[];t.unparsed.push(`${s}=${r}`)}return parseUnparsedAttributes(e,t)}e.exports={parseSetCookie:parseSetCookie,parseUnparsedAttributes:parseUnparsedAttributes}},3121:(e,t,n)=>{"use strict";const s=n(9491);const{kHeadersList:i}=n(2785);function isCTLExcludingHtab(e){if(e.length===0){return false}for(const t of e){const e=t.charCodeAt(0);if(e>=0||e<=8||(e>=10||e<=31)||e===127){return false}}}function validateCookieName(e){for(const t of e){const e=t.charCodeAt(0);if(e<=32||e>127||t==="("||t===")"||t===">"||t==="<"||t==="@"||t===","||t===";"||t===":"||t==="\\"||t==='"'||t==="/"||t==="["||t==="]"||t==="?"||t==="="||t==="{"||t==="}"){throw new Error("Invalid cookie name")}}}function validateCookieValue(e){for(const t of e){const e=t.charCodeAt(0);if(e<33||e===34||e===44||e===59||e===92||e>126){throw new Error("Invalid header value")}}}function validateCookiePath(e){for(const t of e){const e=t.charCodeAt(0);if(e<33||t===";"){throw new Error("Invalid cookie path")}}}function validateCookieDomain(e){if(e.startsWith("-")||e.endsWith(".")||e.endsWith("-")){throw new Error("Invalid cookie domain")}}function toIMFDate(e){if(typeof e==="number"){e=new Date(e)}const t=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];const n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];const s=t[e.getUTCDay()];const i=e.getUTCDate().toString().padStart(2,"0");const r=n[e.getUTCMonth()];const o=e.getUTCFullYear();const A=e.getUTCHours().toString().padStart(2,"0");const a=e.getUTCMinutes().toString().padStart(2,"0");const c=e.getUTCSeconds().toString().padStart(2,"0");return`${s}, ${i} ${r} ${o} ${A}:${a}:${c} GMT`}function validateCookieMaxAge(e){if(e<0){throw new Error("Invalid cookie max-age")}}function stringify(e){if(e.name.length===0){return null}validateCookieName(e.name);validateCookieValue(e.value);const t=[`${e.name}=${e.value}`];if(e.name.startsWith("__Secure-")){e.secure=true}if(e.name.startsWith("__Host-")){e.secure=true;e.domain=null;e.path="/"}if(e.secure){t.push("Secure")}if(e.httpOnly){t.push("HttpOnly")}if(typeof e.maxAge==="number"){validateCookieMaxAge(e.maxAge);t.push(`Max-Age=${e.maxAge}`)}if(e.domain){validateCookieDomain(e.domain);t.push(`Domain=${e.domain}`)}if(e.path){validateCookiePath(e.path);t.push(`Path=${e.path}`)}if(e.expires&&e.expires.toString()!=="Invalid Date"){t.push(`Expires=${toIMFDate(e.expires)}`)}if(e.sameSite){t.push(`SameSite=${e.sameSite}`)}for(const n of e.unparsed){if(!n.includes("=")){throw new Error("Invalid unparsed")}const[e,...s]=n.split("=");t.push(`${e.trim()}=${s.join("=")}`)}return t.join("; ")}let r;function getHeadersList(e){if(e[i]){return e[i]}if(!r){r=Object.getOwnPropertySymbols(e).find((e=>e.description==="headers list"));s(r,"Headers cannot be parsed")}const t=e[r];s(t);return t}e.exports={isCTLExcludingHtab:isCTLExcludingHtab,stringify:stringify,getHeadersList:getHeadersList}},2067:(e,t,n)=>{"use strict";const s=n(1808);const i=n(9491);const r=n(3983);const{InvalidArgumentError:o,ConnectTimeoutError:A}=n(8045);let a;let c;if(global.FinalizationRegistry&&!process.env.NODE_V8_COVERAGE){c=class WeakSessionCache{constructor(e){this._maxCachedSessions=e;this._sessionCache=new Map;this._sessionRegistry=new global.FinalizationRegistry((e=>{if(this._sessionCache.size=this._maxCachedSessions){const{value:e}=this._sessionCache.keys().next();this._sessionCache.delete(e)}this._sessionCache.set(e,t)}}}function buildConnector({allowH2:e,maxCachedSessions:t,socketPath:A,timeout:u,...l}){if(t!=null&&(!Number.isInteger(t)||t<0)){throw new o("maxCachedSessions must be a positive integer or zero")}const d={path:A,...l};const p=new c(t==null?100:t);u=u==null?1e4:u;e=e!=null?e:false;return function connect({hostname:t,host:o,protocol:A,port:c,servername:l,localAddress:g,httpSocket:h},f){let E;if(A==="https:"){if(!a){a=n(4404)}l=l||d.servername||r.getServerName(o)||null;const s=l||t;const A=p.get(s)||null;i(s);E=a.connect({highWaterMark:16384,...d,servername:l,session:A,localAddress:g,ALPNProtocols:e?["http/1.1","h2"]:["http/1.1"],socket:h,port:c||443,host:t});E.on("session",(function(e){p.set(s,e)}))}else{i(!h,"httpSocket can only be sent on TLS update");E=s.connect({highWaterMark:64*1024,...d,localAddress:g,port:c||80,host:t})}if(d.keepAlive==null||d.keepAlive){const e=d.keepAliveInitialDelay===undefined?6e4:d.keepAliveInitialDelay;E.setKeepAlive(true,e)}const m=setupTimeout((()=>onConnectTimeout(E)),u);E.setNoDelay(true).once(A==="https:"?"secureConnect":"connect",(function(){m();if(f){const e=f;f=null;e(null,this)}})).on("error",(function(e){m();if(f){const t=f;f=null;t(e)}}));return E}}function setupTimeout(e,t){if(!t){return()=>{}}let n=null;let s=null;const i=setTimeout((()=>{n=setImmediate((()=>{if(process.platform==="win32"){s=setImmediate((()=>e()))}else{e()}}))}),t);return()=>{clearTimeout(i);clearImmediate(n);clearImmediate(s)}}function onConnectTimeout(e){r.destroy(e,new A)}e.exports=buildConnector},8045:e=>{"use strict";class UndiciError extends Error{constructor(e){super(e);this.name="UndiciError";this.code="UND_ERR"}}class ConnectTimeoutError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,ConnectTimeoutError);this.name="ConnectTimeoutError";this.message=e||"Connect Timeout Error";this.code="UND_ERR_CONNECT_TIMEOUT"}}class HeadersTimeoutError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,HeadersTimeoutError);this.name="HeadersTimeoutError";this.message=e||"Headers Timeout Error";this.code="UND_ERR_HEADERS_TIMEOUT"}}class HeadersOverflowError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,HeadersOverflowError);this.name="HeadersOverflowError";this.message=e||"Headers Overflow Error";this.code="UND_ERR_HEADERS_OVERFLOW"}}class BodyTimeoutError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,BodyTimeoutError);this.name="BodyTimeoutError";this.message=e||"Body Timeout Error";this.code="UND_ERR_BODY_TIMEOUT"}}class ResponseStatusCodeError extends UndiciError{constructor(e,t,n,s){super(e);Error.captureStackTrace(this,ResponseStatusCodeError);this.name="ResponseStatusCodeError";this.message=e||"Response Status Code Error";this.code="UND_ERR_RESPONSE_STATUS_CODE";this.body=s;this.status=t;this.statusCode=t;this.headers=n}}class InvalidArgumentError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,InvalidArgumentError);this.name="InvalidArgumentError";this.message=e||"Invalid Argument Error";this.code="UND_ERR_INVALID_ARG"}}class InvalidReturnValueError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,InvalidReturnValueError);this.name="InvalidReturnValueError";this.message=e||"Invalid Return Value Error";this.code="UND_ERR_INVALID_RETURN_VALUE"}}class RequestAbortedError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,RequestAbortedError);this.name="AbortError";this.message=e||"Request aborted";this.code="UND_ERR_ABORTED"}}class InformationalError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,InformationalError);this.name="InformationalError";this.message=e||"Request information";this.code="UND_ERR_INFO"}}class RequestContentLengthMismatchError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,RequestContentLengthMismatchError);this.name="RequestContentLengthMismatchError";this.message=e||"Request body length does not match content-length header";this.code="UND_ERR_REQ_CONTENT_LENGTH_MISMATCH"}}class ResponseContentLengthMismatchError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,ResponseContentLengthMismatchError);this.name="ResponseContentLengthMismatchError";this.message=e||"Response body length does not match content-length header";this.code="UND_ERR_RES_CONTENT_LENGTH_MISMATCH"}}class ClientDestroyedError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,ClientDestroyedError);this.name="ClientDestroyedError";this.message=e||"The client is destroyed";this.code="UND_ERR_DESTROYED"}}class ClientClosedError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,ClientClosedError);this.name="ClientClosedError";this.message=e||"The client is closed";this.code="UND_ERR_CLOSED"}}class SocketError extends UndiciError{constructor(e,t){super(e);Error.captureStackTrace(this,SocketError);this.name="SocketError";this.message=e||"Socket error";this.code="UND_ERR_SOCKET";this.socket=t}}class NotSupportedError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,NotSupportedError);this.name="NotSupportedError";this.message=e||"Not supported error";this.code="UND_ERR_NOT_SUPPORTED"}}class BalancedPoolMissingUpstreamError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,NotSupportedError);this.name="MissingUpstreamError";this.message=e||"No upstream has been added to the BalancedPool";this.code="UND_ERR_BPL_MISSING_UPSTREAM"}}class HTTPParserError extends Error{constructor(e,t,n){super(e);Error.captureStackTrace(this,HTTPParserError);this.name="HTTPParserError";this.code=t?`HPE_${t}`:undefined;this.data=n?n.toString():undefined}}class ResponseExceededMaxSizeError extends UndiciError{constructor(e){super(e);Error.captureStackTrace(this,ResponseExceededMaxSizeError);this.name="ResponseExceededMaxSizeError";this.message=e||"Response content exceeded max size";this.code="UND_ERR_RES_EXCEEDED_MAX_SIZE"}}e.exports={HTTPParserError:HTTPParserError,UndiciError:UndiciError,HeadersTimeoutError:HeadersTimeoutError,HeadersOverflowError:HeadersOverflowError,BodyTimeoutError:BodyTimeoutError,RequestContentLengthMismatchError:RequestContentLengthMismatchError,ConnectTimeoutError:ConnectTimeoutError,ResponseStatusCodeError:ResponseStatusCodeError,InvalidArgumentError:InvalidArgumentError,InvalidReturnValueError:InvalidReturnValueError,RequestAbortedError:RequestAbortedError,ClientDestroyedError:ClientDestroyedError,ClientClosedError:ClientClosedError,InformationalError:InformationalError,SocketError:SocketError,NotSupportedError:NotSupportedError,ResponseContentLengthMismatchError:ResponseContentLengthMismatchError,BalancedPoolMissingUpstreamError:BalancedPoolMissingUpstreamError,ResponseExceededMaxSizeError:ResponseExceededMaxSizeError}},2905:(e,t,n)=>{"use strict";const{InvalidArgumentError:s,NotSupportedError:i}=n(8045);const r=n(9491);const{kHTTP2BuildRequest:o,kHTTP2CopyHeaders:A,kHTTP1BuildRequest:a}=n(2785);const c=n(3983);const u=/^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;const l=/[^\t\x20-\x7e\x80-\xff]/;const d=/[^\u0021-\u00ff]/;const p=Symbol("handler");const g={};let h;try{const e=n(7643);g.create=e.channel("undici:request:create");g.bodySent=e.channel("undici:request:bodySent");g.headers=e.channel("undici:request:headers");g.trailers=e.channel("undici:request:trailers");g.error=e.channel("undici:request:error")}catch{g.create={hasSubscribers:false};g.bodySent={hasSubscribers:false};g.headers={hasSubscribers:false};g.trailers={hasSubscribers:false};g.error={hasSubscribers:false}}class Request{constructor(e,{path:t,method:i,body:r,headers:o,query:A,idempotent:a,blocking:l,upgrade:f,headersTimeout:E,bodyTimeout:m,reset:C,throwOnError:Q,expectContinue:I},B){if(typeof t!=="string"){throw new s("path must be a string")}else if(t[0]!=="/"&&!(t.startsWith("http://")||t.startsWith("https://"))&&i!=="CONNECT"){throw new s("path must be an absolute URL or start with a slash")}else if(d.exec(t)!==null){throw new s("invalid request path")}if(typeof i!=="string"){throw new s("method must be a string")}else if(u.exec(i)===null){throw new s("invalid request method")}if(f&&typeof f!=="string"){throw new s("upgrade must be a string")}if(E!=null&&(!Number.isFinite(E)||E<0)){throw new s("invalid headersTimeout")}if(m!=null&&(!Number.isFinite(m)||m<0)){throw new s("invalid bodyTimeout")}if(C!=null&&typeof C!=="boolean"){throw new s("invalid reset")}if(I!=null&&typeof I!=="boolean"){throw new s("invalid expectContinue")}this.headersTimeout=E;this.bodyTimeout=m;this.throwOnError=Q===true;this.method=i;if(r==null){this.body=null}else if(c.isStream(r)){this.body=r}else if(c.isBuffer(r)){this.body=r.byteLength?r:null}else if(ArrayBuffer.isView(r)){this.body=r.buffer.byteLength?Buffer.from(r.buffer,r.byteOffset,r.byteLength):null}else if(r instanceof ArrayBuffer){this.body=r.byteLength?Buffer.from(r):null}else if(typeof r==="string"){this.body=r.length?Buffer.from(r):null}else if(c.isFormDataLike(r)||c.isIterable(r)||c.isBlobLike(r)){this.body=r}else{throw new s("body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable")}this.completed=false;this.aborted=false;this.upgrade=f||null;this.path=A?c.buildURL(t,A):t;this.origin=e;this.idempotent=a==null?i==="HEAD"||i==="GET":a;this.blocking=l==null?false:l;this.reset=C==null?null:C;this.host=null;this.contentLength=null;this.contentType=null;this.headers="";this.expectContinue=I!=null?I:false;if(Array.isArray(o)){if(o.length%2!==0){throw new s("headers array must be even")}for(let e=0;e{e.exports={kClose:Symbol("close"),kDestroy:Symbol("destroy"),kDispatch:Symbol("dispatch"),kUrl:Symbol("url"),kWriting:Symbol("writing"),kResuming:Symbol("resuming"),kQueue:Symbol("queue"),kConnect:Symbol("connect"),kConnecting:Symbol("connecting"),kHeadersList:Symbol("headers list"),kKeepAliveDefaultTimeout:Symbol("default keep alive timeout"),kKeepAliveMaxTimeout:Symbol("max keep alive timeout"),kKeepAliveTimeoutThreshold:Symbol("keep alive timeout threshold"),kKeepAliveTimeoutValue:Symbol("keep alive timeout"),kKeepAlive:Symbol("keep alive"),kHeadersTimeout:Symbol("headers timeout"),kBodyTimeout:Symbol("body timeout"),kServerName:Symbol("server name"),kLocalAddress:Symbol("local address"),kHost:Symbol("host"),kNoRef:Symbol("no ref"),kBodyUsed:Symbol("used"),kRunning:Symbol("running"),kBlocking:Symbol("blocking"),kPending:Symbol("pending"),kSize:Symbol("size"),kBusy:Symbol("busy"),kQueued:Symbol("queued"),kFree:Symbol("free"),kConnected:Symbol("connected"),kClosed:Symbol("closed"),kNeedDrain:Symbol("need drain"),kReset:Symbol("reset"),kDestroyed:Symbol.for("nodejs.stream.destroyed"),kMaxHeadersSize:Symbol("max headers size"),kRunningIdx:Symbol("running index"),kPendingIdx:Symbol("pending index"),kError:Symbol("error"),kClients:Symbol("clients"),kClient:Symbol("client"),kParser:Symbol("parser"),kOnDestroyed:Symbol("destroy callbacks"),kPipelining:Symbol("pipelining"),kSocket:Symbol("socket"),kHostHeader:Symbol("host header"),kConnector:Symbol("connector"),kStrictContentLength:Symbol("strict content length"),kMaxRedirections:Symbol("maxRedirections"),kMaxRequests:Symbol("maxRequestsPerClient"),kProxy:Symbol("proxy agent options"),kCounter:Symbol("socket request counter"),kInterceptors:Symbol("dispatch interceptors"),kMaxResponseSize:Symbol("max response size"),kHTTP2Session:Symbol("http2Session"),kHTTP2SessionState:Symbol("http2Session state"),kHTTP2BuildRequest:Symbol("http2 build request"),kHTTP1BuildRequest:Symbol("http1 build request"),kHTTP2CopyHeaders:Symbol("http2 copy headers"),kHTTPConnVersion:Symbol("http connection version")}},3983:(e,t,n)=>{"use strict";const s=n(9491);const{kDestroyed:i,kBodyUsed:r}=n(2785);const{IncomingMessage:o}=n(3685);const A=n(2781);const a=n(1808);const{InvalidArgumentError:c}=n(8045);const{Blob:u}=n(4300);const l=n(3837);const{stringify:d}=n(3477);const[p,g]=process.versions.node.split(".").map((e=>Number(e)));function nop(){}function isStream(e){return e&&typeof e==="object"&&typeof e.pipe==="function"&&typeof e.on==="function"}function isBlobLike(e){return u&&e instanceof u||e&&typeof e==="object"&&(typeof e.stream==="function"||typeof e.arrayBuffer==="function")&&/^(Blob|File)$/.test(e[Symbol.toStringTag])}function buildURL(e,t){if(e.includes("?")||e.includes("#")){throw new Error('Query params cannot be passed when url already contains "?" or "#".')}const n=d(t);if(n){e+="?"+n}return e}function parseURL(e){if(typeof e==="string"){e=new URL(e);if(!/^https?:/.test(e.origin||e.protocol)){throw new c("Invalid URL protocol: the URL must start with `http:` or `https:`.")}return e}if(!e||typeof e!=="object"){throw new c("Invalid URL: The URL argument must be a non-null object.")}if(!/^https?:/.test(e.origin||e.protocol)){throw new c("Invalid URL protocol: the URL must start with `http:` or `https:`.")}if(!(e instanceof URL)){if(e.port!=null&&e.port!==""&&!Number.isFinite(parseInt(e.port))){throw new c("Invalid URL: port must be a valid integer or a string representation of an integer.")}if(e.path!=null&&typeof e.path!=="string"){throw new c("Invalid URL path: the path must be a string or null/undefined.")}if(e.pathname!=null&&typeof e.pathname!=="string"){throw new c("Invalid URL pathname: the pathname must be a string or null/undefined.")}if(e.hostname!=null&&typeof e.hostname!=="string"){throw new c("Invalid URL hostname: the hostname must be a string or null/undefined.")}if(e.origin!=null&&typeof e.origin!=="string"){throw new c("Invalid URL origin: the origin must be a string or null/undefined.")}const t=e.port!=null?e.port:e.protocol==="https:"?443:80;let n=e.origin!=null?e.origin:`${e.protocol}//${e.hostname}:${t}`;let s=e.path!=null?e.path:`${e.pathname||""}${e.search||""}`;if(n.endsWith("/")){n=n.substring(0,n.length-1)}if(s&&!s.startsWith("/")){s=`/${s}`}e=new URL(n+s)}return e}function parseOrigin(e){e=parseURL(e);if(e.pathname!=="/"||e.search||e.hash){throw new c("invalid url")}return e}function getHostname(e){if(e[0]==="["){const t=e.indexOf("]");s(t!==-1);return e.substr(1,t-1)}const t=e.indexOf(":");if(t===-1)return e;return e.substr(0,t)}function getServerName(e){if(!e){return null}s.strictEqual(typeof e,"string");const t=getHostname(e);if(a.isIP(t)){return""}return t}function deepClone(e){return JSON.parse(JSON.stringify(e))}function isAsyncIterable(e){return!!(e!=null&&typeof e[Symbol.asyncIterator]==="function")}function isIterable(e){return!!(e!=null&&(typeof e[Symbol.iterator]==="function"||typeof e[Symbol.asyncIterator]==="function"))}function bodyLength(e){if(e==null){return 0}else if(isStream(e)){const t=e._readableState;return t&&t.objectMode===false&&t.ended===true&&Number.isFinite(t.length)?t.length:null}else if(isBlobLike(e)){return e.size!=null?e.size:null}else if(isBuffer(e)){return e.byteLength}return null}function isDestroyed(e){return!e||!!(e.destroyed||e[i])}function isReadableAborted(e){const t=e&&e._readableState;return isDestroyed(e)&&t&&!t.endEmitted}function destroy(e,t){if(!isStream(e)||isDestroyed(e)){return}if(typeof e.destroy==="function"){if(Object.getPrototypeOf(e).constructor===o){e.socket=null}e.destroy(t)}else if(t){process.nextTick(((e,t)=>{e.emit("error",t)}),e,t)}if(e.destroyed!==true){e[i]=true}}const h=/timeout=(\d+)/;function parseKeepAliveTimeout(e){const t=e.toString().match(h);return t?parseInt(t[1],10)*1e3:null}function parseHeaders(e,t={}){if(!Array.isArray(e))return e;for(let n=0;n{e.close()}))}else{const t=Buffer.isBuffer(s)?s:Buffer.from(s);e.enqueue(new Uint8Array(t))}return e.desiredSize>0},async cancel(e){await t.return()}},0)}function isFormDataLike(e){return e&&typeof e==="object"&&typeof e.append==="function"&&typeof e.delete==="function"&&typeof e.get==="function"&&typeof e.getAll==="function"&&typeof e.has==="function"&&typeof e.set==="function"&&e[Symbol.toStringTag]==="FormData"}function throwIfAborted(e){if(!e){return}if(typeof e.throwIfAborted==="function"){e.throwIfAborted()}else{if(e.aborted){const e=new Error("The operation was aborted");e.name="AbortError";throw e}}}let E;function addAbortListener(e,t){if(typeof Symbol.dispose==="symbol"){if(!E){E=n(2361)}if(typeof E.addAbortListener==="function"&&"aborted"in e){return E.addAbortListener(e,t)}}if("addEventListener"in e){e.addEventListener("abort",t,{once:true});return()=>e.removeEventListener("abort",t)}e.addListener("abort",t);return()=>e.removeListener("abort",t)}const m=!!String.prototype.toWellFormed;function toUSVString(e){if(m){return`${e}`.toWellFormed()}else if(l.toUSVString){return l.toUSVString(e)}return`${e}`}const C=Object.create(null);C.enumerable=true;e.exports={kEnumerableProperty:C,nop:nop,isDisturbed:isDisturbed,isErrored:isErrored,isReadable:isReadable,toUSVString:toUSVString,isReadableAborted:isReadableAborted,isBlobLike:isBlobLike,parseOrigin:parseOrigin,parseURL:parseURL,getServerName:getServerName,isStream:isStream,isIterable:isIterable,isAsyncIterable:isAsyncIterable,isDestroyed:isDestroyed,parseRawHeaders:parseRawHeaders,parseHeaders:parseHeaders,parseKeepAliveTimeout:parseKeepAliveTimeout,destroy:destroy,bodyLength:bodyLength,deepClone:deepClone,ReadableStreamFrom:ReadableStreamFrom,isBuffer:isBuffer,validateHandler:validateHandler,getSocketInfo:getSocketInfo,isFormDataLike:isFormDataLike,buildURL:buildURL,throwIfAborted:throwIfAborted,addAbortListener:addAbortListener,nodeMajor:p,nodeMinor:g,nodeHasAutoSelectFamily:p>18||p===18&&g>=13}},4839:(e,t,n)=>{"use strict";const s=n(412);const{ClientDestroyedError:i,ClientClosedError:r,InvalidArgumentError:o}=n(8045);const{kDestroy:A,kClose:a,kDispatch:c,kInterceptors:u}=n(2785);const l=Symbol("destroyed");const d=Symbol("closed");const p=Symbol("onDestroyed");const g=Symbol("onClosed");const h=Symbol("Intercepted Dispatch");class DispatcherBase extends s{constructor(){super();this[l]=false;this[p]=null;this[d]=false;this[g]=[]}get destroyed(){return this[l]}get closed(){return this[d]}get interceptors(){return this[u]}set interceptors(e){if(e){for(let t=e.length-1;t>=0;t--){const e=this[u][t];if(typeof e!=="function"){throw new o("interceptor must be an function")}}}this[u]=e}close(e){if(e===undefined){return new Promise(((e,t)=>{this.close(((n,s)=>n?t(n):e(s)))}))}if(typeof e!=="function"){throw new o("invalid callback")}if(this[l]){queueMicrotask((()=>e(new i,null)));return}if(this[d]){if(this[g]){this[g].push(e)}else{queueMicrotask((()=>e(null,null)))}return}this[d]=true;this[g].push(e);const onClosed=()=>{const e=this[g];this[g]=null;for(let t=0;tthis.destroy())).then((()=>{queueMicrotask(onClosed)}))}destroy(e,t){if(typeof e==="function"){t=e;e=null}if(t===undefined){return new Promise(((t,n)=>{this.destroy(e,((e,s)=>e?n(e):t(s)))}))}if(typeof t!=="function"){throw new o("invalid callback")}if(this[l]){if(this[p]){this[p].push(t)}else{queueMicrotask((()=>t(null,null)))}return}if(!e){e=new i}this[l]=true;this[p]=this[p]||[];this[p].push(t);const onDestroyed=()=>{const e=this[p];this[p]=null;for(let t=0;t{queueMicrotask(onDestroyed)}))}[h](e,t){if(!this[u]||this[u].length===0){this[h]=this[c];return this[c](e,t)}let n=this[c].bind(this);for(let e=this[u].length-1;e>=0;e--){n=this[u][e](n)}this[h]=n;return n(e,t)}dispatch(e,t){if(!t||typeof t!=="object"){throw new o("handler must be an object")}try{if(!e||typeof e!=="object"){throw new o("opts must be an object.")}if(this[l]||this[p]){throw new i}if(this[d]){throw new r}return this[h](e,t)}catch(e){if(typeof t.onError!=="function"){throw new o("invalid onError method")}t.onError(e);return false}}}e.exports=DispatcherBase},412:(e,t,n)=>{"use strict";const s=n(2361);class Dispatcher extends s{dispatch(){throw new Error("not implemented")}close(){throw new Error("not implemented")}destroy(){throw new Error("not implemented")}}e.exports=Dispatcher},1472:(e,t,n)=>{"use strict";const s=n(3438);const i=n(3983);const{ReadableStreamFrom:r,isBlobLike:o,isReadableStreamLike:A,readableStreamClose:a,createDeferredPromise:c,fullyReadBody:u}=n(2538);const{FormData:l}=n(2015);const{kState:d}=n(5861);const{webidl:p}=n(1744);const{DOMException:g,structuredClone:h}=n(1037);const{Blob:f,File:E}=n(4300);const{kBodyUsed:m}=n(2785);const C=n(9491);const{isErrored:Q}=n(3983);const{isUint8Array:I,isArrayBuffer:B}=n(4978);const{File:y}=n(8511);const{parseMIMEType:b,serializeAMimeType:w}=n(685);let R=globalThis.ReadableStream;const v=E??y;const k=new TextEncoder;const S=new TextDecoder;function extractBody(e,t=false){if(!R){R=n(5356).ReadableStream}let s=null;if(e instanceof R){s=e}else if(o(e)){s=e.stream()}else{s=new R({async pull(e){e.enqueue(typeof u==="string"?k.encode(u):u);queueMicrotask((()=>a(e)))},start(){},type:undefined})}C(A(s));let c=null;let u=null;let l=null;let d=null;if(typeof e==="string"){u=e;d="text/plain;charset=UTF-8"}else if(e instanceof URLSearchParams){u=e.toString();d="application/x-www-form-urlencoded;charset=UTF-8"}else if(B(e)){u=new Uint8Array(e.slice())}else if(ArrayBuffer.isView(e)){u=new Uint8Array(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength))}else if(i.isFormDataLike(e)){const t=`----formdata-undici-0${`${Math.floor(Math.random()*1e11)}`.padStart(11,"0")}`;const n=`--${t}\r\nContent-Disposition: form-data` -/*! formdata-polyfill. MIT License. Jimmy Wärting */;const escape=e=>e.replace(/\n/g,"%0A").replace(/\r/g,"%0D").replace(/"/g,"%22");const normalizeLinefeeds=e=>e.replace(/\r?\n|\r/g,"\r\n");const s=[];const i=new Uint8Array([13,10]);l=0;let r=false;for(const[t,o]of e){if(typeof o==="string"){const e=k.encode(n+`; name="${escape(normalizeLinefeeds(t))}"`+`\r\n\r\n${normalizeLinefeeds(o)}\r\n`);s.push(e);l+=e.byteLength}else{const e=k.encode(`${n}; name="${escape(normalizeLinefeeds(t))}"`+(o.name?`; filename="${escape(o.name)}"`:"")+"\r\n"+`Content-Type: ${o.type||"application/octet-stream"}\r\n\r\n`);s.push(e,o,i);if(typeof o.size==="number"){l+=e.byteLength+o.size+i.byteLength}else{r=true}}}const o=k.encode(`--${t}--`);s.push(o);l+=o.byteLength;if(r){l=null}u=e;c=async function*(){for(const e of s){if(e.stream){yield*e.stream()}else{yield e}}};d="multipart/form-data; boundary="+t}else if(o(e)){u=e;l=e.size;if(e.type){d=e.type}}else if(typeof e[Symbol.asyncIterator]==="function"){if(t){throw new TypeError("keepalive")}if(i.isDisturbed(e)||e.locked){throw new TypeError("Response body object should not be disturbed or locked")}s=e instanceof R?e:r(e)}if(typeof u==="string"||i.isBuffer(u)){l=Buffer.byteLength(u)}if(c!=null){let t;s=new R({async start(){t=c(e)[Symbol.asyncIterator]()},async pull(e){const{value:n,done:i}=await t.next();if(i){queueMicrotask((()=>{e.close()}))}else{if(!Q(s)){e.enqueue(new Uint8Array(n))}}return e.desiredSize>0},async cancel(e){await t.return()},type:undefined})}const p={stream:s,source:u,length:l};return[p,d]}function safelyExtractBody(e,t=false){if(!R){R=n(5356).ReadableStream}if(e instanceof R){C(!i.isDisturbed(e),"The body has already been consumed.");C(!e.locked,"The stream is locked.")}return extractBody(e,t)}function cloneBody(e){const[t,n]=e.stream.tee();const s=h(n,{transfer:[n]});const[,i]=s.tee();e.stream=t;return{stream:i,length:e.length,source:e.source}}async function*consumeBody(e){if(e){if(I(e)){yield e}else{const t=e.stream;if(i.isDisturbed(t)){throw new TypeError("The body has already been consumed.")}if(t.locked){throw new TypeError("The stream is locked.")}t[m]=true;yield*t}}}function throwIfAborted(e){if(e.aborted){throw new g("The operation was aborted.","AbortError")}}function bodyMixinMethods(e){const t={blob(){return specConsumeBody(this,(e=>{let t=bodyMimeType(this);if(t==="failure"){t=""}else if(t){t=w(t)}return new f([e],{type:t})}),e)},arrayBuffer(){return specConsumeBody(this,(e=>new Uint8Array(e).buffer),e)},text(){return specConsumeBody(this,utf8DecodeBytes,e)},json(){return specConsumeBody(this,parseJSONFromBytes,e)},async formData(){p.brandCheck(this,e);throwIfAborted(this[d]);const t=this.headers.get("Content-Type");if(/multipart\/form-data/.test(t)){const e={};for(const[t,n]of this.headers)e[t.toLowerCase()]=n;const t=new l;let n;try{n=new s({headers:e,preservePath:true})}catch(e){throw new g(`${e}`,"AbortError")}n.on("field",((e,n)=>{t.append(e,n)}));n.on("file",((e,n,s,i,r)=>{const o=[];if(i==="base64"||i.toLowerCase()==="base64"){let i="";n.on("data",(e=>{i+=e.toString().replace(/[\r\n]/gm,"");const t=i.length-i.length%4;o.push(Buffer.from(i.slice(0,t),"base64"));i=i.slice(t)}));n.on("end",(()=>{o.push(Buffer.from(i,"base64"));t.append(e,new v(o,s,{type:r}))}))}else{n.on("data",(e=>{o.push(e)}));n.on("end",(()=>{t.append(e,new v(o,s,{type:r}))}))}}));const i=new Promise(((e,t)=>{n.on("finish",e);n.on("error",(e=>t(new TypeError(e))))}));if(this.body!==null)for await(const e of consumeBody(this[d].body))n.write(e);n.end();await i;return t}else if(/application\/x-www-form-urlencoded/.test(t)){let e;try{let t="";const n=new TextDecoder("utf-8",{ignoreBOM:true});for await(const e of consumeBody(this[d].body)){if(!I(e)){throw new TypeError("Expected Uint8Array chunk")}t+=n.decode(e,{stream:true})}t+=n.decode();e=new URLSearchParams(t)}catch(e){throw Object.assign(new TypeError,{cause:e})}const t=new l;for(const[n,s]of e){t.append(n,s)}return t}else{await Promise.resolve();throwIfAborted(this[d]);throw p.errors.exception({header:`${e.name}.formData`,message:"Could not parse content as FormData."})}}};return t}function mixinBody(e){Object.assign(e.prototype,bodyMixinMethods(e))}async function specConsumeBody(e,t,n){p.brandCheck(e,n);throwIfAborted(e[d]);if(bodyUnusable(e[d].body)){throw new TypeError("Body is unusable")}const s=c();const errorSteps=e=>s.reject(e);const successSteps=e=>{try{s.resolve(t(e))}catch(e){errorSteps(e)}};if(e[d].body==null){successSteps(new Uint8Array);return s.promise}await u(e[d].body,successSteps,errorSteps);return s.promise}function bodyUnusable(e){return e!=null&&(e.stream.locked||i.isDisturbed(e.stream))}function utf8DecodeBytes(e){if(e.length===0){return""}if(e[0]===239&&e[1]===187&&e[2]===191){e=e.subarray(3)}const t=S.decode(e);return t}function parseJSONFromBytes(e){return JSON.parse(utf8DecodeBytes(e))}function bodyMimeType(e){const{headersList:t}=e[d];const n=t.get("content-type");if(n===null){return"failure"}return b(n)}e.exports={extractBody:extractBody,safelyExtractBody:safelyExtractBody,cloneBody:cloneBody,mixinBody:mixinBody}},1037:(e,t,n)=>{"use strict";const{MessageChannel:s,receiveMessageOnPort:i}=n(1267);const r=["GET","HEAD","POST"];const o=new Set(r);const A=[101,204,205,304];const a=[301,302,303,307,308];const c=new Set(a);const u=["1","7","9","11","13","15","17","19","20","21","22","23","25","37","42","43","53","69","77","79","87","95","101","102","103","104","109","110","111","113","115","117","119","123","135","137","139","143","161","179","389","427","465","512","513","514","515","526","530","531","532","540","548","554","556","563","587","601","636","989","990","993","995","1719","1720","1723","2049","3659","4045","5060","5061","6000","6566","6665","6666","6667","6668","6669","6697","10080"];const l=new Set(u);const d=["","no-referrer","no-referrer-when-downgrade","same-origin","origin","strict-origin","origin-when-cross-origin","strict-origin-when-cross-origin","unsafe-url"];const p=new Set(d);const g=["follow","manual","error"];const h=["GET","HEAD","OPTIONS","TRACE"];const f=new Set(h);const E=["navigate","same-origin","no-cors","cors"];const m=["omit","same-origin","include"];const C=["default","no-store","reload","no-cache","force-cache","only-if-cached"];const Q=["content-encoding","content-language","content-location","content-type","content-length"];const I=["half"];const B=["CONNECT","TRACE","TRACK"];const y=new Set(B);const b=["audio","audioworklet","font","image","manifest","paintworklet","script","style","track","video","xslt",""];const w=new Set(b);const R=globalThis.DOMException??(()=>{try{atob("~")}catch(e){return Object.getPrototypeOf(e).constructor}})();let v;const k=globalThis.structuredClone??function structuredClone(e,t=undefined){if(arguments.length===0){throw new TypeError("missing argument")}if(!v){v=new s}v.port1.unref();v.port2.unref();v.port1.postMessage(e,t?.transfer);return i(v.port2).message};e.exports={DOMException:R,structuredClone:k,subresource:b,forbiddenMethods:B,requestBodyHeader:Q,referrerPolicy:d,requestRedirect:g,requestMode:E,requestCredentials:m,requestCache:C,redirectStatus:a,corsSafeListedMethods:r,nullBodyStatus:A,safeMethods:h,badPorts:u,requestDuplex:I,subresourceSet:w,badPortsSet:l,redirectStatusSet:c,corsSafeListedMethodsSet:o,safeMethodsSet:f,forbiddenMethodsSet:y,referrerPolicySet:p}},685:(e,t,n)=>{const s=n(9491);const{atob:i}=n(4300);const{isomorphicDecode:r}=n(2538);const o=new TextEncoder;const A=/^[!#$%&'*+-.^_|~A-Za-z0-9]+$/;const a=/(\u000A|\u000D|\u0009|\u0020)/;const c=/[\u0009|\u0020-\u007E|\u0080-\u00FF]/;function dataURLProcessor(e){s(e.protocol==="data:");let t=URLSerializer(e,true);t=t.slice(5);const n={position:0};let i=collectASequenceOfCodePointsFast(",",t,n);const o=i.length;i=removeASCIIWhitespace(i,true,true);if(n.position>=t.length){return"failure"}n.position++;const A=t.slice(o+1);let a=stringPercentDecode(A);if(/;(\u0020){0,}base64$/i.test(i)){const e=r(a);a=forgivingBase64(e);if(a==="failure"){return"failure"}i=i.slice(0,-6);i=i.replace(/(\u0020)+$/,"");i=i.slice(0,-1)}if(i.startsWith(";")){i="text/plain"+i}let c=parseMIMEType(i);if(c==="failure"){c=parseMIMEType("text/plain;charset=US-ASCII")}return{mimeType:c,body:a}}function URLSerializer(e,t=false){const n=e.href;if(!t){return n}const s=n.lastIndexOf("#");if(s===-1){return n}return n.slice(0,s)}function collectASequenceOfCodePoints(e,t,n){let s="";while(n.positione.length){return"failure"}t.position++;let s=collectASequenceOfCodePointsFast(";",e,t);s=removeHTTPWhitespace(s,false,true);if(s.length===0||!A.test(s)){return"failure"}const i=n.toLowerCase();const r=s.toLowerCase();const o={type:i,subtype:r,parameters:new Map,essence:`${i}/${r}`};while(t.positiona.test(e)),e,t);let n=collectASequenceOfCodePoints((e=>e!==";"&&e!=="="),e,t);n=n.toLowerCase();if(t.positione.length){break}let s=null;if(e[t.position]==='"'){s=collectAnHTTPQuotedString(e,t,true);collectASequenceOfCodePointsFast(";",e,t)}else{s=collectASequenceOfCodePointsFast(";",e,t);s=removeHTTPWhitespace(s,false,true);if(s.length===0){continue}}if(n.length!==0&&A.test(n)&&(s.length===0||c.test(s))&&!o.parameters.has(n)){o.parameters.set(n,s)}}return o}function forgivingBase64(e){e=e.replace(/[\u0009\u000A\u000C\u000D\u0020]/g,"");if(e.length%4===0){e=e.replace(/=?=$/,"")}if(e.length%4===1){return"failure"}if(/[^+/0-9A-Za-z]/.test(e)){return"failure"}const t=i(e);const n=new Uint8Array(t.length);for(let e=0;ee!=='"'&&e!=="\\"),e,t);if(t.position>=e.length){break}const n=e[t.position];t.position++;if(n==="\\"){if(t.position>=e.length){r+="\\";break}r+=e[t.position];t.position++}else{s(n==='"');break}}if(n){return r}return e.slice(i,t.position)}function serializeAMimeType(e){s(e!=="failure");const{parameters:t,essence:n}=e;let i=n;for(let[e,n]of t.entries()){i+=";";i+=e;i+="=";if(!A.test(n)){n=n.replace(/(\\|")/g,"\\$1");n='"'+n;n+='"'}i+=n}return i}function isHTTPWhiteSpace(e){return e==="\r"||e==="\n"||e==="\t"||e===" "}function removeHTTPWhitespace(e,t=true,n=true){let s=0;let i=e.length-1;if(t){for(;s0&&isHTTPWhiteSpace(e[i]);i--);}return e.slice(s,i+1)}function isASCIIWhitespace(e){return e==="\r"||e==="\n"||e==="\t"||e==="\f"||e===" "}function removeASCIIWhitespace(e,t=true,n=true){let s=0;let i=e.length-1;if(t){for(;s0&&isASCIIWhitespace(e[i]);i--);}return e.slice(s,i+1)}e.exports={dataURLProcessor:dataURLProcessor,URLSerializer:URLSerializer,collectASequenceOfCodePoints:collectASequenceOfCodePoints,collectASequenceOfCodePointsFast:collectASequenceOfCodePointsFast,stringPercentDecode:stringPercentDecode,parseMIMEType:parseMIMEType,collectAnHTTPQuotedString:collectAnHTTPQuotedString,serializeAMimeType:serializeAMimeType}},8511:(e,t,n)=>{"use strict";const{Blob:s,File:i}=n(4300);const{types:r}=n(3837);const{kState:o}=n(5861);const{isBlobLike:A}=n(2538);const{webidl:a}=n(1744);const{parseMIMEType:c,serializeAMimeType:u}=n(685);const{kEnumerableProperty:l}=n(3983);const d=new TextEncoder;class File extends s{constructor(e,t,n={}){a.argumentLengthCheck(arguments,2,{header:"File constructor"});e=a.converters["sequence"](e);t=a.converters.USVString(t);n=a.converters.FilePropertyBag(n);const s=t;let i=n.type;let r;e:{if(i){i=c(i);if(i==="failure"){i="";break e}i=u(i).toLowerCase()}r=n.lastModified}super(processBlobParts(e,n),{type:i});this[o]={name:s,lastModified:r,type:i}}get name(){a.brandCheck(this,File);return this[o].name}get lastModified(){a.brandCheck(this,File);return this[o].lastModified}get type(){a.brandCheck(this,File);return this[o].type}}class FileLike{constructor(e,t,n={}){const s=t;const i=n.type;const r=n.lastModified??Date.now();this[o]={blobLike:e,name:s,type:i,lastModified:r}}stream(...e){a.brandCheck(this,FileLike);return this[o].blobLike.stream(...e)}arrayBuffer(...e){a.brandCheck(this,FileLike);return this[o].blobLike.arrayBuffer(...e)}slice(...e){a.brandCheck(this,FileLike);return this[o].blobLike.slice(...e)}text(...e){a.brandCheck(this,FileLike);return this[o].blobLike.text(...e)}get size(){a.brandCheck(this,FileLike);return this[o].blobLike.size}get type(){a.brandCheck(this,FileLike);return this[o].blobLike.type}get name(){a.brandCheck(this,FileLike);return this[o].name}get lastModified(){a.brandCheck(this,FileLike);return this[o].lastModified}get[Symbol.toStringTag](){return"File"}}Object.defineProperties(File.prototype,{[Symbol.toStringTag]:{value:"File",configurable:true},name:l,lastModified:l});a.converters.Blob=a.interfaceConverter(s);a.converters.BlobPart=function(e,t){if(a.util.Type(e)==="Object"){if(A(e)){return a.converters.Blob(e,{strict:false})}if(ArrayBuffer.isView(e)||r.isAnyArrayBuffer(e)){return a.converters.BufferSource(e,t)}}return a.converters.USVString(e,t)};a.converters["sequence"]=a.sequenceConverter(a.converters.BlobPart);a.converters.FilePropertyBag=a.dictionaryConverter([{key:"lastModified",converter:a.converters["long long"],get defaultValue(){return Date.now()}},{key:"type",converter:a.converters.DOMString,defaultValue:""},{key:"endings",converter:e=>{e=a.converters.DOMString(e);e=e.toLowerCase();if(e!=="native"){e="transparent"}return e},defaultValue:"transparent"}]);function processBlobParts(e,t){const n=[];for(const s of e){if(typeof s==="string"){let e=s;if(t.endings==="native"){e=convertLineEndingsNative(e)}n.push(d.encode(e))}else if(r.isAnyArrayBuffer(s)||r.isTypedArray(s)){if(!s.buffer){n.push(new Uint8Array(s))}else{n.push(new Uint8Array(s.buffer,s.byteOffset,s.byteLength))}}else if(A(s)){n.push(s)}}return n}function convertLineEndingsNative(e){let t="\n";if(process.platform==="win32"){t="\r\n"}return e.replace(/\r?\n/g,t)}function isFileLike(e){return i&&e instanceof i||e instanceof File||e&&(typeof e.stream==="function"||typeof e.arrayBuffer==="function")&&e[Symbol.toStringTag]==="File"}e.exports={File:File,FileLike:FileLike,isFileLike:isFileLike}},2015:(e,t,n)=>{"use strict";const{isBlobLike:s,toUSVString:i,makeIterator:r}=n(2538);const{kState:o}=n(5861);const{File:A,FileLike:a,isFileLike:c}=n(8511);const{webidl:u}=n(1744);const{Blob:l,File:d}=n(4300);const p=d??A;class FormData{constructor(e){if(e!==undefined){throw u.errors.conversionFailed({prefix:"FormData constructor",argument:"Argument 1",types:["undefined"]})}this[o]=[]}append(e,t,n=undefined){u.brandCheck(this,FormData);u.argumentLengthCheck(arguments,2,{header:"FormData.append"});if(arguments.length===3&&!s(t)){throw new TypeError("Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'")}e=u.converters.USVString(e);t=s(t)?u.converters.Blob(t,{strict:false}):u.converters.USVString(t);n=arguments.length===3?u.converters.USVString(n):undefined;const i=makeEntry(e,t,n);this[o].push(i)}delete(e){u.brandCheck(this,FormData);u.argumentLengthCheck(arguments,1,{header:"FormData.delete"});e=u.converters.USVString(e);this[o]=this[o].filter((t=>t.name!==e))}get(e){u.brandCheck(this,FormData);u.argumentLengthCheck(arguments,1,{header:"FormData.get"});e=u.converters.USVString(e);const t=this[o].findIndex((t=>t.name===e));if(t===-1){return null}return this[o][t].value}getAll(e){u.brandCheck(this,FormData);u.argumentLengthCheck(arguments,1,{header:"FormData.getAll"});e=u.converters.USVString(e);return this[o].filter((t=>t.name===e)).map((e=>e.value))}has(e){u.brandCheck(this,FormData);u.argumentLengthCheck(arguments,1,{header:"FormData.has"});e=u.converters.USVString(e);return this[o].findIndex((t=>t.name===e))!==-1}set(e,t,n=undefined){u.brandCheck(this,FormData);u.argumentLengthCheck(arguments,2,{header:"FormData.set"});if(arguments.length===3&&!s(t)){throw new TypeError("Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'")}e=u.converters.USVString(e);t=s(t)?u.converters.Blob(t,{strict:false}):u.converters.USVString(t);n=arguments.length===3?i(n):undefined;const r=makeEntry(e,t,n);const A=this[o].findIndex((t=>t.name===e));if(A!==-1){this[o]=[...this[o].slice(0,A),r,...this[o].slice(A+1).filter((t=>t.name!==e))]}else{this[o].push(r)}}entries(){u.brandCheck(this,FormData);return r((()=>this[o].map((e=>[e.name,e.value]))),"FormData","key+value")}keys(){u.brandCheck(this,FormData);return r((()=>this[o].map((e=>[e.name,e.value]))),"FormData","key")}values(){u.brandCheck(this,FormData);return r((()=>this[o].map((e=>[e.name,e.value]))),"FormData","value")}forEach(e,t=globalThis){u.brandCheck(this,FormData);u.argumentLengthCheck(arguments,1,{header:"FormData.forEach"});if(typeof e!=="function"){throw new TypeError("Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'.")}for(const[n,s]of this){e.apply(t,[s,n,this])}}}FormData.prototype[Symbol.iterator]=FormData.prototype.entries;Object.defineProperties(FormData.prototype,{[Symbol.toStringTag]:{value:"FormData",configurable:true}});function makeEntry(e,t,n){e=Buffer.from(e).toString("utf8");if(typeof t==="string"){t=Buffer.from(t).toString("utf8")}else{if(!c(t)){t=t instanceof l?new p([t],"blob",{type:t.type}):new a(t,"blob",{type:t.type})}if(n!==undefined){const e={type:t.type,lastModified:t.lastModified};t=d&&t instanceof d||t instanceof A?new p([t],n,e):new a(t,n,e)}}return{name:e,value:t}}e.exports={FormData:FormData}},1246:e=>{"use strict";const t=Symbol.for("undici.globalOrigin.1");function getGlobalOrigin(){return globalThis[t]}function setGlobalOrigin(e){if(e===undefined){Object.defineProperty(globalThis,t,{value:undefined,writable:true,enumerable:false,configurable:false});return}const n=new URL(e);if(n.protocol!=="http:"&&n.protocol!=="https:"){throw new TypeError(`Only http & https urls are allowed, received ${n.protocol}`)}Object.defineProperty(globalThis,t,{value:n,writable:true,enumerable:false,configurable:false})}e.exports={getGlobalOrigin:getGlobalOrigin,setGlobalOrigin:setGlobalOrigin}},554:(e,t,n)=>{"use strict";const{kHeadersList:s}=n(2785);const{kGuard:i}=n(5861);const{kEnumerableProperty:r}=n(3983);const{makeIterator:o,isValidHeaderName:A,isValidHeaderValue:a}=n(2538);const{webidl:c}=n(1744);const u=n(9491);const l=Symbol("headers map");const d=Symbol("headers map sorted");function headerValueNormalize(e){let t=e.length;while(/[\r\n\t ]/.test(e.charAt(--t)));return e.slice(0,t+1).replace(/^[\r\n\t ]+/,"")}function fill(e,t){if(Array.isArray(t)){for(const n of t){if(n.length!==2){throw c.errors.exception({header:"Headers constructor",message:`expected name/value pair to be length 2, found ${n.length}.`})}e.append(n[0],n[1])}}else if(typeof t==="object"&&t!==null){for(const[n,s]of Object.entries(t)){e.append(n,s)}}else{throw c.errors.conversionFailed({prefix:"Headers constructor",argument:"Argument 1",types:["sequence>","record"]})}}class HeadersList{cookies=null;constructor(e){if(e instanceof HeadersList){this[l]=new Map(e[l]);this[d]=e[d];this.cookies=e.cookies}else{this[l]=new Map(e);this[d]=null}}contains(e){e=e.toLowerCase();return this[l].has(e)}clear(){this[l].clear();this[d]=null;this.cookies=null}append(e,t){this[d]=null;const n=e.toLowerCase();const s=this[l].get(n);if(s){const e=n==="cookie"?"; ":", ";this[l].set(n,{name:s.name,value:`${s.value}${e}${t}`})}else{this[l].set(n,{name:e,value:t})}if(n==="set-cookie"){this.cookies??=[];this.cookies.push(t)}}set(e,t){this[d]=null;const n=e.toLowerCase();if(n==="set-cookie"){this.cookies=[t]}return this[l].set(n,{name:e,value:t})}delete(e){this[d]=null;e=e.toLowerCase();if(e==="set-cookie"){this.cookies=null}return this[l].delete(e)}get(e){if(!this.contains(e)){return null}return this[l].get(e.toLowerCase())?.value??null}*[Symbol.iterator](){for(const[e,{value:t}]of this[l]){yield[e,t]}}get entries(){const e={};if(this[l].size){for(const{name:t,value:n}of this[l].values()){e[t]=n}}return e}}class Headers{constructor(e=undefined){this[s]=new HeadersList;this[i]="none";if(e!==undefined){e=c.converters.HeadersInit(e);fill(this,e)}}append(e,t){c.brandCheck(this,Headers);c.argumentLengthCheck(arguments,2,{header:"Headers.append"});e=c.converters.ByteString(e);t=c.converters.ByteString(t);t=headerValueNormalize(t);if(!A(e)){throw c.errors.invalidArgument({prefix:"Headers.append",value:e,type:"header name"})}else if(!a(t)){throw c.errors.invalidArgument({prefix:"Headers.append",value:t,type:"header value"})}if(this[i]==="immutable"){throw new TypeError("immutable")}else if(this[i]==="request-no-cors"){}return this[s].append(e,t)}delete(e){c.brandCheck(this,Headers);c.argumentLengthCheck(arguments,1,{header:"Headers.delete"});e=c.converters.ByteString(e);if(!A(e)){throw c.errors.invalidArgument({prefix:"Headers.delete",value:e,type:"header name"})}if(this[i]==="immutable"){throw new TypeError("immutable")}else if(this[i]==="request-no-cors"){}if(!this[s].contains(e)){return}return this[s].delete(e)}get(e){c.brandCheck(this,Headers);c.argumentLengthCheck(arguments,1,{header:"Headers.get"});e=c.converters.ByteString(e);if(!A(e)){throw c.errors.invalidArgument({prefix:"Headers.get",value:e,type:"header name"})}return this[s].get(e)}has(e){c.brandCheck(this,Headers);c.argumentLengthCheck(arguments,1,{header:"Headers.has"});e=c.converters.ByteString(e);if(!A(e)){throw c.errors.invalidArgument({prefix:"Headers.has",value:e,type:"header name"})}return this[s].contains(e)}set(e,t){c.brandCheck(this,Headers);c.argumentLengthCheck(arguments,2,{header:"Headers.set"});e=c.converters.ByteString(e);t=c.converters.ByteString(t);t=headerValueNormalize(t);if(!A(e)){throw c.errors.invalidArgument({prefix:"Headers.set",value:e,type:"header name"})}else if(!a(t)){throw c.errors.invalidArgument({prefix:"Headers.set",value:t,type:"header value"})}if(this[i]==="immutable"){throw new TypeError("immutable")}else if(this[i]==="request-no-cors"){}return this[s].set(e,t)}getSetCookie(){c.brandCheck(this,Headers);const e=this[s].cookies;if(e){return[...e]}return[]}get[d](){if(this[s][d]){return this[s][d]}const e=[];const t=[...this[s]].sort(((e,t)=>e[0][...this[d].values()]),"Headers","key")}values(){c.brandCheck(this,Headers);return o((()=>[...this[d].values()]),"Headers","value")}entries(){c.brandCheck(this,Headers);return o((()=>[...this[d].values()]),"Headers","key+value")}forEach(e,t=globalThis){c.brandCheck(this,Headers);c.argumentLengthCheck(arguments,1,{header:"Headers.forEach"});if(typeof e!=="function"){throw new TypeError("Failed to execute 'forEach' on 'Headers': parameter 1 is not of type 'Function'.")}for(const[n,s]of this){e.apply(t,[s,n,this])}}[Symbol.for("nodejs.util.inspect.custom")](){c.brandCheck(this,Headers);return this[s]}}Headers.prototype[Symbol.iterator]=Headers.prototype.entries;Object.defineProperties(Headers.prototype,{append:r,delete:r,get:r,has:r,set:r,getSetCookie:r,keys:r,values:r,entries:r,forEach:r,[Symbol.iterator]:{enumerable:false},[Symbol.toStringTag]:{value:"Headers",configurable:true}});c.converters.HeadersInit=function(e){if(c.util.Type(e)==="Object"){if(e[Symbol.iterator]){return c.converters["sequence>"](e)}return c.converters["record"](e)}throw c.errors.conversionFailed({prefix:"Headers constructor",argument:"Argument 1",types:["sequence>","record"]})};e.exports={fill:fill,Headers:Headers,HeadersList:HeadersList}},4881:(e,t,n)=>{"use strict";const{Response:s,makeNetworkError:i,makeAppropriateNetworkError:r,filterResponse:o,makeResponse:A}=n(7823);const{Headers:a}=n(554);const{Request:c,makeRequest:u}=n(8359);const l=n(9796);const{bytesMatch:d,makePolicyContainer:p,clonePolicyContainer:g,requestBadPort:h,TAOCheck:f,appendRequestOriginHeader:E,responseLocationURL:m,requestCurrentURL:C,setRequestReferrerPolicyOnRedirect:Q,tryUpgradeRequestToAPotentiallyTrustworthyURL:I,createOpaqueTimingInfo:B,appendFetchMetadata:y,corsCheck:b,crossOriginResourcePolicyCheck:w,determineRequestsReferrer:R,coarsenedSharedCurrentTime:v,createDeferredPromise:k,isBlobLike:S,sameOrigin:x,isCancelled:D,isAborted:_,isErrorLike:N,fullyReadBody:F,readableStreamClose:U,isomorphicEncode:T,urlIsLocal:M,urlIsHttpHttpsScheme:L,urlHasHttpsScheme:O}=n(2538);const{kState:P,kHeaders:J,kGuard:H,kRealm:q}=n(5861);const G=n(9491);const{safelyExtractBody:Y}=n(1472);const{redirectStatusSet:V,nullBodyStatus:j,safeMethodsSet:W,requestBodyHeader:K,subresourceSet:z,DOMException:Z}=n(1037);const{kHeadersList:X}=n(2785);const $=n(2361);const{Readable:ee,pipeline:te}=n(2781);const{addAbortListener:ne,isErrored:se,isReadable:ie,nodeMajor:re,nodeMinor:oe}=n(3983);const{dataURLProcessor:Ae,serializeAMimeType:ae}=n(685);const{TransformStream:ce}=n(5356);const{getGlobalDispatcher:ue}=n(1892);const{webidl:le}=n(1744);const{STATUS_CODES:de}=n(3685);const pe=["GET","HEAD"];let ge;let he=globalThis.ReadableStream;class Fetch extends ${constructor(e){super();this.dispatcher=e;this.connection=null;this.dump=false;this.state="ongoing";this.setMaxListeners(21)}terminate(e){if(this.state!=="ongoing"){return}this.state="terminated";this.connection?.destroy(e);this.emit("terminated",e)}abort(e){if(this.state!=="ongoing"){return}this.state="aborted";if(!e){e=new Z("The operation was aborted.","AbortError")}this.serializedAbortReason=e;this.connection?.destroy(e);this.emit("terminated",e)}}function fetch(e,t={}){le.argumentLengthCheck(arguments,1,{header:"globalThis.fetch"});const n=k();let i;try{i=new c(e,t)}catch(e){n.reject(e);return n.promise}const r=i[P];if(i.signal.aborted){abortFetch(n,r,null,i.signal.reason);return n.promise}const o=r.client.globalObject;if(o?.constructor?.name==="ServiceWorkerGlobalScope"){r.serviceWorkers="none"}let A=null;const a=null;let u=false;let l=null;ne(i.signal,(()=>{u=true;G(l!=null);l.abort(i.signal.reason);abortFetch(n,r,A,i.signal.reason)}));const handleFetchDone=e=>finalizeAndReportTiming(e,"fetch");const processResponse=e=>{if(u){return Promise.resolve()}if(e.aborted){abortFetch(n,r,A,l.serializedAbortReason);return Promise.resolve()}if(e.type==="error"){n.reject(Object.assign(new TypeError("fetch failed"),{cause:e.error}));return Promise.resolve()}A=new s;A[P]=e;A[q]=a;A[J][X]=e.headersList;A[J][H]="immutable";A[J][q]=a;n.resolve(A)};l=fetching({request:r,processResponseEndOfBody:handleFetchDone,processResponse:processResponse,dispatcher:t.dispatcher??ue()});return n.promise}function finalizeAndReportTiming(e,t="other"){if(e.type==="error"&&e.aborted){return}if(!e.urlList?.length){return}const n=e.urlList[0];let s=e.timingInfo;let i=e.cacheState;if(!L(n)){return}if(s===null){return}if(!s.timingAllowPassed){s=B({startTime:s.startTime});i=""}s.endTime=v();e.timingInfo=s;markResourceTiming(s,n,t,globalThis,i)}function markResourceTiming(e,t,n,s,i){if(re>18||re===18&&oe>=2){performance.markResourceTiming(e,t.href,n,s,i)}}function abortFetch(e,t,n,s){if(!s){s=new Z("The operation was aborted.","AbortError")}e.reject(s);if(t.body!=null&&ie(t.body?.stream)){t.body.stream.cancel(s).catch((e=>{if(e.code==="ERR_INVALID_STATE"){return}throw e}))}if(n==null){return}const i=n[P];if(i.body!=null&&ie(i.body?.stream)){i.body.stream.cancel(s).catch((e=>{if(e.code==="ERR_INVALID_STATE"){return}throw e}))}}function fetching({request:e,processRequestBodyChunkLength:t,processRequestEndOfBody:n,processResponse:s,processResponseEndOfBody:i,processResponseConsumeBody:r,useParallelQueue:o=false,dispatcher:A}){let a=null;let c=false;if(e.client!=null){a=e.client.globalObject;c=e.client.crossOriginIsolatedCapability}const u=v(c);const l=B({startTime:u});const d={controller:new Fetch(A),request:e,timingInfo:l,processRequestBodyChunkLength:t,processRequestEndOfBody:n,processResponse:s,processResponseConsumeBody:r,processResponseEndOfBody:i,taskDestination:a,crossOriginIsolatedCapability:c};G(!e.body||e.body.stream);if(e.window==="client"){e.window=e.client?.globalObject?.constructor?.name==="Window"?e.client:"no-window"}if(e.origin==="client"){e.origin=e.client?.origin}if(e.policyContainer==="client"){if(e.client!=null){e.policyContainer=g(e.client.policyContainer)}else{e.policyContainer=p()}}if(!e.headersList.contains("accept")){const t="*/*";e.headersList.append("accept",t)}if(!e.headersList.contains("accept-language")){e.headersList.append("accept-language","*")}if(e.priority===null){}if(z.has(e.destination)){}mainFetch(d).catch((e=>{d.controller.terminate(e)}));return d.controller}async function mainFetch(e,t=false){const n=e.request;let s=null;if(n.localURLsOnly&&!M(C(n))){s=i("local URLs only")}I(n);if(h(n)==="blocked"){s=i("bad port")}if(n.referrerPolicy===""){n.referrerPolicy=n.policyContainer.referrerPolicy}if(n.referrer!=="no-referrer"){n.referrer=R(n)}if(s===null){s=await(async()=>{const t=C(n);if(x(t,n.url)&&n.responseTainting==="basic"||t.protocol==="data:"||(n.mode==="navigate"||n.mode==="websocket")){n.responseTainting="basic";return await schemeFetch(e)}if(n.mode==="same-origin"){return i('request mode cannot be "same-origin"')}if(n.mode==="no-cors"){if(n.redirect!=="follow"){return i('redirect mode cannot be "follow" for "no-cors" request')}n.responseTainting="opaque";return await schemeFetch(e)}if(!L(C(n))){return i("URL scheme must be a HTTP(S) scheme")}n.responseTainting="cors";return await httpFetch(e)})()}if(t){return s}if(s.status!==0&&!s.internalResponse){if(n.responseTainting==="cors"){}if(n.responseTainting==="basic"){s=o(s,"basic")}else if(n.responseTainting==="cors"){s=o(s,"cors")}else if(n.responseTainting==="opaque"){s=o(s,"opaque")}else{G(false)}}let r=s.status===0?s:s.internalResponse;if(r.urlList.length===0){r.urlList.push(...n.urlList)}if(!n.timingAllowFailed){s.timingAllowPassed=true}if(s.type==="opaque"&&r.status===206&&r.rangeRequested&&!n.headers.contains("range")){s=r=i()}if(s.status!==0&&(n.method==="HEAD"||n.method==="CONNECT"||j.includes(r.status))){r.body=null;e.controller.dump=true}if(n.integrity){const processBodyError=t=>fetchFinale(e,i(t));if(n.responseTainting==="opaque"||s.body==null){processBodyError(s.error);return}const processBody=t=>{if(!d(t,n.integrity)){processBodyError("integrity mismatch");return}s.body=Y(t)[0];fetchFinale(e,s)};await F(s.body,processBody,processBodyError)}else{fetchFinale(e,s)}}function schemeFetch(e){if(D(e)&&e.request.redirectCount===0){return Promise.resolve(r(e))}const{request:t}=e;const{protocol:s}=C(t);switch(s){case"about:":{return Promise.resolve(i("about scheme is not supported"))}case"blob:":{if(!ge){ge=n(4300).resolveObjectURL}const e=C(t);if(e.search.length!==0){return Promise.resolve(i("NetworkError when attempting to fetch resource."))}const s=ge(e.toString());if(t.method!=="GET"||!S(s)){return Promise.resolve(i("invalid method"))}const r=Y(s);const o=r[0];const a=T(`${o.length}`);const c=r[1]??"";const u=A({statusText:"OK",headersList:[["content-length",{name:"Content-Length",value:a}],["content-type",{name:"Content-Type",value:c}]]});u.body=o;return Promise.resolve(u)}case"data:":{const e=C(t);const n=Ae(e);if(n==="failure"){return Promise.resolve(i("failed to fetch the data URL"))}const s=ae(n.mimeType);return Promise.resolve(A({statusText:"OK",headersList:[["content-type",{name:"Content-Type",value:s}]],body:Y(n.body)[0]}))}case"file:":{return Promise.resolve(i("not implemented... yet..."))}case"http:":case"https:":{return httpFetch(e).catch((e=>i(e)))}default:{return Promise.resolve(i("unknown scheme"))}}}function finalizeResponse(e,t){e.request.done=true;if(e.processResponseDone!=null){queueMicrotask((()=>e.processResponseDone(t)))}}function fetchFinale(e,t){if(t.type==="error"){t.urlList=[e.request.urlList[0]];t.timingInfo=B({startTime:e.timingInfo.startTime})}const processResponseEndOfBody=()=>{e.request.done=true;if(e.processResponseEndOfBody!=null){queueMicrotask((()=>e.processResponseEndOfBody(t)))}};if(e.processResponse!=null){queueMicrotask((()=>e.processResponse(t)))}if(t.body==null){processResponseEndOfBody()}else{const identityTransformAlgorithm=(e,t)=>{t.enqueue(e)};const e=new ce({start(){},transform:identityTransformAlgorithm,flush:processResponseEndOfBody},{size(){return 1}},{size(){return 1}});t.body={stream:t.body.stream.pipeThrough(e)}}if(e.processResponseConsumeBody!=null){const processBody=n=>e.processResponseConsumeBody(t,n);const processBodyError=n=>e.processResponseConsumeBody(t,n);if(t.body==null){queueMicrotask((()=>processBody(null)))}else{return F(t.body,processBody,processBodyError)}return Promise.resolve()}}async function httpFetch(e){const t=e.request;let n=null;let s=null;const r=e.timingInfo;if(t.serviceWorkers==="all"){}if(n===null){if(t.redirect==="follow"){t.serviceWorkers="none"}s=n=await httpNetworkOrCacheFetch(e);if(t.responseTainting==="cors"&&b(t,n)==="failure"){return i("cors failure")}if(f(t,n)==="failure"){t.timingAllowFailed=true}}if((t.responseTainting==="opaque"||n.type==="opaque")&&w(t.origin,t.client,t.destination,s)==="blocked"){return i("blocked")}if(V.has(s.status)){if(t.redirect!=="manual"){e.controller.connection.destroy()}if(t.redirect==="error"){n=i("unexpected redirect")}else if(t.redirect==="manual"){n=s}else if(t.redirect==="follow"){n=await httpRedirectFetch(e,n)}else{G(false)}}n.timingInfo=r;return n}function httpRedirectFetch(e,t){const n=e.request;const s=t.internalResponse?t.internalResponse:t;let r;try{r=m(s,C(n).hash);if(r==null){return t}}catch(e){return Promise.resolve(i(e))}if(!L(r)){return Promise.resolve(i("URL scheme must be a HTTP(S) scheme"))}if(n.redirectCount===20){return Promise.resolve(i("redirect count exceeded"))}n.redirectCount+=1;if(n.mode==="cors"&&(r.username||r.password)&&!x(n,r)){return Promise.resolve(i('cross origin not allowed for request mode "cors"'))}if(n.responseTainting==="cors"&&(r.username||r.password)){return Promise.resolve(i('URL cannot contain credentials for request mode "cors"'))}if(s.status!==303&&n.body!=null&&n.body.source==null){return Promise.resolve(i())}if([301,302].includes(s.status)&&n.method==="POST"||s.status===303&&!pe.includes(n.method)){n.method="GET";n.body=null;for(const e of K){n.headersList.delete(e)}}if(!x(C(n),r)){n.headersList.delete("authorization");n.headersList.delete("cookie");n.headersList.delete("host")}if(n.body!=null){G(n.body.source!=null);n.body=Y(n.body.source)[0]}const o=e.timingInfo;o.redirectEndTime=o.postRedirectStartTime=v(e.crossOriginIsolatedCapability);if(o.redirectStartTime===0){o.redirectStartTime=o.startTime}n.urlList.push(r);Q(n,s);return mainFetch(e,true)}async function httpNetworkOrCacheFetch(e,t=false,n=false){const s=e.request;let o=null;let A=null;let a=null;const c=null;const l=false;if(s.window==="no-window"&&s.redirect==="error"){o=e;A=s}else{A=u(s);o={...e};o.request=A}const d=s.credentials==="include"||s.credentials==="same-origin"&&s.responseTainting==="basic";const p=A.body?A.body.length:null;let g=null;if(A.body==null&&["POST","PUT"].includes(A.method)){g="0"}if(p!=null){g=T(`${p}`)}if(g!=null){A.headersList.append("content-length",g)}if(p!=null&&A.keepalive){}if(A.referrer instanceof URL){A.headersList.append("referer",T(A.referrer.href))}E(A);y(A);if(!A.headersList.contains("user-agent")){A.headersList.append("user-agent",typeof esbuildDetection==="undefined"?"undici":"node")}if(A.cache==="default"&&(A.headersList.contains("if-modified-since")||A.headersList.contains("if-none-match")||A.headersList.contains("if-unmodified-since")||A.headersList.contains("if-match")||A.headersList.contains("if-range"))){A.cache="no-store"}if(A.cache==="no-cache"&&!A.preventNoCacheCacheControlHeaderModification&&!A.headersList.contains("cache-control")){A.headersList.append("cache-control","max-age=0")}if(A.cache==="no-store"||A.cache==="reload"){if(!A.headersList.contains("pragma")){A.headersList.append("pragma","no-cache")}if(!A.headersList.contains("cache-control")){A.headersList.append("cache-control","no-cache")}}if(A.headersList.contains("range")){A.headersList.append("accept-encoding","identity")}if(!A.headersList.contains("accept-encoding")){if(O(C(A))){A.headersList.append("accept-encoding","br, gzip, deflate")}else{A.headersList.append("accept-encoding","gzip, deflate")}}A.headersList.delete("host");if(d){}if(c==null){A.cache="no-store"}if(A.mode!=="no-store"&&A.mode!=="reload"){}if(a==null){if(A.mode==="only-if-cached"){return i("only if cached")}const e=await httpNetworkFetch(o,d,n);if(!W.has(A.method)&&e.status>=200&&e.status<=399){}if(l&&e.status===304){}if(a==null){a=e}}a.urlList=[...A.urlList];if(A.headersList.contains("range")){a.rangeRequested=true}a.requestIncludesCredentials=d;if(a.status===407){if(s.window==="no-window"){return i()}if(D(e)){return r(e)}return i("proxy authentication required")}if(a.status===421&&!n&&(s.body==null||s.body.source!=null)){if(D(e)){return r(e)}e.controller.connection.destroy();a=await httpNetworkOrCacheFetch(e,t,true)}if(t){}return a}async function httpNetworkFetch(e,t=false,s=false){G(!e.controller.connection||e.controller.connection.destroyed);e.controller.connection={abort:null,destroyed:false,destroy(e){if(!this.destroyed){this.destroyed=true;this.abort?.(e??new Z("The operation was aborted.","AbortError"))}}};const o=e.request;let c=null;const u=e.timingInfo;const d=null;if(d==null){o.cache="no-store"}const p=s?"yes":"no";if(o.mode==="websocket"){}else{}let g=null;if(o.body==null&&e.processRequestEndOfBody){queueMicrotask((()=>e.processRequestEndOfBody()))}else if(o.body!=null){const processBodyChunk=async function*(t){if(D(e)){return}yield t;e.processRequestBodyChunkLength?.(t.byteLength)};const processEndOfBody=()=>{if(D(e)){return}if(e.processRequestEndOfBody){e.processRequestEndOfBody()}};const processBodyError=t=>{if(D(e)){return}if(t.name==="AbortError"){e.controller.abort()}else{e.controller.terminate(t)}};g=async function*(){try{for await(const e of o.body.stream){yield*processBodyChunk(e)}processEndOfBody()}catch(e){processBodyError(e)}}()}try{const{body:t,status:n,statusText:s,headersList:i,socket:r}=await dispatch({body:g});if(r){c=A({status:n,statusText:s,headersList:i,socket:r})}else{const r=t[Symbol.asyncIterator]();e.controller.next=()=>r.next();c=A({status:n,statusText:s,headersList:i})}}catch(t){if(t.name==="AbortError"){e.controller.connection.destroy();return r(e,t)}return i(t)}const pullAlgorithm=()=>{e.controller.resume()};const cancelAlgorithm=t=>{e.controller.abort(t)};if(!he){he=n(5356).ReadableStream}const h=new he({async start(t){e.controller.controller=t},async pull(e){await pullAlgorithm(e)},async cancel(e){await cancelAlgorithm(e)}},{highWaterMark:0,size(){return 1}});c.body={stream:h};e.controller.on("terminated",onAborted);e.controller.resume=async()=>{while(true){let t;let n;try{const{done:n,value:s}=await e.controller.next();if(_(e)){break}t=n?undefined:s}catch(s){if(e.controller.ended&&!u.encodedBodySize){t=undefined}else{t=s;n=true}}if(t===undefined){U(e.controller.controller);finalizeResponse(e,c);return}u.decodedBodySize+=t?.byteLength??0;if(n){e.controller.terminate(t);return}e.controller.controller.enqueue(new Uint8Array(t));if(se(h)){e.controller.terminate();return}if(!e.controller.controller.desiredSize){return}}};function onAborted(t){if(_(e)){c.aborted=true;if(ie(h)){e.controller.controller.error(e.controller.serializedAbortReason)}}else{if(ie(h)){e.controller.controller.error(new TypeError("terminated",{cause:N(t)?t:undefined}))}}e.controller.connection.destroy()}return c;async function dispatch({body:t}){const n=C(o);const s=e.controller.dispatcher;return new Promise(((i,r)=>s.dispatch({path:n.pathname+n.search,origin:n.origin,method:o.method,body:e.controller.dispatcher.isMockActive?o.body&&o.body.source:t,headers:o.headersList.entries,maxRedirections:0,upgrade:o.mode==="websocket"?"websocket":undefined},{body:null,abort:null,onConnect(t){const{connection:n}=e.controller;if(n.destroyed){t(new Z("The operation was aborted.","AbortError"))}else{e.controller.on("terminated",t);this.abort=n.abort=t}},onHeaders(e,t,n,s){if(e<200){return}let r=[];let A="";const c=new a;if(Array.isArray(t)){for(let e=0;ee.trim()))}else if(n.toLowerCase()==="location"){A=s}c.append(n,s)}}else{const e=Object.keys(t);for(const n of e){const e=t[n];if(n.toLowerCase()==="content-encoding"){r=e.toLowerCase().split(",").map((e=>e.trim())).reverse()}else if(n.toLowerCase()==="location"){A=e}c.append(n,e)}}this.body=new ee({read:n});const u=[];const d=o.redirect==="follow"&&A&&V.has(e);if(o.method!=="HEAD"&&o.method!=="CONNECT"&&!j.includes(e)&&!d){for(const e of r){if(e==="x-gzip"||e==="gzip"){u.push(l.createGunzip({flush:l.constants.Z_SYNC_FLUSH,finishFlush:l.constants.Z_SYNC_FLUSH}))}else if(e==="deflate"){u.push(l.createInflate())}else if(e==="br"){u.push(l.createBrotliDecompress())}else{u.length=0;break}}}i({status:e,statusText:s,headersList:c[X],body:u.length?te(this.body,...u,(()=>{})):this.body.on("error",(()=>{}))});return true},onData(t){if(e.controller.dump){return}const n=t;u.encodedBodySize+=n.byteLength;return this.body.push(n)},onComplete(){if(this.abort){e.controller.off("terminated",this.abort)}e.controller.ended=true;this.body.push(null)},onError(t){if(this.abort){e.controller.off("terminated",this.abort)}this.body?.destroy(t);e.controller.terminate(t);r(t)},onUpgrade(e,t,n){if(e!==101){return}const s=new a;for(let e=0;e{"use strict";const{extractBody:s,mixinBody:i,cloneBody:r}=n(1472);const{Headers:o,fill:A,HeadersList:a}=n(554);const{FinalizationRegistry:c}=n(6436)();const u=n(3983);const{isValidHTTPToken:l,sameOrigin:d,normalizeMethod:p,makePolicyContainer:g}=n(2538);const{forbiddenMethodsSet:h,corsSafeListedMethodsSet:f,referrerPolicy:E,requestRedirect:m,requestMode:C,requestCredentials:Q,requestCache:I,requestDuplex:B}=n(1037);const{kEnumerableProperty:y}=u;const{kHeaders:b,kSignal:w,kState:R,kGuard:v,kRealm:k}=n(5861);const{webidl:S}=n(1744);const{getGlobalOrigin:x}=n(1246);const{URLSerializer:D}=n(685);const{kHeadersList:_}=n(2785);const N=n(9491);const{getMaxListeners:F,setMaxListeners:U,getEventListeners:T,defaultMaxListeners:M}=n(2361);let L=globalThis.TransformStream;const O=Symbol("init");const P=Symbol("abortController");const J=new c((({signal:e,abort:t})=>{e.removeEventListener("abort",t)}));class Request{constructor(e,t={}){if(e===O){return}S.argumentLengthCheck(arguments,1,{header:"Request constructor"});e=S.converters.RequestInfo(e);t=S.converters.RequestInit(t);this[k]={settingsObject:{baseUrl:x(),get origin(){return this.baseUrl?.origin},policyContainer:g()}};let i=null;let r=null;const a=this[k].settingsObject.baseUrl;let c=null;if(typeof e==="string"){let t;try{t=new URL(e,a)}catch(t){throw new TypeError("Failed to parse URL from "+e,{cause:t})}if(t.username||t.password){throw new TypeError("Request cannot be constructed from a URL that includes credentials: "+e)}i=makeRequest({urlList:[t]});r="cors"}else{N(e instanceof Request);i=e[R];c=e[w]}const E=this[k].settingsObject.origin;let m="client";if(i.window?.constructor?.name==="EnvironmentSettingsObject"&&d(i.window,E)){m=i.window}if(t.window!=null){throw new TypeError(`'window' option '${m}' must be null`)}if("window"in t){m="no-window"}i=makeRequest({method:i.method,headersList:i.headersList,unsafeRequest:i.unsafeRequest,client:this[k].settingsObject,window:m,priority:i.priority,origin:i.origin,referrer:i.referrer,referrerPolicy:i.referrerPolicy,mode:i.mode,credentials:i.credentials,cache:i.cache,redirect:i.redirect,integrity:i.integrity,keepalive:i.keepalive,reloadNavigation:i.reloadNavigation,historyNavigation:i.historyNavigation,urlList:[...i.urlList]});if(Object.keys(t).length>0){if(i.mode==="navigate"){i.mode="same-origin"}i.reloadNavigation=false;i.historyNavigation=false;i.origin="client";i.referrer="client";i.referrerPolicy="";i.url=i.urlList[i.urlList.length-1];i.urlList=[i.url]}if(t.referrer!==undefined){const e=t.referrer;if(e===""){i.referrer="no-referrer"}else{let t;try{t=new URL(e,a)}catch(t){throw new TypeError(`Referrer "${e}" is not a valid URL.`,{cause:t})}if(t.protocol==="about:"&&t.hostname==="client"||E&&!d(t,this[k].settingsObject.baseUrl)){i.referrer="client"}else{i.referrer=t}}}if(t.referrerPolicy!==undefined){i.referrerPolicy=t.referrerPolicy}let C;if(t.mode!==undefined){C=t.mode}else{C=r}if(C==="navigate"){throw S.errors.exception({header:"Request constructor",message:"invalid request mode navigate."})}if(C!=null){i.mode=C}if(t.credentials!==undefined){i.credentials=t.credentials}if(t.cache!==undefined){i.cache=t.cache}if(i.cache==="only-if-cached"&&i.mode!=="same-origin"){throw new TypeError("'only-if-cached' can be set only with 'same-origin' mode")}if(t.redirect!==undefined){i.redirect=t.redirect}if(t.integrity!==undefined&&t.integrity!=null){i.integrity=String(t.integrity)}if(t.keepalive!==undefined){i.keepalive=Boolean(t.keepalive)}if(t.method!==undefined){let e=t.method;if(!l(t.method)){throw TypeError(`'${t.method}' is not a valid HTTP method.`)}if(h.has(e.toUpperCase())){throw TypeError(`'${t.method}' HTTP method is unsupported.`)}e=p(t.method);i.method=e}if(t.signal!==undefined){c=t.signal}this[R]=i;const Q=new AbortController;this[w]=Q.signal;this[w][k]=this[k];if(c!=null){if(!c||typeof c.aborted!=="boolean"||typeof c.addEventListener!=="function"){throw new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")}if(c.aborted){Q.abort(c.reason)}else{this[P]=Q;const e=new WeakRef(Q);const abort=function(){const t=e.deref();if(t!==undefined){t.abort(this.reason)}};try{if(typeof F==="function"&&F(c)===M){U(100,c)}else if(T(c,"abort").length>=M){U(100,c)}}catch{}u.addAbortListener(c,abort);J.register(Q,{signal:c,abort:abort})}}this[b]=new o;this[b][_]=i.headersList;this[b][v]="request";this[b][k]=this[k];if(C==="no-cors"){if(!f.has(i.method)){throw new TypeError(`'${i.method} is unsupported in no-cors mode.`)}this[b][v]="request-no-cors"}if(Object.keys(t).length!==0){let e=new o(this[b]);if(t.headers!==undefined){e=t.headers}this[b][_].clear();if(e.constructor.name==="Headers"){for(const[t,n]of e){this[b].append(t,n)}}else{A(this[b],e)}}const I=e instanceof Request?e[R].body:null;if((t.body!=null||I!=null)&&(i.method==="GET"||i.method==="HEAD")){throw new TypeError("Request with GET/HEAD method cannot have body.")}let B=null;if(t.body!=null){const[e,n]=s(t.body,i.keepalive);B=e;if(n&&!this[b][_].contains("content-type")){this[b].append("content-type",n)}}const y=B??I;if(y!=null&&y.source==null){if(B!=null&&t.duplex==null){throw new TypeError("RequestInit: duplex option is required when sending a body.")}if(i.mode!=="same-origin"&&i.mode!=="cors"){throw new TypeError('If request is made from ReadableStream, mode should be "same-origin" or "cors"')}i.useCORSPreflightFlag=true}let D=y;if(B==null&&I!=null){if(u.isDisturbed(I.stream)||I.stream.locked){throw new TypeError("Cannot construct a Request with a Request object that has already been used.")}if(!L){L=n(5356).TransformStream}const e=new L;I.stream.pipeThrough(e);D={source:I.source,length:I.length,stream:e.readable}}this[R].body=D}get method(){S.brandCheck(this,Request);return this[R].method}get url(){S.brandCheck(this,Request);return D(this[R].url)}get headers(){S.brandCheck(this,Request);return this[b]}get destination(){S.brandCheck(this,Request);return this[R].destination}get referrer(){S.brandCheck(this,Request);if(this[R].referrer==="no-referrer"){return""}if(this[R].referrer==="client"){return"about:client"}return this[R].referrer.toString()}get referrerPolicy(){S.brandCheck(this,Request);return this[R].referrerPolicy}get mode(){S.brandCheck(this,Request);return this[R].mode}get credentials(){return this[R].credentials}get cache(){S.brandCheck(this,Request);return this[R].cache}get redirect(){S.brandCheck(this,Request);return this[R].redirect}get integrity(){S.brandCheck(this,Request);return this[R].integrity}get keepalive(){S.brandCheck(this,Request);return this[R].keepalive}get isReloadNavigation(){S.brandCheck(this,Request);return this[R].reloadNavigation}get isHistoryNavigation(){S.brandCheck(this,Request);return this[R].historyNavigation}get signal(){S.brandCheck(this,Request);return this[w]}get body(){S.brandCheck(this,Request);return this[R].body?this[R].body.stream:null}get bodyUsed(){S.brandCheck(this,Request);return!!this[R].body&&u.isDisturbed(this[R].body.stream)}get duplex(){S.brandCheck(this,Request);return"half"}clone(){S.brandCheck(this,Request);if(this.bodyUsed||this.body?.locked){throw new TypeError("unusable")}const e=cloneRequest(this[R]);const t=new Request(O);t[R]=e;t[k]=this[k];t[b]=new o;t[b][_]=e.headersList;t[b][v]=this[b][v];t[b][k]=this[b][k];const n=new AbortController;if(this.signal.aborted){n.abort(this.signal.reason)}else{u.addAbortListener(this.signal,(()=>{n.abort(this.signal.reason)}))}t[w]=n.signal;return t}}i(Request);function makeRequest(e){const t={method:"GET",localURLsOnly:false,unsafeRequest:false,body:null,client:null,reservedClient:null,replacesClientId:"",window:"client",keepalive:false,serviceWorkers:"all",initiator:"",destination:"",priority:null,origin:"client",policyContainer:"client",referrer:"client",referrerPolicy:"",mode:"no-cors",useCORSPreflightFlag:false,credentials:"same-origin",useCredentials:false,cache:"default",redirect:"follow",integrity:"",cryptoGraphicsNonceMetadata:"",parserMetadata:"",reloadNavigation:false,historyNavigation:false,userActivation:false,taintedOrigin:false,redirectCount:0,responseTainting:"basic",preventNoCacheCacheControlHeaderModification:false,done:false,timingAllowFailed:false,...e,headersList:e.headersList?new a(e.headersList):new a};t.url=t.urlList[0];return t}function cloneRequest(e){const t=makeRequest({...e,body:null});if(e.body!=null){t.body=r(e.body)}return t}Object.defineProperties(Request.prototype,{method:y,url:y,headers:y,redirect:y,clone:y,signal:y,duplex:y,destination:y,body:y,bodyUsed:y,isHistoryNavigation:y,isReloadNavigation:y,keepalive:y,integrity:y,cache:y,credentials:y,attribute:y,referrerPolicy:y,referrer:y,mode:y,[Symbol.toStringTag]:{value:"Request",configurable:true}});S.converters.Request=S.interfaceConverter(Request);S.converters.RequestInfo=function(e){if(typeof e==="string"){return S.converters.USVString(e)}if(e instanceof Request){return S.converters.Request(e)}return S.converters.USVString(e)};S.converters.AbortSignal=S.interfaceConverter(AbortSignal);S.converters.RequestInit=S.dictionaryConverter([{key:"method",converter:S.converters.ByteString},{key:"headers",converter:S.converters.HeadersInit},{key:"body",converter:S.nullableConverter(S.converters.BodyInit)},{key:"referrer",converter:S.converters.USVString},{key:"referrerPolicy",converter:S.converters.DOMString,allowedValues:E},{key:"mode",converter:S.converters.DOMString,allowedValues:C},{key:"credentials",converter:S.converters.DOMString,allowedValues:Q},{key:"cache",converter:S.converters.DOMString,allowedValues:I},{key:"redirect",converter:S.converters.DOMString,allowedValues:m},{key:"integrity",converter:S.converters.DOMString},{key:"keepalive",converter:S.converters.boolean},{key:"signal",converter:S.nullableConverter((e=>S.converters.AbortSignal(e,{strict:false})))},{key:"window",converter:S.converters.any},{key:"duplex",converter:S.converters.DOMString,allowedValues:B}]);e.exports={Request:Request,makeRequest:makeRequest}},7823:(e,t,n)=>{"use strict";const{Headers:s,HeadersList:i,fill:r}=n(554);const{extractBody:o,cloneBody:A,mixinBody:a}=n(1472);const c=n(3983);const{kEnumerableProperty:u}=c;const{isValidReasonPhrase:l,isCancelled:d,isAborted:p,isBlobLike:g,serializeJavascriptValueToJSONString:h,isErrorLike:f,isomorphicEncode:E}=n(2538);const{redirectStatusSet:m,nullBodyStatus:C,DOMException:Q}=n(1037);const{kState:I,kHeaders:B,kGuard:y,kRealm:b}=n(5861);const{webidl:w}=n(1744);const{FormData:R}=n(2015);const{getGlobalOrigin:v}=n(1246);const{URLSerializer:k}=n(685);const{kHeadersList:S}=n(2785);const x=n(9491);const{types:D}=n(3837);const _=globalThis.ReadableStream||n(5356).ReadableStream;const N=new TextEncoder("utf-8");class Response{static error(){const e={settingsObject:{}};const t=new Response;t[I]=makeNetworkError();t[b]=e;t[B][S]=t[I].headersList;t[B][y]="immutable";t[B][b]=e;return t}static json(e,t={}){w.argumentLengthCheck(arguments,1,{header:"Response.json"});if(t!==null){t=w.converters.ResponseInit(t)}const n=N.encode(h(e));const s=o(n);const i={settingsObject:{}};const r=new Response;r[b]=i;r[B][y]="response";r[B][b]=i;initializeResponse(r,t,{body:s[0],type:"application/json"});return r}static redirect(e,t=302){const n={settingsObject:{}};w.argumentLengthCheck(arguments,1,{header:"Response.redirect"});e=w.converters.USVString(e);t=w.converters["unsigned short"](t);let s;try{s=new URL(e,v())}catch(t){throw Object.assign(new TypeError("Failed to parse URL from "+e),{cause:t})}if(!m.has(t)){throw new RangeError("Invalid status code "+t)}const i=new Response;i[b]=n;i[B][y]="immutable";i[B][b]=n;i[I].status=t;const r=E(k(s));i[I].headersList.append("location",r);return i}constructor(e=null,t={}){if(e!==null){e=w.converters.BodyInit(e)}t=w.converters.ResponseInit(t);this[b]={settingsObject:{}};this[I]=makeResponse({});this[B]=new s;this[B][y]="response";this[B][S]=this[I].headersList;this[B][b]=this[b];let n=null;if(e!=null){const[t,s]=o(e);n={body:t,type:s}}initializeResponse(this,t,n)}get type(){w.brandCheck(this,Response);return this[I].type}get url(){w.brandCheck(this,Response);const e=this[I].urlList;const t=e[e.length-1]??null;if(t===null){return""}return k(t,true)}get redirected(){w.brandCheck(this,Response);return this[I].urlList.length>1}get status(){w.brandCheck(this,Response);return this[I].status}get ok(){w.brandCheck(this,Response);return this[I].status>=200&&this[I].status<=299}get statusText(){w.brandCheck(this,Response);return this[I].statusText}get headers(){w.brandCheck(this,Response);return this[B]}get body(){w.brandCheck(this,Response);return this[I].body?this[I].body.stream:null}get bodyUsed(){w.brandCheck(this,Response);return!!this[I].body&&c.isDisturbed(this[I].body.stream)}clone(){w.brandCheck(this,Response);if(this.bodyUsed||this.body&&this.body.locked){throw w.errors.exception({header:"Response.clone",message:"Body has already been consumed."})}const e=cloneResponse(this[I]);const t=new Response;t[I]=e;t[b]=this[b];t[B][S]=e.headersList;t[B][y]=this[B][y];t[B][b]=this[B][b];return t}}a(Response);Object.defineProperties(Response.prototype,{type:u,url:u,status:u,ok:u,redirected:u,statusText:u,headers:u,clone:u,body:u,bodyUsed:u,[Symbol.toStringTag]:{value:"Response",configurable:true}});Object.defineProperties(Response,{json:u,redirect:u,error:u});function cloneResponse(e){if(e.internalResponse){return filterResponse(cloneResponse(e.internalResponse),e.type)}const t=makeResponse({...e,body:null});if(e.body!=null){t.body=A(e.body)}return t}function makeResponse(e){return{aborted:false,rangeRequested:false,timingAllowPassed:false,requestIncludesCredentials:false,type:"default",status:200,timingInfo:null,cacheState:"",statusText:"",...e,headersList:e.headersList?new i(e.headersList):new i,urlList:e.urlList?[...e.urlList]:[]}}function makeNetworkError(e){const t=f(e);return makeResponse({type:"error",status:0,error:t?e:new Error(e?String(e):e),aborted:e&&e.name==="AbortError"})}function makeFilteredResponse(e,t){t={internalResponse:e,...t};return new Proxy(e,{get(e,n){return n in t?t[n]:e[n]},set(e,n,s){x(!(n in t));e[n]=s;return true}})}function filterResponse(e,t){if(t==="basic"){return makeFilteredResponse(e,{type:"basic",headersList:e.headersList})}else if(t==="cors"){return makeFilteredResponse(e,{type:"cors",headersList:e.headersList})}else if(t==="opaque"){return makeFilteredResponse(e,{type:"opaque",urlList:Object.freeze([]),status:0,statusText:"",body:null})}else if(t==="opaqueredirect"){return makeFilteredResponse(e,{type:"opaqueredirect",status:0,statusText:"",headersList:[],body:null})}else{x(false)}}function makeAppropriateNetworkError(e,t=null){x(d(e));return p(e)?makeNetworkError(Object.assign(new Q("The operation was aborted.","AbortError"),{cause:t})):makeNetworkError(Object.assign(new Q("Request was cancelled."),{cause:t}))}function initializeResponse(e,t,n){if(t.status!==null&&(t.status<200||t.status>599)){throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.')}if("statusText"in t&&t.statusText!=null){if(!l(String(t.statusText))){throw new TypeError("Invalid statusText")}}if("status"in t&&t.status!=null){e[I].status=t.status}if("statusText"in t&&t.statusText!=null){e[I].statusText=t.statusText}if("headers"in t&&t.headers!=null){r(e[B],t.headers)}if(n){if(C.includes(e.status)){throw w.errors.exception({header:"Response constructor",message:"Invalid response status code "+e.status})}e[I].body=n.body;if(n.type!=null&&!e[I].headersList.contains("Content-Type")){e[I].headersList.append("content-type",n.type)}}}w.converters.ReadableStream=w.interfaceConverter(_);w.converters.FormData=w.interfaceConverter(R);w.converters.URLSearchParams=w.interfaceConverter(URLSearchParams);w.converters.XMLHttpRequestBodyInit=function(e){if(typeof e==="string"){return w.converters.USVString(e)}if(g(e)){return w.converters.Blob(e,{strict:false})}if(D.isAnyArrayBuffer(e)||D.isTypedArray(e)||D.isDataView(e)){return w.converters.BufferSource(e)}if(c.isFormDataLike(e)){return w.converters.FormData(e,{strict:false})}if(e instanceof URLSearchParams){return w.converters.URLSearchParams(e)}return w.converters.DOMString(e)};w.converters.BodyInit=function(e){if(e instanceof _){return w.converters.ReadableStream(e)}if(e?.[Symbol.asyncIterator]){return e}return w.converters.XMLHttpRequestBodyInit(e)};w.converters.ResponseInit=w.dictionaryConverter([{key:"status",converter:w.converters["unsigned short"],defaultValue:200},{key:"statusText",converter:w.converters.ByteString,defaultValue:""},{key:"headers",converter:w.converters.HeadersInit}]);e.exports={makeNetworkError:makeNetworkError,makeResponse:makeResponse,makeAppropriateNetworkError:makeAppropriateNetworkError,filterResponse:filterResponse,Response:Response,cloneResponse:cloneResponse}},5861:e=>{"use strict";e.exports={kUrl:Symbol("url"),kHeaders:Symbol("headers"),kSignal:Symbol("signal"),kState:Symbol("state"),kGuard:Symbol("guard"),kRealm:Symbol("realm")}},2538:(e,t,n)=>{"use strict";const{redirectStatusSet:s,referrerPolicySet:i,badPortsSet:r}=n(1037);const{getGlobalOrigin:o}=n(1246);const{performance:A}=n(4074);const{isBlobLike:a,toUSVString:c,ReadableStreamFrom:u}=n(3983);const l=n(9491);const{isUint8Array:d}=n(4978);let p;try{p=n(6113)}catch{}function responseURL(e){const t=e.urlList;const n=t.length;return n===0?null:t[n-1].toString()}function responseLocationURL(e,t){if(!s.has(e.status)){return null}let n=e.headersList.get("location");if(n!==null&&isValidHeaderValue(n)){n=new URL(n,responseURL(e))}if(n&&!n.hash){n.hash=t}return n}function requestCurrentURL(e){return e.urlList[e.urlList.length-1]}function requestBadPort(e){const t=requestCurrentURL(e);if(urlIsHttpHttpsScheme(t)&&r.has(t.port)){return"blocked"}return"allowed"}function isErrorLike(e){return e instanceof Error||(e?.constructor?.name==="Error"||e?.constructor?.name==="DOMException")}function isValidReasonPhrase(e){for(let t=0;t=32&&n<=126||n>=128&&n<=255)){return false}}return true}function isTokenChar(e){return!(e>=127||e<=32||e==="("||e===")"||e==="<"||e===">"||e==="@"||e===","||e===";"||e===":"||e==="\\"||e==='"'||e==="/"||e==="["||e==="]"||e==="?"||e==="="||e==="{"||e==="}")}function isValidHTTPToken(e){if(!e||typeof e!=="string"){return false}for(let t=0;t127||!isTokenChar(n)){return false}}return true}function isValidHeaderName(e){if(e.length===0){return false}return isValidHTTPToken(e)}function isValidHeaderValue(e){if(e.startsWith("\t")||e.startsWith(" ")||e.endsWith("\t")||e.endsWith(" ")){return false}if(e.includes("\0")||e.includes("\r")||e.includes("\n")){return false}return true}function setRequestReferrerPolicyOnRedirect(e,t){const{headersList:n}=t;const s=(n.get("referrer-policy")??"").split(",");let r="";if(s.length>0){for(let e=s.length;e!==0;e--){const t=s[e-1].trim();if(i.has(t)){r=t;break}}}if(r!==""){e.referrerPolicy=r}}function crossOriginResourcePolicyCheck(){return"allowed"}function corsCheck(){return"success"}function TAOCheck(){return"success"}function appendFetchMetadata(e){let t=null;t=e.mode;e.headersList.set("sec-fetch-mode",t)}function appendRequestOriginHeader(e){let t=e.origin;if(e.responseTainting==="cors"||e.mode==="websocket"){if(t){e.headersList.append("origin",t)}}else if(e.method!=="GET"&&e.method!=="HEAD"){switch(e.referrerPolicy){case"no-referrer":t=null;break;case"no-referrer-when-downgrade":case"strict-origin":case"strict-origin-when-cross-origin":if(e.origin&&urlHasHttpsScheme(e.origin)&&!urlHasHttpsScheme(requestCurrentURL(e))){t=null}break;case"same-origin":if(!sameOrigin(e,requestCurrentURL(e))){t=null}break;default:}if(t){e.headersList.append("origin",t)}}}function coarsenedSharedCurrentTime(e){return A.now()}function createOpaqueTimingInfo(e){return{startTime:e.startTime??0,redirectStartTime:0,redirectEndTime:0,postRedirectStartTime:e.startTime??0,finalServiceWorkerStartTime:0,finalNetworkResponseStartTime:0,finalNetworkRequestStartTime:0,endTime:0,encodedBodySize:0,decodedBodySize:0,finalConnectionTimingInfo:null}}function makePolicyContainer(){return{referrerPolicy:"strict-origin-when-cross-origin"}}function clonePolicyContainer(e){return{referrerPolicy:e.referrerPolicy}}function determineRequestsReferrer(e){const t=e.referrerPolicy;l(t);let n=null;if(e.referrer==="client"){const e=o();if(!e||e.origin==="null"){return"no-referrer"}n=new URL(e)}else if(e.referrer instanceof URL){n=e.referrer}let s=stripURLForReferrer(n);const i=stripURLForReferrer(n,true);if(s.toString().length>4096){s=i}const r=sameOrigin(e,s);const A=isURLPotentiallyTrustworthy(s)&&!isURLPotentiallyTrustworthy(e.url);switch(t){case"origin":return i!=null?i:stripURLForReferrer(n,true);case"unsafe-url":return s;case"same-origin":return r?i:"no-referrer";case"origin-when-cross-origin":return r?s:i;case"strict-origin-when-cross-origin":{const t=requestCurrentURL(e);if(sameOrigin(s,t)){return s}if(isURLPotentiallyTrustworthy(s)&&!isURLPotentiallyTrustworthy(t)){return"no-referrer"}return i}case"strict-origin":case"no-referrer-when-downgrade":default:return A?"no-referrer":i}}function stripURLForReferrer(e,t){l(e instanceof URL);if(e.protocol==="file:"||e.protocol==="about:"||e.protocol==="blank:"){return"no-referrer"}e.username="";e.password="";e.hash="";if(t){e.pathname="";e.search=""}return e}function isURLPotentiallyTrustworthy(e){if(!(e instanceof URL)){return false}if(e.href==="about:blank"||e.href==="about:srcdoc"){return true}if(e.protocol==="data:")return true;if(e.protocol==="file:")return true;return isOriginPotentiallyTrustworthy(e.origin);function isOriginPotentiallyTrustworthy(e){if(e==null||e==="null")return false;const t=new URL(e);if(t.protocol==="https:"||t.protocol==="wss:"){return true}if(/^127(?:\.[0-9]+){0,2}\.[0-9]+$|^\[(?:0*:)*?:?0*1\]$/.test(t.hostname)||(t.hostname==="localhost"||t.hostname.includes("localhost."))||t.hostname.endsWith(".localhost")){return true}return false}}function bytesMatch(e,t){if(p===undefined){return true}const n=parseMetadata(t);if(n==="no metadata"){return true}if(n.length===0){return true}const s=n.sort(((e,t)=>t.algo.localeCompare(e.algo)));const i=s[0].algo;const r=s.filter((e=>e.algo===i));for(const t of r){const n=t.algo;let s=t.hash;if(s.endsWith("==")){s=s.slice(0,-2)}let i=p.createHash(n).update(e).digest("base64");if(i.endsWith("==")){i=i.slice(0,-2)}if(i===s){return true}let r=p.createHash(n).update(e).digest("base64url");if(r.endsWith("==")){r=r.slice(0,-2)}if(r===s){return true}}return false}const g=/((?sha256|sha384|sha512)-(?[A-z0-9+/]{1}.*={0,2}))( +[\x21-\x7e]?)?/i;function parseMetadata(e){const t=[];let n=true;const s=p.getHashes();for(const i of e.split(" ")){n=false;const e=g.exec(i);if(e===null||e.groups===undefined){continue}const r=e.groups.algo;if(s.includes(r.toLowerCase())){t.push(e.groups)}}if(n===true){return"no metadata"}return t}function tryUpgradeRequestToAPotentiallyTrustworthyURL(e){}function sameOrigin(e,t){if(e.origin===t.origin&&e.origin==="null"){return true}if(e.protocol===t.protocol&&e.hostname===t.hostname&&e.port===t.port){return true}return false}function createDeferredPromise(){let e;let t;const n=new Promise(((n,s)=>{e=n;t=s}));return{promise:n,resolve:e,reject:t}}function isAborted(e){return e.controller.state==="aborted"}function isCancelled(e){return e.controller.state==="aborted"||e.controller.state==="terminated"}function normalizeMethod(e){return/^(DELETE|GET|HEAD|OPTIONS|POST|PUT)$/i.test(e)?e.toUpperCase():e}function serializeJavascriptValueToJSONString(e){const t=JSON.stringify(e);if(t===undefined){throw new TypeError("Value is not JSON serializable")}l(typeof t==="string");return t}const h=Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));function makeIterator(e,t,n){const s={index:0,kind:n,target:e};const i={next(){if(Object.getPrototypeOf(this)!==i){throw new TypeError(`'next' called on an object that does not implement interface ${t} Iterator.`)}const{index:e,kind:n,target:r}=s;const o=r();const A=o.length;if(e>=A){return{value:undefined,done:true}}const a=o[e];s.index=e+1;return iteratorResult(a,n)},[Symbol.toStringTag]:`${t} Iterator`};Object.setPrototypeOf(i,h);return Object.setPrototypeOf({},i)}function iteratorResult(e,t){let n;switch(t){case"key":{n=e[0];break}case"value":{n=e[1];break}case"key+value":{n=e;break}}return{value:n,done:false}}async function fullyReadBody(e,t,n){const s=t;const i=n;let r;try{r=e.stream.getReader()}catch(e){i(e);return}try{const e=await readAllBytes(r);s(e)}catch(e){i(e)}}let f=globalThis.ReadableStream;function isReadableStreamLike(e){if(!f){f=n(5356).ReadableStream}return e instanceof f||e[Symbol.toStringTag]==="ReadableStream"&&typeof e.tee==="function"}const E=65535;function isomorphicDecode(e){if(e.lengthe+String.fromCharCode(t)),"")}function readableStreamClose(e){try{e.close()}catch(e){if(!e.message.includes("Controller is already closed")){throw e}}}function isomorphicEncode(e){for(let t=0;tObject.prototype.hasOwnProperty.call(e,t));e.exports={isAborted:isAborted,isCancelled:isCancelled,createDeferredPromise:createDeferredPromise,ReadableStreamFrom:u,toUSVString:c,tryUpgradeRequestToAPotentiallyTrustworthyURL:tryUpgradeRequestToAPotentiallyTrustworthyURL,coarsenedSharedCurrentTime:coarsenedSharedCurrentTime,determineRequestsReferrer:determineRequestsReferrer,makePolicyContainer:makePolicyContainer,clonePolicyContainer:clonePolicyContainer,appendFetchMetadata:appendFetchMetadata,appendRequestOriginHeader:appendRequestOriginHeader,TAOCheck:TAOCheck,corsCheck:corsCheck,crossOriginResourcePolicyCheck:crossOriginResourcePolicyCheck,createOpaqueTimingInfo:createOpaqueTimingInfo,setRequestReferrerPolicyOnRedirect:setRequestReferrerPolicyOnRedirect,isValidHTTPToken:isValidHTTPToken,requestBadPort:requestBadPort,requestCurrentURL:requestCurrentURL,responseURL:responseURL,responseLocationURL:responseLocationURL,isBlobLike:a,isURLPotentiallyTrustworthy:isURLPotentiallyTrustworthy,isValidReasonPhrase:isValidReasonPhrase,sameOrigin:sameOrigin,normalizeMethod:normalizeMethod,serializeJavascriptValueToJSONString:serializeJavascriptValueToJSONString,makeIterator:makeIterator,isValidHeaderName:isValidHeaderName,isValidHeaderValue:isValidHeaderValue,hasOwn:m,isErrorLike:isErrorLike,fullyReadBody:fullyReadBody,bytesMatch:bytesMatch,isReadableStreamLike:isReadableStreamLike,readableStreamClose:readableStreamClose,isomorphicEncode:isomorphicEncode,isomorphicDecode:isomorphicDecode,urlIsLocal:urlIsLocal,urlHasHttpsScheme:urlHasHttpsScheme,urlIsHttpHttpsScheme:urlIsHttpHttpsScheme,readAllBytes:readAllBytes}},1744:(e,t,n)=>{"use strict";const{types:s}=n(3837);const{hasOwn:i,toUSVString:r}=n(2538);const o={};o.converters={};o.util={};o.errors={};o.errors.exception=function(e){return new TypeError(`${e.header}: ${e.message}`)};o.errors.conversionFailed=function(e){const t=e.types.length===1?"":" one of";const n=`${e.argument} could not be converted to`+`${t}: ${e.types.join(", ")}.`;return o.errors.exception({header:e.prefix,message:n})};o.errors.invalidArgument=function(e){return o.errors.exception({header:e.prefix,message:`"${e.value}" is an invalid ${e.type}.`})};o.brandCheck=function(e,t,n=undefined){if(n?.strict!==false&&!(e instanceof t)){throw new TypeError("Illegal invocation")}else{return e?.[Symbol.toStringTag]===t.prototype[Symbol.toStringTag]}};o.argumentLengthCheck=function({length:e},t,n){if(ei){throw o.errors.exception({header:"Integer conversion",message:`Value must be between ${r}-${i}, got ${A}.`})}return A}if(!Number.isNaN(A)&&s.clamp===true){A=Math.min(Math.max(A,r),i);if(Math.floor(A)%2===0){A=Math.floor(A)}else{A=Math.ceil(A)}return A}if(Number.isNaN(A)||A===0&&Object.is(0,A)||A===Number.POSITIVE_INFINITY||A===Number.NEGATIVE_INFINITY){return 0}A=o.util.IntegerPart(A);A=A%Math.pow(2,t);if(n==="signed"&&A>=Math.pow(2,t)-1){return A-Math.pow(2,t)}return A};o.util.IntegerPart=function(e){const t=Math.floor(Math.abs(e));if(e<0){return-1*t}return t};o.sequenceConverter=function(e){return t=>{if(o.util.Type(t)!=="Object"){throw o.errors.exception({header:"Sequence",message:`Value of type ${o.util.Type(t)} is not an Object.`})}const n=t?.[Symbol.iterator]?.();const s=[];if(n===undefined||typeof n.next!=="function"){throw o.errors.exception({header:"Sequence",message:"Object is not an iterator."})}while(true){const{done:t,value:i}=n.next();if(t){break}s.push(e(i))}return s}};o.recordConverter=function(e,t){return n=>{if(o.util.Type(n)!=="Object"){throw o.errors.exception({header:"Record",message:`Value of type ${o.util.Type(n)} is not an Object.`})}const i={};if(!s.isProxy(n)){const s=Object.keys(n);for(const r of s){const s=e(r);const o=t(n[r]);i[s]=o}return i}const r=Reflect.ownKeys(n);for(const s of r){const r=Reflect.getOwnPropertyDescriptor(n,s);if(r?.enumerable){const r=e(s);const o=t(n[s]);i[r]=o}}return i}};o.interfaceConverter=function(e){return(t,n={})=>{if(n.strict!==false&&!(t instanceof e)){throw o.errors.exception({header:e.name,message:`Expected ${t} to be an instance of ${e.name}.`})}return t}};o.dictionaryConverter=function(e){return t=>{const n=o.util.Type(t);const s={};if(n==="Null"||n==="Undefined"){return s}else if(n!=="Object"){throw o.errors.exception({header:"Dictionary",message:`Expected ${t} to be one of: Null, Undefined, Object.`})}for(const n of e){const{key:e,defaultValue:r,required:A,converter:a}=n;if(A===true){if(!i(t,e)){throw o.errors.exception({header:"Dictionary",message:`Missing required key "${e}".`})}}let c=t[e];const u=i(n,"defaultValue");if(u&&c!==null){c=c??r}if(A||u||c!==undefined){c=a(c);if(n.allowedValues&&!n.allowedValues.includes(c)){throw o.errors.exception({header:"Dictionary",message:`${c} is not an accepted type. Expected one of ${n.allowedValues.join(", ")}.`})}s[e]=c}}return s}};o.nullableConverter=function(e){return t=>{if(t===null){return t}return e(t)}};o.converters.DOMString=function(e,t={}){if(e===null&&t.legacyNullToEmptyString){return""}if(typeof e==="symbol"){throw new TypeError("Could not convert argument of type symbol to string.")}return String(e)};o.converters.ByteString=function(e){const t=o.converters.DOMString(e);for(let e=0;e255){throw new TypeError("Cannot convert argument to a ByteString because the character at "+`index ${e} has a value of ${n} which is greater than 255.`)}}return t};o.converters.USVString=r;o.converters.boolean=function(e){const t=Boolean(e);return t};o.converters.any=function(e){return e};o.converters["long long"]=function(e){const t=o.util.ConvertToInt(e,64,"signed");return t};o.converters["unsigned long long"]=function(e){const t=o.util.ConvertToInt(e,64,"unsigned");return t};o.converters["unsigned long"]=function(e){const t=o.util.ConvertToInt(e,32,"unsigned");return t};o.converters["unsigned short"]=function(e,t){const n=o.util.ConvertToInt(e,16,"unsigned",t);return n};o.converters.ArrayBuffer=function(e,t={}){if(o.util.Type(e)!=="Object"||!s.isAnyArrayBuffer(e)){throw o.errors.conversionFailed({prefix:`${e}`,argument:`${e}`,types:["ArrayBuffer"]})}if(t.allowShared===false&&s.isSharedArrayBuffer(e)){throw o.errors.exception({header:"ArrayBuffer",message:"SharedArrayBuffer is not allowed."})}return e};o.converters.TypedArray=function(e,t,n={}){if(o.util.Type(e)!=="Object"||!s.isTypedArray(e)||e.constructor.name!==t.name){throw o.errors.conversionFailed({prefix:`${t.name}`,argument:`${e}`,types:[t.name]})}if(n.allowShared===false&&s.isSharedArrayBuffer(e.buffer)){throw o.errors.exception({header:"ArrayBuffer",message:"SharedArrayBuffer is not allowed."})}return e};o.converters.DataView=function(e,t={}){if(o.util.Type(e)!=="Object"||!s.isDataView(e)){throw o.errors.exception({header:"DataView",message:"Object is not a DataView."})}if(t.allowShared===false&&s.isSharedArrayBuffer(e.buffer)){throw o.errors.exception({header:"ArrayBuffer",message:"SharedArrayBuffer is not allowed."})}return e};o.converters.BufferSource=function(e,t={}){if(s.isAnyArrayBuffer(e)){return o.converters.ArrayBuffer(e,t)}if(s.isTypedArray(e)){return o.converters.TypedArray(e,e.constructor)}if(s.isDataView(e)){return o.converters.DataView(e,t)}throw new TypeError(`Could not convert ${e} to a BufferSource.`)};o.converters["sequence"]=o.sequenceConverter(o.converters.ByteString);o.converters["sequence>"]=o.sequenceConverter(o.converters["sequence"]);o.converters["record"]=o.recordConverter(o.converters.ByteString,o.converters.ByteString);e.exports={webidl:o}},4854:e=>{"use strict";function getEncoding(e){if(!e){return"failure"}switch(e.trim().toLowerCase()){case"unicode-1-1-utf-8":case"unicode11utf8":case"unicode20utf8":case"utf-8":case"utf8":case"x-unicode20utf8":return"UTF-8";case"866":case"cp866":case"csibm866":case"ibm866":return"IBM866";case"csisolatin2":case"iso-8859-2":case"iso-ir-101":case"iso8859-2":case"iso88592":case"iso_8859-2":case"iso_8859-2:1987":case"l2":case"latin2":return"ISO-8859-2";case"csisolatin3":case"iso-8859-3":case"iso-ir-109":case"iso8859-3":case"iso88593":case"iso_8859-3":case"iso_8859-3:1988":case"l3":case"latin3":return"ISO-8859-3";case"csisolatin4":case"iso-8859-4":case"iso-ir-110":case"iso8859-4":case"iso88594":case"iso_8859-4":case"iso_8859-4:1988":case"l4":case"latin4":return"ISO-8859-4";case"csisolatincyrillic":case"cyrillic":case"iso-8859-5":case"iso-ir-144":case"iso8859-5":case"iso88595":case"iso_8859-5":case"iso_8859-5:1988":return"ISO-8859-5";case"arabic":case"asmo-708":case"csiso88596e":case"csiso88596i":case"csisolatinarabic":case"ecma-114":case"iso-8859-6":case"iso-8859-6-e":case"iso-8859-6-i":case"iso-ir-127":case"iso8859-6":case"iso88596":case"iso_8859-6":case"iso_8859-6:1987":return"ISO-8859-6";case"csisolatingreek":case"ecma-118":case"elot_928":case"greek":case"greek8":case"iso-8859-7":case"iso-ir-126":case"iso8859-7":case"iso88597":case"iso_8859-7":case"iso_8859-7:1987":case"sun_eu_greek":return"ISO-8859-7";case"csiso88598e":case"csisolatinhebrew":case"hebrew":case"iso-8859-8":case"iso-8859-8-e":case"iso-ir-138":case"iso8859-8":case"iso88598":case"iso_8859-8":case"iso_8859-8:1988":case"visual":return"ISO-8859-8";case"csiso88598i":case"iso-8859-8-i":case"logical":return"ISO-8859-8-I";case"csisolatin6":case"iso-8859-10":case"iso-ir-157":case"iso8859-10":case"iso885910":case"l6":case"latin6":return"ISO-8859-10";case"iso-8859-13":case"iso8859-13":case"iso885913":return"ISO-8859-13";case"iso-8859-14":case"iso8859-14":case"iso885914":return"ISO-8859-14";case"csisolatin9":case"iso-8859-15":case"iso8859-15":case"iso885915":case"iso_8859-15":case"l9":return"ISO-8859-15";case"iso-8859-16":return"ISO-8859-16";case"cskoi8r":case"koi":case"koi8":case"koi8-r":case"koi8_r":return"KOI8-R";case"koi8-ru":case"koi8-u":return"KOI8-U";case"csmacintosh":case"mac":case"macintosh":case"x-mac-roman":return"macintosh";case"iso-8859-11":case"iso8859-11":case"iso885911":case"tis-620":case"windows-874":return"windows-874";case"cp1250":case"windows-1250":case"x-cp1250":return"windows-1250";case"cp1251":case"windows-1251":case"x-cp1251":return"windows-1251";case"ansi_x3.4-1968":case"ascii":case"cp1252":case"cp819":case"csisolatin1":case"ibm819":case"iso-8859-1":case"iso-ir-100":case"iso8859-1":case"iso88591":case"iso_8859-1":case"iso_8859-1:1987":case"l1":case"latin1":case"us-ascii":case"windows-1252":case"x-cp1252":return"windows-1252";case"cp1253":case"windows-1253":case"x-cp1253":return"windows-1253";case"cp1254":case"csisolatin5":case"iso-8859-9":case"iso-ir-148":case"iso8859-9":case"iso88599":case"iso_8859-9":case"iso_8859-9:1989":case"l5":case"latin5":case"windows-1254":case"x-cp1254":return"windows-1254";case"cp1255":case"windows-1255":case"x-cp1255":return"windows-1255";case"cp1256":case"windows-1256":case"x-cp1256":return"windows-1256";case"cp1257":case"windows-1257":case"x-cp1257":return"windows-1257";case"cp1258":case"windows-1258":case"x-cp1258":return"windows-1258";case"x-mac-cyrillic":case"x-mac-ukrainian":return"x-mac-cyrillic";case"chinese":case"csgb2312":case"csiso58gb231280":case"gb2312":case"gb_2312":case"gb_2312-80":case"gbk":case"iso-ir-58":case"x-gbk":return"GBK";case"gb18030":return"gb18030";case"big5":case"big5-hkscs":case"cn-big5":case"csbig5":case"x-x-big5":return"Big5";case"cseucpkdfmtjapanese":case"euc-jp":case"x-euc-jp":return"EUC-JP";case"csiso2022jp":case"iso-2022-jp":return"ISO-2022-JP";case"csshiftjis":case"ms932":case"ms_kanji":case"shift-jis":case"shift_jis":case"sjis":case"windows-31j":case"x-sjis":return"Shift_JIS";case"cseuckr":case"csksc56011987":case"euc-kr":case"iso-ir-149":case"korean":case"ks_c_5601-1987":case"ks_c_5601-1989":case"ksc5601":case"ksc_5601":case"windows-949":return"EUC-KR";case"csiso2022kr":case"hz-gb-2312":case"iso-2022-cn":case"iso-2022-cn-ext":case"iso-2022-kr":case"replacement":return"replacement";case"unicodefffe":case"utf-16be":return"UTF-16BE";case"csunicode":case"iso-10646-ucs-2":case"ucs-2":case"unicode":case"unicodefeff":case"utf-16":case"utf-16le":return"UTF-16LE";case"x-user-defined":return"x-user-defined";default:return"failure"}}e.exports={getEncoding:getEncoding}},1446:(e,t,n)=>{"use strict";const{staticPropertyDescriptors:s,readOperation:i,fireAProgressEvent:r}=n(7530);const{kState:o,kError:A,kResult:a,kEvents:c,kAborted:u}=n(9054);const{webidl:l}=n(1744);const{kEnumerableProperty:d}=n(3983);class FileReader extends EventTarget{constructor(){super();this[o]="empty";this[a]=null;this[A]=null;this[c]={loadend:null,error:null,abort:null,load:null,progress:null,loadstart:null}}readAsArrayBuffer(e){l.brandCheck(this,FileReader);l.argumentLengthCheck(arguments,1,{header:"FileReader.readAsArrayBuffer"});e=l.converters.Blob(e,{strict:false});i(this,e,"ArrayBuffer")}readAsBinaryString(e){l.brandCheck(this,FileReader);l.argumentLengthCheck(arguments,1,{header:"FileReader.readAsBinaryString"});e=l.converters.Blob(e,{strict:false});i(this,e,"BinaryString")}readAsText(e,t=undefined){l.brandCheck(this,FileReader);l.argumentLengthCheck(arguments,1,{header:"FileReader.readAsText"});e=l.converters.Blob(e,{strict:false});if(t!==undefined){t=l.converters.DOMString(t)}i(this,e,"Text",t)}readAsDataURL(e){l.brandCheck(this,FileReader);l.argumentLengthCheck(arguments,1,{header:"FileReader.readAsDataURL"});e=l.converters.Blob(e,{strict:false});i(this,e,"DataURL")}abort(){if(this[o]==="empty"||this[o]==="done"){this[a]=null;return}if(this[o]==="loading"){this[o]="done";this[a]=null}this[u]=true;r("abort",this);if(this[o]!=="loading"){r("loadend",this)}}get readyState(){l.brandCheck(this,FileReader);switch(this[o]){case"empty":return this.EMPTY;case"loading":return this.LOADING;case"done":return this.DONE}}get result(){l.brandCheck(this,FileReader);return this[a]}get error(){l.brandCheck(this,FileReader);return this[A]}get onloadend(){l.brandCheck(this,FileReader);return this[c].loadend}set onloadend(e){l.brandCheck(this,FileReader);if(this[c].loadend){this.removeEventListener("loadend",this[c].loadend)}if(typeof e==="function"){this[c].loadend=e;this.addEventListener("loadend",e)}else{this[c].loadend=null}}get onerror(){l.brandCheck(this,FileReader);return this[c].error}set onerror(e){l.brandCheck(this,FileReader);if(this[c].error){this.removeEventListener("error",this[c].error)}if(typeof e==="function"){this[c].error=e;this.addEventListener("error",e)}else{this[c].error=null}}get onloadstart(){l.brandCheck(this,FileReader);return this[c].loadstart}set onloadstart(e){l.brandCheck(this,FileReader);if(this[c].loadstart){this.removeEventListener("loadstart",this[c].loadstart)}if(typeof e==="function"){this[c].loadstart=e;this.addEventListener("loadstart",e)}else{this[c].loadstart=null}}get onprogress(){l.brandCheck(this,FileReader);return this[c].progress}set onprogress(e){l.brandCheck(this,FileReader);if(this[c].progress){this.removeEventListener("progress",this[c].progress)}if(typeof e==="function"){this[c].progress=e;this.addEventListener("progress",e)}else{this[c].progress=null}}get onload(){l.brandCheck(this,FileReader);return this[c].load}set onload(e){l.brandCheck(this,FileReader);if(this[c].load){this.removeEventListener("load",this[c].load)}if(typeof e==="function"){this[c].load=e;this.addEventListener("load",e)}else{this[c].load=null}}get onabort(){l.brandCheck(this,FileReader);return this[c].abort}set onabort(e){l.brandCheck(this,FileReader);if(this[c].abort){this.removeEventListener("abort",this[c].abort)}if(typeof e==="function"){this[c].abort=e;this.addEventListener("abort",e)}else{this[c].abort=null}}}FileReader.EMPTY=FileReader.prototype.EMPTY=0;FileReader.LOADING=FileReader.prototype.LOADING=1;FileReader.DONE=FileReader.prototype.DONE=2;Object.defineProperties(FileReader.prototype,{EMPTY:s,LOADING:s,DONE:s,readAsArrayBuffer:d,readAsBinaryString:d,readAsText:d,readAsDataURL:d,abort:d,readyState:d,result:d,error:d,onloadstart:d,onprogress:d,onload:d,onabort:d,onerror:d,onloadend:d,[Symbol.toStringTag]:{value:"FileReader",writable:false,enumerable:false,configurable:true}});Object.defineProperties(FileReader,{EMPTY:s,LOADING:s,DONE:s});e.exports={FileReader:FileReader}},5504:(e,t,n)=>{"use strict";const{webidl:s}=n(1744);const i=Symbol("ProgressEvent state");class ProgressEvent extends Event{constructor(e,t={}){e=s.converters.DOMString(e);t=s.converters.ProgressEventInit(t??{});super(e,t);this[i]={lengthComputable:t.lengthComputable,loaded:t.loaded,total:t.total}}get lengthComputable(){s.brandCheck(this,ProgressEvent);return this[i].lengthComputable}get loaded(){s.brandCheck(this,ProgressEvent);return this[i].loaded}get total(){s.brandCheck(this,ProgressEvent);return this[i].total}}s.converters.ProgressEventInit=s.dictionaryConverter([{key:"lengthComputable",converter:s.converters.boolean,defaultValue:false},{key:"loaded",converter:s.converters["unsigned long long"],defaultValue:0},{key:"total",converter:s.converters["unsigned long long"],defaultValue:0},{key:"bubbles",converter:s.converters.boolean,defaultValue:false},{key:"cancelable",converter:s.converters.boolean,defaultValue:false},{key:"composed",converter:s.converters.boolean,defaultValue:false}]);e.exports={ProgressEvent:ProgressEvent}},9054:e=>{"use strict";e.exports={kState:Symbol("FileReader state"),kResult:Symbol("FileReader result"),kError:Symbol("FileReader error"),kLastProgressEventFired:Symbol("FileReader last progress event fired timestamp"),kEvents:Symbol("FileReader events"),kAborted:Symbol("FileReader aborted")}},7530:(e,t,n)=>{"use strict";const{kState:s,kError:i,kResult:r,kAborted:o,kLastProgressEventFired:A}=n(9054);const{ProgressEvent:a}=n(5504);const{getEncoding:c}=n(4854);const{DOMException:u}=n(1037);const{serializeAMimeType:l,parseMIMEType:d}=n(685);const{types:p}=n(3837);const{StringDecoder:g}=n(1576);const{btoa:h}=n(4300);const f={enumerable:true,writable:false,configurable:false};function readOperation(e,t,n,a){if(e[s]==="loading"){throw new u("Invalid state","InvalidStateError")}e[s]="loading";e[r]=null;e[i]=null;const c=t.stream();const l=c.getReader();const d=[];let g=l.read();let h=true;(async()=>{while(!e[o]){try{const{done:c,value:u}=await g;if(h&&!e[o]){queueMicrotask((()=>{fireAProgressEvent("loadstart",e)}))}h=false;if(!c&&p.isUint8Array(u)){d.push(u);if((e[A]===undefined||Date.now()-e[A]>=50)&&!e[o]){e[A]=Date.now();queueMicrotask((()=>{fireAProgressEvent("progress",e)}))}g=l.read()}else if(c){queueMicrotask((()=>{e[s]="done";try{const s=packageData(d,n,t.type,a);if(e[o]){return}e[r]=s;fireAProgressEvent("load",e)}catch(t){e[i]=t;fireAProgressEvent("error",e)}if(e[s]!=="loading"){fireAProgressEvent("loadend",e)}}));break}}catch(t){if(e[o]){return}queueMicrotask((()=>{e[s]="done";e[i]=t;fireAProgressEvent("error",e);if(e[s]!=="loading"){fireAProgressEvent("loadend",e)}}));break}}})()}function fireAProgressEvent(e,t){const n=new a(e,{bubbles:false,cancelable:false});t.dispatchEvent(n)}function packageData(e,t,n,s){switch(t){case"DataURL":{let t="data:";const s=d(n||"application/octet-stream");if(s!=="failure"){t+=l(s)}t+=";base64,";const i=new g("latin1");for(const n of e){t+=h(i.write(n))}t+=h(i.end());return t}case"Text":{let t="failure";if(s){t=c(s)}if(t==="failure"&&n){const e=d(n);if(e!=="failure"){t=c(e.parameters.get("charset"))}}if(t==="failure"){t="UTF-8"}return decode(e,t)}case"ArrayBuffer":{const t=combineByteSequences(e);return t.buffer}case"BinaryString":{let t="";const n=new g("latin1");for(const s of e){t+=n.write(s)}t+=n.end();return t}}}function decode(e,t){const n=combineByteSequences(e);const s=BOMSniffing(n);let i=0;if(s!==null){t=s;i=s==="UTF-8"?3:2}const r=n.slice(i);return new TextDecoder(t).decode(r)}function BOMSniffing(e){const[t,n,s]=e;if(t===239&&n===187&&s===191){return"UTF-8"}else if(t===254&&n===255){return"UTF-16BE"}else if(t===255&&n===254){return"UTF-16LE"}return null}function combineByteSequences(e){const t=e.reduce(((e,t)=>e+t.byteLength),0);let n=0;return e.reduce(((e,t)=>{e.set(t,n);n+=t.byteLength;return e}),new Uint8Array(t))}e.exports={staticPropertyDescriptors:f,readOperation:readOperation,fireAProgressEvent:fireAProgressEvent}},1892:(e,t,n)=>{"use strict";const s=Symbol.for("undici.globalDispatcher.1");const{InvalidArgumentError:i}=n(8045);const r=n(7890);if(getGlobalDispatcher()===undefined){setGlobalDispatcher(new r)}function setGlobalDispatcher(e){if(!e||typeof e.dispatch!=="function"){throw new i("Argument agent must implement Agent")}Object.defineProperty(globalThis,s,{value:e,writable:true,enumerable:false,configurable:false})}function getGlobalDispatcher(){return globalThis[s]}e.exports={setGlobalDispatcher:setGlobalDispatcher,getGlobalDispatcher:getGlobalDispatcher}},6930:e=>{"use strict";e.exports=class DecoratorHandler{constructor(e){this.handler=e}onConnect(...e){return this.handler.onConnect(...e)}onError(...e){return this.handler.onError(...e)}onUpgrade(...e){return this.handler.onUpgrade(...e)}onHeaders(...e){return this.handler.onHeaders(...e)}onData(...e){return this.handler.onData(...e)}onComplete(...e){return this.handler.onComplete(...e)}onBodySent(...e){return this.handler.onBodySent(...e)}}},2860:(e,t,n)=>{"use strict";const s=n(3983);const{kBodyUsed:i}=n(2785);const r=n(9491);const{InvalidArgumentError:o}=n(8045);const A=n(2361);const a=[300,301,302,303,307,308];const c=Symbol("body");class BodyAsyncIterable{constructor(e){this[c]=e;this[i]=false}async*[Symbol.asyncIterator](){r(!this[i],"disturbed");this[i]=true;yield*this[c]}}class RedirectHandler{constructor(e,t,n,a){if(t!=null&&(!Number.isInteger(t)||t<0)){throw new o("maxRedirections must be a positive number")}s.validateHandler(a,n.method,n.upgrade);this.dispatch=e;this.location=null;this.abort=null;this.opts={...n,maxRedirections:0};this.maxRedirections=t;this.handler=a;this.history=[];if(s.isStream(this.opts.body)){if(s.bodyLength(this.opts.body)===0){this.opts.body.on("data",(function(){r(false)}))}if(typeof this.opts.body.readableDidRead!=="boolean"){this.opts.body[i]=false;A.prototype.on.call(this.opts.body,"data",(function(){this[i]=true}))}}else if(this.opts.body&&typeof this.opts.body.pipeTo==="function"){this.opts.body=new BodyAsyncIterable(this.opts.body)}else if(this.opts.body&&typeof this.opts.body!=="string"&&!ArrayBuffer.isView(this.opts.body)&&s.isIterable(this.opts.body)){this.opts.body=new BodyAsyncIterable(this.opts.body)}}onConnect(e){this.abort=e;this.handler.onConnect(e,{history:this.history})}onUpgrade(e,t,n){this.handler.onUpgrade(e,t,n)}onError(e){this.handler.onError(e)}onHeaders(e,t,n,i){this.location=this.history.length>=this.maxRedirections||s.isDisturbed(this.opts.body)?null:parseLocation(e,t);if(this.opts.origin){this.history.push(new URL(this.opts.path,this.opts.origin))}if(!this.location){return this.handler.onHeaders(e,t,n,i)}const{origin:r,pathname:o,search:A}=s.parseURL(new URL(this.location,this.opts.origin&&new URL(this.opts.path,this.opts.origin)));const a=A?`${o}${A}`:o;this.opts.headers=cleanRequestHeaders(this.opts.headers,e===303,this.opts.origin!==r);this.opts.path=a;this.opts.origin=r;this.opts.maxRedirections=0;this.opts.query=null;if(e===303&&this.opts.method!=="HEAD"){this.opts.method="GET";this.opts.body=null}}onData(e){if(this.location){}else{return this.handler.onData(e)}}onComplete(e){if(this.location){this.location=null;this.abort=null;this.dispatch(this.opts,this)}else{this.handler.onComplete(e)}}onBodySent(e){if(this.handler.onBodySent){this.handler.onBodySent(e)}}}function parseLocation(e,t){if(a.indexOf(e)===-1){return null}for(let e=0;e{"use strict";const s=n(2860);function createRedirectInterceptor({maxRedirections:e}){return t=>function Intercept(n,i){const{maxRedirections:r=e}=n;if(!r){return t(n,i)}const o=new s(t,r,n,i);n={...n,maxRedirections:0};return t(n,o)}}e.exports=createRedirectInterceptor},953:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.SPECIAL_HEADERS=t.HEADER_STATE=t.MINOR=t.MAJOR=t.CONNECTION_TOKEN_CHARS=t.HEADER_CHARS=t.TOKEN=t.STRICT_TOKEN=t.HEX=t.URL_CHAR=t.STRICT_URL_CHAR=t.USERINFO_CHARS=t.MARK=t.ALPHANUM=t.NUM=t.HEX_MAP=t.NUM_MAP=t.ALPHA=t.FINISH=t.H_METHOD_MAP=t.METHOD_MAP=t.METHODS_RTSP=t.METHODS_ICE=t.METHODS_HTTP=t.METHODS=t.LENIENT_FLAGS=t.FLAGS=t.TYPE=t.ERROR=void 0;const s=n(1891);var i;(function(e){e[e["OK"]=0]="OK";e[e["INTERNAL"]=1]="INTERNAL";e[e["STRICT"]=2]="STRICT";e[e["LF_EXPECTED"]=3]="LF_EXPECTED";e[e["UNEXPECTED_CONTENT_LENGTH"]=4]="UNEXPECTED_CONTENT_LENGTH";e[e["CLOSED_CONNECTION"]=5]="CLOSED_CONNECTION";e[e["INVALID_METHOD"]=6]="INVALID_METHOD";e[e["INVALID_URL"]=7]="INVALID_URL";e[e["INVALID_CONSTANT"]=8]="INVALID_CONSTANT";e[e["INVALID_VERSION"]=9]="INVALID_VERSION";e[e["INVALID_HEADER_TOKEN"]=10]="INVALID_HEADER_TOKEN";e[e["INVALID_CONTENT_LENGTH"]=11]="INVALID_CONTENT_LENGTH";e[e["INVALID_CHUNK_SIZE"]=12]="INVALID_CHUNK_SIZE";e[e["INVALID_STATUS"]=13]="INVALID_STATUS";e[e["INVALID_EOF_STATE"]=14]="INVALID_EOF_STATE";e[e["INVALID_TRANSFER_ENCODING"]=15]="INVALID_TRANSFER_ENCODING";e[e["CB_MESSAGE_BEGIN"]=16]="CB_MESSAGE_BEGIN";e[e["CB_HEADERS_COMPLETE"]=17]="CB_HEADERS_COMPLETE";e[e["CB_MESSAGE_COMPLETE"]=18]="CB_MESSAGE_COMPLETE";e[e["CB_CHUNK_HEADER"]=19]="CB_CHUNK_HEADER";e[e["CB_CHUNK_COMPLETE"]=20]="CB_CHUNK_COMPLETE";e[e["PAUSED"]=21]="PAUSED";e[e["PAUSED_UPGRADE"]=22]="PAUSED_UPGRADE";e[e["PAUSED_H2_UPGRADE"]=23]="PAUSED_H2_UPGRADE";e[e["USER"]=24]="USER"})(i=t.ERROR||(t.ERROR={}));var r;(function(e){e[e["BOTH"]=0]="BOTH";e[e["REQUEST"]=1]="REQUEST";e[e["RESPONSE"]=2]="RESPONSE"})(r=t.TYPE||(t.TYPE={}));var o;(function(e){e[e["CONNECTION_KEEP_ALIVE"]=1]="CONNECTION_KEEP_ALIVE";e[e["CONNECTION_CLOSE"]=2]="CONNECTION_CLOSE";e[e["CONNECTION_UPGRADE"]=4]="CONNECTION_UPGRADE";e[e["CHUNKED"]=8]="CHUNKED";e[e["UPGRADE"]=16]="UPGRADE";e[e["CONTENT_LENGTH"]=32]="CONTENT_LENGTH";e[e["SKIPBODY"]=64]="SKIPBODY";e[e["TRAILING"]=128]="TRAILING";e[e["TRANSFER_ENCODING"]=512]="TRANSFER_ENCODING"})(o=t.FLAGS||(t.FLAGS={}));var A;(function(e){e[e["HEADERS"]=1]="HEADERS";e[e["CHUNKED_LENGTH"]=2]="CHUNKED_LENGTH";e[e["KEEP_ALIVE"]=4]="KEEP_ALIVE"})(A=t.LENIENT_FLAGS||(t.LENIENT_FLAGS={}));var a;(function(e){e[e["DELETE"]=0]="DELETE";e[e["GET"]=1]="GET";e[e["HEAD"]=2]="HEAD";e[e["POST"]=3]="POST";e[e["PUT"]=4]="PUT";e[e["CONNECT"]=5]="CONNECT";e[e["OPTIONS"]=6]="OPTIONS";e[e["TRACE"]=7]="TRACE";e[e["COPY"]=8]="COPY";e[e["LOCK"]=9]="LOCK";e[e["MKCOL"]=10]="MKCOL";e[e["MOVE"]=11]="MOVE";e[e["PROPFIND"]=12]="PROPFIND";e[e["PROPPATCH"]=13]="PROPPATCH";e[e["SEARCH"]=14]="SEARCH";e[e["UNLOCK"]=15]="UNLOCK";e[e["BIND"]=16]="BIND";e[e["REBIND"]=17]="REBIND";e[e["UNBIND"]=18]="UNBIND";e[e["ACL"]=19]="ACL";e[e["REPORT"]=20]="REPORT";e[e["MKACTIVITY"]=21]="MKACTIVITY";e[e["CHECKOUT"]=22]="CHECKOUT";e[e["MERGE"]=23]="MERGE";e[e["M-SEARCH"]=24]="M-SEARCH";e[e["NOTIFY"]=25]="NOTIFY";e[e["SUBSCRIBE"]=26]="SUBSCRIBE";e[e["UNSUBSCRIBE"]=27]="UNSUBSCRIBE";e[e["PATCH"]=28]="PATCH";e[e["PURGE"]=29]="PURGE";e[e["MKCALENDAR"]=30]="MKCALENDAR";e[e["LINK"]=31]="LINK";e[e["UNLINK"]=32]="UNLINK";e[e["SOURCE"]=33]="SOURCE";e[e["PRI"]=34]="PRI";e[e["DESCRIBE"]=35]="DESCRIBE";e[e["ANNOUNCE"]=36]="ANNOUNCE";e[e["SETUP"]=37]="SETUP";e[e["PLAY"]=38]="PLAY";e[e["PAUSE"]=39]="PAUSE";e[e["TEARDOWN"]=40]="TEARDOWN";e[e["GET_PARAMETER"]=41]="GET_PARAMETER";e[e["SET_PARAMETER"]=42]="SET_PARAMETER";e[e["REDIRECT"]=43]="REDIRECT";e[e["RECORD"]=44]="RECORD";e[e["FLUSH"]=45]="FLUSH"})(a=t.METHODS||(t.METHODS={}));t.METHODS_HTTP=[a.DELETE,a.GET,a.HEAD,a.POST,a.PUT,a.CONNECT,a.OPTIONS,a.TRACE,a.COPY,a.LOCK,a.MKCOL,a.MOVE,a.PROPFIND,a.PROPPATCH,a.SEARCH,a.UNLOCK,a.BIND,a.REBIND,a.UNBIND,a.ACL,a.REPORT,a.MKACTIVITY,a.CHECKOUT,a.MERGE,a["M-SEARCH"],a.NOTIFY,a.SUBSCRIBE,a.UNSUBSCRIBE,a.PATCH,a.PURGE,a.MKCALENDAR,a.LINK,a.UNLINK,a.PRI,a.SOURCE];t.METHODS_ICE=[a.SOURCE];t.METHODS_RTSP=[a.OPTIONS,a.DESCRIBE,a.ANNOUNCE,a.SETUP,a.PLAY,a.PAUSE,a.TEARDOWN,a.GET_PARAMETER,a.SET_PARAMETER,a.REDIRECT,a.RECORD,a.FLUSH,a.GET,a.POST];t.METHOD_MAP=s.enumToMap(a);t.H_METHOD_MAP={};Object.keys(t.METHOD_MAP).forEach((e=>{if(/^H/.test(e)){t.H_METHOD_MAP[e]=t.METHOD_MAP[e]}}));var c;(function(e){e[e["SAFE"]=0]="SAFE";e[e["SAFE_WITH_CB"]=1]="SAFE_WITH_CB";e[e["UNSAFE"]=2]="UNSAFE"})(c=t.FINISH||(t.FINISH={}));t.ALPHA=[];for(let e="A".charCodeAt(0);e<="Z".charCodeAt(0);e++){t.ALPHA.push(String.fromCharCode(e));t.ALPHA.push(String.fromCharCode(e+32))}t.NUM_MAP={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9};t.HEX_MAP={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15};t.NUM=["0","1","2","3","4","5","6","7","8","9"];t.ALPHANUM=t.ALPHA.concat(t.NUM);t.MARK=["-","_",".","!","~","*","'","(",")"];t.USERINFO_CHARS=t.ALPHANUM.concat(t.MARK).concat(["%",";",":","&","=","+","$",","]);t.STRICT_URL_CHAR=["!",'"',"$","%","&","'","(",")","*","+",",","-",".","/",":",";","<","=",">","@","[","\\","]","^","_","`","{","|","}","~"].concat(t.ALPHANUM);t.URL_CHAR=t.STRICT_URL_CHAR.concat(["\t","\f"]);for(let e=128;e<=255;e++){t.URL_CHAR.push(e)}t.HEX=t.NUM.concat(["a","b","c","d","e","f","A","B","C","D","E","F"]);t.STRICT_TOKEN=["!","#","$","%","&","'","*","+","-",".","^","_","`","|","~"].concat(t.ALPHANUM);t.TOKEN=t.STRICT_TOKEN.concat([" "]);t.HEADER_CHARS=["\t"];for(let e=32;e<=255;e++){if(e!==127){t.HEADER_CHARS.push(e)}}t.CONNECTION_TOKEN_CHARS=t.HEADER_CHARS.filter((e=>e!==44));t.MAJOR=t.NUM_MAP;t.MINOR=t.MAJOR;var u;(function(e){e[e["GENERAL"]=0]="GENERAL";e[e["CONNECTION"]=1]="CONNECTION";e[e["CONTENT_LENGTH"]=2]="CONTENT_LENGTH";e[e["TRANSFER_ENCODING"]=3]="TRANSFER_ENCODING";e[e["UPGRADE"]=4]="UPGRADE";e[e["CONNECTION_KEEP_ALIVE"]=5]="CONNECTION_KEEP_ALIVE";e[e["CONNECTION_CLOSE"]=6]="CONNECTION_CLOSE";e[e["CONNECTION_UPGRADE"]=7]="CONNECTION_UPGRADE";e[e["TRANSFER_ENCODING_CHUNKED"]=8]="TRANSFER_ENCODING_CHUNKED"})(u=t.HEADER_STATE||(t.HEADER_STATE={}));t.SPECIAL_HEADERS={connection:u.CONNECTION,"content-length":u.CONTENT_LENGTH,"proxy-connection":u.CONNECTION,"transfer-encoding":u.TRANSFER_ENCODING,upgrade:u.UPGRADE}},1145:e=>{e.exports="AGFzbQEAAAABMAhgAX8Bf2ADf39/AX9gBH9/f38Bf2AAAGADf39/AGABfwBgAn9/AGAGf39/f39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQACA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAA0ZFAwMEAAAFAAAAAAAABQEFAAUFBQAABgAAAAAGBgYGAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAABAQcAAAUFAwABBAUBcAESEgUDAQACBggBfwFBgNQECwfRBSIGbWVtb3J5AgALX2luaXRpYWxpemUACRlfX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlAQALbGxodHRwX2luaXQAChhsbGh0dHBfc2hvdWxkX2tlZXBfYWxpdmUAQQxsbGh0dHBfYWxsb2MADAZtYWxsb2MARgtsbGh0dHBfZnJlZQANBGZyZWUASA9sbGh0dHBfZ2V0X3R5cGUADhVsbGh0dHBfZ2V0X2h0dHBfbWFqb3IADxVsbGh0dHBfZ2V0X2h0dHBfbWlub3IAEBFsbGh0dHBfZ2V0X21ldGhvZAARFmxsaHR0cF9nZXRfc3RhdHVzX2NvZGUAEhJsbGh0dHBfZ2V0X3VwZ3JhZGUAEwxsbGh0dHBfcmVzZXQAFA5sbGh0dHBfZXhlY3V0ZQAVFGxsaHR0cF9zZXR0aW5nc19pbml0ABYNbGxodHRwX2ZpbmlzaAAXDGxsaHR0cF9wYXVzZQAYDWxsaHR0cF9yZXN1bWUAGRtsbGh0dHBfcmVzdW1lX2FmdGVyX3VwZ3JhZGUAGhBsbGh0dHBfZ2V0X2Vycm5vABsXbGxodHRwX2dldF9lcnJvcl9yZWFzb24AHBdsbGh0dHBfc2V0X2Vycm9yX3JlYXNvbgAdFGxsaHR0cF9nZXRfZXJyb3JfcG9zAB4RbGxodHRwX2Vycm5vX25hbWUAHxJsbGh0dHBfbWV0aG9kX25hbWUAIBJs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ADcDACAAQShqQgA3AwAgAEEgakIANwMAIABBEGpCADcDACAAQQhqQgA3AwAgAEHdATYCHEEAC3sBAX8CQCAAKAIMIgMNAAJAIAAoAgRFDQAgACABNgIECwJAIAAgASACEMSAgIAAIgMNACAAKAIMDwsgACADNgIcQQAhAyAAKAIEIgFFDQAgACABIAIgACgCCBGBgICAAAAiAUUNACAAIAI2AhQgACABNgIMIAEhAwsgAwvk8wEDDn8DfgR/I4CAgIAAQRBrIgMkgICAgAAgASEEIAEhBSABIQYgASEHIAEhCCABIQkgASEKIAEhCyABIQwgASENIAEhDiABIQ8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgACgCHCIQQX9qDt0B2gEB2QECAwQFBgcICQoLDA0O2AEPENcBERLWARMUFRYXGBkaG+AB3wEcHR7VAR8gISIjJCXUASYnKCkqKyzTAdIBLS7RAdABLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVG2wFHSElKzwHOAUvNAUzMAU1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4ABgQGCAYMBhAGFAYYBhwGIAYkB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QAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAQDscBAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxweHyAhIyUoP0BBREVGR0hJSktMTU9QUVJT3gNXWVtcXWBiZWZnaGlqa2xtb3BxcnN0dXZ3eHl6e3x9foABggGFAYYBhwGJAYsBjAGNAY4BjwGQAZEBlAGVAZYBlwGYAZkBmgGbAZwBnQGeAZ8BoAGhAaIBowGkAaUBpgGnAagBqQGqAasBrAGtAa4BrwGwAbEBsgGzAbQBtQG2AbcBuAG5AboBuwG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBxwHIAckBygHLAcwBzQHOAc8B0AHRAdIB0wHUAdUB1gHXAdgB2QHaAdsB3AHdAd4B4AHhAeIB4wHkAeUB5gHnAegB6QHqAesB7AHtAe4B7wHwAfEB8gHzAZkCpAKwAv4C/gILIAEiBCACRw3zAUHdASEQDP8DCyABIhAgAkcN3QFBwwEhEAz+AwsgASIBIAJHDZABQfcAIRAM/QMLIAEiASACRw2GAUHvACEQDPwDCyABIgEgAkcNf0HqACEQDPsDCyABIgEgAkcNe0HoACEQDPoDCyABIgEgAkcNeEHmACEQDPkDCyABIgEgAkcNGkEYIRAM+AMLIAEiASACRw0UQRIhEAz3AwsgASIBIAJHDVlBxQAhEAz2AwsgASIBIAJHDUpBPyEQDPUDCyABIgEgAkcNSEE8IRAM9AMLIAEiASACRw1BQTEhEAzzAwsgAC0ALkEBRg3rAwyHAgsgACABIgEgAhDAgICAAEEBRw3mASAAQgA3AyAM5wELIAAgASIBIAIQtICAgAAiEA3nASABIQEM9QILAkAgASIBIAJHDQBBBiEQDPADCyAAIAFBAWoiASACELuAgIAAIhAN6AEgASEBDDELIABCADcDIEESIRAM1QMLIAEiECACRw0rQR0hEAztAwsCQCABIgEgAkYNACABQQFqIQFBECEQDNQDC0EHIRAM7AMLIABCACAAKQMgIhEgAiABIhBrrSISfSITIBMgEVYbNwMgIBEgElYiFEUN5QFBCCEQDOsDCwJAIAEiASACRg0AIABBiYCAgAA2AgggACABNgIEIAEhAUEUIRAM0gMLQQkhEAzqAwsgASEBIAApAyBQDeQBIAEhAQzyAgsCQCABIgEgAkcNAEELIRAM6QMLIAAgAUEBaiIBIAIQtoCAgAAiEA3lASABIQEM8gILIAAgASIBIAIQuICAgAAiEA3lASABIQEM8gILIAAgASIBIAIQuICAgAAiEA3mASABIQEMDQsgACABIgEgAhC6gICAACIQDecBIAEhAQzwAgsCQCABIgEgAkcNAEEPIRAM5QMLIAEtAAAiEEE7Rg0IIBBBDUcN6AEgAUEBaiEBDO8CCyAAIAEiASACELqAgIAAIhAN6AEgASEBDPICCwNAAkAgAS0AAEHwtYCAAGotAAAiEEEBRg0AIBBBAkcN6wEgACgCBCEQIABBADYCBCAAIBAgAUEBaiIBELmAgIAAIhAN6gEgASEBDPQCCyABQQFqIgEgAkcNAAtBEiEQDOIDCyAAIAEiASACELqAgIAAIhAN6QEgASEBDAoLIAEiASACRw0GQRshEAzgAwsCQCABIgEgAkcNAEEWIRAM4AMLIABBioCAgAA2AgggACABNgIEIAAgASACELiAgIAAIhAN6gEgASEBQSAhEAzGAwsCQCABIgEgAkYNAANAAkAgAS0AAEHwt4CAAGotAAAiEEECRg0AAkAgEEF/ag4E5QHsAQDrAewBCyABQQFqIQFBCCEQDMgDCyABQQFqIgEgAkcNAAtBFSEQDN8DC0EVIRAM3gMLA0ACQCABLQAAQfC5gIAAai0AACIQQQJGDQAgEEF/ag4E3gHsAeAB6wHsAQsgAUEBaiIBIAJHDQALQRghEAzdAwsCQCABIgEgAkYNACAAQYuAgIAANgIIIAAgATYCBCABIQFBByEQDMQDC0EZIRAM3AMLIAFBAWohAQwCCwJAIAEiFCACRw0AQRohEAzbAwsgFCEBAkAgFC0AAEFzag4U3QLuAu4C7gLuAu4C7gLuAu4C7gLuAu4C7gLuAu4C7gLuAu4C7gIA7gILQQAhECAAQQA2AhwgAEGvi4CAADYCECAAQQI2AgwgACAUQQFqNgIUDNoDCwJAIAEtAAAiEEE7Rg0AIBBBDUcN6AEgAUEBaiEBDOUCCyABQQFqIQELQSIhEAy/AwsCQCABIhAgAkcNAEEcIRAM2AMLQgAhESAQIQEgEC0AAEFQag435wHmAQECAwQFBgcIAAAAAAAAAAkKCwwNDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxAREhMUAAtBHiEQDL0DC0ICIREM5QELQgMhEQzkAQtCBCERDOMBC0IFIREM4gELQgYhEQzhAQtCByERDOABC0IIIREM3wELQgkhEQzeAQtCCiERDN0BC0ILIREM3AELQgwhEQzbAQtCDSERDNoBC0IOIREM2QELQg8hEQzYAQtCCiERDNcBC0ILIREM1gELQgwhEQzVAQtCDSERDNQBC0IOIREM0wELQg8hEQzSAQtCACERAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAQLQAAQVBqDjflAeQBAAECAwQFBgfmAeYB5gHmAeYB5gHmAQgJCgsMDeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gEODxAREhPmAQtCAiERDOQBC0IDIREM4wELQgQhEQziAQtCBSERDOEBC0IGIREM4AELQgchEQzfAQtCCCERDN4BC0IJIREM3QELQgohEQzcAQtCCyERDNsBC0IMIREM2gELQg0hEQzZAQtCDiERDNgBC0IPIREM1wELQgohEQzWAQtCCyERDNUBC0IMIREM1AELQg0hEQzTAQtCDiERDNIBC0IPIREM0QELIABCACAAKQMgIhEgAiABIhBrrSISfSITIBMgEVYbNwMgIBEgElYiFEUN0gFBHyEQDMADCwJAIAEiASACRg0AIABBiYCAgAA2AgggACABNgIEIAEhAUEkIRAMpwMLQSAhEAy/AwsgACABIhAgAhC+gICAAEF/ag4FtgEAxQIB0QHSAQtBESEQDKQDCyAAQQE6AC8gECEBDLsDCyABIgEgAkcN0gFBJCEQDLsDCyABIg0gAkcNHkHGACEQDLoDCyAAIAEiASACELKAgIAAIhAN1AEgASEBDLUBCyABIhAgAkcNJkHQACEQDLgDCwJAIAEiASACRw0AQSghEAy4AwsgAEEANgIEIABBjICAgAA2AgggACABIAEQsYCAgAAiEA3TASABIQEM2AELAkAgASIQIAJHDQBBKSEQDLcDCyAQLQAAIgFBIEYNFCABQQlHDdMBIBBBAWohAQwVCwJAIAEiASACRg0AIAFBAWohAQwXC0EqIRAMtQMLAkAgASIQIAJHDQBBKyEQDLUDCwJAIBAtAAAiAUEJRg0AIAFBIEcN1QELIAAtACxBCEYN0wEgECEBDJEDCwJAIAEiASACRw0AQSwhEAy0AwsgAS0AAEEKRw3VASABQQFqIQEMyQILIAEiDiACRw3VAUEvIRAMsgMLA0ACQCABLQAAIhBBIEYNAAJAIBBBdmoOBADcAdwBANoBCyABIQEM4AELIAFBAWoiASACRw0AC0ExIRAMsQMLQTIhECABIhQgAkYNsAMgAiAUayAAKAIAIgFqIRUgFCABa0EDaiEWAkADQCAULQAAIhdBIHIgFyAXQb9/akH/AXFBGkkbQf8BcSABQfC7gIAAai0AAEcNAQJAIAFBA0cNAEEGIQEMlgMLIAFBAWohASAUQQFqIhQgAkcNAAsgACAVNgIADLEDCyAAQQA2AgAgFCEBDNkBC0EzIRAgASIUIAJGDa8DIAIgFGsgACgCACIBaiEVIBQgAWtBCGohFgJAA0AgFC0AACIXQSByIBcgF0G/f2pB/wFxQRpJG0H/AXEgAUH0u4CAAGotAABHDQECQCABQQhHDQBBBSEBDJUDCyABQQFqIQEgFEEBaiIUIAJHDQALIAAgFTYCAAywAwsgAEEANgIAIBQhAQzYAQtBNCEQIAEiFCACRg2uAyACIBRrIAAoAgAiAWohFSAUIAFrQQVqIRYCQANAIBQtAAAiF0EgciAXIBdBv39qQf8BcUEaSRtB/wFxIAFB0MKAgABqLQAARw0BAkAgAUEFRw0AQQchAQyUAwsgAUEBaiEBIBRBAWoiFCACRw0ACyAAIBU2AgAMrwMLIABBADYCACAUIQEM1wELAkAgASIBIAJGDQADQAJAIAEtAABBgL6AgABqLQAAIhBBAUYNACAQQQJGDQogASEBDN0BCyABQQFqIgEgAkcNAAtBMCEQDK4DC0EwIRAMrQMLAkAgASIBIAJGDQADQAJAIAEtAAAiEEEgRg0AIBBBdmoOBNkB2gHaAdkB2gELIAFBAWoiASACRw0AC0E4IRAMrQMLQTghEAysAwsDQAJAIAEtAAAiEEEgRg0AIBBBCUcNAwsgAUEBaiIBIAJHDQALQTwhEAyrAwsDQAJAIAEtAAAiEEEgRg0AAkACQCAQQXZqDgTaAQEB2gEACyAQQSxGDdsBCyABIQEMBAsgAUEBaiIBIAJHDQALQT8hEAyqAwsgASEBDNsBC0HAACEQIAEiFCACRg2oAyACIBRrIAAoAgAiAWohFiAUIAFrQQZqIRcCQANAIBQtAABBIHIgAUGAwICAAGotAABHDQEgAUEGRg2OAyABQQFqIQEgFEEBaiIUIAJHDQALIAAgFjYCAAypAwsgAEEANgIAIBQhAQtBNiEQDI4DCwJAIAEiDyACRw0AQcEAIRAMpwMLIABBjICAgAA2AgggACAPNgIEIA8hASAALQAsQX9qDgTNAdUB1wHZAYcDCyABQQFqIQEMzAELAkAgASIBIAJGDQADQAJAIAEtAAAiEEEgciAQIBBBv39qQf8BcUEaSRtB/wFxIhBBCUYNACAQQSBGDQACQAJAAkACQCAQQZ1/ag4TAAMDAwMDAwMBAwMDAwMDAwMDAgMLIAFBAWohAUExIRAMkQMLIAFBAWohAUEyIRAMkAMLIAFBAWohAUEzIRAMjwMLIAEhAQzQAQsgAUEBaiIBIAJHDQALQTUhEAylAwtBNSEQDKQDCwJAIAEiASACRg0AA0ACQCABLQAAQYC8gIAAai0AAEEBRg0AIAEhAQzTAQsgAUEBaiIBIAJHDQALQT0hEAykAwtBPSEQDKMDCyAAIAEiASACELCAgIAAIhAN1gEgASEBDAELIBBBAWohAQtBPCEQDIcDCwJAIAEiASACRw0AQcIAIRAMoAMLAkADQAJAIAEtAABBd2oOGAAC/gL+AoQD/gL+Av4C/gL+Av4C/gL+Av4C/gL+Av4C/gL+Av4C/gL+Av4CAP4CCyABQQFqIgEgAkcNAAtBwgAhEAygAwsgAUEBaiEBIAAtAC1BAXFFDb0BIAEhAQtBLCEQDIUDCyABIgEgAkcN0wFBxAAhEAydAwsDQAJAIAEtAABBkMCAgABqLQAAQQFGDQAgASEBDLcCCyABQQFqIgEgAkcNAAtBxQAhEAycAwsgDS0AACIQQSBGDbMBIBBBOkcNgQMgACgCBCEBIABBADYCBCAAIAEgDRCvgICAACIBDdABIA1BAWohAQyzAgtBxwAhECABIg0gAkYNmgMgAiANayAAKAIAIgFqIRYgDSABa0EFaiEXA0AgDS0AACIUQSByIBQgFEG/f2pB/wFxQRpJG0H/AXEgAUGQwoCAAGotAABHDYADIAFBBUYN9AIgAUEBaiEBIA1BAWoiDSACRw0ACyAAIBY2AgAMmgMLQcgAIRAgASINIAJGDZkDIAIgDWsgACgCACIBaiEWIA0gAWtBCWohFwNAIA0tAAAiFEEgciAUIBRBv39qQf8BcUEaSRtB/wFxIAFBlsKAgABqLQAARw3/AgJAIAFBCUcNAEECIQEM9QILIAFBAWohASANQQFqIg0gAkcNAAsgACAWNgIADJkDCwJAIAEiDSACRw0AQckAIRAMmQMLAkACQCANLQAAIgFBIHIgASABQb9/akH/AXFBGkkbQf8BcUGSf2oOBwCAA4ADgAOAA4ADAYADCyANQQFqIQFBPiEQDIADCyANQQFqIQFBPyEQDP8CC0HKACEQIAEiDSACRg2XAyACIA1rIAAoAgAiAWohFiANIAFrQQFqIRcDQCANLQAAIhRBIHIgFCAUQb9/akH/AXFBGkkbQf8BcSABQaDCgIAAai0AAEcN/QIgAUEBRg3wAiABQQFqIQEgDUEBaiINIAJHDQALIAAgFjYCAAyXAwtBywAhECABIg0gAkYNlgMgAiANayAAKAIAIgFqIRYgDSABa0EOaiEXA0AgDS0AACIUQSByIBQgFEG/f2pB/wFxQRpJG0H/AXEgAUGiwoCAAGotAABHDfwCIAFBDkYN8AIgAUEBaiEBIA1BAWoiDSACRw0ACyAAIBY2AgAMlgMLQcwAIRAgASINIAJGDZUDIAIgDWsgACgCACIBaiEWIA0gAWtBD2ohFwNAIA0tAAAiFEEgciAUIBRBv39qQf8BcUEaSRtB/wFxIAFBwMKAgABqLQAARw37AgJAIAFBD0cNAEEDIQEM8QILIAFBAWohASANQQFqIg0gAkcNAAsgACAWNgIADJUDC0HNACEQIAEiDSACRg2UAyACIA1rIAAoAgAiAWohFiANIAFrQQVqIRcDQCANLQAAIhRBIHIgFCAUQb9/akH/AXFBGkkbQf8BcSABQdDCgIAAai0AAEcN+gICQCABQQVHDQBBBCEBDPACCyABQQFqIQEgDUEBaiINIAJHDQALIAAgFjYCAAyUAwsCQCABIg0gAkcNAEHOACEQDJQDCwJAAkACQAJAIA0tAAAiAUEgciABIAFBv39qQf8BcUEaSRtB/wFxQZ1/ag4TAP0C/QL9Av0C/QL9Av0C/QL9Av0C/QL9AgH9Av0C/QICA/0CCyANQQFqIQFBwQAhEAz9AgsgDUEBaiEBQcIAIRAM/AILIA1BAWohAUHDACEQDPsCCyANQQFqIQFBxAAhEAz6AgsCQCABIgEgAkYNACAAQY2AgIAANgIIIAAgATYCBCABIQFBxQAhEAz6AgtBzwAhEAySAwsgECEBAkACQCAQLQAAQXZqDgQBqAKoAgCoAgsgEEEBaiEBC0EnIRAM+AILAkAgASIBIAJHDQBB0QAhEAyRAwsCQCABLQAAQSBGDQAgASEBDI0BCyABQQFqIQEgAC0ALUEBcUUNxwEgASEBDIwBCyABIhcgAkcNyAFB0gAhEAyPAwtB0wAhECABIhQgAkYNjgMgAiAUayAAKAIAIgFqIRYgFCABa0EBaiEXA0AgFC0AACABQdbCgIAAai0AAEcNzAEgAUEBRg3HASABQQFqIQEgFEEBaiIUIAJHDQALIAAgFjYCAAyOAwsCQCABIgEgAkcNAEHVACEQDI4DCyABLQAAQQpHDcwBIAFBAWohAQzHAQsCQCABIgEgAkcNAEHWACEQDI0DCwJAAkAgAS0AAEF2ag4EAM0BzQEBzQELIAFBAWohAQzHAQsgAUEBaiEBQcoAIRAM8wILIAAgASIBIAIQroCAgAAiEA3LASABIQFBzQAhEAzyAgsgAC0AKUEiRg2FAwymAgsCQCABIgEgAkcNAEHbACEQDIoDC0EAIRRBASEXQQEhFkEAIRACQAJAAkACQAJAAkACQAJAAkAgAS0AAEFQag4K1AHTAQABAgMEBQYI1QELQQIhEAwGC0EDIRAMBQtBBCEQDAQLQQUhEAwDC0EGIRAMAgtBByEQDAELQQghEAtBACEXQQAhFkEAIRQMzAELQQkhEEEBIRRBACEXQQAhFgzLAQsCQCABIgEgAkcNAEHdACEQDIkDCyABLQAAQS5HDcwBIAFBAWohAQymAgsgASIBIAJHDcwBQd8AIRAMhwMLAkAgASIBIAJGDQAgAEGOgICAADYCCCAAIAE2AgQgASEBQdAAIRAM7gILQeAAIRAMhgMLQeEAIRAgASIBIAJGDYUDIAIgAWsgACgCACIUaiEWIAEgFGtBA2ohFwNAIAEtAAAgFEHiwoCAAGotAABHDc0BIBRBA0YNzAEgFEEBaiEUIAFBAWoiASACRw0ACyAAIBY2AgAMhQMLQeIAIRAgASIBIAJGDYQDIAIgAWsgACgCACIUaiEWIAEgFGtBAmohFwNAIAEtAAAgFEHmwoCAAGotAABHDcwBIBRBAkYNzgEgFEEBaiEUIAFBAWoiASACRw0ACyAAIBY2AgAMhAMLQeMAIRAgASIBIAJGDYMDIAIgAWsgACgCACIUaiEWIAEgFGtBA2ohFwNAIAEtAAAgFEHpwoCAAGotAABHDcsBIBRBA0YNzgEgFEEBaiEUIAFBAWoiASACRw0ACyAAIBY2AgAMgwMLAkAgASIBIAJHDQBB5QAhEAyDAwsgACABQQFqIgEgAhCogICAACIQDc0BIAEhAUHWACEQDOkCCwJAIAEiASACRg0AA0ACQCABLQAAIhBBIEYNAAJAAkACQCAQQbh/ag4LAAHPAc8BzwHPAc8BzwHPAc8BAs8BCyABQQFqIQFB0gAhEAztAgsgAUEBaiEBQdMAIRAM7AILIAFBAWohAUHUACEQDOsCCyABQQFqIgEgAkcNAAtB5AAhEAyCAwtB5AAhEAyBAwsDQAJAIAEtAABB8MKAgABqLQAAIhBBAUYNACAQQX5qDgPPAdAB0QHSAQsgAUEBaiIBIAJHDQALQeYAIRAMgAMLAkAgASIBIAJGDQAgAUEBaiEBDAMLQecAIRAM/wILA0ACQCABLQAAQfDEgIAAai0AACIQQQFGDQACQCAQQX5qDgTSAdMB1AEA1QELIAEhAUHXACEQDOcCCyABQQFqIgEgAkcNAAtB6AAhEAz+AgsCQCABIgEgAkcNAEHpACEQDP4CCwJAIAEtAAAiEEF2ag4augHVAdUBvAHVAdUB1QHVAdUB1QHVAdUB1QHVAdUB1QHVAdUB1QHVAdUB1QHKAdUB1QEA0wELIAFBAWohAQtBBiEQDOMCCwNAAkAgAS0AAEHwxoCAAGotAABBAUYNACABIQEMngILIAFBAWoiASACRw0AC0HqACEQDPsCCwJAIAEiASACRg0AIAFBAWohAQwDC0HrACEQDPoCCwJAIAEiASACRw0AQewAIRAM+gILIAFBAWohAQwBCwJAIAEiASACRw0AQe0AIRAM+QILIAFBAWohAQtBBCEQDN4CCwJAIAEiFCACRw0AQe4AIRAM9wILIBQhAQJAAkACQCAULQAAQfDIgIAAai0AAEF/ag4H1AHVAdYBAJwCAQLXAQsgFEEBaiEBDAoLIBRBAWohAQzNAQtBACEQIABBADYCHCAAQZuSgIAANgIQIABBBzYCDCAAIBRBAWo2AhQM9gILAkADQAJAIAEtAABB8MiAgABqLQAAIhBBBEYNAAJAAkAgEEF/ag4H0gHTAdQB2QEABAHZAQsgASEBQdoAIRAM4AILIAFBAWohAUHcACEQDN8CCyABQQFqIgEgAkcNAAtB7wAhEAz2AgsgAUEBaiEBDMsBCwJAIAEiFCACRw0AQfAAIRAM9QILIBQtAABBL0cN1AEgFEEBaiEBDAYLAkAgASIUIAJHDQBB8QAhEAz0AgsCQCAULQAAIgFBL0cNACAUQQFqIQFB3QAhEAzbAgsgAUF2aiIEQRZLDdMBQQEgBHRBiYCAAnFFDdMBDMoCCwJAIAEiASACRg0AIAFBAWohAUHeACEQDNoCC0HyACEQDPICCwJAIAEiFCACRw0AQfQAIRAM8gILIBQhAQJAIBQtAABB8MyAgABqLQAAQX9qDgPJApQCANQBC0HhACEQDNgCCwJAIAEiFCACRg0AA0ACQCAULQAAQfDKgIAAai0AACIBQQNGDQACQCABQX9qDgLLAgDVAQsgFCEBQd8AIRAM2gILIBRBAWoiFCACRw0AC0HzACEQDPECC0HzACEQDPACCwJAIAEiASACRg0AIABBj4CAgAA2AgggACABNgIEIAEhAUHgACEQDNcCC0H1ACEQDO8CCwJAIAEiASACRw0AQfYAIRAM7wILIABBj4CAgAA2AgggACABNgIEIAEhAQtBAyEQDNQCCwNAIAEtAABBIEcNwwIgAUEBaiIBIAJHDQALQfcAIRAM7AILAkAgASIBIAJHDQBB+AAhEAzsAgsgAS0AAEEgRw3OASABQQFqIQEM7wELIAAgASIBIAIQrICAgAAiEA3OASABIQEMjgILAkAgASIEIAJHDQBB+gAhEAzqAgsgBC0AAEHMAEcN0QEgBEEBaiEBQRMhEAzPAQsCQCABIgQgAkcNAEH7ACEQDOkCCyACIARrIAAoAgAiAWohFCAEIAFrQQVqIRADQCAELQAAIAFB8M6AgABqLQAARw3QASABQQVGDc4BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQfsAIRAM6AILAkAgASIEIAJHDQBB/AAhEAzoAgsCQAJAIAQtAABBvX9qDgwA0QHRAdEB0QHRAdEB0QHRAdEB0QEB0QELIARBAWohAUHmACEQDM8CCyAEQQFqIQFB5wAhEAzOAgsCQCABIgQgAkcNAEH9ACEQDOcCCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUHtz4CAAGotAABHDc8BIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEH9ACEQDOcCCyAAQQA2AgAgEEEBaiEBQRAhEAzMAQsCQCABIgQgAkcNAEH+ACEQDOYCCyACIARrIAAoAgAiAWohFCAEIAFrQQVqIRACQANAIAQtAAAgAUH2zoCAAGotAABHDc4BIAFBBUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEH+ACEQDOYCCyAAQQA2AgAgEEEBaiEBQRYhEAzLAQsCQCABIgQgAkcNAEH/ACEQDOUCCyACIARrIAAoAgAiAWohFCAEIAFrQQNqIRACQANAIAQtAAAgAUH8zoCAAGotAABHDc0BIAFBA0YNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEH/ACEQDOUCCyAAQQA2AgAgEEEBaiEBQQUhEAzKAQsCQCABIgQgAkcNAEGAASEQDOQCCyAELQAAQdkARw3LASAEQQFqIQFBCCEQDMkBCwJAIAEiBCACRw0AQYEBIRAM4wILAkACQCAELQAAQbJ/ag4DAMwBAcwBCyAEQQFqIQFB6wAhEAzKAgsgBEEBaiEBQewAIRAMyQILAkAgASIEIAJHDQBBggEhEAziAgsCQAJAIAQtAABBuH9qDggAywHLAcsBywHLAcsBAcsBCyAEQQFqIQFB6gAhEAzJAgsgBEEBaiEBQe0AIRAMyAILAkAgASIEIAJHDQBBgwEhEAzhAgsgAiAEayAAKAIAIgFqIRAgBCABa0ECaiEUAkADQCAELQAAIAFBgM+AgABqLQAARw3JASABQQJGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBA2AgBBgwEhEAzhAgtBACEQIABBADYCACAUQQFqIQEMxgELAkAgASIEIAJHDQBBhAEhEAzgAgsgAiAEayAAKAIAIgFqIRQgBCABa0EEaiEQAkADQCAELQAAIAFBg8+AgABqLQAARw3IASABQQRGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBhAEhEAzgAgsgAEEANgIAIBBBAWohAUEjIRAMxQELAkAgASIEIAJHDQBBhQEhEAzfAgsCQAJAIAQtAABBtH9qDggAyAHIAcgByAHIAcgBAcgBCyAEQQFqIQFB7wAhEAzGAgsgBEEBaiEBQfAAIRAMxQILAkAgASIEIAJHDQBBhgEhEAzeAgsgBC0AAEHFAEcNxQEgBEEBaiEBDIMCCwJAIAEiBCACRw0AQYcBIRAM3QILIAIgBGsgACgCACIBaiEUIAQgAWtBA2ohEAJAA0AgBC0AACABQYjPgIAAai0AAEcNxQEgAUEDRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQYcBIRAM3QILIABBADYCACAQQQFqIQFBLSEQDMIBCwJAIAEiBCACRw0AQYgBIRAM3AILIAIgBGsgACgCACIBaiEUIAQgAWtBCGohEAJAA0AgBC0AACABQdDPgIAAai0AAEcNxAEgAUEIRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQYgBIRAM3AILIABBADYCACAQQQFqIQFBKSEQDMEBCwJAIAEiASACRw0AQYkBIRAM2wILQQEhECABLQAAQd8ARw3AASABQQFqIQEMgQILAkAgASIEIAJHDQBBigEhEAzaAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQA0AgBC0AACABQYzPgIAAai0AAEcNwQEgAUEBRg2vAiABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGKASEQDNkCCwJAIAEiBCACRw0AQYsBIRAM2QILIAIgBGsgACgCACIBaiEUIAQgAWtBAmohEAJAA0AgBC0AACABQY7PgIAAai0AAEcNwQEgAUECRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQYsBIRAM2QILIABBADYCACAQQQFqIQFBAiEQDL4BCwJAIAEiBCACRw0AQYwBIRAM2AILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQfDPgIAAai0AAEcNwAEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQYwBIRAM2AILIABBADYCACAQQQFqIQFBHyEQDL0BCwJAIAEiBCACRw0AQY0BIRAM1wILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQfLPgIAAai0AAEcNvwEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQY0BIRAM1wILIABBADYCACAQQQFqIQFBCSEQDLwBCwJAIAEiBCACRw0AQY4BIRAM1gILAkACQCAELQAAQbd/ag4HAL8BvwG/Ab8BvwEBvwELIARBAWohAUH4ACEQDL0CCyAEQQFqIQFB+QAhEAy8AgsCQCABIgQgAkcNAEGPASEQDNUCCyACIARrIAAoAgAiAWohFCAEIAFrQQVqIRACQANAIAQtAAAgAUGRz4CAAGotAABHDb0BIAFBBUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGPASEQDNUCCyAAQQA2AgAgEEEBaiEBQRghEAy6AQsCQCABIgQgAkcNAEGQASEQDNQCCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUGXz4CAAGotAABHDbwBIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGQASEQDNQCCyAAQQA2AgAgEEEBaiEBQRchEAy5AQsCQCABIgQgAkcNAEGRASEQDNMCCyACIARrIAAoAgAiAWohFCAEIAFrQQZqIRACQANAIAQtAAAgAUGaz4CAAGotAABHDbsBIAFBBkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGRASEQDNMCCyAAQQA2AgAgEEEBaiEBQRUhEAy4AQsCQCABIgQgAkcNAEGSASEQDNICCyACIARrIAAoAgAiAWohFCAEIAFrQQVqIRACQANAIAQtAAAgAUGhz4CAAGotAABHDboBIAFBBUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGSASEQDNICCyAAQQA2AgAgEEEBaiEBQR4hEAy3AQsCQCABIgQgAkcNAEGTASEQDNECCyAELQAAQcwARw24ASAEQQFqIQFBCiEQDLYBCwJAIAQgAkcNAEGUASEQDNACCwJAAkAgBC0AAEG/f2oODwC5AbkBuQG5AbkBuQG5AbkBuQG5AbkBuQG5AQG5AQsgBEEBaiEBQf4AIRAMtwILIARBAWohAUH/ACEQDLYCCwJAIAQgAkcNAEGVASEQDM8CCwJAAkAgBC0AAEG/f2oOAwC4AQG4AQsgBEEBaiEBQf0AIRAMtgILIARBAWohBEGAASEQDLUCCwJAIAQgAkcNAEGWASEQDM4CCyACIARrIAAoAgAiAWohFCAEIAFrQQFqIRACQANAIAQtAAAgAUGnz4CAAGotAABHDbYBIAFBAUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGWASEQDM4CCyAAQQA2AgAgEEEBaiEBQQshEAyzAQsCQCAEIAJHDQBBlwEhEAzNAgsCQAJAAkACQCAELQAAQVNqDiMAuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AQG4AbgBuAG4AbgBArgBuAG4AQO4AQsgBEEBaiEBQfsAIRAMtgILIARBAWohAUH8ACEQDLUCCyAEQQFqIQRBgQEhEAy0AgsgBEEBaiEEQYIBIRAMswILAkAgBCACRw0AQZgBIRAMzAILIAIgBGsgACgCACIBaiEUIAQgAWtBBGohEAJAA0AgBC0AACABQanPgIAAai0AAEcNtAEgAUEERg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZgBIRAMzAILIABBADYCACAQQQFqIQFBGSEQDLEBCwJAIAQgAkcNAEGZASEQDMsCCyACIARrIAAoAgAiAWohFCAEIAFrQQVqIRACQANAIAQtAAAgAUGuz4CAAGotAABHDbMBIAFBBUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGZASEQDMsCCyAAQQA2AgAgEEEBaiEBQQYhEAywAQsCQCAEIAJHDQBBmgEhEAzKAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQAkADQCAELQAAIAFBtM+AgABqLQAARw2yASABQQFGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBmgEhEAzKAgsgAEEANgIAIBBBAWohAUEcIRAMrwELAkAgBCACRw0AQZsBIRAMyQILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQbbPgIAAai0AAEcNsQEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZsBIRAMyQILIABBADYCACAQQQFqIQFBJyEQDK4BCwJAIAQgAkcNAEGcASEQDMgCCwJAAkAgBC0AAEGsf2oOAgABsQELIARBAWohBEGGASEQDK8CCyAEQQFqIQRBhwEhEAyuAgsCQCAEIAJHDQBBnQEhEAzHAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQAkADQCAELQAAIAFBuM+AgABqLQAARw2vASABQQFGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBnQEhEAzHAgsgAEEANgIAIBBBAWohAUEmIRAMrAELAkAgBCACRw0AQZ4BIRAMxgILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQbrPgIAAai0AAEcNrgEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZ4BIRAMxgILIABBADYCACAQQQFqIQFBAyEQDKsBCwJAIAQgAkcNAEGfASEQDMUCCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUHtz4CAAGotAABHDa0BIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGfASEQDMUCCyAAQQA2AgAgEEEBaiEBQQwhEAyqAQsCQCAEIAJHDQBBoAEhEAzEAgsgAiAEayAAKAIAIgFqIRQgBCABa0EDaiEQAkADQCAELQAAIAFBvM+AgABqLQAARw2sASABQQNGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBoAEhEAzEAgsgAEEANgIAIBBBAWohAUENIRAMqQELAkAgBCACRw0AQaEBIRAMwwILAkACQCAELQAAQbp/ag4LAKwBrAGsAawBrAGsAawBrAGsAQGsAQsgBEEBaiEEQYsBIRAMqgILIARBAWohBEGMASEQDKkCCwJAIAQgAkcNAEGiASEQDMICCyAELQAAQdAARw2pASAEQQFqIQQM6QELAkAgBCACRw0AQaMBIRAMwQILAkACQCAELQAAQbd/ag4HAaoBqgGqAaoBqgEAqgELIARBAWohBEGOASEQDKgCCyAEQQFqIQFBIiEQDKYBCwJAIAQgAkcNAEGkASEQDMACCyACIARrIAAoAgAiAWohFCAEIAFrQQFqIRACQANAIAQtAAAgAUHAz4CAAGotAABHDagBIAFBAUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGkASEQDMACCyAAQQA2AgAgEEEBaiEBQR0hEAylAQsCQCAEIAJHDQBBpQEhEAy/AgsCQAJAIAQtAABBrn9qDgMAqAEBqAELIARBAWohBEGQASEQDKYCCyAEQQFqIQFBBCEQDKQBCwJAIAQgAkcNAEGmASEQDL4CCwJAAkACQAJAAkAgBC0AAEG/f2oOFQCqAaoBqgGqAaoBqgGqAaoBqgGqAQGqAaoBAqoBqgEDqgGqAQSqAQsgBEEBaiEEQYgBIRAMqAILIARBAWohBEGJASEQDKcCCyAEQQFqIQRBigEhEAymAgsgBEEBaiEEQY8BIRAMpQILIARBAWohBEGRASEQDKQCCwJAIAQgAkcNAEGnASEQDL0CCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUHtz4CAAGotAABHDaUBIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGnASEQDL0CCyAAQQA2AgAgEEEBaiEBQREhEAyiAQsCQCAEIAJHDQBBqAEhEAy8AgsgAiAEayAAKAIAIgFqIRQgBCABa0ECaiEQAkADQCAELQAAIAFBws+AgABqLQAARw2kASABQQJGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBqAEhEAy8AgsgAEEANgIAIBBBAWohAUEsIRAMoQELAkAgBCACRw0AQakBIRAMuwILIAIgBGsgACgCACIBaiEUIAQgAWtBBGohEAJAA0AgBC0AACABQcXPgIAAai0AAEcNowEgAUEERg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQakBIRAMuwILIABBADYCACAQQQFqIQFBKyEQDKABCwJAIAQgAkcNAEGqASEQDLoCCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUHKz4CAAGotAABHDaIBIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGqASEQDLoCCyAAQQA2AgAgEEEBaiEBQRQhEAyfAQsCQCAEIAJHDQBBqwEhEAy5AgsCQAJAAkACQCAELQAAQb5/ag4PAAECpAGkAaQBpAGkAaQBpAGkAaQBpAGkAQOkAQsgBEEBaiEEQZMBIRAMogILIARBAWohBEGUASEQDKECCyAEQQFqIQRBlQEhEAygAgsgBEEBaiEEQZYBIRAMnwILAkAgBCACRw0AQawBIRAMuAILIAQtAABBxQBHDZ8BIARBAWohBAzgAQsCQCAEIAJHDQBBrQEhEAy3AgsgAiAEayAAKAIAIgFqIRQgBCABa0ECaiEQAkADQCAELQAAIAFBzc+AgABqLQAARw2fASABQQJGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBrQEhEAy3AgsgAEEANgIAIBBBAWohAUEOIRAMnAELAkAgBCACRw0AQa4BIRAMtgILIAQtAABB0ABHDZ0BIARBAWohAUElIRAMmwELAkAgBCACRw0AQa8BIRAMtQILIAIgBGsgACgCACIBaiEUIAQgAWtBCGohEAJAA0AgBC0AACABQdDPgIAAai0AAEcNnQEgAUEIRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQa8BIRAMtQILIABBADYCACAQQQFqIQFBKiEQDJoBCwJAIAQgAkcNAEGwASEQDLQCCwJAAkAgBC0AAEGrf2oOCwCdAZ0BnQGdAZ0BnQGdAZ0BnQEBnQELIARBAWohBEGaASEQDJsCCyAEQQFqIQRBmwEhEAyaAgsCQCAEIAJHDQBBsQEhEAyzAgsCQAJAIAQtAABBv39qDhQAnAGcAZwBnAGcAZwBnAGcAZwBnAGcAZwBnAGcAZwBnAGcAZwBAZwBCyAEQQFqIQRBmQEhEAyaAgsgBEEBaiEEQZwBIRAMmQILAkAgBCACRw0AQbIBIRAMsgILIAIgBGsgACgCACIBaiEUIAQgAWtBA2ohEAJAA0AgBC0AACABQdnPgIAAai0AAEcNmgEgAUEDRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQbIBIRAMsgILIABBADYCACAQQQFqIQFBISEQDJcBCwJAIAQgAkcNAEGzASEQDLECCyACIARrIAAoAgAiAWohFCAEIAFrQQZqIRACQANAIAQtAAAgAUHdz4CAAGotAABHDZkBIAFBBkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGzASEQDLECCyAAQQA2AgAgEEEBaiEBQRohEAyWAQsCQCAEIAJHDQBBtAEhEAywAgsCQAJAAkAgBC0AAEG7f2oOEQCaAZoBmgGaAZoBmgGaAZoBmgEBmgGaAZoBmgGaAQKaAQsgBEEBaiEEQZ0BIRAMmAILIARBAWohBEGeASEQDJcCCyAEQQFqIQRBnwEhEAyWAgsCQCAEIAJHDQBBtQEhEAyvAgsgAiAEayAAKAIAIgFqIRQgBCABa0EFaiEQAkADQCAELQAAIAFB5M+AgABqLQAARw2XASABQQVGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBtQEhEAyvAgsgAEEANgIAIBBBAWohAUEoIRAMlAELAkAgBCACRw0AQbYBIRAMrgILIAIgBGsgACgCACIBaiEUIAQgAWtBAmohEAJAA0AgBC0AACABQerPgIAAai0AAEcNlgEgAUECRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQbYBIRAMrgILIABBADYCACAQQQFqIQFBByEQDJMBCwJAIAQgAkcNAEG3ASEQDK0CCwJAAkAgBC0AAEG7f2oODgCWAZYBlgGWAZYBlgGWAZYBlgGWAZYBlgEBlgELIARBAWohBEGhASEQDJQCCyAEQQFqIQRBogEhEAyTAgsCQCAEIAJHDQBBuAEhEAysAgsgAiAEayAAKAIAIgFqIRQgBCABa0ECaiEQAkADQCAELQAAIAFB7c+AgABqLQAARw2UASABQQJGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBuAEhEAysAgsgAEEANgIAIBBBAWohAUESIRAMkQELAkAgBCACRw0AQbkBIRAMqwILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQfDPgIAAai0AAEcNkwEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQbkBIRAMqwILIABBADYCACAQQQFqIQFBICEQDJABCwJAIAQgAkcNAEG6ASEQDKoCCyACIARrIAAoAgAiAWohFCAEIAFrQQFqIRACQANAIAQtAAAgAUHyz4CAAGotAABHDZIBIAFBAUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEG6ASEQDKoCCyAAQQA2AgAgEEEBaiEBQQ8hEAyPAQsCQCAEIAJHDQBBuwEhEAypAgsCQAJAIAQtAABBt39qDgcAkgGSAZIBkgGSAQGSAQsgBEEBaiEEQaUBIRAMkAILIARBAWohBEGmASEQDI8CCwJAIAQgAkcNAEG8ASEQDKgCCyACIARrIAAoAgAiAWohFCAEIAFrQQdqIRACQANAIAQtAAAgAUH0z4CAAGotAABHDZABIAFBB0YNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEG8ASEQDKgCCyAAQQA2AgAgEEEBaiEBQRshEAyNAQsCQCAEIAJHDQBBvQEhEAynAgsCQAJAAkAgBC0AAEG+f2oOEgCRAZEBkQGRAZEBkQGRAZEBkQEBkQGRAZEBkQGRAZEBApEBCyAEQQFqIQRBpAEhEAyPAgsgBEEBaiEEQacBIRAMjgILIARBAWohBEGoASEQDI0CCwJAIAQgAkcNAEG+ASEQDKYCCyAELQAAQc4ARw2NASAEQQFqIQQMzwELAkAgBCACRw0AQb8BIRAMpQILAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgBC0AAEG/f2oOFQABAgOcAQQFBpwBnAGcAQcICQoLnAEMDQ4PnAELIARBAWohAUHoACEQDJoCCyAEQQFqIQFB6QAhEAyZAgsgBEEBaiEBQe4AIRAMmAILIARBAWohAUHyACEQDJcCCyAEQQFqIQFB8wAhEAyWAgsgBEEBaiEBQfYAIRAMlQILIARBAWohAUH3ACEQDJQCCyAEQQFqIQFB+gAhEAyTAgsgBEEBaiEEQYMBIRAMkgILIARBAWohBEGEASEQDJECCyAEQQFqIQRBhQEhEAyQAgsgBEEBaiEEQZIBIRAMjwILIARBAWohBEGYASEQDI4CCyAEQQFqIQRBoAEhEAyNAgsgBEEBaiEEQaMBIRAMjAILIARBAWohBEGqASEQDIsCCwJAIAQgAkYNACAAQZCAgIAANgIIIAAgBDYCBEGrASEQDIsCC0HAASEQDKMCCyAAIAUgAhCqgICAACIBDYsBIAUhAQxcCwJAIAYgAkYNACAGQQFqIQUMjQELQcIBIRAMoQILA0ACQCAQLQAAQXZqDgSMAQAAjwEACyAQQQFqIhAgAkcNAAtBwwEhEAygAgsCQCAHIAJGDQAgAEGRgICAADYCCCAAIAc2AgQgByEBQQEhEAyHAgtBxAEhEAyfAgsCQCAHIAJHDQBBxQEhEAyfAgsCQAJAIActAABBdmoOBAHOAc4BAM4BCyAHQQFqIQYMjQELIAdBAWohBQyJAQsCQCAHIAJHDQBBxgEhEAyeAgsCQAJAIActAABBdmoOFwGPAY8BAY8BjwGPAY8BjwGPAY8BjwGPAY8BjwGPAY8BjwGPAY8BjwGPAQCPAQsgB0EBaiEHC0GwASEQDIQCCwJAIAggAkcNAEHIASEQDJ0CCyAILQAAQSBHDY0BIABBADsBMiAIQQFqIQFBswEhEAyDAgsgASEXAkADQCAXIgcgAkYNASAHLQAAQVBqQf8BcSIQQQpPDcwBAkAgAC8BMiIUQZkzSw0AIAAgFEEKbCIUOwEyIBBB//8DcyAUQf7/A3FJDQAgB0EBaiEXIAAgFCAQaiIQOwEyIBBB//8DcUHoB0kNAQsLQQAhECAAQQA2AhwgAEHBiYCAADYCECAAQQ02AgwgACAHQQFqNgIUDJwCC0HHASEQDJsCCyAAIAggAhCugICAACIQRQ3KASAQQRVHDYwBIABByAE2AhwgACAINgIUIABByZeAgAA2AhAgAEEVNgIMQQAhEAyaAgsCQCAJIAJHDQBBzAEhEAyaAgtBACEUQQEhF0EBIRZBACEQAkACQAJAAkACQAJAAkACQAJAIAktAABBUGoOCpYBlQEAAQIDBAUGCJcBC0ECIRAMBgtBAyEQDAULQQQhEAwEC0EFIRAMAwtBBiEQDAILQQchEAwBC0EIIRALQQAhF0EAIRZBACEUDI4BC0EJIRBBASEUQQAhF0EAIRYMjQELAkAgCiACRw0AQc4BIRAMmQILIAotAABBLkcNjgEgCkEBaiEJDMoBCyALIAJHDY4BQdABIRAMlwILAkAgCyACRg0AIABBjoCAgAA2AgggACALNgIEQbcBIRAM/gELQdEBIRAMlgILAkAgBCACRw0AQdIBIRAMlgILIAIgBGsgACgCACIQaiEUIAQgEGtBBGohCwNAIAQtAAAgEEH8z4CAAGotAABHDY4BIBBBBEYN6QEgEEEBaiEQIARBAWoiBCACRw0ACyAAIBQ2AgBB0gEhEAyVAgsgACAMIAIQrICAgAAiAQ2NASAMIQEMuAELAkAgBCACRw0AQdQBIRAMlAILIAIgBGsgACgCACIQaiEUIAQgEGtBAWohDANAIAQtAAAgEEGB0ICAAGotAABHDY8BIBBBAUYNjgEgEEEBaiEQIARBAWoiBCACRw0ACyAAIBQ2AgBB1AEhEAyTAgsCQCAEIAJHDQBB1gEhEAyTAgsgAiAEayAAKAIAIhBqIRQgBCAQa0ECaiELA0AgBC0AACAQQYPQgIAAai0AAEcNjgEgEEECRg2QASAQQQFqIRAgBEEBaiIEIAJHDQALIAAgFDYCAEHWASEQDJICCwJAIAQgAkcNAEHXASEQDJICCwJAAkAgBC0AAEG7f2oOEACPAY8BjwGPAY8BjwGPAY8BjwGPAY8BjwGPAY8BAY8BCyAEQQFqIQRBuwEhEAz5AQsgBEEBaiEEQbwBIRAM+AELAkAgBCACRw0AQdgBIRAMkQILIAQtAABByABHDYwBIARBAWohBAzEAQsCQCAEIAJGDQAgAEGQgICAADYCCCAAIAQ2AgRBvgEhEAz3AQtB2QEhEAyPAgsCQCAEIAJHDQBB2gEhEAyPAgsgBC0AAEHIAEYNwwEgAEEBOgAoDLkBCyAAQQI6AC8gACAEIAIQpoCAgAAiEA2NAUHCASEQDPQBCyAALQAoQX9qDgK3AbkBuAELA0ACQCAELQAAQXZqDgQAjgGOAQCOAQsgBEEBaiIEIAJHDQALQd0BIRAMiwILIABBADoALyAALQAtQQRxRQ2EAgsgAEEAOgAvIABBAToANCABIQEMjAELIBBBFUYN2gEgAEEANgIcIAAgATYCFCAAQaeOgIAANgIQIABBEjYCDEEAIRAMiAILAkAgACAQIAIQtICAgAAiBA0AIBAhAQyBAgsCQCAEQRVHDQAgAEEDNgIcIAAgEDYCFCAAQbCYgIAANgIQIABBFTYCDEEAIRAMiAILIABBADYCHCAAIBA2AhQgAEGnjoCAADYCECAAQRI2AgxBACEQDIcCCyAQQRVGDdYBIABBADYCHCAAIAE2AhQgAEHajYCAADYCECAAQRQ2AgxBACEQDIYCCyAAKAIEIRcgAEEANgIEIBAgEadqIhYhASAAIBcgECAWIBQbIhAQtYCAgAAiFEUNjQEgAEEHNgIcIAAgEDYCFCAAIBQ2AgxBACEQDIUCCyAAIAAvATBBgAFyOwEwIAEhAQtBKiEQDOoBCyAQQRVGDdEBIABBADYCHCAAIAE2AhQgAEGDjICAADYCECAAQRM2AgxBACEQDIICCyAQQRVGDc8BIABBADYCHCAAIAE2AhQgAEGaj4CAADYCECAAQSI2AgxBACEQDIECCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQt4CAgAAiEA0AIAFBAWohAQyNAQsgAEEMNgIcIAAgEDYCDCAAIAFBAWo2AhRBACEQDIACCyAQQRVGDcwBIABBADYCHCAAIAE2AhQgAEGaj4CAADYCECAAQSI2AgxBACEQDP8BCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQt4CAgAAiEA0AIAFBAWohAQyMAQsgAEENNgIcIAAgEDYCDCAAIAFBAWo2AhRBACEQDP4BCyAQQRVGDckBIABBADYCHCAAIAE2AhQgAEHGjICAADYCECAAQSM2AgxBACEQDP0BCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQuYCAgAAiEA0AIAFBAWohAQyLAQsgAEEONgIcIAAgEDYCDCAAIAFBAWo2AhRBACEQDPwBCyAAQQA2AhwgACABNgIUIABBwJWAgAA2AhAgAEECNgIMQQAhEAz7AQsgEEEVRg3FASAAQQA2AhwgACABNgIUIABBxoyAgAA2AhAgAEEjNgIMQQAhEAz6AQsgAEEQNgIcIAAgATYCFCAAIBA2AgxBACEQDPkBCyAAKAIEIQQgAEEANgIEAkAgACAEIAEQuYCAgAAiBA0AIAFBAWohAQzxAQsgAEERNgIcIAAgBDYCDCAAIAFBAWo2AhRBACEQDPgBCyAQQRVGDcEBIABBADYCHCAAIAE2AhQgAEHGjICAADYCECAAQSM2AgxBACEQDPcBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQuYCAgAAiEA0AIAFBAWohAQyIAQsgAEETNgIcIAAgEDYCDCAAIAFBAWo2AhRBACEQDPYBCyAAKAIEIQQgAEEANgIEAkAgACAEIAEQuYCAgAAiBA0AIAFBAWohAQztAQsgAEEUNgIcIAAgBDYCDCAAIAFBAWo2AhRBACEQDPUBCyAQQRVGDb0BIABBADYCHCAAIAE2AhQgAEGaj4CAADYCECAAQSI2AgxBACEQDPQBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQt4CAgAAiEA0AIAFBAWohAQyGAQsgAEEWNgIcIAAgEDYCDCAAIAFBAWo2AhRBACEQDPMBCyAAKAIEIQQgAEEANgIEAkAgACAEIAEQt4CAgAAiBA0AIAFBAWohAQzpAQsgAEEXNgIcIAAgBDYCDCAAIAFBAWo2AhRBACEQDPIBCyAAQQA2AhwgACABNgIUIABBzZOAgAA2AhAgAEEMNgIMQQAhEAzxAQtCASERCyAQQQFqIQECQCAAKQMgIhJC//////////8PVg0AIAAgEkIEhiARhDcDICABIQEMhAELIABBADYCHCAAIAE2AhQgAEGtiYCAADYCECAAQQw2AgxBACEQDO8BCyAAQQA2AhwgACAQNgIUIABBzZOAgAA2AhAgAEEMNgIMQQAhEAzuAQsgACgCBCEXIABBADYCBCAQIBGnaiIWIQEgACAXIBAgFiAUGyIQELWAgIAAIhRFDXMgAEEFNgIcIAAgEDYCFCAAIBQ2AgxBACEQDO0BCyAAQQA2AhwgACAQNgIUIABBqpyAgAA2AhAgAEEPNgIMQQAhEAzsAQsgACAQIAIQtICAgAAiAQ0BIBAhAQtBDiEQDNEBCwJAIAFBFUcNACAAQQI2AhwgACAQNgIUIABBsJiAgAA2AhAgAEEVNgIMQQAhEAzqAQsgAEEANgIcIAAgEDYCFCAAQaeOgIAANgIQIABBEjYCDEEAIRAM6QELIAFBAWohEAJAIAAvATAiAUGAAXFFDQACQCAAIBAgAhC7gICAACIBDQAgECEBDHALIAFBFUcNugEgAEEFNgIcIAAgEDYCFCAAQfmXgIAANgIQIABBFTYCDEEAIRAM6QELAkAgAUGgBHFBoARHDQAgAC0ALUECcQ0AIABBADYCHCAAIBA2AhQgAEGWk4CAADYCECAAQQQ2AgxBACEQDOkBCyAAIBAgAhC9gICAABogECEBAkACQAJAAkACQCAAIBAgAhCzgICAAA4WAgEABAQEBAQEBAQEBAQEBAQEBAQEAwQLIABBAToALgsgACAALwEwQcAAcjsBMCAQIQELQSYhEAzRAQsgAEEjNgIcIAAgEDYCFCAAQaWWgIAANgIQIABBFTYCDEEAIRAM6QELIABBADYCHCAAIBA2AhQgAEHVi4CAADYCECAAQRE2AgxBACEQDOgBCyAALQAtQQFxRQ0BQcMBIRAMzgELAkAgDSACRg0AA0ACQCANLQAAQSBGDQAgDSEBDMQBCyANQQFqIg0gAkcNAAtBJSEQDOcBC0ElIRAM5gELIAAoAgQhBCAAQQA2AgQgACAEIA0Qr4CAgAAiBEUNrQEgAEEmNgIcIAAgBDYCDCAAIA1BAWo2AhRBACEQDOUBCyAQQRVGDasBIABBADYCHCAAIAE2AhQgAEH9jYCAADYCECAAQR02AgxBACEQDOQBCyAAQSc2AhwgACABNgIUIAAgEDYCDEEAIRAM4wELIBAhAUEBIRQCQAJAAkACQAJAAkACQCAALQAsQX5qDgcGBQUDAQIABQsgACAALwEwQQhyOwEwDAMLQQIhFAwBC0EEIRQLIABBAToALCAAIAAvATAgFHI7ATALIBAhAQtBKyEQDMoBCyAAQQA2AhwgACAQNgIUIABBq5KAgAA2AhAgAEELNgIMQQAhEAziAQsgAEEANgIcIAAgATYCFCAAQeGPgIAANgIQIABBCjYCDEEAIRAM4QELIABBADoALCAQIQEMvQELIBAhAUEBIRQCQAJAAkACQAJAIAAtACxBe2oOBAMBAgAFCyAAIAAvATBBCHI7ATAMAwtBAiEUDAELQQQhFAsgAEEBOgAsIAAgAC8BMCAUcjsBMAsgECEBC0EpIRAMxQELIABBADYCHCAAIAE2AhQgAEHwlICAADYCECAAQQM2AgxBACEQDN0BCwJAIA4tAABBDUcNACAAKAIEIQEgAEEANgIEAkAgACABIA4QsYCAgAAiAQ0AIA5BAWohAQx1CyAAQSw2AhwgACABNgIMIAAgDkEBajYCFEEAIRAM3QELIAAtAC1BAXFFDQFBxAEhEAzDAQsCQCAOIAJHDQBBLSEQDNwBCwJAAkADQAJAIA4tAABBdmoOBAIAAAMACyAOQQFqIg4gAkcNAAtBLSEQDN0BCyAAKAIEIQEgAEEANgIEAkAgACABIA4QsYCAgAAiAQ0AIA4hAQx0CyAAQSw2AhwgACAONgIUIAAgATYCDEEAIRAM3AELIAAoAgQhASAAQQA2AgQCQCAAIAEgDhCxgICAACIBDQAgDkEBaiEBDHMLIABBLDYCHCAAIAE2AgwgACAOQQFqNgIUQQAhEAzbAQsgACgCBCEEIABBADYCBCAAIAQgDhCxgICAACIEDaABIA4hAQzOAQsgEEEsRw0BIAFBAWohEEEBIQECQAJAAkACQAJAIAAtACxBe2oOBAMBAgQACyAQIQEMBAtBAiEBDAELQQQhAQsgAEEBOgAsIAAgAC8BMCABcjsBMCAQIQEMAQsgACAALwEwQQhyOwEwIBAhAQtBOSEQDL8BCyAAQQA6ACwgASEBC0E0IRAMvQELIAAgAC8BMEEgcjsBMCABIQEMAgsgACgCBCEEIABBADYCBAJAIAAgBCABELGAgIAAIgQNACABIQEMxwELIABBNzYCHCAAIAE2AhQgACAENgIMQQAhEAzUAQsgAEEIOgAsIAEhAQtBMCEQDLkBCwJAIAAtAChBAUYNACABIQEMBAsgAC0ALUEIcUUNkwEgASEBDAMLIAAtADBBIHENlAFBxQEhEAy3AQsCQCAPIAJGDQACQANAAkAgDy0AAEFQaiIBQf8BcUEKSQ0AIA8hAUE1IRAMugELIAApAyAiEUKZs+bMmbPmzBlWDQEgACARQgp+IhE3AyAgESABrUL/AYMiEkJ/hVYNASAAIBEgEnw3AyAgD0EBaiIPIAJHDQALQTkhEAzRAQsgACgCBCECIABBADYCBCAAIAIgD0EBaiIEELGAgIAAIgINlQEgBCEBDMMBC0E5IRAMzwELAkAgAC8BMCIBQQhxRQ0AIAAtAChBAUcNACAALQAtQQhxRQ2QAQsgACABQff7A3FBgARyOwEwIA8hAQtBNyEQDLQBCyAAIAAvATBBEHI7ATAMqwELIBBBFUYNiwEgAEEANgIcIAAgATYCFCAAQfCOgIAANgIQIABBHDYCDEEAIRAMywELIABBwwA2AhwgACABNgIMIAAgDUEBajYCFEEAIRAMygELAkAgAS0AAEE6Rw0AIAAoAgQhECAAQQA2AgQCQCAAIBAgARCvgICAACIQDQAgAUEBaiEBDGMLIABBwwA2AhwgACAQNgIMIAAgAUEBajYCFEEAIRAMygELIABBADYCHCAAIAE2AhQgAEGxkYCAADYCECAAQQo2AgxBACEQDMkBCyAAQQA2AhwgACABNgIUIABBoJmAgAA2AhAgAEEeNgIMQQAhEAzIAQsgAEEANgIACyAAQYASOwEqIAAgF0EBaiIBIAIQqICAgAAiEA0BIAEhAQtBxwAhEAysAQsgEEEVRw2DASAAQdEANgIcIAAgATYCFCAAQeOXgIAANgIQIABBFTYCDEEAIRAMxAELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDF4LIABB0gA2AhwgACABNgIUIAAgEDYCDEEAIRAMwwELIABBADYCHCAAIBQ2AhQgAEHBqICAADYCECAAQQc2AgwgAEEANgIAQQAhEAzCAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMXQsgAEHTADYCHCAAIAE2AhQgACAQNgIMQQAhEAzBAQtBACEQIABBADYCHCAAIAE2AhQgAEGAkYCAADYCECAAQQk2AgwMwAELIBBBFUYNfSAAQQA2AhwgACABNgIUIABBlI2AgAA2AhAgAEEhNgIMQQAhEAy/AQtBASEWQQAhF0EAIRRBASEQCyAAIBA6ACsgAUEBaiEBAkACQCAALQAtQRBxDQACQAJAAkAgAC0AKg4DAQACBAsgFkUNAwwCCyAUDQEMAgsgF0UNAQsgACgCBCEQIABBADYCBAJAIAAgECABEK2AgIAAIhANACABIQEMXAsgAEHYADYCHCAAIAE2AhQgACAQNgIMQQAhEAy+AQsgACgCBCEEIABBADYCBAJAIAAgBCABEK2AgIAAIgQNACABIQEMrQELIABB2QA2AhwgACABNgIUIAAgBDYCDEEAIRAMvQELIAAoAgQhBCAAQQA2AgQCQCAAIAQgARCtgICAACIEDQAgASEBDKsBCyAAQdoANgIcIAAgATYCFCAAIAQ2AgxBACEQDLwBCyAAKAIEIQQgAEEANgIEAkAgACAEIAEQrYCAgAAiBA0AIAEhAQypAQsgAEHcADYCHCAAIAE2AhQgACAENgIMQQAhEAy7AQsCQCABLQAAQVBqIhBB/wFxQQpPDQAgACAQOgAqIAFBAWohAUHPACEQDKIBCyAAKAIEIQQgAEEANgIEAkAgACAEIAEQrYCAgAAiBA0AIAEhAQynAQsgAEHeADYCHCAAIAE2AhQgACAENgIMQQAhEAy6AQsgAEEANgIAIBdBAWohAQJAIAAtAClBI08NACABIQEMWQsgAEEANgIcIAAgATYCFCAAQdOJgIAANgIQIABBCDYCDEEAIRAMuQELIABBADYCAAtBACEQIABBADYCHCAAIAE2AhQgAEGQs4CAADYCECAAQQg2AgwMtwELIABBADYCACAXQQFqIQECQCAALQApQSFHDQAgASEBDFYLIABBADYCHCAAIAE2AhQgAEGbioCAADYCECAAQQg2AgxBACEQDLYBCyAAQQA2AgAgF0EBaiEBAkAgAC0AKSIQQV1qQQtPDQAgASEBDFULAkAgEEEGSw0AQQEgEHRBygBxRQ0AIAEhAQxVC0EAIRAgAEEANgIcIAAgATYCFCAAQfeJgIAANgIQIABBCDYCDAy1AQsgEEEVRg1xIABBADYCHCAAIAE2AhQgAEG5jYCAADYCECAAQRo2AgxBACEQDLQBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxUCyAAQeUANgIcIAAgATYCFCAAIBA2AgxBACEQDLMBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxNCyAAQdIANgIcIAAgATYCFCAAIBA2AgxBACEQDLIBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxNCyAAQdMANgIcIAAgATYCFCAAIBA2AgxBACEQDLEBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxRCyAAQeUANgIcIAAgATYCFCAAIBA2AgxBACEQDLABCyAAQQA2AhwgACABNgIUIABBxoqAgAA2AhAgAEEHNgIMQQAhEAyvAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMSQsgAEHSADYCHCAAIAE2AhQgACAQNgIMQQAhEAyuAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMSQsgAEHTADYCHCAAIAE2AhQgACAQNgIMQQAhEAytAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMTQsgAEHlADYCHCAAIAE2AhQgACAQNgIMQQAhEAysAQsgAEEANgIcIAAgATYCFCAAQdyIgIAANgIQIABBBzYCDEEAIRAMqwELIBBBP0cNASABQQFqIQELQQUhEAyQAQtBACEQIABBADYCHCAAIAE2AhQgAEH9koCAADYCECAAQQc2AgwMqAELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEILIABB0gA2AhwgACABNgIUIAAgEDYCDEEAIRAMpwELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEILIABB0wA2AhwgACABNgIUIAAgEDYCDEEAIRAMpgELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEYLIABB5QA2AhwgACABNgIUIAAgEDYCDEEAIRAMpQELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDD8LIABB0gA2AhwgACAUNgIUIAAgATYCDEEAIRAMpAELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDD8LIABB0wA2AhwgACAUNgIUIAAgATYCDEEAIRAMowELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDEMLIABB5QA2AhwgACAU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YWdlX2JlZ2luYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlYCBjYWxsYmFjayBlcnJvcgBgb25fc3RhdHVzX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdmVyc2lvbl9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3VybF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWVzc2FnZV9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX21ldGhvZF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lYCBjYWxsYmFjayBlcnJvcgBVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNlcnZlcgBJbnZhbGlkIGhlYWRlciB2YWx1ZSBjaGFyAEludmFsaWQgaGVhZGVyIGZpZWxkIGNoYXIAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl92ZXJzaW9uAEludmFsaWQgbWlub3IgdmVyc2lvbgBJbnZhbGlkIG1ham9yIHZlcnNpb24ARXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgdmVyc2lvbgBFeHBlY3RlZCBDUkxGIGFmdGVyIHZlcnNpb24ASW52YWxpZCBIVFRQIHZlcnNpb24ASW52YWxpZCBoZWFkZXIgdG9rZW4AU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl91cmwASW52YWxpZCBjaGFyYWN0ZXJzIGluIHVybABVbmV4cGVjdGVkIHN0YXJ0IGNoYXIgaW4gdXJsAERvdWJsZSBAIGluIHVybABFbXB0eSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXJhY3RlciBpbiBDb250ZW50LUxlbmd0aABEdXBsaWNhdGUgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyIGluIHVybCBwYXRoAENvbnRlbnQtTGVuZ3RoIGNhbid0IGJlIHByZXNlbnQgd2l0aCBUcmFuc2Zlci1FbmNvZGluZwBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBzaXplAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25faGVhZGVyX3ZhbHVlAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgdmFsdWUATWlzc2luZyBleHBlY3RlZCBMRiBhZnRlciBoZWFkZXIgdmFsdWUASW52YWxpZCBgVHJhbnNmZXItRW5jb2RpbmdgIGhlYWRlciB2YWx1ZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIHF1b3RlIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAFBhdXNlZCBieSBvbl9oZWFkZXJzX2NvbXBsZXRlAEludmFsaWQgRU9GIHN0YXRlAG9uX3Jlc2V0IHBhdXNlAG9uX2NodW5rX2hlYWRlciBwYXVzZQBvbl9tZXNzYWdlX2JlZ2luIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZSBwYXVzZQBvbl9zdGF0dXNfY29tcGxldGUgcGF1c2UAb25fdmVyc2lvbl9jb21wbGV0ZSBwYXVzZQBvbl91cmxfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlIHBhdXNlAG9uX21lc3NhZ2VfY29tcGxldGUgcGF1c2UAb25fbWV0aG9kX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fbmFtZSBwYXVzZQBVbmV4cGVjdGVkIHNwYWNlIGFmdGVyIHN0YXJ0IGxpbmUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fbmFtZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIG5hbWUAUGF1c2Ugb24gQ09OTkVDVC9VcGdyYWRlAFBhdXNlIG9uIFBSSS9VcGdyYWRlAEV4cGVjdGVkIEhUVFAvMiBDb25uZWN0aW9uIFByZWZhY2UAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9tZXRob2QARXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgbWV0aG9kAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25faGVhZGVyX2ZpZWxkAFBhdXNlZABJbnZhbGlkIHdvcmQgZW5jb3VudGVyZWQASW52YWxpZCBtZXRob2QgZW5jb3VudGVyZWQAVW5leHBlY3RlZCBjaGFyIGluIHVybCBzY2hlbWEAUmVxdWVzdCBoYXMgaW52YWxpZCBgVHJhbnNmZXItRW5jb2RpbmdgAFNXSVRDSF9QUk9YWQBVU0VfUFJPWFkATUtBQ1RJVklUWQBVTlBST0NFU1NBQkxFX0VOVElUWQBDT1BZAE1PVkVEX1BFUk1BTkVOVExZAFRPT19FQVJMWQBOT1RJRlkARkFJTEVEX0RFUEVOREVOQ1kAQkFEX0dBVEVXQVkAUExBWQBQVVQAQ0hFQ0tPVVQAR0FURVdBWV9USU1FT1VUAFJFUVVFU1RfVElNRU9VVABORVRXT1JLX0NPTk5FQ1RfVElNRU9VVABDT05ORUNUSU9OX1RJTUVPVVQATE9HSU5fVElNRU9VVABORVRXT1JLX1JFQURfVElNRU9VVABQT1NUAE1JU0RJUkVDVEVEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfTE9BRF9CQUxBTkNFRF9SRVFVRVNUAEJBRF9SRVFVRVNUAEhUVFBfUkVRVUVTVF9TRU5UX1RPX0hUVFBTX1BPUlQAUkVQT1JUAElNX0FfVEVBUE9UAFJFU0VUX0NPTlRFTlQATk9fQ09OVEVOVABQQVJUSUFMX0NPTlRFTlQASFBFX0lOVkFMSURfQ09OU1RBTlQASFBFX0NCX1JFU0VUAEdFVABIUEVfU1RSSUNUAENPTkZMSUNUAFRFTVBPUkFSWV9SRURJUkVDVABQRVJNQU5FTlRfUkVESVJFQ1QAQ09OTkVDVABNVUxUSV9TVEFUVVMASFBFX0lOVkFMSURfU1RBVFVTAFRPT19NQU5ZX1JFUVVFU1RTAEVBUkxZX0hJTlRTAFVOQVZBSUxBQkxFX0ZPUl9MRUdBTF9SRUFTT05TAE9QVElPTlMAU1dJVENISU5HX1BST1RPQ09MUwBWQVJJQU5UX0FMU09fTkVHT1RJQVRFUwBNVUxUSVBMRV9DSE9JQ0VTAElOVEVSTkFMX1NFUlZFUl9FUlJPUgBXRUJfU0VSVkVSX1VOS05PV05fRVJST1IAUkFJTEdVTl9FUlJPUgBJREVOVElUWV9QUk9WSURFUl9BVVRIRU5USUNBVElPTl9FUlJPUgBTU0xfQ0VSVElGSUNBVEVfRVJST1IASU5WQUxJRF9YX0ZPUldBUkRFRF9GT1IAU0VUX1BBUkFNRVRFUgBHRVRfUEFSQU1FVEVSAEhQRV9VU0VSAFNFRV9PVEhFUgBIUEVfQ0JfQ0hVTktfSEVBREVSAE1LQ0FMRU5EQVIAU0VUVVAAV0VCX1NFUlZFUl9JU19ET1dOAFRFQVJET1dOAEhQRV9DTE9TRURfQ09OTkVDVElPTgBIRVVSSVNUSUNfRVhQSVJBVElPTgBESVNDT05ORUNURURfT1BFUkFUSU9OAE5PTl9BVVRIT1JJVEFUSVZFX0lORk9STUFUSU9OAEhQRV9JTlZBTElEX1ZFUlNJT04ASFBFX0NCX01FU1NBR0VfQkVHSU4AU0lURV9JU19GUk9aRU4ASFBFX0lOVkFMSURfSEVBREVSX1RPS0VOAElOVkFMSURfVE9LRU4ARk9SQklEREVOAEVOSEFOQ0VfWU9VUl9DQUxNAEhQRV9JTlZBTElEX1VSTABCTE9DS0VEX0JZX1BBUkVOVEFMX0NPTlRST0wATUtDT0wAQUNMAEhQRV9JTlRFUk5BTABSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFX1VOT0ZGSUNJQUwASFBFX09LAFVOTElOSwBVTkxPQ0sAUFJJAFJFVFJZX1dJVEgASFBFX0lOVkFMSURfQ09OVEVOVF9MRU5HVEgASFBFX1VORVhQRUNURURfQ09OVEVOVF9MRU5HVEgARkxVU0gAUFJPUFBBVENIAE0tU0VBUkNIAFVSSV9UT09fTE9ORwBQUk9DRVNTSU5HAE1JU0NFTExBTkVPVVNfUEVSU0lTVEVOVF9XQVJOSU5HAE1JU0NFTExBTkVPVVNfV0FSTklORwBIUEVfSU5WQUxJRF9UUkFOU0ZFUl9FTkNPRElORwBFeHBlY3RlZCBDUkxGAEhQRV9JTlZBTElEX0NIVU5LX1NJWkUATU9WRQBDT05USU5VRQBIUEVfQ0JfU1RBVFVTX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJTX0NPTVBMRVRFAEhQRV9DQl9WRVJTSU9OX0NPTVBMRVRFAEhQRV9DQl9VUkxfQ09NUExFVEUASFBFX0NCX0NIVU5LX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJfVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9WQUxVRV9DT01QTEVURQBIUEVfQ0JfQ0hVTktfRVhURU5TSU9OX05BTUVfQ09NUExFVEUASFBFX0NCX01FU1NBR0VfQ09NUExFVEUASFBFX0NCX01FVEhPRF9DT01QTEVURQBIUEVfQ0JfSEVBREVSX0ZJRUxEX0NPTVBMRVRFAERFTEVURQBIUEVfSU5WQUxJRF9FT0ZfU1RBVEUASU5WQUxJRF9TU0xfQ0VSVElGSUNBVEUAUEFVU0UATk9fUkVTUE9OU0UAVU5TVVBQT1JURURfTUVESUFfVFlQRQBHT05FAE5PVF9BQ0NFUFRBQkxFAFNFUlZJQ0VfVU5BVkFJTEFCTEUAUkFOR0VfTk9UX1NBVElTRklBQkxFAE9SSUdJTl9JU19VTlJFQUNIQUJMRQBSRVNQT05TRV9JU19TVEFMRQBQVVJHRQBNRVJHRQBSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFAFJFUVVFU1RfSEVBREVSX1RPT19MQVJHRQBQQVlMT0FEX1RPT19MQVJHRQBJTlNVRkZJQ0lFTlRfU1RPUkFHRQBIUEVfUEFVU0VEX1VQR1JBREUASFBFX1BBVVNFRF9IMl9VUEdSQURFAFNPVVJDRQBBTk5PVU5DRQBUUkFDRQBIUEVfVU5FWFBFQ1RFRF9TUEFDRQBERVNDUklCRQBVTlNVQlNDUklCRQBSRUNPUkQASFBFX0lOVkFMSURfTUVUSE9EAE5PVF9GT1VORABQUk9QRklORABVTkJJTkQAUkVCSU5EAFVOQVVUSE9SSVpFRABNRVRIT0RfTk9UX0FMTE9XRUQASFRUUF9WRVJTSU9OX05PVF9TVVBQT1JURUQAQUxSRUFEWV9SRVBPUlRFRABBQ0NFUFRFRABOT1RfSU1QTEVNRU5URUQATE9PUF9ERVRFQ1RFRABIUEVfQ1JfRVhQRUNURUQASFBFX0xGX0VYUEVDVEVEAENSRUFURUQASU1fVVNFRABIUEVfUEFVU0VEAFRJTUVPVVRfT0NDVVJFRABQQVlNRU5UX1JFUVVJUkVEAFBSRUNPTkRJVElPTl9SRVFVSVJFRABQUk9YWV9BVVRIRU5USUNBVElPTl9SRVFVSVJFRABORVRXT1JLX0FVVEhFTlRJQ0FUSU9OX1JFUVVJUkVEAExFTkdUSF9SRVFVSVJFRABTU0xfQ0VSVElGSUNBVEVfUkVRVUlSRUQAVVBHUkFERV9SRVFVSVJFRABQQUdFX0VYUElSRUQAUFJFQ09ORElUSU9OX0ZBSUxFRABFWFBFQ1RBVElPTl9GQUlMRUQAUkVWQUxJREFUSU9OX0ZBSUxFRABTU0xfSEFORFNIQUtFX0ZBSUxFRABMT0NLRUQAVFJBTlNGT1JNQVRJT05fQVBQTElFRABOT1RfTU9ESUZJRUQATk9UX0VYVEVOREVEAEJBTkRXSURUSF9MSU1JVF9FWENFRURFRABTSVRFX0lTX09WRVJMT0FERUQASEVBRABFeHBlY3RlZCBIVFRQLwAAXhMAACYTAAAwEAAA8BcAAJ0TAAAVEgAAORcAAPASAAAKEAAAdRIAAK0SAACCEwAATxQAAH8QAACgFQAAIxQAAIkSAACLFAAATRUAANQRAADPFAAAEBgAAMkWAADcFgAAwREAAOAXAAC7FAAAdBQAAHwVAADlFAAACBcAAB8QAABlFQAAoxQAACgVAAACFQAAmRUAACwQAACLGQAATw8AANQOAABqEAAAzhAAAAIXAACJDgAAbhMAABwTAABmFAAAVhcAAMETAADNEwAAbBMAAGgXAABmFwAAXxcAACITAADODwAAaQ4AANgOAABjFgAAyxMAAKoOAAAoFwAAJhcAAMUTAABdFgAA6BEAAGcTAABlEwAA8hYAAHMTAAAdFwAA+RYAAPMRAADPDgAAzhUAAAwSAACzEQAApREAAGEQAAAyFwAAuxMAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIDAgICAgIAAAICAAICAAICAgICAgICAgIABAAAAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgIAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgACAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAICAgICAAACAgACAgACAgICAgICAgICAAMABAAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbG9zZWVlcC1hbGl2ZQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBY2h1bmtlZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAQEBAQEAAAEBAAEBAAEBAQEBAQEBAQEAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AAAAAAAAAAAAAAAAAAAByYW5zZmVyLWVuY29kaW5ncGdyYWRlDQoNCg0KU00NCg0KVFRQL0NFL1RTUC8AAAAAAAAAAAAAAAABAgABAwAAAAAAAAAAAAAAAAAAAAAAAAQBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAQIAAQMAAAAAAAAAAAAAAAAAAAAAAAAEAQEFAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAQAAAgAAAAAAAAAAAAAAAAAAAAAAAAMEAAAEBAQEBAQEBAQEBAUEBAQEBAQEBAQEBAQABAAGBwQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAIAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOT1VOQ0VFQ0tPVVRORUNURVRFQ1JJQkVMVVNIRVRFQURTRUFSQ0hSR0VDVElWSVRZTEVOREFSVkVPVElGWVBUSU9OU0NIU0VBWVNUQVRDSEdFT1JESVJFQ1RPUlRSQ0hQQVJBTUVURVJVUkNFQlNDUklCRUFSRE9XTkFDRUlORE5LQ0tVQlNDUklCRUhUVFAvQURUUC8="},5627:e=>{e.exports="AGFzbQEAAAABMAhgAX8Bf2ADf39/AX9gBH9/f38Bf2AAAGADf39/AGABfwBgAn9/AGAGf39/f39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQACA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAA0ZFAwMEAAAFAAAAAAAABQEFAAUFBQAABgAAAAAGBgYGAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAABAQcAAAUFAwABBAUBcAESEgUDAQACBggBfwFBgNQECwfRBSIGbWVtb3J5AgALX2luaXRpYWxpemUACRlfX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlAQALbGxodHRwX2luaXQAChhsbGh0dHBfc2hvdWxkX2tlZXBfYWxpdmUAQQxsbGh0dHBfYWxsb2MADAZtYWxsb2MARgtsbGh0dHBfZnJlZQANBGZyZWUASA9sbGh0dHBfZ2V0X3R5cGUADhVsbGh0dHBfZ2V0X2h0dHBfbWFqb3IADxVsbGh0dHBfZ2V0X2h0dHBfbWlub3IAEBFsbGh0dHBfZ2V0X21ldGhvZAARFmxsaHR0cF9nZXRfc3RhdHVzX2NvZGUAEhJsbGh0dHBfZ2V0X3VwZ3JhZGUAEwxsbGh0dHBfcmVzZXQAFA5sbGh0dHBfZXhlY3V0ZQAVFGxsaHR0cF9zZXR0aW5nc19pbml0ABYNbGxodHRwX2ZpbmlzaAAXDGxsaHR0cF9wYXVzZQAYDWxsaHR0cF9yZXN1bWUAGRtsbGh0dHBfcmVzdW1lX2FmdGVyX3VwZ3JhZGUAGhBsbGh0dHBfZ2V0X2Vycm5vABsXbGxodHRwX2dldF9lcnJvcl9yZWFzb24AHBdsbGh0dHBfc2V0X2Vycm9yX3JlYXNvbgAdFGxsaHR0cF9nZXRfZXJyb3JfcG9zAB4RbGxodHRwX2Vycm5vX25hbWUAHxJsbGh0dHBfbWV0aG9kX25hbWUAIBJs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IAH9CwMAIABBMGogAf0LAwAgAEEgaiAB/QsDACAAQd0BNgIcQQALewEBfwJAIAAoAgwiAw0AAkAgACgCBEUNACAAIAE2AgQLAkAgACABIAIQxICAgAAiAw0AIAAoAgwPCyAAIAM2AhxBACEDIAAoAgQiAUUNACAAIAEgAiAAKAIIEYGAgIAAACIBRQ0AIAAgAjYCFCAAIAE2AgwgASEDCyADC+TzAQMOfwN+BH8jgICAgABBEGsiAySAgICAACABIQQgASEFIAEhBiABIQcgASEIIAEhCSABIQogASELIAEhDCABIQ0gASEOIAEhDwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAIcIhBBf2oO3QHaAQHZAQIDBAUGBwgJCgsMDQ7YAQ8Q1wEREtYBExQVFhcYGRob4AHfARwdHtUBHyAhIiMkJdQBJicoKSorLNMB0gEtLtEB0AEvMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUbbAUdISUrPAc4BS80BTMwBTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEBkgGTAZQBlQGWAZcBmAGZAZoBmwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQGuAa8BsAGxAbIBswG0AbUBtgG3AcsBygG4AckBuQHIAboBuwG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBANwBC0EAIRAMxgELQQ4hEAzFAQtBDSEQDMQBC0EPIRAMwwELQRAhEAzCAQtBEyEQDMEBC0EUIRAMwAELQRUhEAy/AQtBFiEQDL4BC0EXIRAMvQELQRghEAy8AQtBGSEQDLsBC0EaIRAMugELQRshEAy5AQtBHCEQDLgBC0EIIRAMtwELQR0hEAy2AQtBICEQDLUBC0EfIRAMtAELQQchEAyzAQtBISEQDLIBC0EiIRAMsQELQR4hEAywAQtBIyEQDK8BC0ESIRAMrgELQREhEAytAQtBJCEQDKwBC0ElIRAMqwELQSYhEAyqAQtBJyEQDKkBC0HDASEQDKgBC0EpIRAMpwELQSshEAymAQtBLCEQDKUBC0EtIRAMpAELQS4hEAyjAQtBLyEQDKIBC0HEASEQDKEBC0EwIRAMoAELQTQhEAyfAQtBDCEQDJ4BC0ExIRAMnQELQTIhEAycAQtBMyEQDJsBC0E5IRAMmgELQTUhEAyZAQtBxQEhEAyYAQtBCyEQDJcBC0E6IRAMlgELQTYhEAyVAQtBCiEQDJQBC0E3IRAMkwELQTghEAySAQtBPCEQDJEBC0E7IRAMkAELQT0hEAyPAQtBCSEQDI4BC0EoIRAMjQELQT4hEAyMAQtBPyEQDIsBC0HAACEQDIoBC0HBACEQDIkBC0HCACEQDIgBC0HDACEQDIcBC0HEACEQDIYBC0HFACEQDIUBC0HGACEQDIQBC0EqIRAMgwELQccAIRAMggELQcgAIRAMgQELQckAIRAMgAELQcoAIRAMfwtBywAhEAx+C0HNACEQDH0LQcwAIRAMfAtBzgAhEAx7C0HPACEQDHoLQdAAIRAMeQtB0QAhEAx4C0HSACEQDHcLQdMAIRAMdgtB1AAhEAx1C0HWACEQDHQLQdUAIRAMcwtBBiEQDHILQdcAIRAMcQtBBSEQDHALQdgAIRAMbwtBBCEQDG4LQdkAIRAMbQtB2gAhEAxsC0HbACEQDGsLQdwAIRAMagtBAyEQDGkLQd0AIRAMaAtB3gAhEAxnC0HfACEQDGYLQeEAIRAMZQtB4AAhEAxkC0HiACEQDGMLQeMAIRAMYgtBAiEQDGELQeQAIRAMYAtB5QAhEAxfC0HmACEQDF4LQecAIRAMXQtB6AAhEAxcC0HpACEQDFsLQeoAIRAMWgtB6wAhEAxZC0HsACEQDFgLQe0AIRAMVwtB7gAhEAxWC0HvACEQDFULQfAAIRAMVAtB8QAhEAxTC0HyACEQDFILQfMAIRAMUQtB9AAhEAxQC0H1ACEQDE8LQfYAIRAMTgtB9wAhEAxNC0H4ACEQDEwLQfkAIRAMSwtB+gAhEAxKC0H7ACEQDEkLQfwAIRAMSAtB/QAhEAxHC0H+ACEQDEYLQf8AIRAMRQtBgAEhEAxEC0GBASEQDEMLQYIBIRAMQgtBgwEhEAxBC0GEASEQDEALQYUBIRAMPwtBhgEhEAw+C0GHASEQDD0LQYgBIRAMPAtBiQEhEAw7C0GKASEQDDoLQYsBIRAMOQtBjAEhEAw4C0GNASEQDDcLQY4BIRAMNgtBjwEhEAw1C0GQASEQDDQLQZEBIRAMMwtBkgEhEAwyC0GTASEQDDELQZQBIRAMMAtBlQEhEAwvC0GWASEQDC4LQZcBIRAMLQtBmAEhEAwsC0GZASEQDCsLQZoBIRAMKgtBmwEhEAwpC0GcASEQDCgLQZ0BIRAMJwtBngEhEAwmC0GfASEQDCULQaABIRAMJAtBoQEhEAwjC0GiASEQDCILQaMBIRAMIQtBpAEhEAwgC0GlASEQDB8LQaYBIRAMHgtBpwEhEAwdC0GoASEQDBwLQakBIRAMGwtBqgEhEAwaC0GrASEQDBkLQawBIRAMGAtBrQEhEAwXC0GuASEQDBYLQQEhEAwVC0GvASEQDBQLQbABIRAMEwtBsQEhEAwSC0GzASEQDBELQbIBIRAMEAtBtAEhEAwPC0G1ASEQDA4LQbYBIRAMDQtBtwEhEAwMC0G4ASEQDAsLQbkBIRAMCgtBugEhEAwJC0G7ASEQDAgLQcYBIRAMBwtBvAEhEAwGC0G9ASEQDAULQb4BIRAMBAtBvwEhEAwDC0HAASEQDAILQcIBIRAMAQtBwQEhEAsDQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIBAOxwEAAQID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YWNrIGVycm9yAGBvbl9jaHVua19leHRlbnNpb25fdmFsdWVgIGNhbGxiYWNrIGVycm9yAGBvbl9zdGF0dXNfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl92ZXJzaW9uX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdXJsX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAEVtcHR5IENvbnRlbnQtTGVuZ3RoAEludmFsaWQgY2hhcmFjdGVyIGluIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBNaXNzaW5nIGV4cGVjdGVkIExGIGFmdGVyIGhlYWRlciB2YWx1ZQBJbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AgaGVhZGVyIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGUgdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZWQgdmFsdWUAUGF1c2VkIGJ5IG9uX2hlYWRlcnNfY29tcGxldGUASW52YWxpZCBFT0Ygc3RhdGUAb25fcmVzZXQgcGF1c2UAb25fY2h1bmtfaGVhZGVyIHBhdXNlAG9uX21lc3NhZ2VfYmVnaW4gcGF1c2UAb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlIHBhdXNlAG9uX3N0YXR1c19jb21wbGV0ZSBwYXVzZQBvbl92ZXJzaW9uX2NvbXBsZXRlIHBhdXNlAG9uX3VybF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19jb21wbGV0ZSBwYXVzZQBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGUgcGF1c2UAb25fbWVzc2FnZV9jb21wbGV0ZSBwYXVzZQBvbl9tZXRob2RfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lIHBhdXNlAFVuZXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgc3RhcnQgbGluZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgbmFtZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AAU1dJVENIX1BST1hZAFVTRV9QUk9YWQBNS0FDVElWSVRZAFVOUFJPQ0VTU0FCTEVfRU5USVRZAENPUFkATU9WRURfUEVSTUFORU5UTFkAVE9PX0VBUkxZAE5PVElGWQBGQUlMRURfREVQRU5ERU5DWQBCQURfR0FURVdBWQBQTEFZAFBVVABDSEVDS09VVABHQVRFV0FZX1RJTUVPVVQAUkVRVUVTVF9USU1FT1VUAE5FVFdPUktfQ09OTkVDVF9USU1FT1VUAENPTk5FQ1RJT05fVElNRU9VVABMT0dJTl9USU1FT1VUAE5FVFdPUktfUkVBRF9USU1FT1VUAFBPU1QATUlTRElSRUNURURfUkVRVUVTVABDTElFTlRfQ0xPU0VEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9MT0FEX0JBTEFOQ0VEX1JFUVVFU1QAQkFEX1JFUVVFU1QASFRUUF9SRVFVRVNUX1NFTlRfVE9fSFRUUFNfUE9SVABSRVBPUlQASU1fQV9URUFQT1QAUkVTRVRfQ09OVEVOVABOT19DT05URU5UAFBBUlRJQUxfQ09OVEVOVABIUEVfSU5WQUxJRF9DT05TVEFOVABIUEVfQ0JfUkVTRVQAR0VUAEhQRV9TVFJJQ1QAQ09ORkxJQ1QAVEVNUE9SQVJZX1JFRElSRUNUAFBFUk1BTkVOVF9SRURJUkVDVABDT05ORUNUAE1VTFRJX1NUQVRVUwBIUEVfSU5WQUxJRF9TVEFUVVMAVE9PX01BTllfUkVRVUVTVFMARUFSTFlfSElOVFMAVU5BVkFJTEFCTEVfRk9SX0xFR0FMX1JFQVNPTlMAT1BUSU9OUwBTV0lUQ0hJTkdfUFJPVE9DT0xTAFZBUklBTlRfQUxTT19ORUdPVElBVEVTAE1VTFRJUExFX0NIT0lDRVMASU5URVJOQUxfU0VSVkVSX0VSUk9SAFdFQl9TRVJWRVJfVU5LTk9XTl9FUlJPUgBSQUlMR1VOX0VSUk9SAElERU5USVRZX1BST1ZJREVSX0FVVEhFTlRJQ0FUSU9OX0VSUk9SAFNTTF9DRVJUSUZJQ0FURV9FUlJPUgBJTlZBTElEX1hfRk9SV0FSREVEX0ZPUgBTRVRfUEFSQU1FVEVSAEdFVF9QQVJBTUVURVIASFBFX1VTRVIAU0VFX09USEVSAEhQRV9DQl9DSFVOS19IRUFERVIATUtDQUxFTkRBUgBTRVRVUABXRUJfU0VSVkVSX0lTX0RPV04AVEVBUkRPV04ASFBFX0NMT1NFRF9DT05ORUNUSU9OAEhFVVJJU1RJQ19FWFBJUkFUSU9OAERJU0NPTk5FQ1RFRF9PUEVSQVRJT04ATk9OX0FVVEhPUklUQVRJVkVfSU5GT1JNQVRJT04ASFBFX0lOVkFMSURfVkVSU0lPTgBIUEVfQ0JfTUVTU0FHRV9CRUdJTgBTSVRFX0lTX0ZST1pFTgBIUEVfSU5WQUxJRF9IRUFERVJfVE9LRU4ASU5WQUxJRF9UT0tFTgBGT1JCSURERU4ARU5IQU5DRV9ZT1VSX0NBTE0ASFBFX0lOVkFMSURfVVJMAEJMT0NLRURfQllfUEFSRU5UQUxfQ09OVFJPTABNS0NPTABBQ0wASFBFX0lOVEVSTkFMAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0VfVU5PRkZJQ0lBTABIUEVfT0sAVU5MSU5LAFVOTE9DSwBQUkkAUkVUUllfV0lUSABIUEVfSU5WQUxJRF9DT05URU5UX0xFTkdUSABIUEVfVU5FWFBFQ1RFRF9DT05URU5UX0xFTkdUSABGTFVTSABQUk9QUEFUQ0gATS1TRUFSQ0gAVVJJX1RPT19MT05HAFBST0NFU1NJTkcATUlTQ0VMTEFORU9VU19QRVJTSVNURU5UX1dBUk5JTkcATUlTQ0VMTEFORU9VU19XQVJOSU5HAEhQRV9JTlZBTElEX1RSQU5TRkVSX0VOQ09ESU5HAEV4cGVjdGVkIENSTEYASFBFX0lOVkFMSURfQ0hVTktfU0laRQBNT1ZFAENPTlRJTlVFAEhQRV9DQl9TVEFUVVNfQ09NUExFVEUASFBFX0NCX0hFQURFUlNfQ09NUExFVEUASFBFX0NCX1ZFUlNJT05fQ09NUExFVEUASFBFX0NCX1VSTF9DT01QTEVURQBIUEVfQ0JfQ0hVTktfQ09NUExFVEUASFBFX0NCX0hFQURFUl9WQUxVRV9DT01QTEVURQBIUEVfQ0JfQ0hVTktfRVhURU5TSU9OX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fTkFNRV9DT01QTEVURQBIUEVfQ0JfTUVTU0FHRV9DT01QTEVURQBIUEVfQ0JfTUVUSE9EX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJfRklFTERfQ09NUExFVEUAREVMRVRFAEhQRV9JTlZBTElEX0VPRl9TVEFURQBJTlZBTElEX1NTTF9DRVJUSUZJQ0FURQBQQVVTRQBOT19SRVNQT05TRQBVTlNVUFBPUlRFRF9NRURJQV9UWVBFAEdPTkUATk9UX0FDQ0VQVEFCTEUAU0VSVklDRV9VTkFWQUlMQUJMRQBSQU5HRV9OT1RfU0FUSVNGSUFCTEUAT1JJR0lOX0lTX1VOUkVBQ0hBQkxFAFJFU1BPTlNFX0lTX1NUQUxFAFBVUkdFAE1FUkdFAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0UAUkVRVUVTVF9IRUFERVJfVE9PX0xBUkdFAFBBWUxPQURfVE9PX0xBUkdFAElOU1VGRklDSUVOVF9TVE9SQUdFAEhQRV9QQVVTRURfVVBHUkFERQBIUEVfUEFVU0VEX0gyX1VQR1JBREUAU09VUkNFAEFOTk9VTkNFAFRSQUNFAEhQRV9VTkVYUEVDVEVEX1NQQUNFAERFU0NSSUJFAFVOU1VCU0NSSUJFAFJFQ09SRABIUEVfSU5WQUxJRF9NRVRIT0QATk9UX0ZPVU5EAFBST1BGSU5EAFVOQklORABSRUJJTkQAVU5BVVRIT1JJWkVEAE1FVEhPRF9OT1RfQUxMT1dFRABIVFRQX1ZFUlNJT05fTk9UX1NVUFBPUlRFRABBTFJFQURZX1JFUE9SVEVEAEFDQ0VQVEVEAE5PVF9JTVBMRU1FTlRFRABMT09QX0RFVEVDVEVEAEhQRV9DUl9FWFBFQ1RFRABIUEVfTEZfRVhQRUNURUQAQ1JFQVRFRABJTV9VU0VEAEhQRV9QQVVTRUQAVElNRU9VVF9PQ0NVUkVEAFBBWU1FTlRfUkVRVUlSRUQAUFJFQ09ORElUSU9OX1JFUVVJUkVEAFBST1hZX0FVVEhFTlRJQ0FUSU9OX1JFUVVJUkVEAE5FVFdPUktfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATEVOR1RIX1JFUVVJUkVEAFNTTF9DRVJUSUZJQ0FURV9SRVFVSVJFRABVUEdSQURFX1JFUVVJUkVEAFBBR0VfRVhQSVJFRABQUkVDT05ESVRJT05fRkFJTEVEAEVYUEVDVEFUSU9OX0ZBSUxFRABSRVZBTElEQVRJT05fRkFJTEVEAFNTTF9IQU5EU0hBS0VfRkFJTEVEAExPQ0tFRABUUkFOU0ZPUk1BVElPTl9BUFBMSUVEAE5PVF9NT0RJRklFRABOT1RfRVhURU5ERUQAQkFORFdJRFRIX0xJTUlUX0VYQ0VFREVEAFNJVEVfSVNfT1ZFUkxPQURFRABIRUFEAEV4cGVjdGVkIEhUVFAvAABeEwAAJhMAADAQAADwFwAAnRMAABUSAAA5FwAA8BIAAAoQAAB1EgAArRIAAIITAABPFAAAfxAAAKAVAAAjFAAAiRIAAIsUAABNFQAA1BEAAM8UAAAQGAAAyRYAANwWAADBEQAA4BcAALsUAAB0FAAAfBUAAOUUAAAIFwAAHxAAAGUVAACjFAAAKBUAAAIVAACZFQAALBAAAIsZAABPDwAA1A4AAGoQAADOEAAAAhcAAIkOAABuEwAAHBMAAGYUAABWFwAAwRMAAM0TAABsEwAAaBcAAGYXAABfFwAAIhMAAM4PAABpDgAA2A4AAGMWAADLEwAAqg4AACgXAAAmFwAAxRMAAF0WAADoEQAAZxMAAGUTAADyFgAAcxMAAB0XAAD5FgAA8xEAAM8OAADOFQAADBIAALMRAAClEQAAYRAAADIXAAC7EwAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgMCAgICAgAAAgIAAgIAAgICAgICAgICAgAEAAAAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAIAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgICAgIAAAICAAICAAICAgICAgICAgIAAwAEAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgIAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgACAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsb3NlZWVwLWFsaXZlAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQFjaHVua2VkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVjdGlvbmVudC1sZW5ndGhvbnJveHktY29ubmVjdGlvbgAAAAAAAAAAAAAAAAAAAHJhbnNmZXItZW5jb2RpbmdwZ3JhZGUNCg0KDQpTTQ0KDQpUVFAvQ0UvVFNQLwAAAAAAAAAAAAAAAAECAAEDAAAAAAAAAAAAAAAAAAAAAAAABAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAABAgABAwAAAAAAAAAAAAAAAAAAAAAAAAQBAQUBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAABAAACAAAAAAAAAAAAAAAAAAAAAAAAAwQAAAQEBAQEBAQEBAQEBQQEBAQEBAQEBAQEBAAEAAYHBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQABAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAgAAAAACAAAAAAAAAAAAAAAAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5PVU5DRUVDS09VVE5FQ1RFVEVDUklCRUxVU0hFVEVBRFNFQVJDSFJHRUNUSVZJVFlMRU5EQVJWRU9USUZZUFRJT05TQ0hTRUFZU1RBVENIR0VPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFSFRUUC9BRFRQLw=="},1891:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.enumToMap=void 0;function enumToMap(e){const t={};Object.keys(e).forEach((n=>{const s=e[n];if(typeof s==="number"){t[n]=s}}));return t}t.enumToMap=enumToMap},6771:(e,t,n)=>{"use strict";const{kClients:s}=n(2785);const i=n(7890);const{kAgent:r,kMockAgentSet:o,kMockAgentGet:A,kDispatches:a,kIsMockActive:c,kNetConnect:u,kGetNetConnect:l,kOptions:d,kFactory:p}=n(4347);const g=n(8687);const h=n(6193);const{matchValue:f,buildMockOptions:E}=n(9323);const{InvalidArgumentError:m,UndiciError:C}=n(8045);const Q=n(412);const I=n(8891);const B=n(6823);class FakeWeakRef{constructor(e){this.value=e}deref(){return this.value}}class MockAgent extends Q{constructor(e){super(e);this[u]=true;this[c]=true;if(e&&e.agent&&typeof e.agent.dispatch!=="function"){throw new m("Argument opts.agent must implement Agent")}const t=e&&e.agent?e.agent:new i(e);this[r]=t;this[s]=t[s];this[d]=E(e)}get(e){let t=this[A](e);if(!t){t=this[p](e);this[o](e,t)}return t}dispatch(e,t){this.get(e.origin);return this[r].dispatch(e,t)}async close(){await this[r].close();this[s].clear()}deactivate(){this[c]=false}activate(){this[c]=true}enableNetConnect(e){if(typeof e==="string"||typeof e==="function"||e instanceof RegExp){if(Array.isArray(this[u])){this[u].push(e)}else{this[u]=[e]}}else if(typeof e==="undefined"){this[u]=true}else{throw new m("Unsupported matcher. Must be one of String|Function|RegExp.")}}disableNetConnect(){this[u]=false}get isMockActive(){return this[c]}[o](e,t){this[s].set(e,new FakeWeakRef(t))}[p](e){const t=Object.assign({agent:this},this[d]);return this[d]&&this[d].connections===1?new g(e,t):new h(e,t)}[A](e){const t=this[s].get(e);if(t){return t.deref()}if(typeof e!=="string"){const t=this[p]("http://localhost:9999");this[o](e,t);return t}for(const[t,n]of Array.from(this[s])){const s=n.deref();if(s&&typeof t!=="string"&&f(t,e)){const t=this[p](e);this[o](e,t);t[a]=s[a];return t}}}[l](){return this[u]}pendingInterceptors(){const e=this[s];return Array.from(e.entries()).flatMap((([e,t])=>t.deref()[a].map((t=>({...t,origin:e}))))).filter((({pending:e})=>e))}assertNoPendingInterceptors({pendingInterceptorsFormatter:e=new B}={}){const t=this.pendingInterceptors();if(t.length===0){return}const n=new I("interceptor","interceptors").pluralize(t.length);throw new C(`\n${n.count} ${n.noun} ${n.is} pending:\n\n${e.format(t)}\n`.trim())}}e.exports=MockAgent},8687:(e,t,n)=>{"use strict";const{promisify:s}=n(3837);const i=n(3598);const{buildMockDispatch:r}=n(9323);const{kDispatches:o,kMockAgent:A,kClose:a,kOriginalClose:c,kOrigin:u,kOriginalDispatch:l,kConnected:d}=n(4347);const{MockInterceptor:p}=n(410);const g=n(2785);const{InvalidArgumentError:h}=n(8045);class MockClient extends i{constructor(e,t){super(e,t);if(!t||!t.agent||typeof t.agent.dispatch!=="function"){throw new h("Argument opts.agent must implement Agent")}this[A]=t.agent;this[u]=e;this[o]=[];this[d]=1;this[l]=this.dispatch;this[c]=this.close.bind(this);this.dispatch=r.call(this);this.close=this[a]}get[g.kConnected](){return this[d]}intercept(e){return new p(e,this[o])}async[a](){await s(this[c])();this[d]=0;this[A][g.kClients].delete(this[u])}}e.exports=MockClient},888:(e,t,n)=>{"use strict";const{UndiciError:s}=n(8045);class MockNotMatchedError extends s{constructor(e){super(e);Error.captureStackTrace(this,MockNotMatchedError);this.name="MockNotMatchedError";this.message=e||"The request does not match any registered mock dispatches";this.code="UND_MOCK_ERR_MOCK_NOT_MATCHED"}}e.exports={MockNotMatchedError:MockNotMatchedError}},410:(e,t,n)=>{"use strict";const{getResponseData:s,buildKey:i,addMockDispatch:r}=n(9323);const{kDispatches:o,kDispatchKey:A,kDefaultHeaders:a,kDefaultTrailers:c,kContentLength:u,kMockDispatch:l}=n(4347);const{InvalidArgumentError:d}=n(8045);const{buildURL:p}=n(3983);class MockScope{constructor(e){this[l]=e}delay(e){if(typeof e!=="number"||!Number.isInteger(e)||e<=0){throw new d("waitInMs must be a valid integer > 0")}this[l].delay=e;return this}persist(){this[l].persist=true;return this}times(e){if(typeof e!=="number"||!Number.isInteger(e)||e<=0){throw new d("repeatTimes must be a valid integer > 0")}this[l].times=e;return this}}class MockInterceptor{constructor(e,t){if(typeof e!=="object"){throw new d("opts must be an object")}if(typeof e.path==="undefined"){throw new d("opts.path must be defined")}if(typeof e.method==="undefined"){e.method="GET"}if(typeof e.path==="string"){if(e.query){e.path=p(e.path,e.query)}else{const t=new URL(e.path,"data://");e.path=t.pathname+t.search}}if(typeof e.method==="string"){e.method=e.method.toUpperCase()}this[A]=i(e);this[o]=t;this[a]={};this[c]={};this[u]=false}createMockScopeDispatchData(e,t,n={}){const i=s(t);const r=this[u]?{"content-length":i.length}:{};const o={...this[a],...r,...n.headers};const A={...this[c],...n.trailers};return{statusCode:e,data:t,headers:o,trailers:A}}validateReplyParameters(e,t,n){if(typeof e==="undefined"){throw new d("statusCode must be defined")}if(typeof t==="undefined"){throw new d("data must be defined")}if(typeof n!=="object"){throw new d("responseOptions must be an object")}}reply(e){if(typeof e==="function"){const wrappedDefaultsCallback=t=>{const n=e(t);if(typeof n!=="object"){throw new d("reply options callback must return an object")}const{statusCode:s,data:i="",responseOptions:r={}}=n;this.validateReplyParameters(s,i,r);return{...this.createMockScopeDispatchData(s,i,r)}};const t=r(this[o],this[A],wrappedDefaultsCallback);return new MockScope(t)}const[t,n="",s={}]=[...arguments];this.validateReplyParameters(t,n,s);const i=this.createMockScopeDispatchData(t,n,s);const a=r(this[o],this[A],i);return new MockScope(a)}replyWithError(e){if(typeof e==="undefined"){throw new d("error must be defined")}const t=r(this[o],this[A],{error:e});return new MockScope(t)}defaultReplyHeaders(e){if(typeof e==="undefined"){throw new d("headers must be defined")}this[a]=e;return this}defaultReplyTrailers(e){if(typeof e==="undefined"){throw new d("trailers must be defined")}this[c]=e;return this}replyContentLength(){this[u]=true;return this}}e.exports.MockInterceptor=MockInterceptor;e.exports.MockScope=MockScope},6193:(e,t,n)=>{"use strict";const{promisify:s}=n(3837);const i=n(4634);const{buildMockDispatch:r}=n(9323);const{kDispatches:o,kMockAgent:A,kClose:a,kOriginalClose:c,kOrigin:u,kOriginalDispatch:l,kConnected:d}=n(4347);const{MockInterceptor:p}=n(410);const g=n(2785);const{InvalidArgumentError:h}=n(8045);class MockPool extends i{constructor(e,t){super(e,t);if(!t||!t.agent||typeof t.agent.dispatch!=="function"){throw new h("Argument opts.agent must implement Agent")}this[A]=t.agent;this[u]=e;this[o]=[];this[d]=1;this[l]=this.dispatch;this[c]=this.close.bind(this);this.dispatch=r.call(this);this.close=this[a]}get[g.kConnected](){return this[d]}intercept(e){return new p(e,this[o])}async[a](){await s(this[c])();this[d]=0;this[A][g.kClients].delete(this[u])}}e.exports=MockPool},4347:e=>{"use strict";e.exports={kAgent:Symbol("agent"),kOptions:Symbol("options"),kFactory:Symbol("factory"),kDispatches:Symbol("dispatches"),kDispatchKey:Symbol("dispatch key"),kDefaultHeaders:Symbol("default headers"),kDefaultTrailers:Symbol("default trailers"),kContentLength:Symbol("content length"),kMockAgent:Symbol("mock agent"),kMockAgentSet:Symbol("mock agent set"),kMockAgentGet:Symbol("mock agent get"),kMockDispatch:Symbol("mock dispatch"),kClose:Symbol("close"),kOriginalClose:Symbol("original agent close"),kOrigin:Symbol("origin"),kIsMockActive:Symbol("is mock active"),kNetConnect:Symbol("net connect"),kGetNetConnect:Symbol("get net connect"),kConnected:Symbol("connected")}},9323:(e,t,n)=>{"use strict";const{MockNotMatchedError:s}=n(888);const{kDispatches:i,kMockAgent:r,kOriginalDispatch:o,kOrigin:A,kGetNetConnect:a}=n(4347);const{buildURL:c,nop:u}=n(3983);const{STATUS_CODES:l}=n(3685);const{types:{isPromise:d}}=n(3837);function matchValue(e,t){if(typeof e==="string"){return e===t}if(e instanceof RegExp){return e.test(t)}if(typeof e==="function"){return e(t)===true}return false}function lowerCaseEntries(e){return Object.fromEntries(Object.entries(e).map((([e,t])=>[e.toLocaleLowerCase(),t])))}function getHeaderByName(e,t){if(Array.isArray(e)){for(let n=0;n!e)).filter((({path:e})=>matchValue(safeUrl(e),i)));if(r.length===0){throw new s(`Mock dispatch not matched for path '${i}'`)}r=r.filter((({method:e})=>matchValue(e,t.method)));if(r.length===0){throw new s(`Mock dispatch not matched for method '${t.method}'`)}r=r.filter((({body:e})=>typeof e!=="undefined"?matchValue(e,t.body):true));if(r.length===0){throw new s(`Mock dispatch not matched for body '${t.body}'`)}r=r.filter((e=>matchHeaders(e,t.headers)));if(r.length===0){throw new s(`Mock dispatch not matched for headers '${typeof t.headers==="object"?JSON.stringify(t.headers):t.headers}'`)}return r[0]}function addMockDispatch(e,t,n){const s={timesInvoked:0,times:1,persist:false,consumed:false};const i=typeof n==="function"?{callback:n}:{...n};const r={...s,...t,pending:true,data:{error:null,...i}};e.push(r);return r}function deleteMockDispatch(e,t){const n=e.findIndex((e=>{if(!e.consumed){return false}return matchKey(e,t)}));if(n!==-1){e.splice(n,1)}}function buildKey(e){const{path:t,method:n,body:s,headers:i,query:r}=e;return{path:t,method:n,body:s,headers:i,query:r}}function generateKeyValues(e){return Object.entries(e).reduce(((e,[t,n])=>[...e,Buffer.from(`${t}`),Array.isArray(n)?n.map((e=>Buffer.from(`${e}`))):Buffer.from(`${n}`)]),[])}function getStatusText(e){return l[e]||"unknown"}async function getResponse(e){const t=[];for await(const n of e){t.push(n)}return Buffer.concat(t).toString("utf8")}function mockDispatch(e,t){const n=buildKey(e);const s=getMockDispatch(this[i],n);s.timesInvoked++;if(s.data.callback){s.data={...s.data,...s.data.callback(e)}}const{data:{statusCode:r,data:o,headers:A,trailers:a,error:c},delay:l,persist:p}=s;const{timesInvoked:g,times:h}=s;s.consumed=!p&&g>=h;s.pending=g0){setTimeout((()=>{handleReply(this[i])}),l)}else{handleReply(this[i])}function handleReply(s,i=o){const c=Array.isArray(e.headers)?buildHeadersFromArray(e.headers):e.headers;const l=typeof i==="function"?i({...e,headers:c}):i;if(d(l)){l.then((e=>handleReply(s,e)));return}const p=getResponseData(l);const g=generateKeyValues(A);const h=generateKeyValues(a);t.abort=u;t.onHeaders(r,g,resume,getStatusText(r));t.onData(Buffer.from(p));t.onComplete(h);deleteMockDispatch(s,n)}function resume(){}return true}function buildMockDispatch(){const e=this[r];const t=this[A];const n=this[o];return function dispatch(i,r){if(e.isMockActive){try{mockDispatch.call(this,i,r)}catch(o){if(o instanceof s){const A=e[a]();if(A===false){throw new s(`${o.message}: subsequent request to origin ${t} was not allowed (net.connect disabled)`)}if(checkNetConnect(A,t)){n.call(this,i,r)}else{throw new s(`${o.message}: subsequent request to origin ${t} was not allowed (net.connect is not enabled for this origin)`)}}else{throw o}}}else{n.call(this,i,r)}}}function checkNetConnect(e,t){const n=new URL(t);if(e===true){return true}else if(Array.isArray(e)&&e.some((e=>matchValue(e,n.host)))){return true}return false}function buildMockOptions(e){if(e){const{agent:t,...n}=e;return n}}e.exports={getResponseData:getResponseData,getMockDispatch:getMockDispatch,addMockDispatch:addMockDispatch,deleteMockDispatch:deleteMockDispatch,buildKey:buildKey,generateKeyValues:generateKeyValues,matchValue:matchValue,getResponse:getResponse,getStatusText:getStatusText,mockDispatch:mockDispatch,buildMockDispatch:buildMockDispatch,checkNetConnect:checkNetConnect,buildMockOptions:buildMockOptions,getHeaderByName:getHeaderByName}},6823:(e,t,n)=>{"use strict";const{Transform:s}=n(2781);const{Console:i}=n(6206);e.exports=class PendingInterceptorsFormatter{constructor({disableColors:e}={}){this.transform=new s({transform(e,t,n){n(null,e)}});this.logger=new i({stdout:this.transform,inspectOptions:{colors:!e&&!process.env.CI}})}format(e){const t=e.map((({method:e,path:t,data:{statusCode:n},persist:s,times:i,timesInvoked:r,origin:o})=>({Method:e,Origin:o,Path:t,"Status code":n,Persistent:s?"✅":"❌",Invocations:r,Remaining:s?Infinity:i-r})));this.logger.table(t);return this.transform.read().toString()}}},8891:e=>{"use strict";const t={pronoun:"it",is:"is",was:"was",this:"this"};const n={pronoun:"they",is:"are",was:"were",this:"these"};e.exports=class Pluralizer{constructor(e,t){this.singular=e;this.plural=t}pluralize(e){const s=e===1;const i=s?t:n;const r=s?this.singular:this.plural;return{...i,count:e,noun:r}}}},8266:e=>{"use strict";const t=2048;const n=t-1;class FixedCircularBuffer{constructor(){this.bottom=0;this.top=0;this.list=new Array(t);this.next=null}isEmpty(){return this.top===this.bottom}isFull(){return(this.top+1&n)===this.bottom}push(e){this.list[this.top]=e;this.top=this.top+1&n}shift(){const e=this.list[this.bottom];if(e===undefined)return null;this.list[this.bottom]=undefined;this.bottom=this.bottom+1&n;return e}}e.exports=class FixedQueue{constructor(){this.head=this.tail=new FixedCircularBuffer}isEmpty(){return this.head.isEmpty()}push(e){if(this.head.isFull()){this.head=this.head.next=new FixedCircularBuffer}this.head.push(e)}shift(){const e=this.tail;const t=e.shift();if(e.isEmpty()&&e.next!==null){this.tail=e.next}return t}}},3198:(e,t,n)=>{"use strict";const s=n(4839);const i=n(8266);const{kConnected:r,kSize:o,kRunning:A,kPending:a,kQueued:c,kBusy:u,kFree:l,kUrl:d,kClose:p,kDestroy:g,kDispatch:h}=n(2785);const f=n(9689);const E=Symbol("clients");const m=Symbol("needDrain");const C=Symbol("queue");const Q=Symbol("closed resolve");const I=Symbol("onDrain");const B=Symbol("onConnect");const y=Symbol("onDisconnect");const b=Symbol("onConnectionError");const w=Symbol("get dispatcher");const R=Symbol("add client");const v=Symbol("remove client");const k=Symbol("stats");class PoolBase extends s{constructor(){super();this[C]=new i;this[E]=[];this[c]=0;const e=this;this[I]=function onDrain(t,n){const s=e[C];let i=false;while(!i){const t=s.shift();if(!t){break}e[c]--;i=!this.dispatch(t.opts,t.handler)}this[m]=i;if(!this[m]&&e[m]){e[m]=false;e.emit("drain",t,[e,...n])}if(e[Q]&&s.isEmpty()){Promise.all(e[E].map((e=>e.close()))).then(e[Q])}};this[B]=(t,n)=>{e.emit("connect",t,[e,...n])};this[y]=(t,n,s)=>{e.emit("disconnect",t,[e,...n],s)};this[b]=(t,n,s)=>{e.emit("connectionError",t,[e,...n],s)};this[k]=new f(this)}get[u](){return this[m]}get[r](){return this[E].filter((e=>e[r])).length}get[l](){return this[E].filter((e=>e[r]&&!e[m])).length}get[a](){let e=this[c];for(const{[a]:t}of this[E]){e+=t}return e}get[A](){let e=0;for(const{[A]:t}of this[E]){e+=t}return e}get[o](){let e=this[c];for(const{[o]:t}of this[E]){e+=t}return e}get stats(){return this[k]}async[p](){if(this[C].isEmpty()){return Promise.all(this[E].map((e=>e.close())))}else{return new Promise((e=>{this[Q]=e}))}}async[g](e){while(true){const t=this[C].shift();if(!t){break}t.handler.onError(e)}return Promise.all(this[E].map((t=>t.destroy(e))))}[h](e,t){const n=this[w]();if(!n){this[m]=true;this[C].push({opts:e,handler:t});this[c]++}else if(!n.dispatch(e,t)){n[m]=true;this[m]=!this[w]()}return!this[m]}[R](e){e.on("drain",this[I]).on("connect",this[B]).on("disconnect",this[y]).on("connectionError",this[b]);this[E].push(e);if(this[m]){process.nextTick((()=>{if(this[m]){this[I](e[d],[this,e])}}))}return this}[v](e){e.close((()=>{const t=this[E].indexOf(e);if(t!==-1){this[E].splice(t,1)}}));this[m]=this[E].some((e=>!e[m]&&e.closed!==true&&e.destroyed!==true))}}e.exports={PoolBase:PoolBase,kClients:E,kNeedDrain:m,kAddClient:R,kRemoveClient:v,kGetDispatcher:w}},9689:(e,t,n)=>{const{kFree:s,kConnected:i,kPending:r,kQueued:o,kRunning:A,kSize:a}=n(2785);const c=Symbol("pool");class PoolStats{constructor(e){this[c]=e}get connected(){return this[c][i]}get free(){return this[c][s]}get pending(){return this[c][r]}get queued(){return this[c][o]}get running(){return this[c][A]}get size(){return this[c][a]}}e.exports=PoolStats},4634:(e,t,n)=>{"use strict";const{PoolBase:s,kClients:i,kNeedDrain:r,kAddClient:o,kGetDispatcher:A}=n(3198);const a=n(3598);const{InvalidArgumentError:c}=n(8045);const u=n(3983);const{kUrl:l,kInterceptors:d}=n(2785);const p=n(2067);const g=Symbol("options");const h=Symbol("connections");const f=Symbol("factory");function defaultFactory(e,t){return new a(e,t)}class Pool extends s{constructor(e,{connections:t,factory:n=defaultFactory,connect:s,connectTimeout:i,tls:r,maxCachedSessions:o,socketPath:A,autoSelectFamily:a,autoSelectFamilyAttemptTimeout:E,allowH2:m,...C}={}){super();if(t!=null&&(!Number.isFinite(t)||t<0)){throw new c("invalid connections")}if(typeof n!=="function"){throw new c("factory must be a function.")}if(s!=null&&typeof s!=="function"&&typeof s!=="object"){throw new c("connect must be a function or an object")}if(typeof s!=="function"){s=p({...r,maxCachedSessions:o,allowH2:m,socketPath:A,timeout:i==null?1e4:i,...u.nodeHasAutoSelectFamily&&a?{autoSelectFamily:a,autoSelectFamilyAttemptTimeout:E}:undefined,...s})}this[d]=C.interceptors&&C.interceptors.Pool&&Array.isArray(C.interceptors.Pool)?C.interceptors.Pool:[];this[h]=t||null;this[l]=u.parseOrigin(e);this[g]={...u.deepClone(C),connect:s,allowH2:m};this[g].interceptors=C.interceptors?{...C.interceptors}:undefined;this[f]=n}[A](){let e=this[i].find((e=>!e[r]));if(e){return e}if(!this[h]||this[i].length{"use strict";const{kProxy:s,kClose:i,kDestroy:r,kInterceptors:o}=n(2785);const{URL:A}=n(7310);const a=n(7890);const c=n(4634);const u=n(4839);const{InvalidArgumentError:l,RequestAbortedError:d}=n(8045);const p=n(2067);const g=Symbol("proxy agent");const h=Symbol("proxy client");const f=Symbol("proxy headers");const E=Symbol("request tls settings");const m=Symbol("proxy tls settings");const C=Symbol("connect endpoint function");function defaultProtocolPort(e){return e==="https:"?443:80}function buildProxyOptions(e){if(typeof e==="string"){e={uri:e}}if(!e||!e.uri){throw new l("Proxy opts.uri is mandatory")}return{uri:e.uri,protocol:e.protocol||"https"}}function defaultFactory(e,t){return new c(e,t)}class ProxyAgent extends u{constructor(e){super(e);this[s]=buildProxyOptions(e);this[g]=new a(e);this[o]=e.interceptors&&e.interceptors.ProxyAgent&&Array.isArray(e.interceptors.ProxyAgent)?e.interceptors.ProxyAgent:[];if(typeof e==="string"){e={uri:e}}if(!e||!e.uri){throw new l("Proxy opts.uri is mandatory")}const{clientFactory:t=defaultFactory}=e;if(typeof t!=="function"){throw new l("Proxy opts.clientFactory must be a function.")}this[E]=e.requestTls;this[m]=e.proxyTls;this[f]=e.headers||{};if(e.auth&&e.token){throw new l("opts.auth cannot be used in combination with opts.token")}else if(e.auth){this[f]["proxy-authorization"]=`Basic ${e.auth}`}else if(e.token){this[f]["proxy-authorization"]=e.token}const n=new A(e.uri);const{origin:i,port:r,host:c}=n;const u=p({...e.proxyTls});this[C]=p({...e.requestTls});this[h]=t(n,{connect:u});this[g]=new a({...e,connect:async(e,t)=>{let n=e.host;if(!e.port){n+=`:${defaultProtocolPort(e.protocol)}`}try{const{socket:s,statusCode:o}=await this[h].connect({origin:i,port:r,path:n,signal:e.signal,headers:{...this[f],host:c}});if(o!==200){s.on("error",(()=>{})).destroy();t(new d("Proxy response !== 200 when HTTP Tunneling"))}if(e.protocol!=="https:"){t(null,s);return}let A;if(this[E]){A=this[E].servername}else{A=e.servername}this[C]({...e,servername:A,httpSocket:s},t)}catch(e){t(e)}}})}dispatch(e,t){const{host:n}=new A(e.origin);const s=buildHeaders(e.headers);throwIfProxyAuthIsSent(s);return this[g].dispatch({...e,headers:{...s,host:n}},t)}async[i](){await this[g].close();await this[h].close()}async[r](){await this[g].destroy();await this[h].destroy()}}function buildHeaders(e){if(Array.isArray(e)){const t={};for(let n=0;ne.toLowerCase()==="proxy-authorization"));if(t){throw new l("Proxy-Authorization should be sent in ProxyAgent constructor")}}e.exports=ProxyAgent},9459:e=>{"use strict";let t=Date.now();let n;const s=[];function onTimeout(){t=Date.now();let e=s.length;let n=0;while(n0&&t>=i.state){i.state=-1;i.callback(i.opaque)}if(i.state===-1){i.state=-2;if(n!==e-1){s[n]=s.pop()}else{s.pop()}e-=1}else{n+=1}}if(s.length>0){refreshTimeout()}}function refreshTimeout(){if(n&&n.refresh){n.refresh()}else{clearTimeout(n);n=setTimeout(onTimeout,1e3);if(n.unref){n.unref()}}}class Timeout{constructor(e,t,n){this.callback=e;this.delay=t;this.opaque=n;this.state=-2;this.refresh()}refresh(){if(this.state===-2){s.push(this);if(!n||s.length===1){refreshTimeout()}}this.state=0}clear(){this.state=-1}}e.exports={setTimeout(e,t,n){return t<1e3?setTimeout(e,t,n):new Timeout(e,t,n)},clearTimeout(e){if(e instanceof Timeout){e.clear()}else{clearTimeout(e)}}}},5354:(e,t,n)=>{"use strict";const s=n(7643);const{uid:i,states:r}=n(9188);const{kReadyState:o,kSentClose:A,kByteParser:a,kReceivedClose:c}=n(7578);const{fireEvent:u,failWebsocketConnection:l}=n(5515);const{CloseEvent:d}=n(2611);const{makeRequest:p}=n(8359);const{fetching:g}=n(4881);const{Headers:h}=n(554);const{getGlobalDispatcher:f}=n(1892);const{kHeadersList:E}=n(2785);const m={};m.open=s.channel("undici:websocket:open");m.close=s.channel("undici:websocket:close");m.socketError=s.channel("undici:websocket:socket_error");let C;try{C=n(6113)}catch{}function establishWebSocketConnection(e,t,n,s,r){const o=e;o.protocol=e.protocol==="ws:"?"http:":"https:";const A=p({urlList:[o],serviceWorkers:"none",referrer:"no-referrer",mode:"websocket",credentials:"include",cache:"no-store",redirect:"error"});if(r.headers){const e=new h(r.headers)[E];A.headersList=e}const a=C.randomBytes(16).toString("base64");A.headersList.append("sec-websocket-key",a);A.headersList.append("sec-websocket-version","13");for(const e of t){A.headersList.append("sec-websocket-protocol",e)}const c="";const u=g({request:A,useParallelQueue:true,dispatcher:r.dispatcher??f(),processResponse(e){if(e.type==="error"||e.status!==101){l(n,"Received network error or non-101 status code.");return}if(t.length!==0&&!e.headersList.get("Sec-WebSocket-Protocol")){l(n,"Server did not respond with sent protocols.");return}if(e.headersList.get("Upgrade")?.toLowerCase()!=="websocket"){l(n,'Server did not set Upgrade header to "websocket".');return}if(e.headersList.get("Connection")?.toLowerCase()!=="upgrade"){l(n,'Server did not set Connection header to "upgrade".');return}const r=e.headersList.get("Sec-WebSocket-Accept");const o=C.createHash("sha1").update(a+i).digest("base64");if(r!==o){l(n,"Incorrect hash received in Sec-WebSocket-Accept header.");return}const u=e.headersList.get("Sec-WebSocket-Extensions");if(u!==null&&u!==c){l(n,"Received different permessage-deflate than the one set.");return}const d=e.headersList.get("Sec-WebSocket-Protocol");if(d!==null&&d!==A.headersList.get("Sec-WebSocket-Protocol")){l(n,"Protocol was not set in the opening handshake.");return}e.socket.on("data",onSocketData);e.socket.on("close",onSocketClose);e.socket.on("error",onSocketError);if(m.open.hasSubscribers){m.open.publish({address:e.socket.address(),protocol:d,extensions:u})}s(e)}});return u}function onSocketData(e){if(!this.ws[a].write(e)){this.pause()}}function onSocketClose(){const{ws:e}=this;const t=e[A]&&e[c];let n=1005;let s="";const i=e[a].closingInfo;if(i){n=i.code??1005;s=i.reason}else if(!e[A]){n=1006}e[o]=r.CLOSED;u("close",e,d,{wasClean:t,code:n,reason:s});if(m.close.hasSubscribers){m.close.publish({websocket:e,code:n,reason:s})}}function onSocketError(e){const{ws:t}=this;t[o]=r.CLOSING;if(m.socketError.hasSubscribers){m.socketError.publish(e)}this.destroy()}e.exports={establishWebSocketConnection:establishWebSocketConnection}},9188:e=>{"use strict";const t="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";const n={enumerable:true,writable:false,configurable:false};const s={CONNECTING:0,OPEN:1,CLOSING:2,CLOSED:3};const i={CONTINUATION:0,TEXT:1,BINARY:2,CLOSE:8,PING:9,PONG:10};const r=2**16-1;const o={INFO:0,PAYLOADLENGTH_16:2,PAYLOADLENGTH_64:3,READ_DATA:4};const A=Buffer.allocUnsafe(0);e.exports={uid:t,staticPropertyDescriptors:n,states:s,opcodes:i,maxUnsigned16Bit:r,parserStates:o,emptyBuffer:A}},2611:(e,t,n)=>{"use strict";const{webidl:s}=n(1744);const{kEnumerableProperty:i}=n(3983);const{MessagePort:r}=n(1267);class MessageEvent extends Event{#r;constructor(e,t={}){s.argumentLengthCheck(arguments,1,{header:"MessageEvent constructor"});e=s.converters.DOMString(e);t=s.converters.MessageEventInit(t);super(e,t);this.#r=t}get data(){s.brandCheck(this,MessageEvent);return this.#r.data}get origin(){s.brandCheck(this,MessageEvent);return this.#r.origin}get lastEventId(){s.brandCheck(this,MessageEvent);return this.#r.lastEventId}get source(){s.brandCheck(this,MessageEvent);return this.#r.source}get ports(){s.brandCheck(this,MessageEvent);if(!Object.isFrozen(this.#r.ports)){Object.freeze(this.#r.ports)}return this.#r.ports}initMessageEvent(e,t=false,n=false,i=null,r="",o="",A=null,a=[]){s.brandCheck(this,MessageEvent);s.argumentLengthCheck(arguments,1,{header:"MessageEvent.initMessageEvent"});return new MessageEvent(e,{bubbles:t,cancelable:n,data:i,origin:r,lastEventId:o,source:A,ports:a})}}class CloseEvent extends Event{#r;constructor(e,t={}){s.argumentLengthCheck(arguments,1,{header:"CloseEvent constructor"});e=s.converters.DOMString(e);t=s.converters.CloseEventInit(t);super(e,t);this.#r=t}get wasClean(){s.brandCheck(this,CloseEvent);return this.#r.wasClean}get code(){s.brandCheck(this,CloseEvent);return this.#r.code}get reason(){s.brandCheck(this,CloseEvent);return this.#r.reason}}class ErrorEvent extends Event{#r;constructor(e,t){s.argumentLengthCheck(arguments,1,{header:"ErrorEvent constructor"});super(e,t);e=s.converters.DOMString(e);t=s.converters.ErrorEventInit(t??{});this.#r=t}get message(){s.brandCheck(this,ErrorEvent);return this.#r.message}get filename(){s.brandCheck(this,ErrorEvent);return this.#r.filename}get lineno(){s.brandCheck(this,ErrorEvent);return this.#r.lineno}get colno(){s.brandCheck(this,ErrorEvent);return this.#r.colno}get error(){s.brandCheck(this,ErrorEvent);return this.#r.error}}Object.defineProperties(MessageEvent.prototype,{[Symbol.toStringTag]:{value:"MessageEvent",configurable:true},data:i,origin:i,lastEventId:i,source:i,ports:i,initMessageEvent:i});Object.defineProperties(CloseEvent.prototype,{[Symbol.toStringTag]:{value:"CloseEvent",configurable:true},reason:i,code:i,wasClean:i});Object.defineProperties(ErrorEvent.prototype,{[Symbol.toStringTag]:{value:"ErrorEvent",configurable:true},message:i,filename:i,lineno:i,colno:i,error:i});s.converters.MessagePort=s.interfaceConverter(r);s.converters["sequence"]=s.sequenceConverter(s.converters.MessagePort);const o=[{key:"bubbles",converter:s.converters.boolean,defaultValue:false},{key:"cancelable",converter:s.converters.boolean,defaultValue:false},{key:"composed",converter:s.converters.boolean,defaultValue:false}];s.converters.MessageEventInit=s.dictionaryConverter([...o,{key:"data",converter:s.converters.any,defaultValue:null},{key:"origin",converter:s.converters.USVString,defaultValue:""},{key:"lastEventId",converter:s.converters.DOMString,defaultValue:""},{key:"source",converter:s.nullableConverter(s.converters.MessagePort),defaultValue:null},{key:"ports",converter:s.converters["sequence"],get defaultValue(){return[]}}]);s.converters.CloseEventInit=s.dictionaryConverter([...o,{key:"wasClean",converter:s.converters.boolean,defaultValue:false},{key:"code",converter:s.converters["unsigned short"],defaultValue:0},{key:"reason",converter:s.converters.USVString,defaultValue:""}]);s.converters.ErrorEventInit=s.dictionaryConverter([...o,{key:"message",converter:s.converters.DOMString,defaultValue:""},{key:"filename",converter:s.converters.USVString,defaultValue:""},{key:"lineno",converter:s.converters["unsigned long"],defaultValue:0},{key:"colno",converter:s.converters["unsigned long"],defaultValue:0},{key:"error",converter:s.converters.any}]);e.exports={MessageEvent:MessageEvent,CloseEvent:CloseEvent,ErrorEvent:ErrorEvent}},5444:(e,t,n)=>{"use strict";const{maxUnsigned16Bit:s}=n(9188);let i;try{i=n(6113)}catch{}class WebsocketFrameSend{constructor(e){this.frameData=e;this.maskKey=i.randomBytes(4)}createFrame(e){const t=this.frameData?.byteLength??0;let n=t;let i=6;if(t>s){i+=8;n=127}else if(t>125){i+=2;n=126}const r=Buffer.allocUnsafe(t+i);r[0]=r[1]=0;r[0]|=128;r[0]=(r[0]&240)+e; -/*! ws. MIT License. Einar Otto Stangvik */r[i-4]=this.maskKey[0];r[i-3]=this.maskKey[1];r[i-2]=this.maskKey[2];r[i-1]=this.maskKey[3];r[1]=n;if(n===126){r.writeUInt16BE(t,2)}else if(n===127){r[2]=r[3]=0;r.writeUIntBE(t,4,6)}r[1]|=128;for(let e=0;e{"use strict";const{Writable:s}=n(2781);const i=n(7643);const{parserStates:r,opcodes:o,states:A,emptyBuffer:a}=n(9188);const{kReadyState:c,kSentClose:u,kResponse:l,kReceivedClose:d}=n(7578);const{isValidStatusCode:p,failWebsocketConnection:g,websocketMessageReceived:h}=n(5515);const{WebsocketFrameSend:f}=n(5444);const E={};E.ping=i.channel("undici:websocket:ping");E.pong=i.channel("undici:websocket:pong");class ByteParser extends s{#o=[];#A=0;#a=r.INFO;#c={};#u=[];constructor(e){super();this.ws=e}_write(e,t,n){this.#o.push(e);this.#A+=e.length;this.run(n)}run(e){while(true){if(this.#a===r.INFO){if(this.#A<2){return e()}const t=this.consume(2);this.#c.fin=(t[0]&128)!==0;this.#c.opcode=t[0]&15;this.#c.originalOpcode??=this.#c.opcode;this.#c.fragmented=!this.#c.fin&&this.#c.opcode!==o.CONTINUATION;if(this.#c.fragmented&&this.#c.opcode!==o.BINARY&&this.#c.opcode!==o.TEXT){g(this.ws,"Invalid frame type was fragmented.");return}const n=t[1]&127;if(n<=125){this.#c.payloadLength=n;this.#a=r.READ_DATA}else if(n===126){this.#a=r.PAYLOADLENGTH_16}else if(n===127){this.#a=r.PAYLOADLENGTH_64}if(this.#c.fragmented&&n>125){g(this.ws,"Fragmented frame exceeded 125 bytes.");return}else if((this.#c.opcode===o.PING||this.#c.opcode===o.PONG||this.#c.opcode===o.CLOSE)&&n>125){g(this.ws,"Payload length for control frame exceeded 125 bytes.");return}else if(this.#c.opcode===o.CLOSE){if(n===1){g(this.ws,"Received close frame with a 1-byte body.");return}const e=this.consume(n);this.#c.closeInfo=this.parseCloseBody(false,e);if(!this.ws[u]){const e=Buffer.allocUnsafe(2);e.writeUInt16BE(this.#c.closeInfo.code,0);const t=new f(e);this.ws[l].socket.write(t.createFrame(o.CLOSE),(e=>{if(!e){this.ws[u]=true}}))}this.ws[c]=A.CLOSING;this.ws[d]=true;this.end();return}else if(this.#c.opcode===o.PING){const t=this.consume(n);if(!this.ws[d]){const e=new f(t);this.ws[l].socket.write(e.createFrame(o.PONG));if(E.ping.hasSubscribers){E.ping.publish({payload:t})}}this.#a=r.INFO;if(this.#A>0){continue}else{e();return}}else if(this.#c.opcode===o.PONG){const t=this.consume(n);if(E.pong.hasSubscribers){E.pong.publish({payload:t})}if(this.#A>0){continue}else{e();return}}}else if(this.#a===r.PAYLOADLENGTH_16){if(this.#A<2){return e()}const t=this.consume(2);this.#c.payloadLength=t.readUInt16BE(0);this.#a=r.READ_DATA}else if(this.#a===r.PAYLOADLENGTH_64){if(this.#A<8){return e()}const t=this.consume(8);const n=t.readUInt32BE(0);if(n>2**31-1){g(this.ws,"Received payload length > 2^31 bytes.");return}const s=t.readUInt32BE(4);this.#c.payloadLength=(n<<8)+s;this.#a=r.READ_DATA}else if(this.#a===r.READ_DATA){if(this.#A=this.#c.payloadLength){const e=this.consume(this.#c.payloadLength);this.#u.push(e);if(!this.#c.fragmented||this.#c.fin&&this.#c.opcode===o.CONTINUATION){const e=Buffer.concat(this.#u);h(this.ws,this.#c.originalOpcode,e);this.#c={};this.#u.length=0}this.#a=r.INFO}}if(this.#A>0){continue}else{e();break}}}consume(e){if(e>this.#A){return null}else if(e===0){return a}if(this.#o[0].length===e){this.#A-=this.#o[0].length;return this.#o.shift()}const t=Buffer.allocUnsafe(e);let n=0;while(n!==e){const s=this.#o[0];const{length:i}=s;if(i+n===e){t.set(this.#o.shift(),n);break}else if(i+n>e){t.set(s.subarray(0,e-n),n);this.#o[0]=s.subarray(e-n);break}else{t.set(this.#o.shift(),n);n+=s.length}}this.#A-=e;return t}parseCloseBody(e,t){let n;if(t.length>=2){n=t.readUInt16BE(0)}if(e){if(!p(n)){return null}return{code:n}}let s=t.subarray(2);if(s[0]===239&&s[1]===187&&s[2]===191){s=s.subarray(3)}if(n!==undefined&&!p(n)){return null}try{s=new TextDecoder("utf-8",{fatal:true}).decode(s)}catch{return null}return{code:n,reason:s}}get closingInfo(){return this.#c.closeInfo}}e.exports={ByteParser:ByteParser}},7578:e=>{"use strict";e.exports={kWebSocketURL:Symbol("url"),kReadyState:Symbol("ready state"),kController:Symbol("controller"),kResponse:Symbol("response"),kBinaryType:Symbol("binary type"),kSentClose:Symbol("sent close"),kReceivedClose:Symbol("received close"),kByteParser:Symbol("byte parser")}},5515:(e,t,n)=>{"use strict";const{kReadyState:s,kController:i,kResponse:r,kBinaryType:o,kWebSocketURL:A}=n(7578);const{states:a,opcodes:c}=n(9188);const{MessageEvent:u,ErrorEvent:l}=n(2611);function isEstablished(e){return e[s]===a.OPEN}function isClosing(e){return e[s]===a.CLOSING}function isClosed(e){return e[s]===a.CLOSED}function fireEvent(e,t,n=Event,s){const i=new n(e,s);t.dispatchEvent(i)}function websocketMessageReceived(e,t,n){if(e[s]!==a.OPEN){return}let i;if(t===c.TEXT){try{i=new TextDecoder("utf-8",{fatal:true}).decode(n)}catch{failWebsocketConnection(e,"Received invalid UTF-8 in text frame.");return}}else if(t===c.BINARY){if(e[o]==="blob"){i=new Blob([n])}else{i=new Uint8Array(n).buffer}}fireEvent("message",e,u,{origin:e[A].origin,data:i})}function isValidSubprotocol(e){if(e.length===0){return false}for(const t of e){const e=t.charCodeAt(0);if(e<33||e>126||t==="("||t===")"||t==="<"||t===">"||t==="@"||t===","||t===";"||t===":"||t==="\\"||t==='"'||t==="/"||t==="["||t==="]"||t==="?"||t==="="||t==="{"||t==="}"||e===32||e===9){return false}}return true}function isValidStatusCode(e){if(e>=1e3&&e<1015){return e!==1004&&e!==1005&&e!==1006}return e>=3e3&&e<=4999}function failWebsocketConnection(e,t){const{[i]:n,[r]:s}=e;n.abort();if(s?.socket&&!s.socket.destroyed){s.socket.destroy()}if(t){fireEvent("error",e,l,{error:new Error(t)})}}e.exports={isEstablished:isEstablished,isClosing:isClosing,isClosed:isClosed,fireEvent:fireEvent,isValidSubprotocol:isValidSubprotocol,isValidStatusCode:isValidStatusCode,failWebsocketConnection:failWebsocketConnection,websocketMessageReceived:websocketMessageReceived}},4284:(e,t,n)=>{"use strict";const{webidl:s}=n(1744);const{DOMException:i}=n(1037);const{URLSerializer:r}=n(685);const{getGlobalOrigin:o}=n(1246);const{staticPropertyDescriptors:A,states:a,opcodes:c,emptyBuffer:u}=n(9188);const{kWebSocketURL:l,kReadyState:d,kController:p,kBinaryType:g,kResponse:h,kSentClose:f,kByteParser:E}=n(7578);const{isEstablished:m,isClosing:C,isValidSubprotocol:Q,failWebsocketConnection:I,fireEvent:B}=n(5515);const{establishWebSocketConnection:y}=n(5354);const{WebsocketFrameSend:b}=n(5444);const{ByteParser:w}=n(1688);const{kEnumerableProperty:R,isBlobLike:v}=n(3983);const{getGlobalDispatcher:k}=n(1892);const{types:S}=n(3837);let x=false;class WebSocket extends EventTarget{#l={open:null,error:null,close:null,message:null};#d=0;#p="";#g="";constructor(e,t=[]){super();s.argumentLengthCheck(arguments,1,{header:"WebSocket constructor"});if(!x){x=true;process.emitWarning("WebSockets are experimental, expect them to change at any time.",{code:"UNDICI-WS"})}const n=s.converters["DOMString or sequence or WebSocketInit"](t);e=s.converters.USVString(e);t=n.protocols;const r=o();let A;try{A=new URL(e,r)}catch(e){throw new i(e,"SyntaxError")}if(A.protocol==="http:"){A.protocol="ws:"}else if(A.protocol==="https:"){A.protocol="wss:"}if(A.protocol!=="ws:"&&A.protocol!=="wss:"){throw new i(`Expected a ws: or wss: protocol, got ${A.protocol}`,"SyntaxError")}if(A.hash||A.href.endsWith("#")){throw new i("Got fragment","SyntaxError")}if(typeof t==="string"){t=[t]}if(t.length!==new Set(t.map((e=>e.toLowerCase()))).size){throw new i("Invalid Sec-WebSocket-Protocol value","SyntaxError")}if(t.length>0&&!t.every((e=>Q(e)))){throw new i("Invalid Sec-WebSocket-Protocol value","SyntaxError")}this[l]=new URL(A.href);this[p]=y(A,t,this,(e=>this.#h(e)),n);this[d]=WebSocket.CONNECTING;this[g]="blob"}close(e=undefined,t=undefined){s.brandCheck(this,WebSocket);if(e!==undefined){e=s.converters["unsigned short"](e,{clamp:true})}if(t!==undefined){t=s.converters.USVString(t)}if(e!==undefined){if(e!==1e3&&(e<3e3||e>4999)){throw new i("invalid code","InvalidAccessError")}}let n=0;if(t!==undefined){n=Buffer.byteLength(t);if(n>123){throw new i(`Reason must be less than 123 bytes; received ${n}`,"SyntaxError")}}if(this[d]===WebSocket.CLOSING||this[d]===WebSocket.CLOSED){}else if(!m(this)){I(this,"Connection was closed before it was established.");this[d]=WebSocket.CLOSING}else if(!C(this)){const s=new b;if(e!==undefined&&t===undefined){s.frameData=Buffer.allocUnsafe(2);s.frameData.writeUInt16BE(e,0)}else if(e!==undefined&&t!==undefined){s.frameData=Buffer.allocUnsafe(2+n);s.frameData.writeUInt16BE(e,0);s.frameData.write(t,2,"utf-8")}else{s.frameData=u}const i=this[h].socket;i.write(s.createFrame(c.CLOSE),(e=>{if(!e){this[f]=true}}));this[d]=a.CLOSING}else{this[d]=WebSocket.CLOSING}}send(e){s.brandCheck(this,WebSocket);s.argumentLengthCheck(arguments,1,{header:"WebSocket.send"});e=s.converters.WebSocketSendData(e);if(this[d]===WebSocket.CONNECTING){throw new i("Sent before connected.","InvalidStateError")}if(!m(this)||C(this)){return}const t=this[h].socket;if(typeof e==="string"){const n=Buffer.from(e);const s=new b(n);const i=s.createFrame(c.TEXT);this.#d+=n.byteLength;t.write(i,(()=>{this.#d-=n.byteLength}))}else if(S.isArrayBuffer(e)){const n=Buffer.from(e);const s=new b(n);const i=s.createFrame(c.BINARY);this.#d+=n.byteLength;t.write(i,(()=>{this.#d-=n.byteLength}))}else if(ArrayBuffer.isView(e)){const n=Buffer.from(e,e.byteOffset,e.byteLength);const s=new b(n);const i=s.createFrame(c.BINARY);this.#d+=n.byteLength;t.write(i,(()=>{this.#d-=n.byteLength}))}else if(v(e)){const n=new b;e.arrayBuffer().then((e=>{const s=Buffer.from(e);n.frameData=s;const i=n.createFrame(c.BINARY);this.#d+=s.byteLength;t.write(i,(()=>{this.#d-=s.byteLength}))}))}}get readyState(){s.brandCheck(this,WebSocket);return this[d]}get bufferedAmount(){s.brandCheck(this,WebSocket);return this.#d}get url(){s.brandCheck(this,WebSocket);return r(this[l])}get extensions(){s.brandCheck(this,WebSocket);return this.#g}get protocol(){s.brandCheck(this,WebSocket);return this.#p}get onopen(){s.brandCheck(this,WebSocket);return this.#l.open}set onopen(e){s.brandCheck(this,WebSocket);if(this.#l.open){this.removeEventListener("open",this.#l.open)}if(typeof e==="function"){this.#l.open=e;this.addEventListener("open",e)}else{this.#l.open=null}}get onerror(){s.brandCheck(this,WebSocket);return this.#l.error}set onerror(e){s.brandCheck(this,WebSocket);if(this.#l.error){this.removeEventListener("error",this.#l.error)}if(typeof e==="function"){this.#l.error=e;this.addEventListener("error",e)}else{this.#l.error=null}}get onclose(){s.brandCheck(this,WebSocket);return this.#l.close}set onclose(e){s.brandCheck(this,WebSocket);if(this.#l.close){this.removeEventListener("close",this.#l.close)}if(typeof e==="function"){this.#l.close=e;this.addEventListener("close",e)}else{this.#l.close=null}}get onmessage(){s.brandCheck(this,WebSocket);return this.#l.message}set onmessage(e){s.brandCheck(this,WebSocket);if(this.#l.message){this.removeEventListener("message",this.#l.message)}if(typeof e==="function"){this.#l.message=e;this.addEventListener("message",e)}else{this.#l.message=null}}get binaryType(){s.brandCheck(this,WebSocket);return this[g]}set binaryType(e){s.brandCheck(this,WebSocket);if(e!=="blob"&&e!=="arraybuffer"){this[g]="blob"}else{this[g]=e}}#h(e){this[h]=e;const t=new w(this);t.on("drain",(function onParserDrain(){this.ws[h].socket.resume()}));e.socket.ws=this;this[E]=t;this[d]=a.OPEN;const n=e.headersList.get("sec-websocket-extensions");if(n!==null){this.#g=n}const s=e.headersList.get("sec-websocket-protocol");if(s!==null){this.#p=s}B("open",this)}}WebSocket.CONNECTING=WebSocket.prototype.CONNECTING=a.CONNECTING;WebSocket.OPEN=WebSocket.prototype.OPEN=a.OPEN;WebSocket.CLOSING=WebSocket.prototype.CLOSING=a.CLOSING;WebSocket.CLOSED=WebSocket.prototype.CLOSED=a.CLOSED;Object.defineProperties(WebSocket.prototype,{CONNECTING:A,OPEN:A,CLOSING:A,CLOSED:A,url:R,readyState:R,bufferedAmount:R,onopen:R,onerror:R,onclose:R,close:R,onmessage:R,binaryType:R,send:R,extensions:R,protocol:R,[Symbol.toStringTag]:{value:"WebSocket",writable:false,enumerable:false,configurable:true}});Object.defineProperties(WebSocket,{CONNECTING:A,OPEN:A,CLOSING:A,CLOSED:A});s.converters["sequence"]=s.sequenceConverter(s.converters.DOMString);s.converters["DOMString or sequence"]=function(e){if(s.util.Type(e)==="Object"&&Symbol.iterator in e){return s.converters["sequence"](e)}return s.converters.DOMString(e)};s.converters.WebSocketInit=s.dictionaryConverter([{key:"protocols",converter:s.converters["DOMString or sequence"],get defaultValue(){return[]}},{key:"dispatcher",converter:e=>e,get defaultValue(){return k()}},{key:"headers",converter:s.nullableConverter(s.converters.HeadersInit)}]);s.converters["DOMString or sequence or WebSocketInit"]=function(e){if(s.util.Type(e)==="Object"&&!(Symbol.iterator in e)){return s.converters.WebSocketInit(e)}return{protocols:s.converters["DOMString or sequence"](e)}};s.converters.WebSocketSendData=function(e){if(s.util.Type(e)==="Object"){if(v(e)){return s.converters.Blob(e,{strict:false})}if(ArrayBuffer.isView(e)||S.isAnyArrayBuffer(e)){return s.converters.BufferSource(e)}}return s.converters.USVString(e)};e.exports={WebSocket:WebSocket}},5840:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});Object.defineProperty(t,"v1",{enumerable:true,get:function(){return s.default}});Object.defineProperty(t,"v3",{enumerable:true,get:function(){return i.default}});Object.defineProperty(t,"v4",{enumerable:true,get:function(){return r.default}});Object.defineProperty(t,"v5",{enumerable:true,get:function(){return o.default}});Object.defineProperty(t,"NIL",{enumerable:true,get:function(){return A.default}});Object.defineProperty(t,"version",{enumerable:true,get:function(){return a.default}});Object.defineProperty(t,"validate",{enumerable:true,get:function(){return c.default}});Object.defineProperty(t,"stringify",{enumerable:true,get:function(){return u.default}});Object.defineProperty(t,"parse",{enumerable:true,get:function(){return l.default}});var s=_interopRequireDefault(n(8628));var i=_interopRequireDefault(n(6409));var r=_interopRequireDefault(n(5122));var o=_interopRequireDefault(n(9120));var A=_interopRequireDefault(n(5332));var a=_interopRequireDefault(n(1595));var c=_interopRequireDefault(n(6900));var u=_interopRequireDefault(n(8950));var l=_interopRequireDefault(n(2746));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}},4569:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6113));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function md5(e){if(Array.isArray(e)){e=Buffer.from(e)}else if(typeof e==="string"){e=Buffer.from(e,"utf8")}return s.default.createHash("md5").update(e).digest()}var i=md5;t["default"]=i},5332:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var n="00000000-0000-0000-0000-000000000000";t["default"]=n},2746:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6900));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function parse(e){if(!(0,s.default)(e)){throw TypeError("Invalid UUID")}let t;const n=new Uint8Array(16);n[0]=(t=parseInt(e.slice(0,8),16))>>>24;n[1]=t>>>16&255;n[2]=t>>>8&255;n[3]=t&255;n[4]=(t=parseInt(e.slice(9,13),16))>>>8;n[5]=t&255;n[6]=(t=parseInt(e.slice(14,18),16))>>>8;n[7]=t&255;n[8]=(t=parseInt(e.slice(19,23),16))>>>8;n[9]=t&255;n[10]=(t=parseInt(e.slice(24,36),16))/1099511627776&255;n[11]=t/4294967296&255;n[12]=t>>>24&255;n[13]=t>>>16&255;n[14]=t>>>8&255;n[15]=t&255;return n}var i=parse;t["default"]=i},814:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var n=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;t["default"]=n},807:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=rng;var s=_interopRequireDefault(n(6113));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const i=new Uint8Array(256);let r=i.length;function rng(){if(r>i.length-16){s.default.randomFillSync(i);r=0}return i.slice(r,r+=16)}},5274:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6113));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function sha1(e){if(Array.isArray(e)){e=Buffer.from(e)}else if(typeof e==="string"){e=Buffer.from(e,"utf8")}return s.default.createHash("sha1").update(e).digest()}var i=sha1;t["default"]=i},8950:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6900));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const i=[];for(let e=0;e<256;++e){i.push((e+256).toString(16).substr(1))}function stringify(e,t=0){const n=(i[e[t+0]]+i[e[t+1]]+i[e[t+2]]+i[e[t+3]]+"-"+i[e[t+4]]+i[e[t+5]]+"-"+i[e[t+6]]+i[e[t+7]]+"-"+i[e[t+8]]+i[e[t+9]]+"-"+i[e[t+10]]+i[e[t+11]]+i[e[t+12]]+i[e[t+13]]+i[e[t+14]]+i[e[t+15]]).toLowerCase();if(!(0,s.default)(n)){throw TypeError("Stringified UUID is invalid")}return n}var r=stringify;t["default"]=r},8628:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(807));var i=_interopRequireDefault(n(8950));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}let r;let o;let A=0;let a=0;function v1(e,t,n){let c=t&&n||0;const u=t||new Array(16);e=e||{};let l=e.node||r;let d=e.clockseq!==undefined?e.clockseq:o;if(l==null||d==null){const t=e.random||(e.rng||s.default)();if(l==null){l=r=[t[0]|1,t[1],t[2],t[3],t[4],t[5]]}if(d==null){d=o=(t[6]<<8|t[7])&16383}}let p=e.msecs!==undefined?e.msecs:Date.now();let g=e.nsecs!==undefined?e.nsecs:a+1;const h=p-A+(g-a)/1e4;if(h<0&&e.clockseq===undefined){d=d+1&16383}if((h<0||p>A)&&e.nsecs===undefined){g=0}if(g>=1e4){throw new Error("uuid.v1(): Can't create more than 10M uuids/sec")}A=p;a=g;o=d;p+=122192928e5;const f=((p&268435455)*1e4+g)%4294967296;u[c++]=f>>>24&255;u[c++]=f>>>16&255;u[c++]=f>>>8&255;u[c++]=f&255;const E=p/4294967296*1e4&268435455;u[c++]=E>>>8&255;u[c++]=E&255;u[c++]=E>>>24&15|16;u[c++]=E>>>16&255;u[c++]=d>>>8|128;u[c++]=d&255;for(let e=0;e<6;++e){u[c+e]=l[e]}return t||(0,i.default)(u)}var c=v1;t["default"]=c},6409:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(5998));var i=_interopRequireDefault(n(4569));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const r=(0,s.default)("v3",48,i.default);var o=r;t["default"]=o},5998:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=_default;t.URL=t.DNS=void 0;var s=_interopRequireDefault(n(8950));var i=_interopRequireDefault(n(2746));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function stringToBytes(e){e=unescape(encodeURIComponent(e));const t=[];for(let n=0;n{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(807));var i=_interopRequireDefault(n(8950));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function v4(e,t,n){e=e||{};const r=e.random||(e.rng||s.default)();r[6]=r[6]&15|64;r[8]=r[8]&63|128;if(t){n=n||0;for(let e=0;e<16;++e){t[n+e]=r[e]}return t}return(0,i.default)(r)}var r=v4;t["default"]=r},9120:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(5998));var i=_interopRequireDefault(n(5274));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const r=(0,s.default)("v5",80,i.default);var o=r;t["default"]=o},6900:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(814));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function validate(e){return typeof e==="string"&&s.default.test(e)}var i=validate;t["default"]=i},1595:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t["default"]=void 0;var s=_interopRequireDefault(n(6900));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function version(e){if(!(0,s.default)(e)){throw TypeError("Invalid UUID")}return parseInt(e.substr(14,1),16)}var i=version;t["default"]=i},8542:(e,t,n)=>{t.formatArgs=formatArgs;t.save=save;t.load=load;t.useColors=useColors;t.storage=localstorage();t.destroy=(()=>{let e=false;return()=>{if(!e){e=true;console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}}})();t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function useColors(){if(typeof window!=="undefined"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs)){return true}if(typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)){return false}return typeof document!=="undefined"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window!=="undefined"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||typeof navigator!=="undefined"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function formatArgs(t){t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff);if(!this.useColors){return}const n="color: "+this.color;t.splice(1,0,n,"color: inherit");let s=0;let i=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{if(e==="%%"){return}s++;if(e==="%c"){i=s}}));t.splice(i,0,n)}t.log=console.debug||console.log||(()=>{});function save(e){try{if(e){t.storage.setItem("debug",e)}else{t.storage.removeItem("debug")}}catch(e){}}function load(){let e;try{e=t.storage.getItem("debug")}catch(e){}if(!e&&typeof process!=="undefined"&&"env"in process){e=process.env.DEBUG}return e}function localstorage(){try{return localStorage}catch(e){}}e.exports=n(2624)(t);const{formatters:s}=e.exports;s.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},2624:(e,t,n)=>{function setup(e){createDebug.debug=createDebug;createDebug.default=createDebug;createDebug.coerce=coerce;createDebug.disable=disable;createDebug.enable=enable;createDebug.enabled=enabled;createDebug.humanize=n(6628);createDebug.destroy=destroy;Object.keys(e).forEach((t=>{createDebug[t]=e[t]}));createDebug.names=[];createDebug.skips=[];createDebug.formatters={};function selectColor(e){let t=0;for(let n=0;n{if(t==="%%"){return"%"}r++;const i=createDebug.formatters[s];if(typeof i==="function"){const s=e[r];t=i.call(n,s);e.splice(r,1);r--}return t}));createDebug.formatArgs.call(n,e);const o=n.log||createDebug.log;o.apply(n,e)}debug.namespace=e;debug.useColors=createDebug.useColors();debug.color=createDebug.selectColor(e);debug.extend=extend;debug.destroy=createDebug.destroy;Object.defineProperty(debug,"enabled",{enumerable:true,configurable:false,get:()=>{if(n!==null){return n}if(s!==createDebug.namespaces){s=createDebug.namespaces;i=createDebug.enabled(e)}return i},set:e=>{n=e}});if(typeof createDebug.init==="function"){createDebug.init(debug)}return debug}function extend(e,t){const n=createDebug(this.namespace+(typeof t==="undefined"?":":t)+e);n.log=this.log;return n}function enable(e){createDebug.save(e);createDebug.namespaces=e;createDebug.names=[];createDebug.skips=[];let t;const n=(typeof e==="string"?e:"").split(/[\s,]+/);const s=n.length;for(t=0;t"-"+e))].join(",");createDebug.enable("");return e}function enabled(e){if(e[e.length-1]==="*"){return true}let t;let n;for(t=0,n=createDebug.skips.length;t{if(typeof process==="undefined"||process.type==="renderer"||process.browser===true||process.__nwjs){e.exports=n(8542)}else{e.exports=n(5305)}},5305:(e,t,n)=>{const s=n(6224);const i=n(3837);t.init=init;t.log=log;t.formatArgs=formatArgs;t.save=save;t.load=load;t.useColors=useColors;t.destroy=i.deprecate((()=>{}),"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");t.colors=[6,2,3,4,5,1];try{const e=n(1415);if(e&&(e.stderr||e).level>=2){t.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221]}}catch(e){}t.inspectOpts=Object.keys(process.env).filter((e=>/^debug_/i.test(e))).reduce(((e,t)=>{const n=t.substring(6).toLowerCase().replace(/_([a-z])/g,((e,t)=>t.toUpperCase()));let s=process.env[t];if(/^(yes|on|true|enabled)$/i.test(s)){s=true}else if(/^(no|off|false|disabled)$/i.test(s)){s=false}else if(s==="null"){s=null}else{s=Number(s)}e[n]=s;return e}),{});function useColors(){return"colors"in t.inspectOpts?Boolean(t.inspectOpts.colors):s.isatty(process.stderr.fd)}function formatArgs(t){const{namespace:n,useColors:s}=this;if(s){const s=this.color;const i="[3"+(s<8?s:"8;5;"+s);const r=` ${i};1m${n} `;t[0]=r+t[0].split("\n").join("\n"+r);t.push(i+"m+"+e.exports.humanize(this.diff)+"")}else{t[0]=getDate()+n+" "+t[0]}}function getDate(){if(t.inspectOpts.hideDate){return""}return(new Date).toISOString()+" "}function log(...e){return process.stderr.write(i.format(...e)+"\n")}function save(e){if(e){process.env.DEBUG=e}else{delete process.env.DEBUG}}function load(){return process.env.DEBUG}function init(e){e.inspectOpts={};const n=Object.keys(t.inspectOpts);for(let s=0;se.trim())).join(" ")};r.O=function(e){this.inspectOpts.colors=this.useColors;return i.inspect(e,this.inspectOpts)}},3787:e=>{"use strict";e.exports=(e,t=process.argv)=>{const n=e.startsWith("-")?"":e.length===1?"-":"--";const s=t.indexOf(n+e);const i=t.indexOf("--");return s!==-1&&(i===-1||s{var t=1e3;var n=t*60;var s=n*60;var i=s*24;var r=i*7;var o=i*365.25;e.exports=function(e,t){t=t||{};var n=typeof e;if(n==="string"&&e.length>0){return parse(e)}else if(n==="number"&&isFinite(e)){return t.long?fmtLong(e):fmtShort(e)}throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function parse(e){e=String(e);if(e.length>100){return}var A=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!A){return}var a=parseFloat(A[1]);var c=(A[2]||"ms").toLowerCase();switch(c){case"years":case"year":case"yrs":case"yr":case"y":return a*o;case"weeks":case"week":case"w":return a*r;case"days":case"day":case"d":return a*i;case"hours":case"hour":case"hrs":case"hr":case"h":return a*s;case"minutes":case"minute":case"mins":case"min":case"m":return a*n;case"seconds":case"second":case"secs":case"sec":case"s":return a*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return a;default:return undefined}}function fmtShort(e){var r=Math.abs(e);if(r>=i){return Math.round(e/i)+"d"}if(r>=s){return Math.round(e/s)+"h"}if(r>=n){return Math.round(e/n)+"m"}if(r>=t){return Math.round(e/t)+"s"}return e+"ms"}function fmtLong(e){var r=Math.abs(e);if(r>=i){return plural(e,r,i,"day")}if(r>=s){return plural(e,r,s,"hour")}if(r>=n){return plural(e,r,n,"minute")}if(r>=t){return plural(e,r,t,"second")}return e+" ms"}function plural(e,t,n,s){var i=t>=n*1.5;return Math.round(e/n)+" "+s+(i?"s":"")}},1415:(e,t,n)=>{"use strict";const s=n(2037);const i=n(6224);const r=n(3787);const{env:o}=process;let A;if(r("no-color")||r("no-colors")||r("color=false")||r("color=never")){A=0}else if(r("color")||r("colors")||r("color=true")||r("color=always")){A=1}if("FORCE_COLOR"in o){if(o.FORCE_COLOR==="true"){A=1}else if(o.FORCE_COLOR==="false"){A=0}else{A=o.FORCE_COLOR.length===0?1:Math.min(parseInt(o.FORCE_COLOR,10),3)}}function translateLevel(e){if(e===0){return false}return{level:e,hasBasic:true,has256:e>=2,has16m:e>=3}}function supportsColor(e,t){if(A===0){return 0}if(r("color=16m")||r("color=full")||r("color=truecolor")){return 3}if(r("color=256")){return 2}if(e&&!t&&A===undefined){return 0}const n=A||0;if(o.TERM==="dumb"){return n}if(process.platform==="win32"){const e=s.release().split(".");if(Number(e[0])>=10&&Number(e[2])>=10586){return Number(e[2])>=14931?3:2}return 1}if("CI"in o){if(["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","GITHUB_ACTIONS","BUILDKITE"].some((e=>e in o))||o.CI_NAME==="codeship"){return 1}return n}if("TEAMCITY_VERSION"in o){return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(o.TEAMCITY_VERSION)?1:0}if(o.COLORTERM==="truecolor"){return 3}if("TERM_PROGRAM"in o){const e=parseInt((o.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(o.TERM_PROGRAM){case"iTerm.app":return e>=3?3:2;case"Apple_Terminal":return 2}}if(/-256(color)?$/i.test(o.TERM)){return 2}if(/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(o.TERM)){return 1}if("COLORTERM"in o){return 1}return n}function getSupportLevel(e){const t=supportsColor(e,e&&e.isTTY);return translateLevel(t)}e.exports={supportsColor:getSupportLevel,stdout:translateLevel(supportsColor(true,i.isatty(1))),stderr:translateLevel(supportsColor(true,i.isatty(2)))}},4978:module=>{module.exports=eval("require")("util/types")},9491:e=>{"use strict";e.exports=require("assert")},852:e=>{"use strict";e.exports=require("async_hooks")},4300:e=>{"use strict";e.exports=require("buffer")},6206:e=>{"use strict";e.exports=require("console")},6113:e=>{"use strict";e.exports=require("crypto")},7643:e=>{"use strict";e.exports=require("diagnostics_channel")},2361:e=>{"use strict";e.exports=require("events")},7147:e=>{"use strict";e.exports=require("fs")},3685:e=>{"use strict";e.exports=require("http")},5158:e=>{"use strict";e.exports=require("http2")},5687:e=>{"use strict";e.exports=require("https")},1808:e=>{"use strict";e.exports=require("net")},5673:e=>{"use strict";e.exports=require("node:events")},4492:e=>{"use strict";e.exports=require("node:stream")},7261:e=>{"use strict";e.exports=require("node:util")},2037:e=>{"use strict";e.exports=require("os")},1017:e=>{"use strict";e.exports=require("path")},4074:e=>{"use strict";e.exports=require("perf_hooks")},3477:e=>{"use strict";e.exports=require("querystring")},2781:e=>{"use strict";e.exports=require("stream")},5356:e=>{"use strict";e.exports=require("stream/web")},1576:e=>{"use strict";e.exports=require("string_decoder")},4404:e=>{"use strict";e.exports=require("tls")},6224:e=>{"use strict";e.exports=require("tty")},7310:e=>{"use strict";e.exports=require("url")},3837:e=>{"use strict";e.exports=require("util")},1267:e=>{"use strict";e.exports=require("worker_threads")},9796:e=>{"use strict";e.exports=require("zlib")},526:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.grant=t.BaseAuthAPI=t.AuthApiError=void 0;const s=n(8361);const i=n(5467);const r=n(6966);const o=n(8295);class AuthApiError extends Error{constructor(e,t,n,s,i){super(t||e);this.error=e;this.error_description=t;this.statusCode=n;this.body=s;this.headers=i;this.name="AuthApiError"}}t.AuthApiError=AuthApiError;function parseErrorBody(e){const t=JSON.parse(e);let n;if(t.error){n=t}else{n={error:t.code,error_description:t.description}}return n}async function parseError(e){const t=await e.text();try{const n=parseErrorBody(t);return new AuthApiError(n.error,n.error_description,e.status,t,e.headers)}catch(n){return new s.ResponseError(e.status,t,e.headers,"Response returned an error code")}}class BaseAuthAPI extends i.BaseAPI{constructor(e){super({...e,baseUrl:`https://${e.domain}`,middleware:e.telemetry!==false?[new o.TelemetryMiddleware(e)]:[],parseError:parseError,retry:{enabled:false,...e.retry}});this.domain=e.domain;this.clientId=e.clientId;this.clientSecret=e.clientSecret;this.clientAssertionSigningKey=e.clientAssertionSigningKey;this.clientAssertionSigningAlg=e.clientAssertionSigningAlg}async addClientAuthentication(e){return(0,r.addClientAuthentication)({payload:e,domain:this.domain,clientId:this.clientId,clientSecret:this.clientSecret,clientAssertionSigningKey:this.clientAssertionSigningKey,clientAssertionSigningAlg:this.clientAssertionSigningAlg})}}t.BaseAuthAPI=BaseAuthAPI;async function grant(e,t,{idTokenValidateOptions:n,initOverrides:s}={},r,o,A){const a=await A({path:"/oauth/token",method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:r,...t,grant_type:e})},s);const c=await i.JSONApiResponse.fromResponse(a);if(c.data.id_token){await o.validate(c.data.id_token,n)}return c}t.grant=grant},6966:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.addClientAuthentication=void 0;const o=r(n(4061));const A=n(6008);const addClientAuthentication=async({payload:e,domain:t,clientId:n,clientAssertionSigningKey:s,clientAssertionSigningAlg:i,clientSecret:r})=>{const a=e.client_id||n;if(s&&!e.client_assertion){const n=i||"RS256";const r=await o.importPKCS8(s,n);e.client_assertion=await new o.SignJWT({}).setProtectedHeader({alg:n}).setIssuedAt().setSubject(a).setJti((0,A.v4)()).setIssuer(a).setAudience(`https://${t}/`).setExpirationTime("2mins").sign(r);e.client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer"}else if(r&&!e.client_secret){e.client_secret=r}if((!e.client_secret||e.client_secret.trim().length===0)&&(!e.client_assertion||e.client_assertion.trim().length===0)){throw new Error("The client_secret or client_assertion field is required.")}return e};t.addClientAuthentication=addClientAuthentication},8657:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.Database=void 0;const s=n(4667);const i=n(5467);const r=n(526);class Database extends r.BaseAuthAPI{async signUp(e,t){(0,i.validateRequiredRequestParams)(e,["email","password","connection"]);const n=await this.request({path:"/dbconnections/signup",method:"POST",headers:{"Content-Type":"application/json"},body:{client_id:this.clientId,...e}},t);return s.JSONApiResponse.fromResponse(n)}async changePassword(e,t){(0,i.validateRequiredRequestParams)(e,["email","connection"]);const n=await this.request({path:"/dbconnections/change_password",method:"POST",headers:{"Content-Type":"application/json"},body:{client_id:this.clientId,...e}},t);return s.TextApiResponse.fromResponse(n)}}t.Database=Database},2932:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.IDTokenValidator=t.IdTokenValidatorError=void 0;const o=r(n(4061));const A=60;class IdTokenValidatorError extends Error{}t.IdTokenValidatorError=IdTokenValidatorError;class IDTokenValidator{constructor({domain:e,clientId:t,clientSecret:n,agent:s,headers:i,timeoutDuration:r,idTokenSigningAlg:a="RS256",clockTolerance:c=A}){this.jwks=o.createRemoteJWKSet(new URL(`https://${e}/.well-known/jwks.json`),{timeoutDuration:r,agent:s,headers:i});this.alg=a;this.audience=t;this.secret=(new TextEncoder).encode(n);this.issuer=`https://${e}/`;this.clockTolerance=c}async validate(e,{nonce:t,maxAge:n,organization:s}={}){const i=this.alg==="HS256"?this.secret:this.jwks;const r=o.decodeProtectedHeader(e);const A=o.decodeJwt(e);if(r.alg!=="RS256"&&r.alg!=="HS256"){throw new Error(`Signature algorithm of "${r.alg}" is not supported. Expected the ID token to be signed with "RS256" or "HS256".`)}if(!A.iss||typeof A.iss!=="string"){throw new IdTokenValidatorError("Issuer (iss) claim must be a string present in the ID token")}if(A.iss!==this.issuer){throw new IdTokenValidatorError(`Issuer (iss) claim mismatch in the ID token; expected "${this.issuer}", found "${A.iss}"`)}if(!A.sub||typeof A.sub!=="string"){throw new IdTokenValidatorError("Subject (sub) claim must be a string present in the ID token")}if(!A.aud||!(typeof A.aud==="string"||Array.isArray(A.aud))){throw new IdTokenValidatorError("Audience (aud) claim must be a string or array of strings present in the ID token")}if(Array.isArray(A.aud)&&!A.aud.includes(this.audience)){throw new IdTokenValidatorError(`Audience (aud) claim mismatch in the ID token; expected "${this.audience}" but was not one of "${A.aud.join(", ")}"`)}else if(typeof A.aud==="string"&&A.aud!==this.audience){throw new IdTokenValidatorError(`Audience (aud) claim mismatch in the ID token; expected "${this.audience}" but found "${A.aud}"`)}if(s){if(s.indexOf("org_")===0){if(!A.org_id||typeof A.org_id!=="string"){throw new Error("Organization Id (org_id) claim must be a string present in the ID token")}if(A.org_id!==s){throw new Error(`Organization Id (org_id) claim value mismatch in the ID token; expected "${s}", found "${A.org_id}"'`)}}else{if(!A.org_name||typeof A.org_name!=="string"){throw new Error("Organization Name (org_name) claim must be a string present in the ID token")}if(A.org_name!==s.toLowerCase()){throw new Error(`Organization Name (org_name) claim value mismatch in the ID token; expected "${s}", found "${A.org_name}"'`)}}}const a=Math.floor(Date.now()/1e3);if(!A.exp||typeof A.exp!=="number"){throw new IdTokenValidatorError("Expiration Time (exp) claim must be a number present in the ID token")}const c=A.exp+this.clockTolerance;if(a>c){throw new IdTokenValidatorError(`Expiration Time (exp) claim error in the ID token; current time (${a}) is after expiration time (${c})`)}if(!A.iat||typeof A.iat!=="number"){throw new IdTokenValidatorError("Issued At (iat) claim must be a number present in the ID token")}if(t||A.nonce){if(!A.nonce||typeof A.nonce!=="string"){throw new IdTokenValidatorError("Nonce (nonce) claim must be a string present in the ID token")}if(A.nonce!==t){throw new IdTokenValidatorError(`Nonce (nonce) claim mismatch in the ID token; expected "${t}", found "${A.nonce}"`)}}if(Array.isArray(A.aud)&&A.aud.length>1){if(!A.azp||typeof A.azp!=="string"){throw new IdTokenValidatorError("Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values")}if(A.azp!==this.audience){throw new IdTokenValidatorError(`Authorized Party (azp) claim mismatch in the ID token; expected "${this.audience}", found "${A.azp}"`)}}if(n){if(!A.auth_time||typeof A.auth_time!=="number"){throw new IdTokenValidatorError("Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified")}const e=A.auth_time+n+this.clockTolerance;if(a>e){throw new IdTokenValidatorError(`Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Currrent time (${a}) is after last auth at ${e}`)}}await o.jwtVerify(e,i,{issuer:this.issuer,audience:this.audience,clockTolerance:this.clockTolerance,maxTokenAge:n,algorithms:["HS256","RS256"]})}}t.IDTokenValidator=IDTokenValidator},3776:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__exportStar||function(e,t){for(var n in e)if(n!=="default"&&!Object.prototype.hasOwnProperty.call(t,n))s(t,e,n)};Object.defineProperty(t,"__esModule",{value:true});t.AuthenticationClient=t.AuthApiError=t.IdTokenValidatorError=void 0;const r=n(8657);const o=n(9424);const A=n(4475);i(n(8657),t);i(n(9424),t);i(n(4475),t);var a=n(2932);Object.defineProperty(t,"IdTokenValidatorError",{enumerable:true,get:function(){return a.IdTokenValidatorError}});var c=n(526);Object.defineProperty(t,"AuthApiError",{enumerable:true,get:function(){return c.AuthApiError}});class AuthenticationClient{constructor(e){this.database=new r.Database(e);this.oauth=new o.OAuth(e);this.passwordless=new A.Passwordless(e)}}t.AuthenticationClient=AuthenticationClient},9424:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.OAuth=void 0;const s=n(5467);const i=n(526);const r=n(2932);class OAuth extends i.BaseAuthAPI{constructor(e){super(e);this.idTokenValidator=new r.IDTokenValidator(e)}async authorizationCodeGrant(e,t={}){(0,s.validateRequiredRequestParams)(e,["code"]);return(0,i.grant)("authorization_code",await this.addClientAuthentication(e),t,this.clientId,this.idTokenValidator,this.request.bind(this))}async authorizationCodeGrantWithPKCE(e,t={}){(0,s.validateRequiredRequestParams)(e,["code","code_verifier"]);return(0,i.grant)("authorization_code",await this.addClientAuthentication(e),t,this.clientId,this.idTokenValidator,this.request.bind(this))}async clientCredentialsGrant(e,t={}){(0,s.validateRequiredRequestParams)(e,["audience"]);return(0,i.grant)("client_credentials",await this.addClientAuthentication(e),t,this.clientId,this.idTokenValidator,this.request.bind(this))}async passwordGrant(e,t={}){(0,s.validateRequiredRequestParams)(e,["username","password"]);return(0,i.grant)(e.realm?"http://auth0.com/oauth/grant-type/password-realm":"password",await this.addClientAuthentication(e),t,this.clientId,this.idTokenValidator,this.request.bind(this))}async refreshTokenGrant(e,t={}){(0,s.validateRequiredRequestParams)(e,["refresh_token"]);return(0,i.grant)("refresh_token",await this.addClientAuthentication(e),t,this.clientId,this.idTokenValidator,this.request.bind(this))}async revokeRefreshToken(e,t={}){(0,s.validateRequiredRequestParams)(e,["token"]);const n=await this.request({path:"/oauth/revoke",method:"POST",headers:{"Content-Type":"application/json"},body:await this.addClientAuthentication({client_id:this.clientId,...e})},t.initOverrides);return s.VoidApiResponse.fromResponse(n)}}t.OAuth=OAuth},4475:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.Passwordless=void 0;const s=n(5467);const i=n(526);const r=n(2932);class Passwordless extends i.BaseAuthAPI{constructor(e){super(e);this.idTokenValidator=new r.IDTokenValidator(e)}async sendEmail(e,t){(0,s.validateRequiredRequestParams)(e,["email"]);const n=await this.request({path:"/passwordless/start",method:"POST",headers:{"Content-Type":"application/json"},body:await this.addClientAuthentication({client_id:this.clientId,connection:"email",...e})},t);return s.VoidApiResponse.fromResponse(n)}async sendSMS(e,t){(0,s.validateRequiredRequestParams)(e,["phone_number"]);const n=await this.request({path:"/passwordless/start",method:"POST",headers:{"Content-Type":"application/json"},body:await this.addClientAuthentication({client_id:this.clientId,connection:"sms",...e})},t);return s.VoidApiResponse.fromResponse(n)}async loginWithEmail(e,t={}){(0,s.validateRequiredRequestParams)(e,["email","code"]);const{email:n,code:r,...o}=e;return(0,i.grant)("http://auth0.com/oauth/grant-type/passwordless/otp",await this.addClientAuthentication({username:n,otp:r,realm:"email",...o}),t,this.clientId,this.idTokenValidator,this.request.bind(this))}async loginWithSMS(e,t={}){(0,s.validateRequiredRequestParams)(e,["phone_number","code"]);const{phone_number:n,code:r,...o}=e;return(0,i.grant)("http://auth0.com/oauth/grant-type/passwordless/otp",await this.addClientAuthentication({username:n,otp:r,realm:"sms",...o}),t,this.clientId,this.idTokenValidator,this.request.bind(this))}}t.Passwordless=Passwordless},4035:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.PostProviderRequestNameEnum=t.PatchProviderRequestNameEnum=void 0;const s=n(195);t.PatchProviderRequestNameEnum=s.EmailProviderUpdateNameEnum;t.PostProviderRequestNameEnum=s.EmailProviderCreateNameEnum},4712:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__exportStar||function(e,t){for(var n in e)if(n!=="default"&&!Object.prototype.hasOwnProperty.call(t,n))s(t,e,n)};Object.defineProperty(t,"__esModule",{value:true});i(n(3119),t);i(n(3776),t);i(n(6650),t);i(n(8361),t);i(n(4667),t);i(n(4035),t)},8361:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.RequiredError=t.FetchError=t.TimeoutError=t.ResponseError=void 0;class ResponseError extends Error{constructor(e,t,n,s){super(s);this.statusCode=e;this.body=t;this.headers=n;this.name="ResponseError"}}t.ResponseError=ResponseError;class TimeoutError extends Error{constructor(){super("The request was timed out.");this.name="TimeoutError"}}t.TimeoutError=TimeoutError;class FetchError extends Error{constructor(e,t){super(t);this.cause=e;this.name="FetchError"}}t.FetchError=FetchError;class RequiredError extends Error{constructor(e,t){super(t);this.field=e;this.name="RequiredError"}}t.RequiredError=RequiredError},3551:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__exportStar||function(e,t){for(var n in e)if(n!=="default"&&!Object.prototype.hasOwnProperty.call(t,n))s(t,e,n)};Object.defineProperty(t,"__esModule",{value:true});i(n(8361),t);i(n(4667),t)},8295:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.TelemetryMiddleware=void 0;const s=n(8200);const i=n(4061);class TelemetryMiddleware{constructor(e){this.clientInfo=e.clientInfo||(0,s.generateClientInfo)()}async pre(e){if("string"===typeof this.clientInfo.name&&this.clientInfo.name.length>0){e.init.headers={...e.init.headers,"Auth0-Client":i.base64url.encode(JSON.stringify(this.clientInfo))}}return{url:e.url,init:e.init}}}t.TelemetryMiddleware=TelemetryMiddleware},4667:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.TextApiResponse=t.VoidApiResponse=t.JSONApiResponse=void 0;class JSONApiResponse{constructor(e,t,n,s){this.data=e;this.headers=t;this.status=n;this.statusText=s}static async fromResponse(e){const t=await e.json();return new JSONApiResponse(t,e.headers,e.status,e.statusText)}}t.JSONApiResponse=JSONApiResponse;class VoidApiResponse{constructor(e,t,n){this.headers=e;this.status=t;this.statusText=n}static async fromResponse(e){return new VoidApiResponse(e.headers,e.status,e.statusText)}}t.VoidApiResponse=VoidApiResponse;class TextApiResponse{constructor(e,t,n,s){this.data=e;this.headers=t;this.status=n;this.statusText=s}static async fromResponse(e){const t=await e.text();return new TextApiResponse(t,e.headers,e.status,e.statusText)}}t.TextApiResponse=TextApiResponse},577:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.retry=void 0;const n=250;const s=1e4;const i=3;const r=10;const o=500;function getRandomInt(e,t){e=Math.ceil(e);t=Math.floor(t);return Math.floor(Math.random()*(t-e)+e)}async function pause(e){return new Promise((t=>setTimeout(t,e)))}function retry(e,{maxRetries:t,retryWhen:A}){const a=Math.min(r,t!==null&&t!==void 0?t:i);let c=0;const retryAndWait=async()=>{let t;t=await e();if((A||[429]).includes(t.status)&&c{const n=new AbortController;const s=setTimeout((()=>{n.abort()}),this.timeoutDuration);try{return await this.fetchApi(e,{signal:n.signal,...t})}catch(e){if(e.name==="AbortError"){throw new o.TimeoutError}throw e}finally{clearTimeout(s)}};this.fetch=async(e,t)=>{var n;let s={url:e,init:t};for(const e of this.middleware){if(e.pre){s=await e.pre({fetch:this.fetchWithTimeout,...s})||s}}let i=undefined;let A=undefined;try{i=((n=this.configuration.retry)===null||n===void 0?void 0:n.enabled)!==false?await(0,r.retry)((()=>this.fetchWithTimeout(s.url,s.init)),{...this.configuration.retry}):await this.fetchWithTimeout(s.url,s.init)}catch(e){A=e}if(A||!i.ok){for(const e of this.middleware){if(e.onError){i=await e.onError({fetch:this.fetchWithTimeout,...s,error:A,response:i?i.clone():undefined})||i}}if(i===undefined){throw new o.FetchError(A,"The request failed and the interceptors did not return an alternative response")}}else{for(const e of this.middleware){if(e.post){i=await e.post({fetch:this.fetchApi,...s,response:i.clone()})||i}}}return i};if(e.baseUrl===null||e.baseUrl===undefined){throw new Error("Must provide a base URL for the API")}if("string"!==typeof e.baseUrl||e.baseUrl.length===0){throw new Error("The provided base URL is invalid")}this.middleware=e.middleware||[];this.fetchApi=e.fetch||fetch;this.parseError=e.parseError;this.timeoutDuration=typeof e.timeoutDuration==="number"?e.timeoutDuration:1e4}async request(e,t){const{url:n,init:s}=await this.createFetchParams(e,t);const i=await this.fetch(n,s);if(i&&i.status>=200&&i.status<300){return i}const r=await this.parseError(i);throw r}async createFetchParams(e,t){let n=this.configuration.baseUrl+e.path;if(e.query!==undefined&&Object.keys(e.query).length!==0){n+=`?${querystring(e.query)}`}const s=Object.assign({},this.configuration.headers,e.headers);Object.keys(s).forEach((e=>s[e]===undefined?delete s[e]:{}));const i=typeof t==="function"?t:async()=>t;const r={method:e.method,headers:s,body:e.body,agent:this.configuration.agent};const o={...r,...await i({init:r,context:e})};const A={...o,body:o.body instanceof FormData||o.body instanceof URLSearchParams||o.body instanceof Blob?o.body:JSON.stringify(o.body)};return{url:n,init:A}}}t.BaseAPI=BaseAPI;t.COLLECTION_FORMATS={csv:",",ssv:" ",tsv:"\t",pipes:"|"};function querystring(e){return Object.keys(e).map((t=>querystringSingleKey(t,e[t]))).filter((e=>e.length>0)).join("&")}function querystringSingleKey(e,t){if(t instanceof Array){const n=t.map((e=>encodeURIComponent(String(e)))).join(`&${encodeURIComponent(e)}=`);return`${encodeURIComponent(e)}=${n}`}return`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`}function validateRequiredRequestParams(e,t){t.forEach((t=>{if(e[t]===null||e[t]===undefined){throw new o.RequiredError(t,`Required parameter requestParameters.${t} was null or undefined.`)}}))}t.validateRequiredRequestParams=validateRequiredRequestParams;function applyQueryParams(e,n){return n.reduce(((n,{key:s,config:i})=>{let r;if(i.isArray){if(i.isCollectionFormatMulti){r=e[s]}else{r=e[s].join(t.COLLECTION_FORMATS[i.collectionFormat])}}else{if(e[s]!==undefined){r=e[s]}}return r!==undefined?{...n,[s]:r}:n}),{})}t.applyQueryParams=applyQueryParams;async function parseFormParam(e){let t=e;t=typeof t=="number"||typeof t=="boolean"?""+t:t;return t}t.parseFormParam=parseFormParam},7248:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__exportStar||function(e,t){for(var n in e)if(n!=="default"&&!Object.prototype.hasOwnProperty.call(t,n))s(t,e,n)};Object.defineProperty(t,"__esModule",{value:true});t.ManagementClientBase=void 0;i(n(892),t);i(n(195),t);const r=n(892);class ManagementClientBase{constructor(e){this.configuration=e;this.actions=new r.ActionsManager(this.configuration);this.anomaly=new r.AnomalyManager(this.configuration);this.attackProtection=new r.AttackProtectionManager(this.configuration);this.blacklists=new r.BlacklistsManager(this.configuration);this.branding=new r.BrandingManager(this.configuration);this.clientGrants=new r.ClientGrantsManager(this.configuration);this.clients=new r.ClientsManager(this.configuration);this.connections=new r.ConnectionsManager(this.configuration);this.customDomains=new r.CustomDomainsManager(this.configuration);this.deviceCredentials=new r.DeviceCredentialsManager(this.configuration);this.emailTemplates=new r.EmailTemplatesManager(this.configuration);this.emails=new r.EmailsManager(this.configuration);this.grants=new r.GrantsManager(this.configuration);this.guardian=new r.GuardianManager(this.configuration);this.hooks=new r.HooksManager(this.configuration);this.jobs=new r.JobsManager(this.configuration);this.keys=new r.KeysManager(this.configuration);this.logStreams=new r.LogStreamsManager(this.configuration);this.logs=new r.LogsManager(this.configuration);this.organizations=new r.OrganizationsManager(this.configuration);this.prompts=new r.PromptsManager(this.configuration);this.resourceServers=new r.ResourceServersManager(this.configuration);this.roles=new r.RolesManager(this.configuration);this.rules=new r.RulesManager(this.configuration);this.rulesConfigs=new r.RulesConfigsManager(this.configuration);this.stats=new r.StatsManager(this.configuration);this.tenants=new r.TenantsManager(this.configuration);this.tickets=new r.TicketsManager(this.configuration);this.userBlocks=new r.UserBlocksManager(this.configuration);this.users=new r.UsersManager(this.configuration);this.usersByEmail=new r.UsersByEmailManager(this.configuration)}}t.ManagementClientBase=ManagementClientBase},8048:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.ActionsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class ActionsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"force",config:{}}]);const s=await this.request({path:`/actions/actions/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",query:n},t);return o.VoidApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/actions/actions/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getVersion(e,t){o.validateRequiredRequestParams(e,["actionId","id"]);const n=await this.request({path:`/actions/actions/{actionId}/versions/{id}`.replace("{actionId}",encodeURIComponent(String(e.actionId))).replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getVersions(e,t){o.validateRequiredRequestParams(e,["actionId"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}}]);const s=await this.request({path:`/actions/actions/{actionId}/versions`.replace("{actionId}",encodeURIComponent(String(e.actionId))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"triggerId",config:{}},{key:"actionName",config:{}},{key:"deployed",config:{}},{key:"page",config:{}},{key:"per_page",config:{}},{key:"installed",config:{}}]);const s=await this.request({path:`/actions/actions`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getTriggerBindings(e,t){o.validateRequiredRequestParams(e,["triggerId"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}}]);const s=await this.request({path:`/actions/triggers/{triggerId}/bindings`.replace("{triggerId}",encodeURIComponent(String(e.triggerId))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getExecution(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/actions/executions/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getAllTriggers(e){const t=await this.request({path:`/actions/triggers`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/actions/actions/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async updateTriggerBindings(e,t,n){o.validateRequiredRequestParams(e,["triggerId"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/actions/triggers/{triggerId}/bindings`.replace("{triggerId}",encodeURIComponent(String(e.triggerId))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/actions/actions`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async deploy(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/actions/actions/{id}/deploy`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST"},t);return o.JSONApiResponse.fromResponse(n)}async deployVersion(e,t,n){o.validateRequiredRequestParams(e,["id","actionId"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/actions/actions/{actionId}/versions/{id}/deploy`.replace("{id}",encodeURIComponent(String(e.id))).replace("{actionId}",encodeURIComponent(String(e.actionId))),method:"POST",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async test(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/actions/actions/{id}/test`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}}t.ActionsManager=ActionsManager},1734:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.AnomalyManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class AnomalyManager extends A{async deleteBlockedIp(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/anomaly/blocks/ips/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async checkIfIpIsBlocked(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/anomaly/blocks/ips/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.VoidApiResponse.fromResponse(n)}}t.AnomalyManager=AnomalyManager},3716:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.AttackProtectionManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class AttackProtectionManager extends A{async getBreachedPasswordDetectionConfig(e){const t=await this.request({path:`/attack-protection/breached-password-detection`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getBruteForceConfig(e){const t=await this.request({path:`/attack-protection/brute-force-protection`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getSuspiciousIpThrottlingConfig(e){const t=await this.request({path:`/attack-protection/suspicious-ip-throttling`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async updateBreachedPasswordDetectionConfig(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/attack-protection/breached-password-detection`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updateBruteForceConfig(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/attack-protection/brute-force-protection`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updateSuspiciousIpThrottlingConfig(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/attack-protection/suspicious-ip-throttling`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.AttackProtectionManager=AttackProtectionManager},6654:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.BlacklistsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class BlacklistsManager extends A{async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"aud",config:{}}]);const s=await this.request({path:`/blacklists/tokens`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async add(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/blacklists/tokens`,method:"POST",headers:n,body:e},t);return o.VoidApiResponse.fromResponse(s)}}t.BlacklistsManager=BlacklistsManager},1147:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.BrandingManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class BrandingManager extends A{async deleteTheme(e,t){o.validateRequiredRequestParams(e,["themeId"]);const n=await this.request({path:`/branding/themes/{themeId}`.replace("{themeId}",encodeURIComponent(String(e.themeId))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteUniversalLoginTemplate(e){const t=await this.request({path:`/branding/templates/universal-login`,method:"DELETE"},e);return o.VoidApiResponse.fromResponse(t)}async getSettings(e){const t=await this.request({path:`/branding`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getTheme(e,t){o.validateRequiredRequestParams(e,["themeId"]);const n=await this.request({path:`/branding/themes/{themeId}`.replace("{themeId}",encodeURIComponent(String(e.themeId))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getDefaultTheme(e){const t=await this.request({path:`/branding/themes/default`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getUniversalLoginTemplate(e){const t=await this.request({path:`/branding/templates/universal-login`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async updateSettings(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/branding`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updateTheme(e,t,n){o.validateRequiredRequestParams(e,["themeId"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/branding/themes/{themeId}`.replace("{themeId}",encodeURIComponent(String(e.themeId))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async createTheme(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/branding/themes`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setUniversalLoginTemplate(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/branding/templates/universal-login`,method:"PUT",headers:n,body:e},t);return o.VoidApiResponse.fromResponse(s)}}t.BrandingManager=BrandingManager},5345:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.ClientGrantsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class ClientGrantsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/client-grants/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}},{key:"audience",config:{}},{key:"client_id",config:{}}]);const s=await this.request({path:`/client-grants`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/client-grants/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/client-grants`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.ClientGrantsManager=ClientGrantsManager},2909:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.ClientsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class ClientsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["client_id"]);const n=await this.request({path:`/clients/{client_id}`.replace("{client_id}",encodeURIComponent(String(e.client_id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteCredential(e,t){o.validateRequiredRequestParams(e,["client_id","credential_id"]);const n=await this.request({path:`/clients/{client_id}/credentials/{credential_id}`.replace("{client_id}",encodeURIComponent(String(e.client_id))).replace("{credential_id}",encodeURIComponent(String(e.credential_id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}},{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"is_global",config:{}},{key:"is_first_party",config:{}},{key:"app_type",config:{}}]);const s=await this.request({path:`/clients`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["client_id"]);const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/clients/{client_id}`.replace("{client_id}",encodeURIComponent(String(e.client_id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getCredentials(e,t){o.validateRequiredRequestParams(e,["client_id"]);const n=await this.request({path:`/clients/{client_id}/credentials`.replace("{client_id}",encodeURIComponent(String(e.client_id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getCredential(e,t){o.validateRequiredRequestParams(e,["client_id","credential_id"]);const n=await this.request({path:`/clients/{client_id}/credentials/{credential_id}`.replace("{client_id}",encodeURIComponent(String(e.client_id))).replace("{credential_id}",encodeURIComponent(String(e.credential_id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async update(e,t,n){o.validateRequiredRequestParams(e,["client_id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/clients/{client_id}`.replace("{client_id}",encodeURIComponent(String(e.client_id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async updateCredential(e,t,n){o.validateRequiredRequestParams(e,["client_id","credential_id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/clients/{client_id}/credentials/{credential_id}`.replace("{client_id}",encodeURIComponent(String(e.client_id))).replace("{credential_id}",encodeURIComponent(String(e.credential_id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/clients`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async createCredential(e,t,n){o.validateRequiredRequestParams(e,["client_id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/clients/{client_id}/credentials`.replace("{client_id}",encodeURIComponent(String(e.client_id))),method:"POST",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async rotateClientSecret(e,t){o.validateRequiredRequestParams(e,["client_id"]);const n=await this.request({path:`/clients/{client_id}/rotate-secret`.replace("{client_id}",encodeURIComponent(String(e.client_id))),method:"POST"},t);return o.JSONApiResponse.fromResponse(n)}}t.ClientsManager=ClientsManager},1626:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.ConnectionsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class ConnectionsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/connections/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteUserByEmail(e,t){o.validateRequiredRequestParams(e,["id","email"]);const n=o.applyQueryParams(e,[{key:"email",config:{}}]);const s=await this.request({path:`/connections/{id}/users`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",query:n},t);return o.VoidApiResponse.fromResponse(s)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}},{key:"from",config:{}},{key:"take",config:{}},{key:"strategy",config:{isArray:true,isCollectionFormatMulti:true}},{key:"name",config:{}},{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/connections`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/connections/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async checkStatus(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/connections/{id}/status`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.VoidApiResponse.fromResponse(n)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/connections/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/connections`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.ConnectionsManager=ConnectionsManager},6948:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.CustomDomainsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class CustomDomainsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/custom-domains/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e){const t=await this.request({path:`/custom-domains`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/custom-domains/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/custom-domains/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/custom-domains`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async verify(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/custom-domains/{id}/verify`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST"},t);return o.JSONApiResponse.fromResponse(n)}}t.CustomDomainsManager=CustomDomainsManager},4558:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.DeviceCredentialsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class DeviceCredentialsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/device-credentials/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"fields",config:{}},{key:"include_fields",config:{}},{key:"user_id",config:{}},{key:"client_id",config:{}},{key:"type",config:{}}]);const s=await this.request({path:`/device-credentials`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async createPublicKey(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/device-credentials`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.DeviceCredentialsManager=DeviceCredentialsManager},537:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.EmailTemplatesManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class EmailTemplatesManager extends A{async get(e,t){o.validateRequiredRequestParams(e,["templateName"]);const n=await this.request({path:`/email-templates/{templateName}`.replace("{templateName}",encodeURIComponent(String(e.templateName))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async update(e,t,n){o.validateRequiredRequestParams(e,["templateName"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/email-templates/{templateName}`.replace("{templateName}",encodeURIComponent(String(e.templateName))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/email-templates`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async put(e,t,n){o.validateRequiredRequestParams(e,["templateName"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/email-templates/{templateName}`.replace("{templateName}",encodeURIComponent(String(e.templateName))),method:"PUT",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}}t.EmailTemplatesManager=EmailTemplatesManager},3541:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.EmailsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class EmailsManager extends A{async get(e={},t){const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/emails/provider`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async update(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/emails/provider`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async configure(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/emails/provider`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.EmailsManager=EmailsManager},513:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.GrantsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class GrantsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/grants/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteByUserId(e,t){o.validateRequiredRequestParams(e,["user_id"]);const n=o.applyQueryParams(e,[{key:"user_id",config:{}}]);const s=await this.request({path:`/grants`,method:"DELETE",query:n},t);return o.VoidApiResponse.fromResponse(s)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}},{key:"user_id",config:{}},{key:"client_id",config:{}},{key:"audience",config:{}}]);const s=await this.request({path:`/grants`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}}t.GrantsManager=GrantsManager},5428:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.GuardianManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class GuardianManager extends A{async deleteGuardianEnrollment(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/guardian/enrollments/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getPushNotificationProviderAPNS(e){const t=await this.request({path:`/guardian/factors/push-notification/providers/apns`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getGuardianEnrollment(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/guardian/enrollments/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getPhoneFactorTemplates(e){const t=await this.request({path:`/guardian/factors/phone/templates`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getSmsFactorTemplates(e){const t=await this.request({path:`/guardian/factors/sms/templates`,method:"GET"},e);return t.status===204?o.VoidApiResponse.fromResponse(t):o.JSONApiResponse.fromResponse(t)}async getFactors(e){const t=await this.request({path:`/guardian/factors`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getPhoneFactorMessageTypes(e){const t=await this.request({path:`/guardian/factors/phone/message-types`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getPhoneFactorSelectedProvider(e){const t=await this.request({path:`/guardian/factors/phone/selected-provider`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getPhoneFactorProviderTwilio(e){const t=await this.request({path:`/guardian/factors/phone/providers/twilio`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getPushNotificationSelectedProvider(e){const t=await this.request({path:`/guardian/factors/push-notification/selected-provider`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getPolicies(e){const t=await this.request({path:`/guardian/policies`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getSmsSelectedProvider(e){const t=await this.request({path:`/guardian/factors/sms/selected-provider`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getSmsFactorProviderTwilio(e){const t=await this.request({path:`/guardian/factors/sms/providers/twilio`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async getPushNotificationProviderSNS(e){const t=await this.request({path:`/guardian/factors/push-notification/providers/sns`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async updatePushNotificationProviderAPNS(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/push-notification/providers/apns`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updatePushNotificationProviderFCM(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/push-notification/providers/fcm`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updatePushNotificationProviderSNS(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/push-notification/providers/sns`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async createEnrollmentTicket(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/enrollments/ticket`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setPushNotificationProviderAPNS(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/push-notification/providers/apns`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setPhoneFactorTemplates(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/phone/templates`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setSmsFactorTemplates(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/sms/templates`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updateFactor(e,t,n){o.validateRequiredRequestParams(e,["name"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/guardian/factors/{name}`.replace("{name}",encodeURIComponent(String(e.name))),method:"PUT",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async setPushNotificationProviderFCM(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/push-notification/providers/fcm`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updatePhoneFactorMessageTypes(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/phone/message-types`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updatePhoneFactorSelectedProvider(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/phone/selected-provider`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setPushNotificationSelectedProvider(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/push-notification/selected-provider`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updatePolicies(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/policies`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setSmsSelectedProvider(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/sms/selected-provider`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setSmsFactorProviderTwilio(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/sms/providers/twilio`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async setPushNotificationProviderSNS(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/push-notification/providers/sns`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updatePhoneFactorProviderTwilio(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/guardian/factors/phone/providers/twilio`,method:"PUT",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.GuardianManager=GuardianManager},8476:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.HooksManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class HooksManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/hooks/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteSecrets(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/hooks/{id}/secrets`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"enabled",config:{}},{key:"fields",config:{}},{key:"triggerId",config:{}}]);const s=await this.request({path:`/hooks`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"fields",config:{}}]);const s=await this.request({path:`/hooks/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getSecrets(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/hooks/{id}/secrets`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/hooks/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async updateSecrets(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/hooks/{id}/secrets`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/hooks`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async addSecrets(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/hooks/{id}/secrets`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}}t.HooksManager=HooksManager},892:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__exportStar||function(e,t){for(var n in e)if(n!=="default"&&!Object.prototype.hasOwnProperty.call(t,n))s(t,e,n)};Object.defineProperty(t,"__esModule",{value:true});i(n(8048),t);i(n(1734),t);i(n(3716),t);i(n(6654),t);i(n(1147),t);i(n(5345),t);i(n(2909),t);i(n(1626),t);i(n(6948),t);i(n(4558),t);i(n(537),t);i(n(3541),t);i(n(513),t);i(n(5428),t);i(n(8476),t);i(n(486),t);i(n(2318),t);i(n(1118),t);i(n(4415),t);i(n(1302),t);i(n(6462),t);i(n(9160),t);i(n(2347),t);i(n(8556),t);i(n(683),t);i(n(5670),t);i(n(74),t);i(n(5744),t);i(n(2521),t);i(n(1713),t);i(n(6207),t)},486:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.JobsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class JobsManager extends A{async getErrors(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/jobs/{id}/errors`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return n.status===204?o.VoidApiResponse.fromResponse(n):o.JSONApiResponse.fromResponse(n)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/jobs/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async exportUsers(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/jobs/users-exports`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async importUsers(e,t){const n=new FormData;if(e.users!==undefined){n.append("users",await o.parseFormParam(e.users))}if(e.connection_id!==undefined){n.append("connection_id",await o.parseFormParam(e.connection_id))}if(e.upsert!==undefined){n.append("upsert",await o.parseFormParam(e.upsert))}if(e.external_id!==undefined){n.append("external_id",await o.parseFormParam(e.external_id))}if(e.send_completion_email!==undefined){n.append("send_completion_email",await o.parseFormParam(e.send_completion_email))}const s=await this.request({path:`/jobs/users-imports`,method:"POST",body:n},t);return o.JSONApiResponse.fromResponse(s)}async verifyEmail(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/jobs/verification-email`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.JobsManager=JobsManager},2318:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.KeysManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class KeysManager extends A{async get(e,t){o.validateRequiredRequestParams(e,["kid"]);const n=await this.request({path:`/keys/signing/{kid}`.replace("{kid}",encodeURIComponent(String(e.kid))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getAll(e){const t=await this.request({path:`/keys/signing`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async rotate(e){const t=await this.request({path:`/keys/signing/rotate`,method:"POST"},e);return o.JSONApiResponse.fromResponse(t)}async revoke(e,t){o.validateRequiredRequestParams(e,["kid"]);const n=await this.request({path:`/keys/signing/{kid}/revoke`.replace("{kid}",encodeURIComponent(String(e.kid))),method:"PUT"},t);return o.JSONApiResponse.fromResponse(n)}}t.KeysManager=KeysManager},1118:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.LogStreamsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class LogStreamsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/log-streams/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e){const t=await this.request({path:`/log-streams`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/log-streams/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/log-streams/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/log-streams`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.LogStreamsManager=LogStreamsManager},4415:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.LogsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class LogsManager extends A{async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"sort",config:{}},{key:"fields",config:{}},{key:"include_fields",config:{}},{key:"include_totals",config:{}},{key:"from",config:{}},{key:"take",config:{}},{key:"q",config:{}}]);const s=await this.request({path:`/logs`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/logs/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}}t.LogsManager=LogsManager},1302:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.OrganizationsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class OrganizationsManager extends A{async deleteEnabledConnection(e,t){o.validateRequiredRequestParams(e,["id","connectionId"]);const n=await this.request({path:`/organizations/{id}/enabled_connections/{connectionId}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{connectionId}",encodeURIComponent(String(e.connectionId))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteInvitation(e,t){o.validateRequiredRequestParams(e,["id","invitation_id"]);const n=await this.request({path:`/organizations/{id}/invitations/{invitation_id}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{invitation_id}",encodeURIComponent(String(e.invitation_id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteMembers(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}/members`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async deleteMemberRoles(e,t,n){o.validateRequiredRequestParams(e,["id","user_id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}/members/{user_id}/roles`.replace("{id}",encodeURIComponent(String(e.id))).replace("{user_id}",encodeURIComponent(String(e.user_id))),method:"DELETE",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/organizations/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getEnabledConnections(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/organizations/{id}/enabled_connections`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getEnabledConnection(e,t){o.validateRequiredRequestParams(e,["id","connectionId"]);const n=await this.request({path:`/organizations/{id}/enabled_connections/{connectionId}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{connectionId}",encodeURIComponent(String(e.connectionId))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getInvitations(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"fields",config:{}},{key:"include_fields",config:{}},{key:"sort",config:{}}]);const s=await this.request({path:`/organizations/{id}/invitations`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getInvitation(e,t){o.validateRequiredRequestParams(e,["id","invitation_id"]);const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/organizations/{id}/invitations/{invitation_id}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{invitation_id}",encodeURIComponent(String(e.invitation_id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getMembers(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"from",config:{}},{key:"take",config:{}},{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/organizations/{id}/members`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getByName(e,t){o.validateRequiredRequestParams(e,["name"]);const n=await this.request({path:`/organizations/name/{name}`.replace("{name}",encodeURIComponent(String(e.name))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getMemberRoles(e,t){o.validateRequiredRequestParams(e,["id","user_id"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/organizations/{id}/members/{user_id}/roles`.replace("{id}",encodeURIComponent(String(e.id))).replace("{user_id}",encodeURIComponent(String(e.user_id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"from",config:{}},{key:"take",config:{}},{key:"sort",config:{}}]);const s=await this.request({path:`/organizations`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/organizations/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async updateEnabledConnection(e,t,n){o.validateRequiredRequestParams(e,["id","connectionId"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}/enabled_connections/{connectionId}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{connectionId}",encodeURIComponent(String(e.connectionId))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async addEnabledConnection(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}/enabled_connections`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async createInvitation(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}/invitations`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async addMembers(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}/members`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async addMemberRoles(e,t,n){o.validateRequiredRequestParams(e,["id","user_id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/organizations/{id}/members/{user_id}/roles`.replace("{id}",encodeURIComponent(String(e.id))).replace("{user_id}",encodeURIComponent(String(e.user_id))),method:"POST",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/organizations`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.OrganizationsManager=OrganizationsManager},6462:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.PromptsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class PromptsManager extends A{async getCustomTextByLanguage(e,t){o.validateRequiredRequestParams(e,["prompt","language"]);const n=await this.request({path:`/prompts/{prompt}/custom-text/{language}`.replace("{prompt}",encodeURIComponent(String(e.prompt))).replace("{language}",encodeURIComponent(String(e.language))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async get(e){const t=await this.request({path:`/prompts`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async update(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/prompts`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updateCustomTextByLanguage(e,t,n){o.validateRequiredRequestParams(e,["prompt","language"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/prompts/{prompt}/custom-text/{language}`.replace("{prompt}",encodeURIComponent(String(e.prompt))).replace("{language}",encodeURIComponent(String(e.language))),method:"PUT",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}}t.PromptsManager=PromptsManager},9160:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.ResourceServersManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class ResourceServersManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/resource-servers/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/resource-servers`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"include_fields",config:{}}]);const s=await this.request({path:`/resource-servers/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/resource-servers/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/resource-servers`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.ResourceServersManager=ResourceServersManager},2347:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.RolesManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class RolesManager extends A{async deletePermissions(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/roles/{id}/permissions`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/roles/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getPermissions(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/roles/{id}/permissions`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getUsers(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}},{key:"from",config:{}},{key:"take",config:{}}]);const s=await this.request({path:`/roles/{id}/users`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}},{key:"name_filter",config:{}}]);const s=await this.request({path:`/roles`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/roles/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/roles/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async addPermissions(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/roles/{id}/permissions`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async assignUsers(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/roles/{id}/users`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/roles`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.RolesManager=RolesManager},683:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.RulesConfigsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class RulesConfigsManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["key"]);const n=await this.request({path:`/rules-configs/{key}`.replace("{key}",encodeURIComponent(String(e.key))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e){const t=await this.request({path:`/rules-configs`,method:"GET"},e);return o.JSONApiResponse.fromResponse(t)}async set(e,t,n){o.validateRequiredRequestParams(e,["key"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/rules-configs/{key}`.replace("{key}",encodeURIComponent(String(e.key))),method:"PUT",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}}t.RulesConfigsManager=RulesConfigsManager},8556:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.RulesManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class RulesManager extends A{async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/rules/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"enabled",config:{}},{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/rules`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/rules/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/rules/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/rules`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.RulesManager=RulesManager},5670:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.StatsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class StatsManager extends A{async getActiveUsersCount(e){const t=await this.request({path:`/stats/active-users`,method:"GET"},e);return o.TextApiResponse.fromResponse(t)}async getDaily(e={},t){const n=o.applyQueryParams(e,[{key:"from",config:{}},{key:"to",config:{}}]);const s=await this.request({path:`/stats/daily`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}}t.StatsManager=StatsManager},74:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.TenantsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class TenantsManager extends A{async updateSettings(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/tenants/settings`,method:"PATCH",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async getSettings(e={},t){const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/tenants/settings`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}}t.TenantsManager=TenantsManager},5744:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.TicketsManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class TicketsManager extends A{async verifyEmail(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/tickets/email-verification`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async changePassword(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/tickets/password-change`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}}t.TicketsManager=TicketsManager},2521:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.UserBlocksManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class UserBlocksManager extends A{async deleteAll(e,t){o.validateRequiredRequestParams(e,["identifier"]);const n=o.applyQueryParams(e,[{key:"identifier",config:{}}]);const s=await this.request({path:`/user-blocks`,method:"DELETE",query:n},t);return o.VoidApiResponse.fromResponse(s)}async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/user-blocks/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAll(e,t){o.validateRequiredRequestParams(e,["identifier"]);const n=o.applyQueryParams(e,[{key:"identifier",config:{}},{key:"consider_brute_force_enablement",config:{}}]);const s=await this.request({path:`/user-blocks`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"consider_brute_force_enablement",config:{}}]);const s=await this.request({path:`/user-blocks/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}}t.UserBlocksManager=UserBlocksManager},6207:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.UsersByEmailManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class UsersByEmailManager extends A{async getByEmail(e,t){o.validateRequiredRequestParams(e,["email"]);const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}},{key:"email",config:{}}]);const s=await this.request({path:`/users-by-email`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}}t.UsersByEmailManager=UsersByEmailManager},1713:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)if(n!=="default"&&Object.prototype.hasOwnProperty.call(e,n))s(t,e,n);i(t,e);return t};Object.defineProperty(t,"__esModule",{value:true});t.UsersManager=void 0;const o=r(n(5467));const{BaseAPI:A}=o;class UsersManager extends A{async deleteAuthenticationMethods(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/users/{id}/authentication-methods`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteAuthenticationMethod(e,t){o.validateRequiredRequestParams(e,["id","authentication_method_id"]);const n=await this.request({path:`/users/{id}/authentication-methods/{authentication_method_id}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{authentication_method_id}",encodeURIComponent(String(e.authentication_method_id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteAllAuthenticators(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/users/{id}/authenticators`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deleteMultifactorProvider(e,t){o.validateRequiredRequestParams(e,["id","provider"]);const n=await this.request({path:`/users/{id}/multifactor/{provider}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{provider}",encodeURIComponent(String(e.provider))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async deletePermissions(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/permissions`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async unlink(e,t){o.validateRequiredRequestParams(e,["id","provider","user_id"]);const n=await this.request({path:`/users/{id}/identities/{provider}/{user_id}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{provider}",encodeURIComponent(String(e.provider))).replace("{user_id}",encodeURIComponent(String(e.user_id))),method:"DELETE"},t);return o.JSONApiResponse.fromResponse(n)}async deleteRoles(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/roles`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async delete(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/users/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"DELETE"},t);return o.VoidApiResponse.fromResponse(n)}async getAuthenticationMethods(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/users/{id}/authentication-methods`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getAuthenticationMethod(e,t){o.validateRequiredRequestParams(e,["id","authentication_method_id"]);const n=await this.request({path:`/users/{id}/authentication-methods/{authentication_method_id}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{authentication_method_id}",encodeURIComponent(String(e.authentication_method_id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getEnrollments(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/users/{id}/enrollments`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET"},t);return o.JSONApiResponse.fromResponse(n)}async getLogs(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"sort",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/users/{id}/logs`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getPermissions(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/users/{id}/permissions`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getUserOrganizations(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/users/{id}/organizations`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getRoles(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"per_page",config:{}},{key:"page",config:{}},{key:"include_totals",config:{}}]);const s=await this.request({path:`/users/{id}/roles`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async getAll(e={},t){const n=o.applyQueryParams(e,[{key:"page",config:{}},{key:"per_page",config:{}},{key:"include_totals",config:{}},{key:"sort",config:{}},{key:"connection",config:{}},{key:"fields",config:{}},{key:"include_fields",config:{}},{key:"q",config:{}},{key:"search_engine",config:{}}]);const s=await this.request({path:`/users`,method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async get(e,t){o.validateRequiredRequestParams(e,["id"]);const n=o.applyQueryParams(e,[{key:"fields",config:{}},{key:"include_fields",config:{}}]);const s=await this.request({path:`/users/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"GET",query:n},t);return o.JSONApiResponse.fromResponse(s)}async updateAuthenticationMethod(e,t,n){o.validateRequiredRequestParams(e,["id","authentication_method_id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/authentication-methods/{authentication_method_id}`.replace("{id}",encodeURIComponent(String(e.id))).replace("{authentication_method_id}",encodeURIComponent(String(e.authentication_method_id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async update(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}`.replace("{id}",encodeURIComponent(String(e.id))),method:"PATCH",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async createAuthenticationMethod(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/authentication-methods`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async link(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/identities`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}async invalidateRememberBrowser(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/users/{id}/multifactor/actions/invalidate-remember-browser`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST"},t);return o.VoidApiResponse.fromResponse(n)}async assignPermissions(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/permissions`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async regenerateRecoveryCode(e,t){o.validateRequiredRequestParams(e,["id"]);const n=await this.request({path:`/users/{id}/recovery-code-regeneration`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST"},t);return o.JSONApiResponse.fromResponse(n)}async assignRoles(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/roles`.replace("{id}",encodeURIComponent(String(e.id))),method:"POST",headers:s,body:t},n);return o.VoidApiResponse.fromResponse(i)}async create(e,t){const n={};n["Content-Type"]="application/json";const s=await this.request({path:`/users`,method:"POST",headers:n,body:e},t);return o.JSONApiResponse.fromResponse(s)}async updateAuthenticationMethods(e,t,n){o.validateRequiredRequestParams(e,["id"]);const s={};s["Content-Type"]="application/json";const i=await this.request({path:`/users/{id}/authentication-methods`.replace("{id}",encodeURIComponent(String(e.id))),method:"PUT",headers:s,body:t},n);return o.JSONApiResponse.fromResponse(i)}}t.UsersManager=UsersManager},195:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.GetExecution200ResponseStatusEnum=t.GetEmailTemplatesByTemplateName200ResponseTemplateEnum=t.GetCredentials200ResponseInnerAlgEnum=t.GetBruteForceProtection200ResponseModeEnum=t.GetBruteForceProtection200ResponseShieldsEnum=t.GetBreachedPasswordDetection200ResponseStagePreUserRegistrationShieldsEnum=t.GetBreachedPasswordDetection200ResponseMethodEnum=t.GetBreachedPasswordDetection200ResponseAdminNotificationFrequencyEnum=t.GetBreachedPasswordDetection200ResponseShieldsEnum=t.GetAuthenticationMethods200ResponseOneOfInnerAuthenticationMethodsInnerTypeEnum=t.GetAuthenticationMethods200ResponseOneOfInnerPreferredAuthenticationMethodEnum=t.GetAuthenticationMethods200ResponseOneOfInnerTypeEnum=t.GetActions200ResponseActionsInnerSupportedTriggersInnerIdAnyOf=t.GetActions200ResponseActionsInnerIntegrationCurrentReleaseRequiredSecretsInnerTypeEnum=t.GetActions200ResponseActionsInnerIntegrationFeatureTypeEnum=t.GetActions200ResponseActionsInnerStatusEnum=t.GetActionVersions200ResponseVersionsInnerStatusEnum=t.FactorNameEnum=t.EnrollmentStatusEnum=t.EmailTemplateUpdateTemplateEnum=t.EmailProviderUpdateNameEnum=t.EmailProviderCreateNameEnum=t.DeviceCredentialCreateTypeEnum=t.DeviceCredentialTypeEnum=t.CustomDomainTypeEnum=t.CustomDomainStatusEnum=t.ConnectionUpdateOptionsSetUserRootAttributesEnum=t.ConnectionUpdateOptionsPasswordPolicyEnum=t.ConnectionCreateOptionsPasskeyOptionsChallengeUiEnum=t.ConnectionCreateOptionsSetUserRootAttributesEnum=t.ConnectionCreateOptionsPasswordPolicyEnum=t.ConnectionCreateStrategyEnum=t.ClientUpdateJwtConfigurationAlgEnum=t.ClientUpdateOrganizationRequireBehaviorEnum=t.ClientUpdateOrganizationUsageEnum=t.ClientUpdateAppTypeEnum=t.ClientUpdateTokenEndpointAuthMethodEnum=t.ClientRefreshTokenExpirationTypeEnum=t.ClientRefreshTokenRotationTypeEnum=t.ClientJwtConfigurationAlgEnum=t.ClientCreateJwtConfigurationAlgEnum=t.ClientCreateClientAuthenticationMethodsPrivateKeyJwtCredentialsInnerAlgEnum=t.ClientCreateClientAuthenticationMethodsPrivateKeyJwtCredentialsInnerCredentialTypeEnum=t.ClientCreateOrganizationRequireBehaviorEnum=t.ClientCreateOrganizationUsageEnum=t.ClientCreateAppTypeEnum=t.ClientCreateTokenEndpointAuthMethodEnum=t.ClientOrganizationRequireBehaviorEnum=t.ClientOrganizationUsageEnum=t.ClientTokenEndpointAuthMethodEnum=void 0;t.PostBrandingThemeRequestBordersInputsStyleEnum=t.PostBrandingThemeRequestBordersButtonsStyleEnum=t.PostAuthenticationMethodsRequestPreferredAuthenticationMethodEnum=t.PostAuthenticationMethodsRequestTypeEnum=t.PostAuthenticationMethods201ResponsePreferredAuthenticationMethodEnum=t.PostAuthenticationMethods201ResponseTypeEnum=t.PatchSuspiciousIpThrottlingRequestShieldsEnum=t.PatchLogStreamsByIdRequestSinkOneOf3MixpanelRegionEnum=t.PatchLogStreamsByIdRequestSinkOneOfDatadogRegionEnum=t.PatchLogStreamsByIdRequestStatusEnum=t.PatchEmailTemplatesByTemplateNameRequestTemplateEnum=t.PatchCustomDomainsByIdRequestCustomClientIpHeaderEnum=t.PatchCustomDomainsByIdRequestTlsPolicyEnum=t.PatchBruteForceProtectionRequestModeEnum=t.PatchBruteForceProtectionRequestShieldsEnum=t.PatchBreachedPasswordDetectionRequestStagePreUserRegistrationShieldsEnum=t.PatchBreachedPasswordDetectionRequestMethodEnum=t.PatchBreachedPasswordDetectionRequestAdminNotificationFrequencyEnum=t.PatchBreachedPasswordDetectionRequestShieldsEnum=t.PatchBindingsRequestBindingsInnerOneOfRefTypeEnum=t.PatchAuthenticationMethodsByAuthenticationMethodIdRequestPreferredAuthenticationMethodEnum=t.JobFormatEnum=t.HookCreateTriggerIdEnum=t.GetSuspiciousIpThrottling200ResponseShieldsEnum=t.GetPnProviders200ResponseProviderEnum=t.GetPhoneProviders200ResponseProviderEnum=t.GetMessageTypes200ResponseMessageTypesEnum=t.GetLogStreams200ResponseInnerOneOfSinkHttpContentFormatEnum=t.GetLogStreams200ResponseInnerOneOfFiltersInnerNameEnum=t.GetLogStreams200ResponseInnerOneOfFiltersInnerTypeEnum=t.GetLogStreams200ResponseInnerOneOf7SinkMixpanelRegionEnum=t.GetLogStreams200ResponseInnerOneOf7TypeEnum=t.GetLogStreams200ResponseInnerOneOf7StatusEnum=t.GetLogStreams200ResponseInnerOneOf6TypeEnum=t.GetLogStreams200ResponseInnerOneOf6StatusEnum=t.GetLogStreams200ResponseInnerOneOf5TypeEnum=t.GetLogStreams200ResponseInnerOneOf5StatusEnum=t.GetLogStreams200ResponseInnerOneOf4TypeEnum=t.GetLogStreams200ResponseInnerOneOf4StatusEnum=t.GetLogStreams200ResponseInnerOneOf3SinkDatadogRegionEnum=t.GetLogStreams200ResponseInnerOneOf3TypeEnum=t.GetLogStreams200ResponseInnerOneOf3StatusEnum=t.GetLogStreams200ResponseInnerOneOf2SinkAzureRegionEnum=t.GetLogStreams200ResponseInnerOneOf2TypeEnum=t.GetLogStreams200ResponseInnerOneOf2StatusEnum=t.GetLogStreams200ResponseInnerOneOf1SinkAwsRegionEnum=t.GetLogStreams200ResponseInnerOneOf1TypeEnum=t.GetLogStreams200ResponseInnerOneOf1StatusEnum=t.GetLogStreams200ResponseInnerOneOfTypeEnum=t.GetLogStreams200ResponseInnerOneOfStatusEnum=void 0;t.TenantSettingsUpdateFlagsChangePwdFlowV1Enum=t.TenantSettingsUpdateDeviceFlowCharsetEnum=t.TenantSettingsUpdateEnabledLocalesEnum=t.TenantSettingsSessionCookieModeEnum=t.TenantSettingsDeviceFlowCharsetEnum=t.TenantSettingsEnabledLocalesEnum=t.ResourceServerUpdateTokenDialectEnum=t.ResourceServerUpdateSigningAlgEnum=t.ResourceServerCreateTokenDialectEnum=t.ResourceServerCreateSigningAlgEnum=t.ResourceServerTokenDialectEnum=t.ResourceServerSigningAlgEnum=t.PutAuthenticationMethodsRequestInnerPreferredAuthenticationMethodEnum=t.PutAuthenticationMethodsRequestInnerTypeEnum=t.PutAuthenticationMethods200ResponseInnerAuthenticationMethodsInnerTypeEnum=t.PutAuthenticationMethods200ResponseInnerPreferredAuthenticationMethodEnum=t.PutAuthenticationMethods200ResponseInnerTypeEnum=t.PromptsSettingsUpdateUniversalLoginExperienceEnum=t.PromptsSettingsUniversalLoginExperienceEnum=t.PostVerify200ResponseTypeEnum=t.PostVerify200ResponseStatusEnum=t.PostVerificationEmailRequestIdentityProviderEnum=t.PostUsersExportsRequestFormatEnum=t.PostLogStreamsRequestOneOfFiltersInnerNameEnum=t.PostLogStreamsRequestOneOfFiltersInnerTypeEnum=t.PostLogStreamsRequestOneOf7TypeEnum=t.PostLogStreamsRequestOneOf6TypeEnum=t.PostLogStreamsRequestOneOf5TypeEnum=t.PostLogStreamsRequestOneOf4TypeEnum=t.PostLogStreamsRequestOneOf3TypeEnum=t.PostLogStreamsRequestOneOf2SinkAzureRegionEnum=t.PostLogStreamsRequestOneOf2TypeEnum=t.PostLogStreamsRequestOneOf1SinkAwsRegionEnum=t.PostLogStreamsRequestOneOf1TypeEnum=t.PostLogStreamsRequestOneOfTypeEnum=t.PostIdentitiesRequestProviderEnum=t.PostEmailTemplatesRequestTemplateEnum=t.PostCustomDomainsRequestCustomClientIpHeaderEnum=t.PostCustomDomainsRequestTlsPolicyEnum=t.PostCustomDomainsRequestVerificationMethodEnum=t.PostCustomDomainsRequestTypeEnum=t.PostCustomDomains201ResponseVerificationMethodsInnerNameEnum=t.PostCustomDomains201ResponseTypeEnum=t.PostCustomDomains201ResponseStatusEnum=t.PostCredentialsRequestCredentialTypeEnum=t.PostBrandingThemeRequestWidgetSocialButtonsLayoutEnum=t.PostBrandingThemeRequestWidgetLogoPositionEnum=t.PostBrandingThemeRequestWidgetHeaderTextAlignmentEnum=t.PostBrandingThemeRequestPageBackgroundPageLayoutEnum=t.PostBrandingThemeRequestFontsLinksStyleEnum=void 0;t.GetUsersSearchEngineEnum=t.DeleteUserIdentityByUserIdProviderEnum=t.DeleteMultifactorByProviderProviderEnum=t.PutCustomTextByLanguageLanguageEnum=t.PutCustomTextByLanguagePromptEnum=t.GetCustomTextByLanguageLanguageEnum=t.GetCustomTextByLanguagePromptEnum=t.GetHooksTriggerIdEnum=t.PutFactorsByNameOperationNameEnum=t.PutEmailTemplatesByTemplateNameTemplateNameEnum=t.PatchEmailTemplatesByTemplateNameOperationTemplateNameEnum=t.GetEmailTemplatesByTemplateNameTemplateNameEnum=t.GetDeviceCredentialsTypeEnum=t.GetConnectionsStrategyEnum=t.UserEnrollmentAuthMethodEnum=t.UserEnrollmentStatusEnum=void 0;t.ClientTokenEndpointAuthMethodEnum={none:"none",client_secret_post:"client_secret_post",client_secret_basic:"client_secret_basic"};t.ClientOrganizationUsageEnum={deny:"deny",allow:"allow",require:"require"};t.ClientOrganizationRequireBehaviorEnum={no_prompt:"no_prompt",pre_login_prompt:"pre_login_prompt",post_login_prompt:"post_login_prompt"};t.ClientCreateTokenEndpointAuthMethodEnum={none:"none",client_secret_post:"client_secret_post",client_secret_basic:"client_secret_basic"};t.ClientCreateAppTypeEnum={native:"native",spa:"spa",regular_web:"regular_web",non_interactive:"non_interactive",rms:"rms",box:"box",cloudbees:"cloudbees",concur:"concur",dropbox:"dropbox",mscrm:"mscrm",echosign:"echosign",egnyte:"egnyte",newrelic:"newrelic",office365:"office365",salesforce:"salesforce",sentry:"sentry",sharepoint:"sharepoint",slack:"slack",springcm:"springcm",zendesk:"zendesk",zoom:"zoom",sso_integration:"sso_integration",oag:"oag"};t.ClientCreateOrganizationUsageEnum={deny:"deny",allow:"allow",require:"require"};t.ClientCreateOrganizationRequireBehaviorEnum={no_prompt:"no_prompt",pre_login_prompt:"pre_login_prompt",post_login_prompt:"post_login_prompt"};t.ClientCreateClientAuthenticationMethodsPrivateKeyJwtCredentialsInnerCredentialTypeEnum={public_key:"public_key"};t.ClientCreateClientAuthenticationMethodsPrivateKeyJwtCredentialsInnerAlgEnum={RS256:"RS256",RS384:"RS384",PS256:"PS256"};t.ClientCreateJwtConfigurationAlgEnum={HS256:"HS256",RS256:"RS256",PS256:"PS256"};t.ClientJwtConfigurationAlgEnum={HS256:"HS256",RS256:"RS256",PS256:"PS256"};t.ClientRefreshTokenRotationTypeEnum={rotating:"rotating",non_rotating:"non-rotating"};t.ClientRefreshTokenExpirationTypeEnum={expiring:"expiring",non_expiring:"non-expiring"};t.ClientUpdateTokenEndpointAuthMethodEnum={none:"none",client_secret_post:"client_secret_post",client_secret_basic:"client_secret_basic",null:"null"};t.ClientUpdateAppTypeEnum={native:"native",spa:"spa",regular_web:"regular_web",non_interactive:"non_interactive",rms:"rms",box:"box",cloudbees:"cloudbees",concur:"concur",dropbox:"dropbox",mscrm:"mscrm",echosign:"echosign",egnyte:"egnyte",newrelic:"newrelic",office365:"office365",salesforce:"salesforce",sentry:"sentry",sharepoint:"sharepoint",slack:"slack",springcm:"springcm",zendesk:"zendesk",zoom:"zoom",sso_integration:"sso_integration",oag:"oag"};t.ClientUpdateOrganizationUsageEnum={deny:"deny",allow:"allow",require:"require"};t.ClientUpdateOrganizationRequireBehaviorEnum={no_prompt:"no_prompt",pre_login_prompt:"pre_login_prompt",post_login_prompt:"post_login_prompt"};t.ClientUpdateJwtConfigurationAlgEnum={HS256:"HS256",RS256:"RS256",PS256:"PS256"};t.ConnectionCreateStrategyEnum={ad:"ad",adfs:"adfs",amazon:"amazon",apple:"apple",dropbox:"dropbox",bitbucket:"bitbucket",aol:"aol",auth0_oidc:"auth0-oidc",auth0:"auth0",baidu:"baidu",bitly:"bitly",box:"box",custom:"custom",daccount:"daccount",dwolla:"dwolla",email:"email",evernote_sandbox:"evernote-sandbox",evernote:"evernote",exact:"exact",facebook:"facebook",fitbit:"fitbit",flickr:"flickr",github:"github",google_apps:"google-apps",google_oauth2:"google-oauth2",instagram:"instagram",ip:"ip",line:"line",linkedin:"linkedin",miicard:"miicard",oauth1:"oauth1",oauth2:"oauth2",office365:"office365",oidc:"oidc",okta:"okta",paypal:"paypal",paypal_sandbox:"paypal-sandbox",pingfederate:"pingfederate",planningcenter:"planningcenter",renren:"renren",salesforce_community:"salesforce-community",salesforce_sandbox:"salesforce-sandbox",salesforce:"salesforce",samlp:"samlp",sharepoint:"sharepoint",shopify:"shopify",sms:"sms",soundcloud:"soundcloud",thecity_sandbox:"thecity-sandbox",thecity:"thecity",thirtysevensignals:"thirtysevensignals",twitter:"twitter",untappd:"untappd",vkontakte:"vkontakte",waad:"waad",weibo:"weibo",windowslive:"windowslive",wordpress:"wordpress",yahoo:"yahoo",yammer:"yammer",yandex:"yandex"};t.ConnectionCreateOptionsPasswordPolicyEnum={none:"none",low:"low",fair:"fair",good:"good",excellent:"excellent",null:"null"};t.ConnectionCreateOptionsSetUserRootAttributesEnum={each_login:"on_each_login",first_login:"on_first_login"};t.ConnectionCreateOptionsPasskeyOptionsChallengeUiEnum={both:"both",autofill:"autofill",button:"button"};t.ConnectionUpdateOptionsPasswordPolicyEnum={none:"none",low:"low",fair:"fair",good:"good",excellent:"excellent",null:"null"};t.ConnectionUpdateOptionsSetUserRootAttributesEnum={each_login:"on_each_login",first_login:"on_first_login"};t.CustomDomainStatusEnum={disabled:"disabled",pending:"pending",pending_verification:"pending_verification",ready:"ready"};t.CustomDomainTypeEnum={auth0_managed_certs:"auth0_managed_certs",self_managed_certs:"self_managed_certs"};t.DeviceCredentialTypeEnum={public_key:"public_key",refresh_token:"refresh_token",rotating_refresh_token:"rotating_refresh_token"};t.DeviceCredentialCreateTypeEnum={public_key:"public_key"};t.EmailProviderCreateNameEnum={mailgun:"mailgun",mandrill:"mandrill",sendgrid:"sendgrid",ses:"ses",sparkpost:"sparkpost",smtp:"smtp",azure_cs:"azure_cs",ms365:"ms365"};t.EmailProviderUpdateNameEnum={mailgun:"mailgun",mandrill:"mandrill",sendgrid:"sendgrid",ses:"ses",sparkpost:"sparkpost",smtp:"smtp",azure_cs:"azure_cs",ms365:"ms365"};t.EmailTemplateUpdateTemplateEnum={verify_email:"verify_email",verify_email_by_code:"verify_email_by_code",reset_email:"reset_email",welcome_email:"welcome_email",blocked_account:"blocked_account",stolen_credentials:"stolen_credentials",enrollment_email:"enrollment_email",mfa_oob_code:"mfa_oob_code",user_invitation:"user_invitation",change_password:"change_password",password_reset:"password_reset"};t.EnrollmentStatusEnum={pending:"pending",confirmed:"confirmed"};t.FactorNameEnum={push_notification:"push-notification",sms:"sms",email:"email",duo:"duo",otp:"otp",webauthn_roaming:"webauthn-roaming",webauthn_platform:"webauthn-platform",recovery_code:"recovery-code"};t.GetActionVersions200ResponseVersionsInnerStatusEnum={pending:"pending",building:"building",packaged:"packaged",built:"built",retrying:"retrying",failed:"failed"};t.GetActions200ResponseActionsInnerStatusEnum={pending:"pending",building:"building",packaged:"packaged",built:"built",retrying:"retrying",failed:"failed"};t.GetActions200ResponseActionsInnerIntegrationFeatureTypeEnum={unspecified:"unspecified",action:"action",social_connection:"social_connection",log_stream:"log_stream",sso_integration:"sso_integration",sms_provider:"sms_provider"};t.GetActions200ResponseActionsInnerIntegrationCurrentReleaseRequiredSecretsInnerTypeEnum={UNSPECIFIED:"UNSPECIFIED",STRING:"STRING"};t.GetActions200ResponseActionsInnerSupportedTriggersInnerIdAnyOf={post_login:"post-login",credentials_exchange:"credentials-exchange",pre_user_registration:"pre-user-registration",post_user_registration:"post-user-registration",post_change_password:"post-change-password",send_phone_message:"send-phone-message",iga_approval:"iga-approval",iga_certification:"iga-certification",iga_fulfillment_assignment:"iga-fulfillment-assignment",iga_fulfillment_execution:"iga-fulfillment-execution",password_reset_post_challenge:"password-reset-post-challenge"};t.GetAuthenticationMethods200ResponseOneOfInnerTypeEnum={recovery_code:"recovery-code",totp:"totp",push:"push",phone:"phone",email:"email",email_verification:"email-verification",webauthn_roaming:"webauthn-roaming",webauthn_platform:"webauthn-platform",guardian:"guardian",passkey:"passkey"};t.GetAuthenticationMethods200ResponseOneOfInnerPreferredAuthenticationMethodEnum={sms:"sms",voice:"voice"};t.GetAuthenticationMethods200ResponseOneOfInnerAuthenticationMethodsInnerTypeEnum={totp:"totp",push:"push",sms:"sms",voice:"voice"};t.GetBreachedPasswordDetection200ResponseShieldsEnum={block:"block",user_notification:"user_notification",admin_notification:"admin_notification"};t.GetBreachedPasswordDetection200ResponseAdminNotificationFrequencyEnum={immediately:"immediately",daily:"daily",weekly:"weekly",monthly:"monthly"};t.GetBreachedPasswordDetection200ResponseMethodEnum={standard:"standard",enhanced:"enhanced"};t.GetBreachedPasswordDetection200ResponseStagePreUserRegistrationShieldsEnum={block:"block",admin_notification:"admin_notification"};t.GetBruteForceProtection200ResponseShieldsEnum={block:"block",user_notification:"user_notification"};t.GetBruteForceProtection200ResponseModeEnum={identifier_and_ip:"count_per_identifier_and_ip",identifier:"count_per_identifier"};t.GetCredentials200ResponseInnerAlgEnum={RS256:"RS256",RS384:"RS384",PS256:"PS256"};t.GetEmailTemplatesByTemplateName200ResponseTemplateEnum={verify_email:"verify_email",verify_email_by_code:"verify_email_by_code",reset_email:"reset_email",welcome_email:"welcome_email",blocked_account:"blocked_account",stolen_credentials:"stolen_credentials",enrollment_email:"enrollment_email",mfa_oob_code:"mfa_oob_code",user_invitation:"user_invitation",change_password:"change_password",password_reset:"password_reset"};t.GetExecution200ResponseStatusEnum={unspecified:"unspecified",pending:"pending",final:"final",partial:"partial",canceled:"canceled",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOfStatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOfTypeEnum={http:"http"};t.GetLogStreams200ResponseInnerOneOf1StatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOf1TypeEnum={eventbridge:"eventbridge"};t.GetLogStreams200ResponseInnerOneOf1SinkAwsRegionEnum={ap_east_1:"ap-east-1",ap_northeast_1:"ap-northeast-1",ap_northeast_2:"ap-northeast-2",ap_northeast_3:"ap-northeast-3",ap_south_1:"ap-south-1",ap_southeast_1:"ap-southeast-1",ap_southeast_2:"ap-southeast-2",ca_central_1:"ca-central-1",cn_north_1:"cn-north-1",cn_northwest_1:"cn-northwest-1",eu_central_1:"eu-central-1",eu_north_1:"eu-north-1",eu_west_1:"eu-west-1",eu_west_2:"eu-west-2",eu_west_3:"eu-west-3",me_south_1:"me-south-1",sa_east_1:"sa-east-1",us_gov_east_1:"us-gov-east-1",us_gov_west_1:"us-gov-west-1",us_east_1:"us-east-1",us_east_2:"us-east-2",us_west_1:"us-west-1",us_west_2:"us-west-2"};t.GetLogStreams200ResponseInnerOneOf2StatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOf2TypeEnum={eventgrid:"eventgrid"};t.GetLogStreams200ResponseInnerOneOf2SinkAzureRegionEnum={australiacentral:"australiacentral",australiaeast:"australiaeast",australiasoutheast:"australiasoutheast",brazilsouth:"brazilsouth",canadacentral:"canadacentral",canadaeast:"canadaeast",centralindia:"centralindia",centralus:"centralus",eastasia:"eastasia",eastus:"eastus",eastus2:"eastus2",francecentral:"francecentral",germanywestcentral:"germanywestcentral",japaneast:"japaneast",japanwest:"japanwest",koreacentral:"koreacentral",koreasouth:"koreasouth",northcentralus:"northcentralus",northeurope:"northeurope",norwayeast:"norwayeast",southafricanorth:"southafricanorth",southcentralus:"southcentralus",southeastasia:"southeastasia",southindia:"southindia",switzerlandnorth:"switzerlandnorth",uaenorth:"uaenorth",uksouth:"uksouth",ukwest:"ukwest",westcentralus:"westcentralus",westeurope:"westeurope",westindia:"westindia",westus:"westus",westus2:"westus2"};t.GetLogStreams200ResponseInnerOneOf3StatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOf3TypeEnum={datadog:"datadog"};t.GetLogStreams200ResponseInnerOneOf3SinkDatadogRegionEnum={us:"us",eu:"eu",us3:"us3",us5:"us5"};t.GetLogStreams200ResponseInnerOneOf4StatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOf4TypeEnum={splunk:"splunk"};t.GetLogStreams200ResponseInnerOneOf5StatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOf5TypeEnum={sumo:"sumo"};t.GetLogStreams200ResponseInnerOneOf6StatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOf6TypeEnum={segment:"segment"};t.GetLogStreams200ResponseInnerOneOf7StatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.GetLogStreams200ResponseInnerOneOf7TypeEnum={mixpanel:"mixpanel"};t.GetLogStreams200ResponseInnerOneOf7SinkMixpanelRegionEnum={us:"us",eu:"eu"};t.GetLogStreams200ResponseInnerOneOfFiltersInnerTypeEnum={category:"category"};t.GetLogStreams200ResponseInnerOneOfFiltersInnerNameEnum={auth_ancillary_fail:"auth.ancillary.fail",auth_ancillary_success:"auth.ancillary.success",auth_login_fail:"auth.login.fail",auth_login_notification:"auth.login.notification",auth_login_success:"auth.login.success",auth_logout_fail:"auth.logout.fail",auth_logout_success:"auth.logout.success",auth_signup_fail:"auth.signup.fail",auth_signup_success:"auth.signup.success",auth_silent_auth_fail:"auth.silent_auth.fail",auth_silent_auth_success:"auth.silent_auth.success",auth_token_exchange_fail:"auth.token_exchange.fail",auth_token_exchange_success:"auth.token_exchange.success",management_fail:"management.fail",management_success:"management.success",system_notification:"system.notification",user_fail:"user.fail",user_notification:"user.notification",user_success:"user.success",other:"other"};t.GetLogStreams200ResponseInnerOneOfSinkHttpContentFormatEnum={JSONARRAY:"JSONARRAY",JSONLINES:"JSONLINES",JSONOBJECT:"JSONOBJECT"};t.GetMessageTypes200ResponseMessageTypesEnum={sms:"sms",voice:"voice"};t.GetPhoneProviders200ResponseProviderEnum={auth0:"auth0",twilio:"twilio",phone_message_hook:"phone-message-hook"};t.GetPnProviders200ResponseProviderEnum={guardian:"guardian",sns:"sns",direct:"direct"};t.GetSuspiciousIpThrottling200ResponseShieldsEnum={block:"block",admin_notification:"admin_notification"};t.HookCreateTriggerIdEnum={credentials_exchange:"credentials-exchange",pre_user_registration:"pre-user-registration",post_user_registration:"post-user-registration",post_change_password:"post-change-password",send_phone_message:"send-phone-message"};t.JobFormatEnum={json:"json",csv:"csv"};t.PatchAuthenticationMethodsByAuthenticationMethodIdRequestPreferredAuthenticationMethodEnum={voice:"voice",sms:"sms"};t.PatchBindingsRequestBindingsInnerOneOfRefTypeEnum={binding_id:"binding_id",action_id:"action_id",action_name:"action_name"};t.PatchBreachedPasswordDetectionRequestShieldsEnum={block:"block",user_notification:"user_notification",admin_notification:"admin_notification"};t.PatchBreachedPasswordDetectionRequestAdminNotificationFrequencyEnum={immediately:"immediately",daily:"daily",weekly:"weekly",monthly:"monthly"};t.PatchBreachedPasswordDetectionRequestMethodEnum={standard:"standard",enhanced:"enhanced"};t.PatchBreachedPasswordDetectionRequestStagePreUserRegistrationShieldsEnum={block:"block",admin_notification:"admin_notification"};t.PatchBruteForceProtectionRequestShieldsEnum={block:"block",user_notification:"user_notification"};t.PatchBruteForceProtectionRequestModeEnum={identifier_and_ip:"count_per_identifier_and_ip",identifier:"count_per_identifier"};t.PatchCustomDomainsByIdRequestTlsPolicyEnum={recommended:"recommended",compatible:"compatible"};t.PatchCustomDomainsByIdRequestCustomClientIpHeaderEnum={true_client_ip:"true-client-ip",cf_connecting_ip:"cf-connecting-ip",x_forwarded_for:"x-forwarded-for",x_azure_clientip:"x-azure-clientip",empty:""};t.PatchEmailTemplatesByTemplateNameRequestTemplateEnum={verify_email:"verify_email",verify_email_by_code:"verify_email_by_code",reset_email:"reset_email",welcome_email:"welcome_email",blocked_account:"blocked_account",stolen_credentials:"stolen_credentials",enrollment_email:"enrollment_email",mfa_oob_code:"mfa_oob_code",user_invitation:"user_invitation",change_password:"change_password",password_reset:"password_reset"};t.PatchLogStreamsByIdRequestStatusEnum={active:"active",paused:"paused",suspended:"suspended"};t.PatchLogStreamsByIdRequestSinkOneOfDatadogRegionEnum={us:"us",eu:"eu",us3:"us3",us5:"us5"};t.PatchLogStreamsByIdRequestSinkOneOf3MixpanelRegionEnum={us:"us",eu:"eu"};t.PatchSuspiciousIpThrottlingRequestShieldsEnum={block:"block",admin_notification:"admin_notification"};t.PostAuthenticationMethods201ResponseTypeEnum={phone:"phone",email:"email",totp:"totp",webauthn_roaming:"webauthn-roaming"};t.PostAuthenticationMethods201ResponsePreferredAuthenticationMethodEnum={voice:"voice",sms:"sms"};t.PostAuthenticationMethodsRequestTypeEnum={phone:"phone",email:"email",totp:"totp",webauthn_roaming:"webauthn-roaming"};t.PostAuthenticationMethodsRequestPreferredAuthenticationMethodEnum={voice:"voice",sms:"sms"};t.PostBrandingThemeRequestBordersButtonsStyleEnum={pill:"pill",rounded:"rounded",sharp:"sharp"};t.PostBrandingThemeRequestBordersInputsStyleEnum={pill:"pill",rounded:"rounded",sharp:"sharp"};t.PostBrandingThemeRequestFontsLinksStyleEnum={normal:"normal",underlined:"underlined"};t.PostBrandingThemeRequestPageBackgroundPageLayoutEnum={center:"center",left:"left",right:"right"};t.PostBrandingThemeRequestWidgetHeaderTextAlignmentEnum={center:"center",left:"left",right:"right"};t.PostBrandingThemeRequestWidgetLogoPositionEnum={center:"center",left:"left",none:"none",right:"right"};t.PostBrandingThemeRequestWidgetSocialButtonsLayoutEnum={bottom:"bottom",top:"top"};t.PostCredentialsRequestCredentialTypeEnum={public_key:"public_key",cert_subject_dn:"cert_subject_dn",x509_cert:"x509_cert"};t.PostCustomDomains201ResponseStatusEnum={disabled:"disabled",pending:"pending",pending_verification:"pending_verification",ready:"ready"};t.PostCustomDomains201ResponseTypeEnum={auth0_managed_certs:"auth0_managed_certs",self_managed_certs:"self_managed_certs"};t.PostCustomDomains201ResponseVerificationMethodsInnerNameEnum={cname:"cname",txt:"txt"};t.PostCustomDomainsRequestTypeEnum={auth0_managed_certs:"auth0_managed_certs",self_managed_certs:"self_managed_certs"};t.PostCustomDomainsRequestVerificationMethodEnum={txt:"txt"};t.PostCustomDomainsRequestTlsPolicyEnum={recommended:"recommended",compatible:"compatible"};t.PostCustomDomainsRequestCustomClientIpHeaderEnum={true_client_ip:"true-client-ip",cf_connecting_ip:"cf-connecting-ip",x_forwarded_for:"x-forwarded-for",x_azure_clientip:"x-azure-clientip",null:"null"};t.PostEmailTemplatesRequestTemplateEnum={verify_email:"verify_email",verify_email_by_code:"verify_email_by_code",reset_email:"reset_email",welcome_email:"welcome_email",blocked_account:"blocked_account",stolen_credentials:"stolen_credentials",enrollment_email:"enrollment_email",mfa_oob_code:"mfa_oob_code",user_invitation:"user_invitation",change_password:"change_password",password_reset:"password_reset"};t.PostIdentitiesRequestProviderEnum={ad:"ad",adfs:"adfs",amazon:"amazon",apple:"apple",dropbox:"dropbox",bitbucket:"bitbucket",aol:"aol",auth0_oidc:"auth0-oidc",auth0:"auth0",baidu:"baidu",bitly:"bitly",box:"box",custom:"custom",daccount:"daccount",dwolla:"dwolla",email:"email",evernote_sandbox:"evernote-sandbox",evernote:"evernote",exact:"exact",facebook:"facebook",fitbit:"fitbit",flickr:"flickr",github:"github",google_apps:"google-apps",google_oauth2:"google-oauth2",instagram:"instagram",ip:"ip",line:"line",linkedin:"linkedin",miicard:"miicard",oauth1:"oauth1",oauth2:"oauth2",office365:"office365",oidc:"oidc",okta:"okta",paypal:"paypal",paypal_sandbox:"paypal-sandbox",pingfederate:"pingfederate",planningcenter:"planningcenter",renren:"renren",salesforce_community:"salesforce-community",salesforce_sandbox:"salesforce-sandbox",salesforce:"salesforce",samlp:"samlp",sharepoint:"sharepoint",shopify:"shopify",sms:"sms",soundcloud:"soundcloud",thecity_sandbox:"thecity-sandbox",thecity:"thecity",thirtysevensignals:"thirtysevensignals",twitter:"twitter",untappd:"untappd",vkontakte:"vkontakte",waad:"waad",weibo:"weibo",windowslive:"windowslive",wordpress:"wordpress",yahoo:"yahoo",yammer:"yammer",yandex:"yandex"};t.PostLogStreamsRequestOneOfTypeEnum={http:"http"};t.PostLogStreamsRequestOneOf1TypeEnum={eventbridge:"eventbridge"};t.PostLogStreamsRequestOneOf1SinkAwsRegionEnum={ap_east_1:"ap-east-1",ap_northeast_1:"ap-northeast-1",ap_northeast_2:"ap-northeast-2",ap_northeast_3:"ap-northeast-3",ap_south_1:"ap-south-1",ap_southeast_1:"ap-southeast-1",ap_southeast_2:"ap-southeast-2",ca_central_1:"ca-central-1",cn_north_1:"cn-north-1",cn_northwest_1:"cn-northwest-1",eu_central_1:"eu-central-1",eu_north_1:"eu-north-1",eu_west_1:"eu-west-1",eu_west_2:"eu-west-2",eu_west_3:"eu-west-3",me_south_1:"me-south-1",sa_east_1:"sa-east-1",us_gov_east_1:"us-gov-east-1",us_gov_west_1:"us-gov-west-1",us_east_1:"us-east-1",us_east_2:"us-east-2",us_west_1:"us-west-1",us_west_2:"us-west-2"};t.PostLogStreamsRequestOneOf2TypeEnum={eventgrid:"eventgrid"};t.PostLogStreamsRequestOneOf2SinkAzureRegionEnum={australiacentral:"australiacentral",australiaeast:"australiaeast",australiasoutheast:"australiasoutheast",brazilsouth:"brazilsouth",canadacentral:"canadacentral",canadaeast:"canadaeast",centralindia:"centralindia",centralus:"centralus",eastasia:"eastasia",eastus:"eastus",eastus2:"eastus2",francecentral:"francecentral",germanywestcentral:"germanywestcentral",japaneast:"japaneast",japanwest:"japanwest",koreacentral:"koreacentral",koreasouth:"koreasouth",northcentralus:"northcentralus",northeurope:"northeurope",norwayeast:"norwayeast",southafricanorth:"southafricanorth",southcentralus:"southcentralus",southeastasia:"southeastasia",southindia:"southindia",switzerlandnorth:"switzerlandnorth",uaenorth:"uaenorth",uksouth:"uksouth",ukwest:"ukwest",westcentralus:"westcentralus",westeurope:"westeurope",westindia:"westindia",westus:"westus",westus2:"westus2"};t.PostLogStreamsRequestOneOf3TypeEnum={datadog:"datadog"};t.PostLogStreamsRequestOneOf4TypeEnum={splunk:"splunk"};t.PostLogStreamsRequestOneOf5TypeEnum={sumo:"sumo"};t.PostLogStreamsRequestOneOf6TypeEnum={segment:"segment"};t.PostLogStreamsRequestOneOf7TypeEnum={mixpanel:"mixpanel"};t.PostLogStreamsRequestOneOfFiltersInnerTypeEnum={category:"category"};t.PostLogStreamsRequestOneOfFiltersInnerNameEnum={auth_ancillary_fail:"auth.ancillary.fail",auth_ancillary_success:"auth.ancillary.success",auth_login_fail:"auth.login.fail",auth_login_notification:"auth.login.notification",auth_login_success:"auth.login.success",auth_logout_fail:"auth.logout.fail",auth_logout_success:"auth.logout.success",auth_signup_fail:"auth.signup.fail",auth_signup_success:"auth.signup.success",auth_silent_auth_fail:"auth.silent_auth.fail",auth_silent_auth_success:"auth.silent_auth.success",auth_token_exchange_fail:"auth.token_exchange.fail",auth_token_exchange_success:"auth.token_exchange.success",management_fail:"management.fail",management_success:"management.success",system_notification:"system.notification",user_fail:"user.fail",user_notification:"user.notification",user_success:"user.success",other:"other"};t.PostUsersExportsRequestFormatEnum={json:"json",csv:"csv"};t.PostVerificationEmailRequestIdentityProviderEnum={ad:"ad",adfs:"adfs",amazon:"amazon",apple:"apple",dropbox:"dropbox",bitbucket:"bitbucket",aol:"aol",auth0_oidc:"auth0-oidc",auth0:"auth0",baidu:"baidu",bitly:"bitly",box:"box",custom:"custom",daccount:"daccount",dwolla:"dwolla",email:"email",evernote_sandbox:"evernote-sandbox",evernote:"evernote",exact:"exact",facebook:"facebook",fitbit:"fitbit",flickr:"flickr",github:"github",google_apps:"google-apps",google_oauth2:"google-oauth2",instagram:"instagram",ip:"ip",line:"line",linkedin:"linkedin",miicard:"miicard",oauth1:"oauth1",oauth2:"oauth2",office365:"office365",oidc:"oidc",okta:"okta",paypal:"paypal",paypal_sandbox:"paypal-sandbox",pingfederate:"pingfederate",planningcenter:"planningcenter",renren:"renren",salesforce_community:"salesforce-community",salesforce_sandbox:"salesforce-sandbox",salesforce:"salesforce",samlp:"samlp",sharepoint:"sharepoint",shopify:"shopify",sms:"sms",soundcloud:"soundcloud",thecity_sandbox:"thecity-sandbox",thecity:"thecity",thirtysevensignals:"thirtysevensignals",twitter:"twitter",untappd:"untappd",vkontakte:"vkontakte",waad:"waad",weibo:"weibo",windowslive:"windowslive",wordpress:"wordpress",yahoo:"yahoo",yammer:"yammer",yandex:"yandex"};t.PostVerify200ResponseStatusEnum={disabled:"disabled",pending:"pending",pending_verification:"pending_verification",ready:"ready"};t.PostVerify200ResponseTypeEnum={auth0_managed_certs:"auth0_managed_certs",self_managed_certs:"self_managed_certs"};t.PromptsSettingsUniversalLoginExperienceEnum={new:"new",classic:"classic"};t.PromptsSettingsUpdateUniversalLoginExperienceEnum={new:"new",classic:"classic"};t.PutAuthenticationMethods200ResponseInnerTypeEnum={phone:"phone",email:"email",totp:"totp",webauthn_roaming:"webauthn-roaming"};t.PutAuthenticationMethods200ResponseInnerPreferredAuthenticationMethodEnum={voice:"voice",sms:"sms"};t.PutAuthenticationMethods200ResponseInnerAuthenticationMethodsInnerTypeEnum={totp:"totp",push:"push",sms:"sms",voice:"voice"};t.PutAuthenticationMethodsRequestInnerTypeEnum={phone:"phone",email:"email",totp:"totp"};t.PutAuthenticationMethodsRequestInnerPreferredAuthenticationMethodEnum={voice:"voice",sms:"sms"};t.ResourceServerSigningAlgEnum={HS256:"HS256",RS256:"RS256",PS256:"PS256"};t.ResourceServerTokenDialectEnum={token:"access_token",token_authz:"access_token_authz"};t.ResourceServerCreateSigningAlgEnum={HS256:"HS256",RS256:"RS256",PS256:"PS256"};t.ResourceServerCreateTokenDialectEnum={token:"access_token",token_authz:"access_token_authz"};t.ResourceServerUpdateSigningAlgEnum={HS256:"HS256",RS256:"RS256",PS256:"PS256"};t.ResourceServerUpdateTokenDialectEnum={token:"access_token",token_authz:"access_token_authz"};t.TenantSettingsEnabledLocalesEnum={ar:"ar",bg:"bg",bs:"bs",ca_ES:"ca-ES",cs:"cs",cy:"cy",da:"da",de:"de",el:"el",en:"en",es:"es",et:"et",eu_ES:"eu-ES",fi:"fi",fr:"fr",fr_CA:"fr-CA",fr_FR:"fr-FR",gl_ES:"gl-ES",he:"he",hi:"hi",hr:"hr",hu:"hu",id:"id",is:"is",it:"it",ja:"ja",ko:"ko",lt:"lt",lv:"lv",nb:"nb",nl:"nl",nn:"nn",no:"no",pl:"pl",pt:"pt",pt_BR:"pt-BR",pt_PT:"pt-PT",ro:"ro",ru:"ru",sk:"sk",sl:"sl",sr:"sr",sv:"sv",th:"th",tr:"tr",uk:"uk",vi:"vi",zh_CN:"zh-CN",zh_TW:"zh-TW"};t.TenantSettingsDeviceFlowCharsetEnum={base20:"base20",digits:"digits"};t.TenantSettingsSessionCookieModeEnum={persistent:"persistent",non_persistent:"non-persistent"};t.TenantSettingsUpdateEnabledLocalesEnum={ar:"ar",bg:"bg",bs:"bs",ca_ES:"ca-ES",cs:"cs",cy:"cy",da:"da",de:"de",el:"el",en:"en",es:"es",et:"et",eu_ES:"eu-ES",fi:"fi",fr:"fr",fr_CA:"fr-CA",fr_FR:"fr-FR",gl_ES:"gl-ES",he:"he",hi:"hi",hr:"hr",hu:"hu",id:"id",is:"is",it:"it",ja:"ja",ko:"ko",lt:"lt",lv:"lv",nb:"nb",nl:"nl",nn:"nn",no:"no",pl:"pl",pt:"pt",pt_BR:"pt-BR",pt_PT:"pt-PT",ro:"ro",ru:"ru",sk:"sk",sl:"sl",sr:"sr",sv:"sv",th:"th",tr:"tr",uk:"uk",vi:"vi",zh_CN:"zh-CN",zh_TW:"zh-TW"};t.TenantSettingsUpdateDeviceFlowCharsetEnum={base20:"base20",digits:"digits"};t.TenantSettingsUpdateFlagsChangePwdFlowV1Enum={false:false};t.UserEnrollmentStatusEnum={pending:"pending",confirmed:"confirmed"};t.UserEnrollmentAuthMethodEnum={authenticator:"authenticator",guardian:"guardian",sms:"sms",webauthn_platform:"webauthn-platform",webauthn_roaming:"webauthn-roaming"};t.GetConnectionsStrategyEnum={ad:"ad",adfs:"adfs",amazon:"amazon",apple:"apple",dropbox:"dropbox",bitbucket:"bitbucket",aol:"aol",auth0_oidc:"auth0-oidc",auth0:"auth0",baidu:"baidu",bitly:"bitly",box:"box",custom:"custom",daccount:"daccount",dwolla:"dwolla",email:"email",evernote_sandbox:"evernote-sandbox",evernote:"evernote",exact:"exact",facebook:"facebook",fitbit:"fitbit",flickr:"flickr",github:"github",google_apps:"google-apps",google_oauth2:"google-oauth2",instagram:"instagram",ip:"ip",line:"line",linkedin:"linkedin",miicard:"miicard",oauth1:"oauth1",oauth2:"oauth2",office365:"office365",oidc:"oidc",okta:"okta",paypal:"paypal",paypal_sandbox:"paypal-sandbox",pingfederate:"pingfederate",planningcenter:"planningcenter",renren:"renren",salesforce_community:"salesforce-community",salesforce_sandbox:"salesforce-sandbox",salesforce:"salesforce",samlp:"samlp",sharepoint:"sharepoint",shopify:"shopify",sms:"sms",soundcloud:"soundcloud",thecity_sandbox:"thecity-sandbox",thecity:"thecity",thirtysevensignals:"thirtysevensignals",twitter:"twitter",untappd:"untappd",vkontakte:"vkontakte",waad:"waad",weibo:"weibo",windowslive:"windowslive",wordpress:"wordpress",yahoo:"yahoo",yammer:"yammer",yandex:"yandex",auth0_adldap:"auth0-adldap"};t.GetDeviceCredentialsTypeEnum={public_key:"public_key",refresh_token:"refresh_token",rotating_refresh_token:"rotating_refresh_token"};t.GetEmailTemplatesByTemplateNameTemplateNameEnum={verify_email:"verify_email",verify_email_by_code:"verify_email_by_code",reset_email:"reset_email",welcome_email:"welcome_email",blocked_account:"blocked_account",stolen_credentials:"stolen_credentials",enrollment_email:"enrollment_email",mfa_oob_code:"mfa_oob_code",user_invitation:"user_invitation",change_password:"change_password",password_reset:"password_reset"};t.PatchEmailTemplatesByTemplateNameOperationTemplateNameEnum={verify_email:"verify_email",verify_email_by_code:"verify_email_by_code",reset_email:"reset_email",welcome_email:"welcome_email",blocked_account:"blocked_account",stolen_credentials:"stolen_credentials",enrollment_email:"enrollment_email",mfa_oob_code:"mfa_oob_code",user_invitation:"user_invitation",change_password:"change_password",password_reset:"password_reset"};t.PutEmailTemplatesByTemplateNameTemplateNameEnum={verify_email:"verify_email",verify_email_by_code:"verify_email_by_code",reset_email:"reset_email",welcome_email:"welcome_email",blocked_account:"blocked_account",stolen_credentials:"stolen_credentials",enrollment_email:"enrollment_email",mfa_oob_code:"mfa_oob_code",user_invitation:"user_invitation",change_password:"change_password",password_reset:"password_reset"};t.PutFactorsByNameOperationNameEnum={push_notification:"push-notification",sms:"sms",email:"email",duo:"duo",otp:"otp",webauthn_roaming:"webauthn-roaming",webauthn_platform:"webauthn-platform",recovery_code:"recovery-code"};t.GetHooksTriggerIdEnum={credentials_exchange:"credentials-exchange",pre_user_registration:"pre-user-registration",post_user_registration:"post-user-registration",post_change_password:"post-change-password",send_phone_message:"send-phone-message"};t.GetCustomTextByLanguagePromptEnum={login:"login",login_id:"login-id",login_password:"login-password",login_passwordless:"login-passwordless",login_email_verification:"login-email-verification",signup:"signup",signup_id:"signup-id",signup_password:"signup-password",phone_identifier_enrollment:"phone-identifier-enrollment",phone_identifier_challenge:"phone-identifier-challenge",reset_password:"reset-password",consent:"consent",logout:"logout",mfa_push:"mfa-push",mfa_otp:"mfa-otp",mfa_voice:"mfa-voice",mfa_phone:"mfa-phone",mfa_webauthn:"mfa-webauthn",mfa_sms:"mfa-sms",mfa_email:"mfa-email",mfa_recovery_code:"mfa-recovery-code",mfa:"mfa",status:"status",device_flow:"device-flow",email_verification:"email-verification",email_otp_challenge:"email-otp-challenge",organizations:"organizations",invitation:"invitation",common:"common",passkeys:"passkeys"};t.GetCustomTextByLanguageLanguageEnum={ar:"ar",bg:"bg",bs:"bs",ca_ES:"ca-ES",cs:"cs",cy:"cy",da:"da",de:"de",el:"el",en:"en",es:"es",et:"et",eu_ES:"eu-ES",fi:"fi",fr:"fr",fr_CA:"fr-CA",fr_FR:"fr-FR",gl_ES:"gl-ES",he:"he",hi:"hi",hr:"hr",hu:"hu",id:"id",is:"is",it:"it",ja:"ja",ko:"ko",lt:"lt",lv:"lv",nb:"nb",nl:"nl",nn:"nn",no:"no",pl:"pl",pt:"pt",pt_BR:"pt-BR",pt_PT:"pt-PT",ro:"ro",ru:"ru",sk:"sk",sl:"sl",sr:"sr",sv:"sv",th:"th",tr:"tr",uk:"uk",vi:"vi",zh_CN:"zh-CN",zh_TW:"zh-TW"};t.PutCustomTextByLanguagePromptEnum={login:"login",login_id:"login-id",login_password:"login-password",login_passwordless:"login-passwordless",login_email_verification:"login-email-verification",signup:"signup",signup_id:"signup-id",signup_password:"signup-password",phone_identifier_enrollment:"phone-identifier-enrollment",phone_identifier_challenge:"phone-identifier-challenge",reset_password:"reset-password",consent:"consent",logout:"logout",mfa_push:"mfa-push",mfa_otp:"mfa-otp",mfa_voice:"mfa-voice",mfa_phone:"mfa-phone",mfa_webauthn:"mfa-webauthn",mfa_sms:"mfa-sms",mfa_email:"mfa-email",mfa_recovery_code:"mfa-recovery-code",mfa:"mfa",status:"status",device_flow:"device-flow",email_verification:"email-verification",email_otp_challenge:"email-otp-challenge",organizations:"organizations",invitation:"invitation",common:"common",passkeys:"passkeys"};t.PutCustomTextByLanguageLanguageEnum={ar:"ar",bg:"bg",bs:"bs",ca_ES:"ca-ES",cs:"cs",cy:"cy",da:"da",de:"de",el:"el",en:"en",es:"es",et:"et",eu_ES:"eu-ES",fi:"fi",fr:"fr",fr_CA:"fr-CA",fr_FR:"fr-FR",gl_ES:"gl-ES",he:"he",hi:"hi",hr:"hr",hu:"hu",id:"id",is:"is",it:"it",ja:"ja",ko:"ko",lt:"lt",lv:"lv",nb:"nb",nl:"nl",nn:"nn",no:"no",pl:"pl",pt:"pt",pt_BR:"pt-BR",pt_PT:"pt-PT",ro:"ro",ru:"ru",sk:"sk",sl:"sl",sr:"sr",sv:"sv",th:"th",tr:"tr",uk:"uk",vi:"vi",zh_CN:"zh-CN",zh_TW:"zh-TW"};t.DeleteMultifactorByProviderProviderEnum={duo:"duo",google_authenticator:"google-authenticator"};t.DeleteUserIdentityByUserIdProviderEnum={ad:"ad",adfs:"adfs",amazon:"amazon",apple:"apple",dropbox:"dropbox",bitbucket:"bitbucket",aol:"aol",auth0_oidc:"auth0-oidc",auth0:"auth0",baidu:"baidu",bitly:"bitly",box:"box",custom:"custom",daccount:"daccount",dwolla:"dwolla",email:"email",evernote_sandbox:"evernote-sandbox",evernote:"evernote",exact:"exact",facebook:"facebook",fitbit:"fitbit",flickr:"flickr",github:"github",google_apps:"google-apps",google_oauth2:"google-oauth2",instagram:"instagram",ip:"ip",line:"line",linkedin:"linkedin",miicard:"miicard",oauth1:"oauth1",oauth2:"oauth2",office365:"office365",oidc:"oidc",okta:"okta",paypal:"paypal",paypal_sandbox:"paypal-sandbox",pingfederate:"pingfederate",planningcenter:"planningcenter",renren:"renren",salesforce_community:"salesforce-community",salesforce_sandbox:"salesforce-sandbox",salesforce:"salesforce",samlp:"samlp",sharepoint:"sharepoint",shopify:"shopify",sms:"sms",soundcloud:"soundcloud",thecity_sandbox:"thecity-sandbox",thecity:"thecity",thirtysevensignals:"thirtysevensignals",twitter:"twitter",untappd:"untappd",vkontakte:"vkontakte",waad:"waad",weibo:"weibo",windowslive:"windowslive",wordpress:"wordpress",yahoo:"yahoo",yammer:"yammer",yandex:"yandex"};t.GetUsersSearchEngineEnum={v1:"v1",v2:"v2",v3:"v3"}},3119:function(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){if(s===undefined)s=n;var i=Object.getOwnPropertyDescriptor(t,n);if(!i||("get"in i?!t.__esModule:i.writable||i.configurable)){i={enumerable:true,get:function(){return t[n]}}}Object.defineProperty(e,s,i)}:function(e,t,n,s){if(s===undefined)s=n;e[s]=t[n]});var i=this&&this.__exportStar||function(e,t){for(var n in e)if(n!=="default"&&!Object.prototype.hasOwnProperty.call(t,n))s(t,e,n)};Object.defineProperty(t,"__esModule",{value:true});i(n(2602),t);i(n(2469),t);i(n(7248),t)},2602:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true})},2469:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.ManagementClient=t.ManagementApiError=void 0;const s=n(7248);const i=n(3387);const r=n(3551);const o=n(8295);class ManagementApiError extends Error{constructor(e,t,n,s,i,r){super(r);this.errorCode=e;this.error=t;this.statusCode=n;this.body=s;this.headers=i;this.msg=r;this.name="ManagementApiError"}}t.ManagementApiError=ManagementApiError;async function parseError(e){const t=await e.text();let n;try{n=JSON.parse(t);return new ManagementApiError(n.errorCode,n.error,n.statusCode||e.status,t,e.headers,n.message)}catch(n){return new r.ResponseError(e.status,t,e.headers,"Response returned an error code")}}class ManagementClient extends s.ManagementClientBase{constructor(e){super({...e,baseUrl:`https://${e.domain}/api/v2`,middleware:[new i.TokenProviderMiddleware(e),...e.telemetry!==false?[new o.TelemetryMiddleware(e)]:[]],parseError:parseError})}}t.ManagementClient=ManagementClient},3387:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.TokenProviderMiddleware=void 0;const s=n(206);class TokenProviderMiddleware{constructor(e){var t;if("token"in e){this.tokenProvider={getAccessToken:()=>Promise.resolve(e.token)}}else{this.tokenProvider=new s.TokenProvider({...e,audience:(t=e.audience)!==null&&t!==void 0?t:`https://${e.domain}/api/v2/`,...{clientSecret:e.clientSecret},...{clientAssertionSigningKey:e.clientAssertionSigningKey}})}}async pre(e){const t=await this.tokenProvider.getAccessToken();e.init.headers={...e.init.headers,Authorization:`Bearer ${t}`};return{url:e.url,init:e.init}}}t.TokenProviderMiddleware=TokenProviderMiddleware},206:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.TokenProvider=void 0;const s=n(3776);const i=10*1e3;class TokenProvider{constructor(e){this.options=e;this.expiresAt=0;this.accessToken="";this.authenticationClient=new s.AuthenticationClient(e)}async getAccessToken(){if(!this.accessToken||Date.now()>this.expiresAt-i){this.pending=this.pending||this.authenticationClient.oauth.clientCredentialsGrant({audience:this.options.audience});const{data:{access_token:e,expires_in:t}}=await this.pending.finally((()=>{delete this.pending}));this.expiresAt=Date.now()+t*1e3;this.accessToken=e}return this.accessToken}}t.TokenProvider=TokenProvider},6650:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.UserInfoClient=t.parseError=t.UserInfoError=void 0;const s=n(8361);const i=n(8295);const r=n(4667);const o=n(5467);class UserInfoError extends Error{constructor(e,t,n,s,i){super(t||e);this.error=e;this.error_description=t;this.statusCode=n;this.body=s;this.headers=i;this.name="UserInfoError"}}t.UserInfoError=UserInfoError;async function parseError(e){const t=await e.text();let n;try{n=JSON.parse(t);return new UserInfoError(n.error,n.error_description,e.status,t,e.headers)}catch(n){return new s.ResponseError(e.status,t,e.headers,"Response returned an error code")}}t.parseError=parseError;class UserInfoClient extends o.BaseAPI{constructor(e){super({...e,baseUrl:`https://${e.domain}`,middleware:e.telemetry!==false?[new i.TelemetryMiddleware(e)]:[],parseError:parseError})}async getUserInfo(e,t){const n=await this.request({path:`/userinfo`,method:"GET",headers:{Authorization:`Bearer ${e}`}},t);return r.JSONApiResponse.fromResponse(n)}}t.UserInfoClient=UserInfoClient},8200:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.generateClientInfo=void 0;const s=n(1817);const generateClientInfo=()=>({name:"node-auth0",version:s.version,env:{node:process.version.replace("v","")}});t.generateClientInfo=generateClientInfo},1817:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.version=void 0;t.version="4.1.0"},8757:(e,t,n)=>{"use strict";const s=n(4334);const i=n(7310);const r=n(3329);const o=n(3685);const A=n(5687);const a=n(3837);const c=n(7707);const u=n(9796);const l=n(2781);const d=n(2361);function _interopDefaultLegacy(e){return e&&typeof e==="object"&&"default"in e?e:{default:e}}const p=_interopDefaultLegacy(s);const g=_interopDefaultLegacy(i);const h=_interopDefaultLegacy(o);const f=_interopDefaultLegacy(A);const E=_interopDefaultLegacy(a);const m=_interopDefaultLegacy(c);const C=_interopDefaultLegacy(u);const Q=_interopDefaultLegacy(l);const I=_interopDefaultLegacy(d);function bind(e,t){return function wrap(){return e.apply(t,arguments)}}const{toString:B}=Object.prototype;const{getPrototypeOf:y}=Object;const b=(e=>t=>{const n=B.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null));const kindOfTest=e=>{e=e.toLowerCase();return t=>b(t)===e};const typeOfTest=e=>t=>typeof t===e;const{isArray:w}=Array;const R=typeOfTest("undefined");function isBuffer(e){return e!==null&&!R(e)&&e.constructor!==null&&!R(e.constructor)&&S(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const v=kindOfTest("ArrayBuffer");function isArrayBufferView(e){let t;if(typeof ArrayBuffer!=="undefined"&&ArrayBuffer.isView){t=ArrayBuffer.isView(e)}else{t=e&&e.buffer&&v(e.buffer)}return t}const k=typeOfTest("string");const S=typeOfTest("function");const x=typeOfTest("number");const isObject=e=>e!==null&&typeof e==="object";const isBoolean=e=>e===true||e===false;const isPlainObject=e=>{if(b(e)!=="object"){return false}const t=y(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)};const D=kindOfTest("Date");const _=kindOfTest("File");const N=kindOfTest("Blob");const F=kindOfTest("FileList");const isStream=e=>isObject(e)&&S(e.pipe);const isFormData=e=>{let t;return e&&(typeof FormData==="function"&&e instanceof FormData||S(e.append)&&((t=b(e))==="formdata"||t==="object"&&S(e.toString)&&e.toString()==="[object FormData]"))};const U=kindOfTest("URLSearchParams");const trim=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function forEach(e,t,{allOwnKeys:n=false}={}){if(e===null||typeof e==="undefined"){return}let s;let i;if(typeof e!=="object"){e=[e]}if(w(e)){for(s=0,i=e.length;s0){i=n[s];if(t===i.toLowerCase()){return i}}return null}const T=(()=>{if(typeof globalThis!=="undefined")return globalThis;return typeof self!=="undefined"?self:typeof window!=="undefined"?window:global})();const isContextDefined=e=>!R(e)&&e!==T;function merge(){const{caseless:e}=isContextDefined(this)&&this||{};const t={};const assignValue=(n,s)=>{const i=e&&findKey(t,s)||s;if(isPlainObject(t[i])&&isPlainObject(n)){t[i]=merge(t[i],n)}else if(isPlainObject(n)){t[i]=merge({},n)}else if(w(n)){t[i]=n.slice()}else{t[i]=n}};for(let e=0,t=arguments.length;e{forEach(t,((t,s)=>{if(n&&S(t)){e[s]=bind(t,n)}else{e[s]=t}}),{allOwnKeys:s});return e};const stripBOM=e=>{if(e.charCodeAt(0)===65279){e=e.slice(1)}return e};const inherits=(e,t,n,s)=>{e.prototype=Object.create(t.prototype,s);e.prototype.constructor=e;Object.defineProperty(e,"super",{value:t.prototype});n&&Object.assign(e.prototype,n)};const toFlatObject=(e,t,n,s)=>{let i;let r;let o;const A={};t=t||{};if(e==null)return t;do{i=Object.getOwnPropertyNames(e);r=i.length;while(r-- >0){o=i[r];if((!s||s(o,e,t))&&!A[o]){t[o]=e[o];A[o]=true}}e=n!==false&&y(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t};const endsWith=(e,t,n)=>{e=String(e);if(n===undefined||n>e.length){n=e.length}n-=t.length;const s=e.indexOf(t,n);return s!==-1&&s===n};const toArray=e=>{if(!e)return null;if(w(e))return e;let t=e.length;if(!x(t))return null;const n=new Array(t);while(t-- >0){n[t]=e[t]}return n};const M=(e=>t=>e&&t instanceof e)(typeof Uint8Array!=="undefined"&&y(Uint8Array));const forEachEntry=(e,t)=>{const n=e&&e[Symbol.iterator];const s=n.call(e);let i;while((i=s.next())&&!i.done){const n=i.value;t.call(e,n[0],n[1])}};const matchAll=(e,t)=>{let n;const s=[];while((n=e.exec(t))!==null){s.push(n)}return s};const L=kindOfTest("HTMLFormElement");const toCamelCase=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function replacer(e,t,n){return t.toUpperCase()+n}));const O=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype);const P=kindOfTest("RegExp");const reduceDescriptors=(e,t)=>{const n=Object.getOwnPropertyDescriptors(e);const s={};forEach(n,((n,i)=>{let r;if((r=t(n,i,e))!==false){s[i]=r||n}}));Object.defineProperties(e,s)};const freezeMethods=e=>{reduceDescriptors(e,((t,n)=>{if(S(e)&&["arguments","caller","callee"].indexOf(n)!==-1){return false}const s=e[n];if(!S(s))return;t.enumerable=false;if("writable"in t){t.writable=false;return}if(!t.set){t.set=()=>{throw Error("Can not rewrite read-only method '"+n+"'")}}}))};const toObjectSet=(e,t)=>{const n={};const define=e=>{e.forEach((e=>{n[e]=true}))};w(e)?define(e):define(String(e).split(t));return n};const noop=()=>{};const toFiniteNumber=(e,t)=>{e=+e;return Number.isFinite(e)?e:t};const J="abcdefghijklmnopqrstuvwxyz";const H="0123456789";const q={DIGIT:H,ALPHA:J,ALPHA_DIGIT:J+J.toUpperCase()+H};const generateString=(e=16,t=q.ALPHA_DIGIT)=>{let n="";const{length:s}=t;while(e--){n+=t[Math.random()*s|0]}return n};function isSpecCompliantForm(e){return!!(e&&S(e.append)&&e[Symbol.toStringTag]==="FormData"&&e[Symbol.iterator])}const toJSONObject=e=>{const t=new Array(10);const visit=(e,n)=>{if(isObject(e)){if(t.indexOf(e)>=0){return}if(!("toJSON"in e)){t[n]=e;const s=w(e)?[]:{};forEach(e,((e,t)=>{const i=visit(e,n+1);!R(i)&&(s[t]=i)}));t[n]=undefined;return s}}return e};return visit(e,0)};const G=kindOfTest("AsyncFunction");const isThenable=e=>e&&(isObject(e)||S(e))&&S(e.then)&&S(e.catch);const Y={isArray:w,isArrayBuffer:v,isBuffer:isBuffer,isFormData:isFormData,isArrayBufferView:isArrayBufferView,isString:k,isNumber:x,isBoolean:isBoolean,isObject:isObject,isPlainObject:isPlainObject,isUndefined:R,isDate:D,isFile:_,isBlob:N,isRegExp:P,isFunction:S,isStream:isStream,isURLSearchParams:U,isTypedArray:M,isFileList:F,forEach:forEach,merge:merge,extend:extend,trim:trim,stripBOM:stripBOM,inherits:inherits,toFlatObject:toFlatObject,kindOf:b,kindOfTest:kindOfTest,endsWith:endsWith,toArray:toArray,forEachEntry:forEachEntry,matchAll:matchAll,isHTMLForm:L,hasOwnProperty:O,hasOwnProp:O,reduceDescriptors:reduceDescriptors,freezeMethods:freezeMethods,toObjectSet:toObjectSet,toCamelCase:toCamelCase,noop:noop,toFiniteNumber:toFiniteNumber,findKey:findKey,global:T,isContextDefined:isContextDefined,ALPHABET:q,generateString:generateString,isSpecCompliantForm:isSpecCompliantForm,toJSONObject:toJSONObject,isAsyncFn:G,isThenable:isThenable};function AxiosError(e,t,n,s,i){Error.call(this);if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}else{this.stack=(new Error).stack}this.message=e;this.name="AxiosError";t&&(this.code=t);n&&(this.config=n);s&&(this.request=s);i&&(this.response=i)}Y.inherits(AxiosError,Error,{toJSON:function toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:Y.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const V=AxiosError.prototype;const j={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((e=>{j[e]={value:e}}));Object.defineProperties(AxiosError,j);Object.defineProperty(V,"isAxiosError",{value:true});AxiosError.from=(e,t,n,s,i,r)=>{const o=Object.create(V);Y.toFlatObject(e,o,(function filter(e){return e!==Error.prototype}),(e=>e!=="isAxiosError"));AxiosError.call(o,e.message,t,n,s,i);o.cause=e;o.name=e.name;r&&Object.assign(o,r);return o};function isVisitable(e){return Y.isPlainObject(e)||Y.isArray(e)}function removeBrackets(e){return Y.endsWith(e,"[]")?e.slice(0,-2):e}function renderKey(e,t,n){if(!e)return t;return e.concat(t).map((function each(e,t){e=removeBrackets(e);return!n&&t?"["+e+"]":e})).join(n?".":"")}function isFlatArray(e){return Y.isArray(e)&&!e.some(isVisitable)}const W=Y.toFlatObject(Y,{},null,(function filter(e){return/^is[A-Z]/.test(e)}));function toFormData(e,t,n){if(!Y.isObject(e)){throw new TypeError("target must be an object")}t=t||new(p["default"]||FormData);n=Y.toFlatObject(n,{metaTokens:true,dots:false,indexes:false},false,(function defined(e,t){return!Y.isUndefined(t[e])}));const s=n.metaTokens;const i=n.visitor||defaultVisitor;const r=n.dots;const o=n.indexes;const A=n.Blob||typeof Blob!=="undefined"&&Blob;const a=A&&Y.isSpecCompliantForm(t);if(!Y.isFunction(i)){throw new TypeError("visitor must be a function")}function convertValue(e){if(e===null)return"";if(Y.isDate(e)){return e.toISOString()}if(!a&&Y.isBlob(e)){throw new AxiosError("Blob is not supported. Use a Buffer instead.")}if(Y.isArrayBuffer(e)||Y.isTypedArray(e)){return a&&typeof Blob==="function"?new Blob([e]):Buffer.from(e)}return e}function defaultVisitor(e,n,i){let A=e;if(e&&!i&&typeof e==="object"){if(Y.endsWith(n,"{}")){n=s?n:n.slice(0,-2);e=JSON.stringify(e)}else if(Y.isArray(e)&&isFlatArray(e)||(Y.isFileList(e)||Y.endsWith(n,"[]"))&&(A=Y.toArray(e))){n=removeBrackets(n);A.forEach((function each(e,s){!(Y.isUndefined(e)||e===null)&&t.append(o===true?renderKey([n],s,r):o===null?n:n+"[]",convertValue(e))}));return false}}if(isVisitable(e)){return true}t.append(renderKey(i,n,r),convertValue(e));return false}const c=[];const u=Object.assign(W,{defaultVisitor:defaultVisitor,convertValue:convertValue,isVisitable:isVisitable});function build(e,n){if(Y.isUndefined(e))return;if(c.indexOf(e)!==-1){throw Error("Circular reference detected in "+n.join("."))}c.push(e);Y.forEach(e,(function each(e,s){const r=!(Y.isUndefined(e)||e===null)&&i.call(t,e,Y.isString(s)?s.trim():s,n,u);if(r===true){build(e,n?n.concat(s):[s])}}));c.pop()}if(!Y.isObject(e)){throw new TypeError("data must be an object")}build(e);return t}function encode$1(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function replacer(e){return t[e]}))}function AxiosURLSearchParams(e,t){this._pairs=[];e&&toFormData(e,this,t)}const K=AxiosURLSearchParams.prototype;K.append=function append(e,t){this._pairs.push([e,t])};K.toString=function toString(e){const t=e?function(t){return e.call(this,t,encode$1)}:encode$1;return this._pairs.map((function each(e){return t(e[0])+"="+t(e[1])}),"").join("&")};function encode(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function buildURL(e,t,n){if(!t){return e}const s=n&&n.encode||encode;const i=n&&n.serialize;let r;if(i){r=i(t,n)}else{r=Y.isURLSearchParams(t)?t.toString():new AxiosURLSearchParams(t,n).toString(s)}if(r){const t=e.indexOf("#");if(t!==-1){e=e.slice(0,t)}e+=(e.indexOf("?")===-1?"?":"&")+r}return e}class InterceptorManager{constructor(){this.handlers=[]}use(e,t,n){this.handlers.push({fulfilled:e,rejected:t,synchronous:n?n.synchronous:false,runWhen:n?n.runWhen:null});return this.handlers.length-1}eject(e){if(this.handlers[e]){this.handlers[e]=null}}clear(){if(this.handlers){this.handlers=[]}}forEach(e){Y.forEach(this.handlers,(function forEachHandler(t){if(t!==null){e(t)}}))}}const z=InterceptorManager;const Z={silentJSONParsing:true,forcedJSONParsing:true,clarifyTimeoutError:false};const X=g["default"].URLSearchParams;const $={isNode:true,classes:{URLSearchParams:X,FormData:p["default"],Blob:typeof Blob!=="undefined"&&Blob||null},protocols:["http","https","file","data"]};function toURLEncodedForm(e,t){return toFormData(e,new $.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,s){if(Y.isBuffer(e)){this.append(t,e.toString("base64"));return false}return s.defaultVisitor.apply(this,arguments)}},t))}function parsePropPath(e){return Y.matchAll(/\w+|\[(\w*)]/g,e).map((e=>e[0]==="[]"?"":e[1]||e[0]))}function arrayToObject(e){const t={};const n=Object.keys(e);let s;const i=n.length;let r;for(s=0;s=e.length;i=!i&&Y.isArray(n)?n.length:i;if(o){if(Y.hasOwnProp(n,i)){n[i]=[n[i],t]}else{n[i]=t}return!r}if(!n[i]||!Y.isObject(n[i])){n[i]=[]}const A=buildPath(e,t,n[i],s);if(A&&Y.isArray(n[i])){n[i]=arrayToObject(n[i])}return!r}if(Y.isFormData(e)&&Y.isFunction(e.entries)){const t={};Y.forEachEntry(e,((e,n)=>{buildPath(parsePropPath(e),n,t,0)}));return t}return null}function stringifySafely(e,t,n){if(Y.isString(e)){try{(t||JSON.parse)(e);return Y.trim(e)}catch(e){if(e.name!=="SyntaxError"){throw e}}}return(n||JSON.stringify)(e)}const ee={transitional:Z,adapter:["xhr","http"],transformRequest:[function transformRequest(e,t){const n=t.getContentType()||"";const s=n.indexOf("application/json")>-1;const i=Y.isObject(e);if(i&&Y.isHTMLForm(e)){e=new FormData(e)}const r=Y.isFormData(e);if(r){if(!s){return e}return s?JSON.stringify(formDataToJSON(e)):e}if(Y.isArrayBuffer(e)||Y.isBuffer(e)||Y.isStream(e)||Y.isFile(e)||Y.isBlob(e)){return e}if(Y.isArrayBufferView(e)){return e.buffer}if(Y.isURLSearchParams(e)){t.setContentType("application/x-www-form-urlencoded;charset=utf-8",false);return e.toString()}let o;if(i){if(n.indexOf("application/x-www-form-urlencoded")>-1){return toURLEncodedForm(e,this.formSerializer).toString()}if((o=Y.isFileList(e))||n.indexOf("multipart/form-data")>-1){const t=this.env&&this.env.FormData;return toFormData(o?{"files[]":e}:e,t&&new t,this.formSerializer)}}if(i||s){t.setContentType("application/json",false);return stringifySafely(e)}return e}],transformResponse:[function transformResponse(e){const t=this.transitional||ee.transitional;const n=t&&t.forcedJSONParsing;const s=this.responseType==="json";if(e&&Y.isString(e)&&(n&&!this.responseType||s)){const n=t&&t.silentJSONParsing;const i=!n&&s;try{return JSON.parse(e)}catch(e){if(i){if(e.name==="SyntaxError"){throw AxiosError.from(e,AxiosError.ERR_BAD_RESPONSE,this,null,this.response)}throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:$.classes.FormData,Blob:$.classes.Blob},validateStatus:function validateStatus(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":undefined}}};Y.forEach(["delete","get","head","post","put","patch"],(e=>{ee.headers[e]={}}));const te=ee;const ne=Y.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]);const parseHeaders=e=>{const t={};let n;let s;let i;e&&e.split("\n").forEach((function parser(e){i=e.indexOf(":");n=e.substring(0,i).trim().toLowerCase();s=e.substring(i+1).trim();if(!n||t[n]&&ne[n]){return}if(n==="set-cookie"){if(t[n]){t[n].push(s)}else{t[n]=[s]}}else{t[n]=t[n]?t[n]+", "+s:s}}));return t};const se=Symbol("internals");function normalizeHeader(e){return e&&String(e).trim().toLowerCase()}function normalizeValue(e){if(e===false||e==null){return e}return Y.isArray(e)?e.map(normalizeValue):String(e)}function parseTokens(e){const t=Object.create(null);const n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let s;while(s=n.exec(e)){t[s[1]]=s[2]}return t}const isValidHeaderName=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function matchHeaderValue(e,t,n,s,i){if(Y.isFunction(s)){return s.call(this,t,n)}if(i){t=n}if(!Y.isString(t))return;if(Y.isString(s)){return t.indexOf(s)!==-1}if(Y.isRegExp(s)){return s.test(t)}}function formatHeader(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,((e,t,n)=>t.toUpperCase()+n))}function buildAccessors(e,t){const n=Y.toCamelCase(" "+t);["get","set","has"].forEach((s=>{Object.defineProperty(e,s+n,{value:function(e,n,i){return this[s].call(this,t,e,n,i)},configurable:true})}))}class AxiosHeaders{constructor(e){e&&this.set(e)}set(e,t,n){const s=this;function setHeader(e,t,n){const i=normalizeHeader(t);if(!i){throw new Error("header name must be a non-empty string")}const r=Y.findKey(s,i);if(!r||s[r]===undefined||n===true||n===undefined&&s[r]!==false){s[r||t]=normalizeValue(e)}}const setHeaders=(e,t)=>Y.forEach(e,((e,n)=>setHeader(e,n,t)));if(Y.isPlainObject(e)||e instanceof this.constructor){setHeaders(e,t)}else if(Y.isString(e)&&(e=e.trim())&&!isValidHeaderName(e)){setHeaders(parseHeaders(e),t)}else{e!=null&&setHeader(t,e,n)}return this}get(e,t){e=normalizeHeader(e);if(e){const n=Y.findKey(this,e);if(n){const e=this[n];if(!t){return e}if(t===true){return parseTokens(e)}if(Y.isFunction(t)){return t.call(this,e,n)}if(Y.isRegExp(t)){return t.exec(e)}throw new TypeError("parser must be boolean|regexp|function")}}}has(e,t){e=normalizeHeader(e);if(e){const n=Y.findKey(this,e);return!!(n&&this[n]!==undefined&&(!t||matchHeaderValue(this,this[n],n,t)))}return false}delete(e,t){const n=this;let s=false;function deleteHeader(e){e=normalizeHeader(e);if(e){const i=Y.findKey(n,e);if(i&&(!t||matchHeaderValue(n,n[i],i,t))){delete n[i];s=true}}}if(Y.isArray(e)){e.forEach(deleteHeader)}else{deleteHeader(e)}return s}clear(e){const t=Object.keys(this);let n=t.length;let s=false;while(n--){const i=t[n];if(!e||matchHeaderValue(this,this[i],i,e,true)){delete this[i];s=true}}return s}normalize(e){const t=this;const n={};Y.forEach(this,((s,i)=>{const r=Y.findKey(n,i);if(r){t[r]=normalizeValue(s);delete t[i];return}const o=e?formatHeader(i):String(i).trim();if(o!==i){delete t[i]}t[o]=normalizeValue(s);n[o]=true}));return this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){const t=Object.create(null);Y.forEach(this,((n,s)=>{n!=null&&n!==false&&(t[s]=e&&Y.isArray(n)?n.join(", "):n)}));return t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map((([e,t])=>e+": "+t)).join("\n")}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){const n=new this(e);t.forEach((e=>n.set(e)));return n}static accessor(e){const t=this[se]=this[se]={accessors:{}};const n=t.accessors;const s=this.prototype;function defineAccessor(e){const t=normalizeHeader(e);if(!n[t]){buildAccessors(s,e);n[t]=true}}Y.isArray(e)?e.forEach(defineAccessor):defineAccessor(e);return this}}AxiosHeaders.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);Y.reduceDescriptors(AxiosHeaders.prototype,(({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}));Y.freezeMethods(AxiosHeaders);const ie=AxiosHeaders;function transformData(e,t){const n=this||te;const s=t||n;const i=ie.from(s.headers);let r=s.data;Y.forEach(e,(function transform(e){r=e.call(n,r,i.normalize(),t?t.status:undefined)}));i.normalize();return r}function isCancel(e){return!!(e&&e.__CANCEL__)}function CanceledError(e,t,n){AxiosError.call(this,e==null?"canceled":e,AxiosError.ERR_CANCELED,t,n);this.name="CanceledError"}Y.inherits(CanceledError,AxiosError,{__CANCEL__:true});function settle(e,t,n){const s=n.config.validateStatus;if(!n.status||!s||s(n.status)){e(n)}else{t(new AxiosError("Request failed with status code "+n.status,[AxiosError.ERR_BAD_REQUEST,AxiosError.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}}function isAbsoluteURL(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function combineURLs(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}function buildFullPath(e,t){if(e&&!isAbsoluteURL(t)){return combineURLs(e,t)}return t}const re="1.6.0";function parseProtocol(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}const oe=/^(?:([^;]+);)?(?:[^;]+;)?(base64|),([\s\S]*)$/;function fromDataURI(e,t,n){const s=n&&n.Blob||$.classes.Blob;const i=parseProtocol(e);if(t===undefined&&s){t=true}if(i==="data"){e=i.length?e.slice(i.length+1):e;const n=oe.exec(e);if(!n){throw new AxiosError("Invalid URL",AxiosError.ERR_INVALID_URL)}const r=n[1];const o=n[2];const A=n[3];const a=Buffer.from(decodeURIComponent(A),o?"base64":"utf8");if(t){if(!s){throw new AxiosError("Blob is not supported",AxiosError.ERR_NOT_SUPPORT)}return new s([a],{type:r})}return a}throw new AxiosError("Unsupported protocol "+i,AxiosError.ERR_NOT_SUPPORT)}function throttle(e,t){let n=0;const s=1e3/t;let i=null;return function throttled(t,r){const o=Date.now();if(t||o-n>s){if(i){clearTimeout(i);i=null}n=o;return e.apply(null,r)}if(!i){i=setTimeout((()=>{i=null;n=Date.now();return e.apply(null,r)}),s-(o-n))}}}function speedometer(e,t){e=e||10;const n=new Array(e);const s=new Array(e);let i=0;let r=0;let o;t=t!==undefined?t:1e3;return function push(A){const a=Date.now();const c=s[r];if(!o){o=a}n[i]=A;s[i]=a;let u=r;let l=0;while(u!==i){l+=n[u++];u=u%e}i=(i+1)%e;if(i===r){r=(r+1)%e}if(a-o!Y.isUndefined(t[e])));super({readableHighWaterMark:e.chunkSize});const t=this;const n=this[Ae]={length:e.length,timeWindow:e.timeWindow,ticksRate:e.ticksRate,chunkSize:e.chunkSize,maxRate:e.maxRate,minChunkSize:e.minChunkSize,bytesSeen:0,isCaptured:false,notifiedBytesLoaded:0,ts:Date.now(),bytes:0,onReadCallback:null};const s=speedometer(n.ticksRate*e.samplesCount,n.timeWindow);this.on("newListener",(e=>{if(e==="progress"){if(!n.isCaptured){n.isCaptured=true}}}));let i=0;n.updateProgress=throttle((function throttledHandler(){const e=n.length;const r=n.bytesSeen;const o=r-i;if(!o||t.destroyed)return;const A=s(o);i=r;process.nextTick((()=>{t.emit("progress",{loaded:r,total:e,progress:e?r/e:undefined,bytes:o,rate:A?A:undefined,estimated:A&&e&&r<=e?(e-r)/A:undefined})}))}),n.ticksRate);const onFinish=()=>{n.updateProgress(true)};this.once("end",onFinish);this.once("error",onFinish)}_read(e){const t=this[Ae];if(t.onReadCallback){t.onReadCallback()}return super._read(e)}_transform(e,t,n){const s=this;const i=this[Ae];const r=i.maxRate;const o=this.readableHighWaterMark;const A=i.timeWindow;const a=1e3/A;const c=r/a;const u=i.minChunkSize!==false?Math.max(i.minChunkSize,c*.01):0;function pushChunk(e,t){const n=Buffer.byteLength(e);i.bytesSeen+=n;i.bytes+=n;if(i.isCaptured){i.updateProgress()}if(s.push(e)){process.nextTick(t)}else{i.onReadCallback=()=>{i.onReadCallback=null;process.nextTick(t)}}}const transformChunk=(e,t)=>{const n=Buffer.byteLength(e);let s=null;let a=o;let l;let d=0;if(r){const e=Date.now();if(!i.ts||(d=e-i.ts)>=A){i.ts=e;l=c-i.bytes;i.bytes=l<0?-l:0;d=0}l=c-i.bytes}if(r){if(l<=0){return setTimeout((()=>{t(null,e)}),A-d)}if(la&&n-a>u){s=e.subarray(a);e=e.subarray(0,a)}pushChunk(e,s?()=>{process.nextTick(t,null,s)}:t)};transformChunk(e,(function transformNextChunk(e,t){if(e){return n(e)}if(t){transformChunk(t,transformNextChunk)}else{n(null)}}))}setLength(e){this[Ae].length=+e;return this}}const ae=AxiosTransformStream;const{asyncIterator:ce}=Symbol;const readBlob=async function*(e){if(e.stream){yield*e.stream()}else if(e.arrayBuffer){yield await e.arrayBuffer()}else if(e[ce]){yield*e[ce]()}else{yield e}};const ue=readBlob;const le=Y.ALPHABET.ALPHA_DIGIT+"-_";const de=new a.TextEncoder;const pe="\r\n";const ge=de.encode(pe);const he=2;class FormDataPart{constructor(e,t){const{escapeName:n}=this.constructor;const s=Y.isString(t);let i=`Content-Disposition: form-data; name="${n(e)}"${!s&&t.name?`; filename="${n(t.name)}"`:""}${pe}`;if(s){t=de.encode(String(t).replace(/\r?\n|\r\n?/g,pe))}else{i+=`Content-Type: ${t.type||"application/octet-stream"}${pe}`}this.headers=de.encode(i+pe);this.contentLength=s?t.byteLength:t.size;this.size=this.headers.byteLength+this.contentLength+he;this.name=e;this.value=t}async*encode(){yield this.headers;const{value:e}=this;if(Y.isTypedArray(e)){yield e}else{yield*ue(e)}yield ge}static escapeName(e){return String(e).replace(/[\r\n"]/g,(e=>({"\r":"%0D","\n":"%0A",'"':"%22"}[e])))}}const formDataToStream=(e,t,n)=>{const{tag:s="form-data-boundary",size:i=25,boundary:r=s+"-"+Y.generateString(i,le)}=n||{};if(!Y.isFormData(e)){throw TypeError("FormData instance required")}if(r.length<1||r.length>70){throw Error("boundary must be 10-70 characters long")}const o=de.encode("--"+r+pe);const A=de.encode("--"+r+"--"+pe+pe);let a=A.byteLength;const c=Array.from(e.entries()).map((([e,t])=>{const n=new FormDataPart(e,t);a+=n.size;return n}));a+=o.byteLength*c.length;a=Y.toFiniteNumber(a);const u={"Content-Type":`multipart/form-data; boundary=${r}`};if(Number.isFinite(a)){u["Content-Length"]=a}t&&t(u);return l.Readable.from(async function*(){for(const e of c){yield o;yield*e.encode()}yield A}())};const fe=formDataToStream;class ZlibHeaderTransformStream extends Q["default"].Transform{__transform(e,t,n){this.push(e);n()}_transform(e,t,n){if(e.length!==0){this._transform=this.__transform;if(e[0]!==120){const e=Buffer.alloc(2);e[0]=120;e[1]=156;this.push(e,t)}}this.__transform(e,t,n)}}const Ee=ZlibHeaderTransformStream;const callbackify=(e,t)=>Y.isAsyncFn(e)?function(...n){const s=n.pop();e.apply(this,n).then((e=>{try{t?s(null,...t(e)):s(null,e)}catch(e){s(e)}}),s)}:e;const me=callbackify;const Ce={flush:C["default"].constants.Z_SYNC_FLUSH,finishFlush:C["default"].constants.Z_SYNC_FLUSH};const Qe={flush:C["default"].constants.BROTLI_OPERATION_FLUSH,finishFlush:C["default"].constants.BROTLI_OPERATION_FLUSH};const Ie=Y.isFunction(C["default"].createBrotliDecompress);const{http:Be,https:ye}=m["default"];const be=/https:?/;const we=$.protocols.map((e=>e+":"));function dispatchBeforeRedirect(e){if(e.beforeRedirects.proxy){e.beforeRedirects.proxy(e)}if(e.beforeRedirects.config){e.beforeRedirects.config(e)}}function setProxy(e,t,n){let s=t;if(!s&&s!==false){const e=r.getProxyForUrl(n);if(e){s=new URL(e)}}if(s){if(s.username){s.auth=(s.username||"")+":"+(s.password||"")}if(s.auth){if(s.auth.username||s.auth.password){s.auth=(s.auth.username||"")+":"+(s.auth.password||"")}const t=Buffer.from(s.auth,"utf8").toString("base64");e.headers["Proxy-Authorization"]="Basic "+t}e.headers.host=e.hostname+(e.port?":"+e.port:"");const t=s.hostname||s.host;e.hostname=t;e.host=t;e.port=s.port;e.path=n;if(s.protocol){e.protocol=s.protocol.includes(":")?s.protocol:`${s.protocol}:`}}e.beforeRedirects.proxy=function beforeRedirect(e){setProxy(e,t,e.href)}}const Re=typeof process!=="undefined"&&Y.kindOf(process)==="process";const wrapAsync=e=>new Promise(((t,n)=>{let s;let i;const done=(e,t)=>{if(i)return;i=true;s&&s(e,t)};const _resolve=e=>{done(e);t(e)};const _reject=e=>{done(e,true);n(e)};e(_resolve,_reject,(e=>s=e)).catch(_reject)}));const resolveFamily=({address:e,family:t})=>{if(!Y.isString(e)){throw TypeError("address must be a string")}return{address:e,family:t||(e.indexOf(".")<0?6:4)}};const buildAddressEntry=(e,t)=>resolveFamily(Y.isObject(e)?e:{address:e,family:t});const ve=Re&&function httpAdapter(e){return wrapAsync((async function dispatchHttpRequest(t,n,s){let{data:i,lookup:r,family:o}=e;const{responseType:A,responseEncoding:a}=e;const c=e.method.toUpperCase();let u;let l=false;let d;if(r){const e=me(r,(e=>Y.isArray(e)?e:[e]));r=(t,n,s)=>{e(t,n,((e,t,i)=>{const r=Y.isArray(t)?t.map((e=>buildAddressEntry(e))):[buildAddressEntry(t,i)];n.all?s(e,r):s(e,r[0].address,r[0].family)}))}}const p=new I["default"];const onFinished=()=>{if(e.cancelToken){e.cancelToken.unsubscribe(abort)}if(e.signal){e.signal.removeEventListener("abort",abort)}p.removeAllListeners()};s(((e,t)=>{u=true;if(t){l=true;onFinished()}}));function abort(t){p.emit("abort",!t||t.type?new CanceledError(null,e,d):t)}p.once("abort",n);if(e.cancelToken||e.signal){e.cancelToken&&e.cancelToken.subscribe(abort);if(e.signal){e.signal.aborted?abort():e.signal.addEventListener("abort",abort)}}const g=buildFullPath(e.baseURL,e.url);const m=new URL(g,"http://localhost");const B=m.protocol||we[0];if(B==="data:"){let s;if(c!=="GET"){return settle(t,n,{status:405,statusText:"method not allowed",headers:{},config:e})}try{s=fromDataURI(e.url,A==="blob",{Blob:e.env&&e.env.Blob})}catch(t){throw AxiosError.from(t,AxiosError.ERR_BAD_REQUEST,e)}if(A==="text"){s=s.toString(a);if(!a||a==="utf8"){s=Y.stripBOM(s)}}else if(A==="stream"){s=Q["default"].Readable.from(s)}return settle(t,n,{data:s,status:200,statusText:"OK",headers:new ie,config:e})}if(we.indexOf(B)===-1){return n(new AxiosError("Unsupported protocol "+B,AxiosError.ERR_BAD_REQUEST,e))}const y=ie.from(e.headers).normalize();y.set("User-Agent","axios/"+re,false);const b=e.onDownloadProgress;const w=e.onUploadProgress;const R=e.maxRate;let v=undefined;let k=undefined;if(Y.isSpecCompliantForm(i)){const e=y.getContentType(/boundary=([-_\w\d]{10,70})/i);i=fe(i,(e=>{y.set(e)}),{tag:`axios-${re}-boundary`,boundary:e&&e[1]||undefined})}else if(Y.isFormData(i)&&Y.isFunction(i.getHeaders)){y.set(i.getHeaders());if(!y.hasContentLength()){try{const e=await E["default"].promisify(i.getLength).call(i);Number.isFinite(e)&&e>=0&&y.setContentLength(e)}catch(e){}}}else if(Y.isBlob(i)){i.size&&y.setContentType(i.type||"application/octet-stream");y.setContentLength(i.size||0);i=Q["default"].Readable.from(ue(i))}else if(i&&!Y.isStream(i)){if(Buffer.isBuffer(i));else if(Y.isArrayBuffer(i)){i=Buffer.from(new Uint8Array(i))}else if(Y.isString(i)){i=Buffer.from(i,"utf-8")}else{return n(new AxiosError("Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream",AxiosError.ERR_BAD_REQUEST,e))}y.setContentLength(i.length,false);if(e.maxBodyLength>-1&&i.length>e.maxBodyLength){return n(new AxiosError("Request body larger than maxBodyLength limit",AxiosError.ERR_BAD_REQUEST,e))}}const S=Y.toFiniteNumber(y.getContentLength());if(Y.isArray(R)){v=R[0];k=R[1]}else{v=k=R}if(i&&(w||v)){if(!Y.isStream(i)){i=Q["default"].Readable.from(i,{objectMode:false})}i=Q["default"].pipeline([i,new ae({length:S,maxRate:Y.toFiniteNumber(v)})],Y.noop);w&&i.on("progress",(e=>{w(Object.assign(e,{upload:true}))}))}let x=undefined;if(e.auth){const t=e.auth.username||"";const n=e.auth.password||"";x=t+":"+n}if(!x&&m.username){const e=m.username;const t=m.password;x=e+":"+t}x&&y.delete("authorization");let D;try{D=buildURL(m.pathname+m.search,e.params,e.paramsSerializer).replace(/^\?/,"")}catch(t){const s=new Error(t.message);s.config=e;s.url=e.url;s.exists=true;return n(s)}y.set("Accept-Encoding","gzip, compress, deflate"+(Ie?", br":""),false);const _={path:D,method:c,headers:y.toJSON(),agents:{http:e.httpAgent,https:e.httpsAgent},auth:x,protocol:B,family:o,beforeRedirect:dispatchBeforeRedirect,beforeRedirects:{}};!Y.isUndefined(r)&&(_.lookup=r);if(e.socketPath){_.socketPath=e.socketPath}else{_.hostname=m.hostname;_.port=m.port;setProxy(_,e.proxy,B+"//"+m.hostname+(m.port?":"+m.port:"")+_.path)}let N;const F=be.test(_.protocol);_.agent=F?e.httpsAgent:e.httpAgent;if(e.transport){N=e.transport}else if(e.maxRedirects===0){N=F?f["default"]:h["default"]}else{if(e.maxRedirects){_.maxRedirects=e.maxRedirects}if(e.beforeRedirect){_.beforeRedirects.config=e.beforeRedirect}N=F?ye:Be}if(e.maxBodyLength>-1){_.maxBodyLength=e.maxBodyLength}else{_.maxBodyLength=Infinity}if(e.insecureHTTPParser){_.insecureHTTPParser=e.insecureHTTPParser}d=N.request(_,(function handleResponse(s){if(d.destroyed)return;const i=[s];const r=+s.headers["content-length"];if(b){const e=new ae({length:Y.toFiniteNumber(r),maxRate:Y.toFiniteNumber(k)});b&&e.on("progress",(e=>{b(Object.assign(e,{download:true}))}));i.push(e)}let o=s;const u=s.req||d;if(e.decompress!==false&&s.headers["content-encoding"]){if(c==="HEAD"||s.statusCode===204){delete s.headers["content-encoding"]}switch((s.headers["content-encoding"]||"").toLowerCase()){case"gzip":case"x-gzip":case"compress":case"x-compress":i.push(C["default"].createUnzip(Ce));delete s.headers["content-encoding"];break;case"deflate":i.push(new Ee);i.push(C["default"].createUnzip(Ce));delete s.headers["content-encoding"];break;case"br":if(Ie){i.push(C["default"].createBrotliDecompress(Qe));delete s.headers["content-encoding"]}}}o=i.length>1?Q["default"].pipeline(i,Y.noop):i[0];const g=Q["default"].finished(o,(()=>{g();onFinished()}));const h={status:s.statusCode,statusText:s.statusMessage,headers:new ie(s.headers),config:e,request:u};if(A==="stream"){h.data=o;settle(t,n,h)}else{const s=[];let i=0;o.on("data",(function handleStreamData(t){s.push(t);i+=t.length;if(e.maxContentLength>-1&&i>e.maxContentLength){l=true;o.destroy();n(new AxiosError("maxContentLength size of "+e.maxContentLength+" exceeded",AxiosError.ERR_BAD_RESPONSE,e,u))}}));o.on("aborted",(function handlerStreamAborted(){if(l){return}const t=new AxiosError("maxContentLength size of "+e.maxContentLength+" exceeded",AxiosError.ERR_BAD_RESPONSE,e,u);o.destroy(t);n(t)}));o.on("error",(function handleStreamError(t){if(d.destroyed)return;n(AxiosError.from(t,null,e,u))}));o.on("end",(function handleStreamEnd(){try{let e=s.length===1?s[0]:Buffer.concat(s);if(A!=="arraybuffer"){e=e.toString(a);if(!a||a==="utf8"){e=Y.stripBOM(e)}}h.data=e}catch(t){return n(AxiosError.from(t,null,e,h.request,h))}settle(t,n,h)}))}p.once("abort",(e=>{if(!o.destroyed){o.emit("error",e);o.destroy()}}))}));p.once("abort",(e=>{n(e);d.destroy(e)}));d.on("error",(function handleRequestError(t){n(AxiosError.from(t,null,e,d))}));d.on("socket",(function handleRequestSocket(e){e.setKeepAlive(true,1e3*60)}));if(e.timeout){const t=parseInt(e.timeout,10);if(Number.isNaN(t)){n(new AxiosError("error trying to parse `config.timeout` to int",AxiosError.ERR_BAD_OPTION_VALUE,e,d));return}d.setTimeout(t,(function handleRequestTimeout(){if(u)return;let t=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded";const s=e.transitional||Z;if(e.timeoutErrorMessage){t=e.timeoutErrorMessage}n(new AxiosError(t,s.clarifyTimeoutError?AxiosError.ETIMEDOUT:AxiosError.ECONNABORTED,e,d));abort()}))}if(Y.isStream(i)){let t=false;let n=false;i.on("end",(()=>{t=true}));i.once("error",(e=>{n=true;d.destroy(e)}));i.on("close",(()=>{if(!t&&!n){abort(new CanceledError("Request stream has been aborted",e,d))}}));i.pipe(d)}else{d.end(i)}}))};const ke=$.isStandardBrowserEnv?function standardBrowserEnv(){return{write:function write(e,t,n,s,i,r){const o=[];o.push(e+"="+encodeURIComponent(t));if(Y.isNumber(n)){o.push("expires="+new Date(n).toGMTString())}if(Y.isString(s)){o.push("path="+s)}if(Y.isString(i)){o.push("domain="+i)}if(r===true){o.push("secure")}document.cookie=o.join("; ")},read:function read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function remove(e){this.write(e,"",Date.now()-864e5)}}}():function nonStandardBrowserEnv(){return{write:function write(){},read:function read(){return null},remove:function remove(){}}}();const Se=$.isStandardBrowserEnv?function standardBrowserEnv(){const e=/(msie|trident)/i.test(navigator.userAgent);const t=document.createElement("a");let n;function resolveURL(n){let s=n;if(e){t.setAttribute("href",s);s=t.href}t.setAttribute("href",s);return{href:t.href,protocol:t.protocol?t.protocol.replace(/:$/,""):"",host:t.host,search:t.search?t.search.replace(/^\?/,""):"",hash:t.hash?t.hash.replace(/^#/,""):"",hostname:t.hostname,port:t.port,pathname:t.pathname.charAt(0)==="/"?t.pathname:"/"+t.pathname}}n=resolveURL(window.location.href);return function isURLSameOrigin(e){const t=Y.isString(e)?resolveURL(e):e;return t.protocol===n.protocol&&t.host===n.host}}():function nonStandardBrowserEnv(){return function isURLSameOrigin(){return true}}();function progressEventReducer(e,t){let n=0;const s=speedometer(50,250);return i=>{const r=i.loaded;const o=i.lengthComputable?i.total:undefined;const A=r-n;const a=s(A);const c=r<=o;n=r;const u={loaded:r,total:o,progress:o?r/o:undefined,bytes:A,rate:a?a:undefined,estimated:a&&o&&c?(o-r)/a:undefined,event:i};u[t?"download":"upload"]=true;e(u)}}const xe=typeof XMLHttpRequest!=="undefined";const De=xe&&function(e){return new Promise((function dispatchXhrRequest(t,n){let s=e.data;const i=ie.from(e.headers).normalize();const r=e.responseType;let o;function done(){if(e.cancelToken){e.cancelToken.unsubscribe(o)}if(e.signal){e.signal.removeEventListener("abort",o)}}let A;if(Y.isFormData(s)){if($.isStandardBrowserEnv||$.isStandardBrowserWebWorkerEnv){i.setContentType(false)}else if(!i.getContentType(/^\s*multipart\/form-data/)){i.setContentType("multipart/form-data")}else if(Y.isString(A=i.getContentType())){i.setContentType(A.replace(/^\s*(multipart\/form-data);+/,"$1"))}}let a=new XMLHttpRequest;if(e.auth){const t=e.auth.username||"";const n=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";i.set("Authorization","Basic "+btoa(t+":"+n))}const c=buildFullPath(e.baseURL,e.url);a.open(e.method.toUpperCase(),buildURL(c,e.params,e.paramsSerializer),true);a.timeout=e.timeout;function onloadend(){if(!a){return}const s=ie.from("getAllResponseHeaders"in a&&a.getAllResponseHeaders());const i=!r||r==="text"||r==="json"?a.responseText:a.response;const o={data:i,status:a.status,statusText:a.statusText,headers:s,config:e,request:a};settle((function _resolve(e){t(e);done()}),(function _reject(e){n(e);done()}),o);a=null}if("onloadend"in a){a.onloadend=onloadend}else{a.onreadystatechange=function handleLoad(){if(!a||a.readyState!==4){return}if(a.status===0&&!(a.responseURL&&a.responseURL.indexOf("file:")===0)){return}setTimeout(onloadend)}}a.onabort=function handleAbort(){if(!a){return}n(new AxiosError("Request aborted",AxiosError.ECONNABORTED,e,a));a=null};a.onerror=function handleError(){n(new AxiosError("Network Error",AxiosError.ERR_NETWORK,e,a));a=null};a.ontimeout=function handleTimeout(){let t=e.timeout?"timeout of "+e.timeout+"ms exceeded":"timeout exceeded";const s=e.transitional||Z;if(e.timeoutErrorMessage){t=e.timeoutErrorMessage}n(new AxiosError(t,s.clarifyTimeoutError?AxiosError.ETIMEDOUT:AxiosError.ECONNABORTED,e,a));a=null};if($.isStandardBrowserEnv){const t=Se(c)&&e.xsrfCookieName&&ke.read(e.xsrfCookieName);if(t){i.set(e.xsrfHeaderName,t)}}s===undefined&&i.setContentType(null);if("setRequestHeader"in a){Y.forEach(i.toJSON(),(function setRequestHeader(e,t){a.setRequestHeader(t,e)}))}if(!Y.isUndefined(e.withCredentials)){a.withCredentials=!!e.withCredentials}if(r&&r!=="json"){a.responseType=e.responseType}if(typeof e.onDownloadProgress==="function"){a.addEventListener("progress",progressEventReducer(e.onDownloadProgress,true))}if(typeof e.onUploadProgress==="function"&&a.upload){a.upload.addEventListener("progress",progressEventReducer(e.onUploadProgress))}if(e.cancelToken||e.signal){o=t=>{if(!a){return}n(!t||t.type?new CanceledError(null,e,a):t);a.abort();a=null};e.cancelToken&&e.cancelToken.subscribe(o);if(e.signal){e.signal.aborted?o():e.signal.addEventListener("abort",o)}}const u=parseProtocol(c);if(u&&$.protocols.indexOf(u)===-1){n(new AxiosError("Unsupported protocol "+u+":",AxiosError.ERR_BAD_REQUEST,e));return}a.send(s||null)}))};const _e={http:ve,xhr:De};Y.forEach(_e,((e,t)=>{if(e){try{Object.defineProperty(e,"name",{value:t})}catch(e){}Object.defineProperty(e,"adapterName",{value:t})}}));const renderReason=e=>`- ${e}`;const isResolvedHandle=e=>Y.isFunction(e)||e===null||e===false;const Ne={getAdapter:e=>{e=Y.isArray(e)?e:[e];const{length:t}=e;let n;let s;const i={};for(let r=0;r`adapter ${e} `+(t===false?"is not supported by the environment":"is not available in the build")));let n=t?e.length>1?"since :\n"+e.map(renderReason).join("\n"):" "+renderReason(e[0]):"as no adapter specified";throw new AxiosError(`There is no suitable adapter to dispatch the request `+n,"ERR_NOT_SUPPORT")}return s},adapters:_e};function throwIfCancellationRequested(e){if(e.cancelToken){e.cancelToken.throwIfRequested()}if(e.signal&&e.signal.aborted){throw new CanceledError(null,e)}}function dispatchRequest(e){throwIfCancellationRequested(e);e.headers=ie.from(e.headers);e.data=transformData.call(e,e.transformRequest);if(["post","put","patch"].indexOf(e.method)!==-1){e.headers.setContentType("application/x-www-form-urlencoded",false)}const t=Ne.getAdapter(e.adapter||te.adapter);return t(e).then((function onAdapterResolution(t){throwIfCancellationRequested(e);t.data=transformData.call(e,e.transformResponse,t);t.headers=ie.from(t.headers);return t}),(function onAdapterRejection(t){if(!isCancel(t)){throwIfCancellationRequested(e);if(t&&t.response){t.response.data=transformData.call(e,e.transformResponse,t.response);t.response.headers=ie.from(t.response.headers)}}return Promise.reject(t)}))}const headersToObject=e=>e instanceof ie?e.toJSON():e;function mergeConfig(e,t){t=t||{};const n={};function getMergedValue(e,t,n){if(Y.isPlainObject(e)&&Y.isPlainObject(t)){return Y.merge.call({caseless:n},e,t)}else if(Y.isPlainObject(t)){return Y.merge({},t)}else if(Y.isArray(t)){return t.slice()}return t}function mergeDeepProperties(e,t,n){if(!Y.isUndefined(t)){return getMergedValue(e,t,n)}else if(!Y.isUndefined(e)){return getMergedValue(undefined,e,n)}}function valueFromConfig2(e,t){if(!Y.isUndefined(t)){return getMergedValue(undefined,t)}}function defaultToConfig2(e,t){if(!Y.isUndefined(t)){return getMergedValue(undefined,t)}else if(!Y.isUndefined(e)){return getMergedValue(undefined,e)}}function mergeDirectKeys(n,s,i){if(i in t){return getMergedValue(n,s)}else if(i in e){return getMergedValue(undefined,n)}}const s={url:valueFromConfig2,method:valueFromConfig2,data:valueFromConfig2,baseURL:defaultToConfig2,transformRequest:defaultToConfig2,transformResponse:defaultToConfig2,paramsSerializer:defaultToConfig2,timeout:defaultToConfig2,timeoutMessage:defaultToConfig2,withCredentials:defaultToConfig2,adapter:defaultToConfig2,responseType:defaultToConfig2,xsrfCookieName:defaultToConfig2,xsrfHeaderName:defaultToConfig2,onUploadProgress:defaultToConfig2,onDownloadProgress:defaultToConfig2,decompress:defaultToConfig2,maxContentLength:defaultToConfig2,maxBodyLength:defaultToConfig2,beforeRedirect:defaultToConfig2,transport:defaultToConfig2,httpAgent:defaultToConfig2,httpsAgent:defaultToConfig2,cancelToken:defaultToConfig2,socketPath:defaultToConfig2,responseEncoding:defaultToConfig2,validateStatus:mergeDirectKeys,headers:(e,t)=>mergeDeepProperties(headersToObject(e),headersToObject(t),true)};Y.forEach(Object.keys(Object.assign({},e,t)),(function computeConfigValue(i){const r=s[i]||mergeDeepProperties;const o=r(e[i],t[i],i);Y.isUndefined(o)&&r!==mergeDirectKeys||(n[i]=o)}));return n}const Fe={};["object","boolean","number","function","string","symbol"].forEach(((e,t)=>{Fe[e]=function validator(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}}));const Ue={};Fe.transitional=function transitional(e,t,n){function formatMessage(e,t){return"[Axios v"+re+"] Transitional option '"+e+"'"+t+(n?". "+n:"")}return(n,s,i)=>{if(e===false){throw new AxiosError(formatMessage(s," has been removed"+(t?" in "+t:"")),AxiosError.ERR_DEPRECATED)}if(t&&!Ue[s]){Ue[s]=true;console.warn(formatMessage(s," has been deprecated since v"+t+" and will be removed in the near future"))}return e?e(n,s,i):true}};function assertOptions(e,t,n){if(typeof e!=="object"){throw new AxiosError("options must be an object",AxiosError.ERR_BAD_OPTION_VALUE)}const s=Object.keys(e);let i=s.length;while(i-- >0){const r=s[i];const o=t[r];if(o){const t=e[r];const n=t===undefined||o(t,r,e);if(n!==true){throw new AxiosError("option "+r+" must be "+n,AxiosError.ERR_BAD_OPTION_VALUE)}continue}if(n!==true){throw new AxiosError("Unknown option "+r,AxiosError.ERR_BAD_OPTION)}}}const Te={assertOptions:assertOptions,validators:Fe};const Me=Te.validators;class Axios{constructor(e){this.defaults=e;this.interceptors={request:new z,response:new z}}request(e,t){if(typeof e==="string"){t=t||{};t.url=e}else{t=e||{}}t=mergeConfig(this.defaults,t);const{transitional:n,paramsSerializer:s,headers:i}=t;if(n!==undefined){Te.assertOptions(n,{silentJSONParsing:Me.transitional(Me.boolean),forcedJSONParsing:Me.transitional(Me.boolean),clarifyTimeoutError:Me.transitional(Me.boolean)},false)}if(s!=null){if(Y.isFunction(s)){t.paramsSerializer={serialize:s}}else{Te.assertOptions(s,{encode:Me.function,serialize:Me.function},true)}}t.method=(t.method||this.defaults.method||"get").toLowerCase();let r=i&&Y.merge(i.common,i[t.method]);i&&Y.forEach(["delete","get","head","post","put","patch","common"],(e=>{delete i[e]}));t.headers=ie.concat(r,i);const o=[];let A=true;this.interceptors.request.forEach((function unshiftRequestInterceptors(e){if(typeof e.runWhen==="function"&&e.runWhen(t)===false){return}A=A&&e.synchronous;o.unshift(e.fulfilled,e.rejected)}));const a=[];this.interceptors.response.forEach((function pushResponseInterceptors(e){a.push(e.fulfilled,e.rejected)}));let c;let u=0;let l;if(!A){const e=[dispatchRequest.bind(this),undefined];e.unshift.apply(e,o);e.push.apply(e,a);l=e.length;c=Promise.resolve(t);while(u{if(!n._listeners)return;let t=n._listeners.length;while(t-- >0){n._listeners[t](e)}n._listeners=null}));this.promise.then=e=>{let t;const s=new Promise((e=>{n.subscribe(e);t=e})).then(e);s.cancel=function reject(){n.unsubscribe(t)};return s};e((function cancel(e,s,i){if(n.reason){return}n.reason=new CanceledError(e,s,i);t(n.reason)}))}throwIfRequested(){if(this.reason){throw this.reason}}subscribe(e){if(this.reason){e(this.reason);return}if(this._listeners){this._listeners.push(e)}else{this._listeners=[e]}}unsubscribe(e){if(!this._listeners){return}const t=this._listeners.indexOf(e);if(t!==-1){this._listeners.splice(t,1)}}static source(){let e;const t=new CancelToken((function executor(t){e=t}));return{token:t,cancel:e}}}const Oe=CancelToken;function spread(e){return function wrap(t){return e.apply(null,t)}}function isAxiosError(e){return Y.isObject(e)&&e.isAxiosError===true}const Pe={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Pe).forEach((([e,t])=>{Pe[t]=e}));const Je=Pe;function createInstance(e){const t=new Le(e);const n=bind(Le.prototype.request,t);Y.extend(n,Le.prototype,t,{allOwnKeys:true});Y.extend(n,t,null,{allOwnKeys:true});n.create=function create(t){return createInstance(mergeConfig(e,t))};return n}const He=createInstance(te);He.Axios=Le;He.CanceledError=CanceledError;He.CancelToken=Oe;He.isCancel=isCancel;He.VERSION=re;He.toFormData=toFormData;He.AxiosError=AxiosError;He.Cancel=He.CanceledError;He.all=function all(e){return Promise.all(e)};He.spread=spread;He.isAxiosError=isAxiosError;He.mergeConfig=mergeConfig;He.AxiosHeaders=ie;He.formToJSON=e=>formDataToJSON(Y.isHTMLForm(e)?new FormData(e):e);He.getAdapter=Ne.getAdapter;He.HttpStatusCode=Je;He.default=He;e.exports=He},3765:e=>{"use strict";e.exports=JSON.parse('{"application/1d-interleaved-parityfec":{"source":"iana"},"application/3gpdash-qoe-report+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/3gpp-ims+xml":{"source":"iana","compressible":true},"application/3gpphal+json":{"source":"iana","compressible":true},"application/3gpphalforms+json":{"source":"iana","compressible":true},"application/a2l":{"source":"iana"},"application/ace+cbor":{"source":"iana"},"application/activemessage":{"source":"iana"},"application/activity+json":{"source":"iana","compressible":true},"application/alto-costmap+json":{"source":"iana","compressible":true},"application/alto-costmapfilter+json":{"source":"iana","compressible":true},"application/alto-directory+json":{"source":"iana","compressible":true},"application/alto-endpointcost+json":{"source":"iana","compressible":true},"application/alto-endpointcostparams+json":{"source":"iana","compressible":true},"application/alto-endpointprop+json":{"source":"iana","compressible":true},"application/alto-endpointpropparams+json":{"source":"iana","compressible":true},"application/alto-error+json":{"source":"iana","compressible":true},"application/alto-networkmap+json":{"source":"iana","compressible":true},"application/alto-networkmapfilter+json":{"source":"iana","compressible":true},"application/alto-updatestreamcontrol+json":{"source":"iana","compressible":true},"application/alto-updatestreamparams+json":{"source":"iana","compressible":true},"application/aml":{"source":"iana"},"application/andrew-inset":{"source":"iana","extensions":["ez"]},"application/applefile":{"source":"iana"},"application/applixware":{"source":"apache","extensions":["aw"]},"application/at+jwt":{"source":"iana"},"application/atf":{"source":"iana"},"application/atfx":{"source":"iana"},"application/atom+xml":{"source":"iana","compressible":true,"extensions":["atom"]},"application/atomcat+xml":{"source":"iana","compressible":true,"extensions":["atomcat"]},"application/atomdeleted+xml":{"source":"iana","compressible":true,"extensions":["atomdeleted"]},"application/atomicmail":{"source":"iana"},"application/atomsvc+xml":{"source":"iana","compressible":true,"extensions":["atomsvc"]},"application/atsc-dwd+xml":{"source":"iana","compressible":true,"extensions":["dwd"]},"application/atsc-dynamic-event-message":{"source":"iana"},"application/atsc-held+xml":{"source":"iana","compressible":true,"extensions":["held"]},"application/atsc-rdt+json":{"source":"iana","compressible":true},"application/atsc-rsat+xml":{"source":"iana","compressible":true,"extensions":["rsat"]},"application/atxml":{"source":"iana"},"application/auth-policy+xml":{"source":"iana","compressible":true},"application/bacnet-xdd+zip":{"source":"iana","compressible":false},"application/batch-smtp":{"source":"iana"},"application/bdoc":{"compressible":false,"extensions":["bdoc"]},"application/beep+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/calendar+json":{"source":"iana","compressible":true},"application/calendar+xml":{"source":"iana","compressible":true,"extensions":["xcs"]},"application/call-completion":{"source":"iana"},"application/cals-1840":{"source":"iana"},"application/captive+json":{"source":"iana","compressible":true},"application/cbor":{"source":"iana"},"application/cbor-seq":{"source":"iana"},"application/cccex":{"source":"iana"},"application/ccmp+xml":{"source":"iana","compressible":true},"application/ccxml+xml":{"source":"iana","compressible":true,"extensions":["ccxml"]},"application/cdfx+xml":{"source":"iana","compressible":true,"extensions":["cdfx"]},"application/cdmi-capability":{"source":"iana","extensions":["cdmia"]},"application/cdmi-container":{"source":"iana","extensions":["cdmic"]},"application/cdmi-domain":{"source":"iana","extensions":["cdmid"]},"application/cdmi-object":{"source":"iana","extensions":["cdmio"]},"application/cdmi-queue":{"source":"iana","extensions":["cdmiq"]},"application/cdni":{"source":"iana"},"application/cea":{"source":"iana"},"application/cea-2018+xml":{"source":"iana","compressible":true},"application/cellml+xml":{"source":"iana","compressible":true},"application/cfw":{"source":"iana"},"application/city+json":{"source":"iana","compressible":true},"application/clr":{"source":"iana"},"application/clue+xml":{"source":"iana","compressible":true},"application/clue_info+xml":{"source":"iana","compressible":true},"application/cms":{"source":"iana"},"application/cnrp+xml":{"source":"iana","compressible":true},"application/coap-group+json":{"source":"iana","compressible":true},"application/coap-payload":{"source":"iana"},"application/commonground":{"source":"iana"},"application/conference-info+xml":{"source":"iana","compressible":true},"application/cose":{"source":"iana"},"application/cose-key":{"source":"iana"},"application/cose-key-set":{"source":"iana"},"application/cpl+xml":{"source":"iana","compressible":true,"extensions":["cpl"]},"application/csrattrs":{"source":"iana"},"application/csta+xml":{"source":"iana","compressible":true},"application/cstadata+xml":{"source":"iana","compressible":true},"application/csvm+json":{"source":"iana","compressible":true},"application/cu-seeme":{"source":"apache","extensions":["cu"]},"application/cwt":{"source":"iana"},"application/cybercash":{"source":"iana"},"application/dart":{"compressible":true},"application/dash+xml":{"source":"iana","compressible":true,"extensions":["mpd"]},"application/dash-patch+xml":{"source":"iana","compressible":true,"extensions":["mpp"]},"application/dashdelta":{"source":"iana"},"application/davmount+xml":{"source":"iana","compressible":true,"extensions":["davmount"]},"application/dca-rft":{"source":"iana"},"application/dcd":{"source":"iana"},"application/dec-dx":{"source":"iana"},"application/dialog-info+xml":{"source":"iana","compressible":true},"application/dicom":{"source":"iana"},"application/dicom+json":{"source":"iana","compressible":true},"application/dicom+xml":{"source":"iana","compressible":true},"application/dii":{"source":"iana"},"application/dit":{"source":"iana"},"application/dns":{"source":"iana"},"application/dns+json":{"source":"iana","compressible":true},"application/dns-message":{"source":"iana"},"application/docbook+xml":{"source":"apache","compressible":true,"extensions":["dbk"]},"application/dots+cbor":{"source":"iana"},"application/dskpp+xml":{"source":"iana","compressible":true},"application/dssc+der":{"source":"iana","extensions":["dssc"]},"application/dssc+xml":{"source":"iana","compressible":true,"extensions":["xdssc"]},"application/dvcs":{"source":"iana"},"application/ecmascript":{"source":"iana","compressible":true,"extensions":["es","ecma"]},"application/edi-consent":{"source":"iana"},"application/edi-x12":{"source":"iana","compressible":false},"application/edifact":{"source":"iana","compressible":false},"application/efi":{"source":"iana"},"application/elm+json":{"source":"iana","charset":"UTF-8","compressible":true},"application/elm+xml":{"source":"iana","compressible":true},"application/emergencycalldata.cap+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/emergencycalldata.comment+xml":{"source":"iana","compressible":true},"application/emergencycalldata.control+xml":{"source":"iana","compressible":true},"application/emergencycalldata.deviceinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.ecall.msd":{"source":"iana"},"application/emergencycalldata.providerinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.serviceinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.subscriberinfo+xml":{"source":"iana","compressible":true},"application/emergencycalldata.veds+xml":{"source":"iana","compressible":true},"application/emma+xml":{"source":"iana","compressible":true,"extensions":["emma"]},"application/emotionml+xml":{"source":"iana","compressible":true,"extensions":["emotionml"]},"application/encaprtp":{"source":"iana"},"application/epp+xml":{"source":"iana","compressible":true},"application/epub+zip":{"source":"iana","compressible":false,"extensions":["epub"]},"application/eshop":{"source":"iana"},"application/exi":{"source":"iana","extensions":["exi"]},"application/expect-ct-report+json":{"source":"iana","compressible":true},"application/express":{"source":"iana","extensions":["exp"]},"application/fastinfoset":{"source":"iana"},"application/fastsoap":{"source":"iana"},"application/fdt+xml":{"source":"iana","compressible":true,"extensions":["fdt"]},"application/fhir+json":{"source":"iana","charset":"UTF-8","compressible":true},"application/fhir+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/fido.trusted-apps+json":{"compressible":true},"application/fits":{"source":"iana"},"application/flexfec":{"source":"iana"},"application/font-sfnt":{"source":"iana"},"application/font-tdpfr":{"source":"iana","extensions":["pfr"]},"application/font-woff":{"source":"iana","compressible":false},"application/framework-attributes+xml":{"source":"iana","compressible":true},"application/geo+json":{"source":"iana","compressible":true,"extensions":["geojson"]},"application/geo+json-seq":{"source":"iana"},"application/geopackage+sqlite3":{"source":"iana"},"application/geoxacml+xml":{"source":"iana","compressible":true},"application/gltf-buffer":{"source":"iana"},"application/gml+xml":{"source":"iana","compressible":true,"extensions":["gml"]},"application/gpx+xml":{"source":"apache","compressible":true,"extensions":["gpx"]},"application/gxf":{"source":"apache","extensions":["gxf"]},"application/gzip":{"source":"iana","compressible":false,"extensions":["gz"]},"application/h224":{"source":"iana"},"application/held+xml":{"source":"iana","compressible":true},"application/hjson":{"extensions":["hjson"]},"application/http":{"source":"iana"},"application/hyperstudio":{"source":"iana","extensions":["stk"]},"application/ibe-key-request+xml":{"source":"iana","compressible":true},"application/ibe-pkg-reply+xml":{"source":"iana","compressible":true},"application/ibe-pp-data":{"source":"iana"},"application/iges":{"source":"iana"},"application/im-iscomposing+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/index":{"source":"iana"},"application/index.cmd":{"source":"iana"},"application/index.obj":{"source":"iana"},"application/index.response":{"source":"iana"},"application/index.vnd":{"source":"iana"},"application/inkml+xml":{"source":"iana","compressible":true,"extensions":["ink","inkml"]},"application/iotp":{"source":"iana"},"application/ipfix":{"source":"iana","extensions":["ipfix"]},"application/ipp":{"source":"iana"},"application/isup":{"source":"iana"},"application/its+xml":{"source":"iana","compressible":true,"extensions":["its"]},"application/java-archive":{"source":"apache","compressible":false,"extensions":["jar","war","ear"]},"application/java-serialized-object":{"source":"apache","compressible":false,"extensions":["ser"]},"application/java-vm":{"source":"apache","compressible":false,"extensions":["class"]},"application/javascript":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["js","mjs"]},"application/jf2feed+json":{"source":"iana","compressible":true},"application/jose":{"source":"iana"},"application/jose+json":{"source":"iana","compressible":true},"application/jrd+json":{"source":"iana","compressible":true},"application/jscalendar+json":{"source":"iana","compressible":true},"application/json":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["json","map"]},"application/json-patch+json":{"source":"iana","compressible":true},"application/json-seq":{"source":"iana"},"application/json5":{"extensions":["json5"]},"application/jsonml+json":{"source":"apache","compressible":true,"extensions":["jsonml"]},"application/jwk+json":{"source":"iana","compressible":true},"application/jwk-set+json":{"source":"iana","compressible":true},"application/jwt":{"source":"iana"},"application/kpml-request+xml":{"source":"iana","compressible":true},"application/kpml-response+xml":{"source":"iana","compressible":true},"application/ld+json":{"source":"iana","compressible":true,"extensions":["jsonld"]},"application/lgr+xml":{"source":"iana","compressible":true,"extensions":["lgr"]},"application/link-format":{"source":"iana"},"application/load-control+xml":{"source":"iana","compressible":true},"application/lost+xml":{"source":"iana","compressible":true,"extensions":["lostxml"]},"application/lostsync+xml":{"source":"iana","compressible":true},"application/lpf+zip":{"source":"iana","compressible":false},"application/lxf":{"source":"iana"},"application/mac-binhex40":{"source":"iana","extensions":["hqx"]},"application/mac-compactpro":{"source":"apache","extensions":["cpt"]},"application/macwriteii":{"source":"iana"},"application/mads+xml":{"source":"iana","compressible":true,"extensions":["mads"]},"application/manifest+json":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["webmanifest"]},"application/marc":{"source":"iana","extensions":["mrc"]},"application/marcxml+xml":{"source":"iana","compressible":true,"extensions":["mrcx"]},"application/mathematica":{"source":"iana","extensions":["ma","nb","mb"]},"application/mathml+xml":{"source":"iana","compressible":true,"extensions":["mathml"]},"application/mathml-content+xml":{"source":"iana","compressible":true},"application/mathml-presentation+xml":{"source":"iana","compressible":true},"application/mbms-associated-procedure-description+xml":{"source":"iana","compressible":true},"application/mbms-deregister+xml":{"source":"iana","compressible":true},"application/mbms-envelope+xml":{"source":"iana","compressible":true},"application/mbms-msk+xml":{"source":"iana","compressible":true},"application/mbms-msk-response+xml":{"source":"iana","compressible":true},"application/mbms-protection-description+xml":{"source":"iana","compressible":true},"application/mbms-reception-report+xml":{"source":"iana","compressible":true},"application/mbms-register+xml":{"source":"iana","compressible":true},"application/mbms-register-response+xml":{"source":"iana","compressible":true},"application/mbms-schedule+xml":{"source":"iana","compressible":true},"application/mbms-user-service-description+xml":{"source":"iana","compressible":true},"application/mbox":{"source":"iana","extensions":["mbox"]},"application/media-policy-dataset+xml":{"source":"iana","compressible":true,"extensions":["mpf"]},"application/media_control+xml":{"source":"iana","compressible":true},"application/mediaservercontrol+xml":{"source":"iana","compressible":true,"extensions":["mscml"]},"application/merge-patch+json":{"source":"iana","compressible":true},"application/metalink+xml":{"source":"apache","compressible":true,"extensions":["metalink"]},"application/metalink4+xml":{"source":"iana","compressible":true,"extensions":["meta4"]},"application/mets+xml":{"source":"iana","compressible":true,"extensions":["mets"]},"application/mf4":{"source":"iana"},"application/mikey":{"source":"iana"},"application/mipc":{"source":"iana"},"application/missing-blocks+cbor-seq":{"source":"iana"},"application/mmt-aei+xml":{"source":"iana","compressible":true,"extensions":["maei"]},"application/mmt-usd+xml":{"source":"iana","compressible":true,"extensions":["musd"]},"application/mods+xml":{"source":"iana","compressible":true,"extensions":["mods"]},"application/moss-keys":{"source":"iana"},"application/moss-signature":{"source":"iana"},"application/mosskey-data":{"source":"iana"},"application/mosskey-request":{"source":"iana"},"application/mp21":{"source":"iana","extensions":["m21","mp21"]},"application/mp4":{"source":"iana","extensions":["mp4s","m4p"]},"application/mpeg4-generic":{"source":"iana"},"application/mpeg4-iod":{"source":"iana"},"application/mpeg4-iod-xmt":{"source":"iana"},"application/mrb-consumer+xml":{"source":"iana","compressible":true},"application/mrb-publish+xml":{"source":"iana","compressible":true},"application/msc-ivr+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/msc-mixer+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/msword":{"source":"iana","compressible":false,"extensions":["doc","dot"]},"application/mud+json":{"source":"iana","compressible":true},"application/multipart-core":{"source":"iana"},"application/mxf":{"source":"iana","extensions":["mxf"]},"application/n-quads":{"source":"iana","extensions":["nq"]},"application/n-triples":{"source":"iana","extensions":["nt"]},"application/nasdata":{"source":"iana"},"application/news-checkgroups":{"source":"iana","charset":"US-ASCII"},"application/news-groupinfo":{"source":"iana","charset":"US-ASCII"},"application/news-transmission":{"source":"iana"},"application/nlsml+xml":{"source":"iana","compressible":true},"application/node":{"source":"iana","extensions":["cjs"]},"application/nss":{"source":"iana"},"application/oauth-authz-req+jwt":{"source":"iana"},"application/oblivious-dns-message":{"source":"iana"},"application/ocsp-request":{"source":"iana"},"application/ocsp-response":{"source":"iana"},"application/octet-stream":{"source":"iana","compressible":false,"extensions":["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"]},"application/oda":{"source":"iana","extensions":["oda"]},"application/odm+xml":{"source":"iana","compressible":true},"application/odx":{"source":"iana"},"application/oebps-package+xml":{"source":"iana","compressible":true,"extensions":["opf"]},"application/ogg":{"source":"iana","compressible":false,"extensions":["ogx"]},"application/omdoc+xml":{"source":"apache","compressible":true,"extensions":["omdoc"]},"application/onenote":{"source":"apache","extensions":["onetoc","onetoc2","onetmp","onepkg"]},"application/opc-nodeset+xml":{"source":"iana","compressible":true},"application/oscore":{"source":"iana"},"application/oxps":{"source":"iana","extensions":["oxps"]},"application/p21":{"source":"iana"},"application/p21+zip":{"source":"iana","compressible":false},"application/p2p-overlay+xml":{"source":"iana","compressible":true,"extensions":["relo"]},"application/parityfec":{"source":"iana"},"application/passport":{"source":"iana"},"application/patch-ops-error+xml":{"source":"iana","compressible":true,"extensions":["xer"]},"application/pdf":{"source":"iana","compressible":false,"extensions":["pdf"]},"application/pdx":{"source":"iana"},"application/pem-certificate-chain":{"source":"iana"},"application/pgp-encrypted":{"source":"iana","compressible":false,"extensions":["pgp"]},"application/pgp-keys":{"source":"iana","extensions":["asc"]},"application/pgp-signature":{"source":"iana","extensions":["asc","sig"]},"application/pics-rules":{"source":"apache","extensions":["prf"]},"application/pidf+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/pidf-diff+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/pkcs10":{"source":"iana","extensions":["p10"]},"application/pkcs12":{"source":"iana"},"application/pkcs7-mime":{"source":"iana","extensions":["p7m","p7c"]},"application/pkcs7-signature":{"source":"iana","extensions":["p7s"]},"application/pkcs8":{"source":"iana","extensions":["p8"]},"application/pkcs8-encrypted":{"source":"iana"},"application/pkix-attr-cert":{"source":"iana","extensions":["ac"]},"application/pkix-cert":{"source":"iana","extensions":["cer"]},"application/pkix-crl":{"source":"iana","extensions":["crl"]},"application/pkix-pkipath":{"source":"iana","extensions":["pkipath"]},"application/pkixcmp":{"source":"iana","extensions":["pki"]},"application/pls+xml":{"source":"iana","compressible":true,"extensions":["pls"]},"application/poc-settings+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/postscript":{"source":"iana","compressible":true,"extensions":["ai","eps","ps"]},"application/ppsp-tracker+json":{"source":"iana","compressible":true},"application/problem+json":{"source":"iana","compressible":true},"application/problem+xml":{"source":"iana","compressible":true},"application/provenance+xml":{"source":"iana","compressible":true,"extensions":["provx"]},"application/prs.alvestrand.titrax-sheet":{"source":"iana"},"application/prs.cww":{"source":"iana","extensions":["cww"]},"application/prs.cyn":{"source":"iana","charset":"7-BIT"},"application/prs.hpub+zip":{"source":"iana","compressible":false},"application/prs.nprend":{"source":"iana"},"application/prs.plucker":{"source":"iana"},"application/prs.rdf-xml-crypt":{"source":"iana"},"application/prs.xsf+xml":{"source":"iana","compressible":true},"application/pskc+xml":{"source":"iana","compressible":true,"extensions":["pskcxml"]},"application/pvd+json":{"source":"iana","compressible":true},"application/qsig":{"source":"iana"},"application/raml+yaml":{"compressible":true,"extensions":["raml"]},"application/raptorfec":{"source":"iana"},"application/rdap+json":{"source":"iana","compressible":true},"application/rdf+xml":{"source":"iana","compressible":true,"extensions":["rdf","owl"]},"application/reginfo+xml":{"source":"iana","compressible":true,"extensions":["rif"]},"application/relax-ng-compact-syntax":{"source":"iana","extensions":["rnc"]},"application/remote-printing":{"source":"iana"},"application/reputon+json":{"source":"iana","compressible":true},"application/resource-lists+xml":{"source":"iana","compressible":true,"extensions":["rl"]},"application/resource-lists-diff+xml":{"source":"iana","compressible":true,"extensions":["rld"]},"application/rfc+xml":{"source":"iana","compressible":true},"application/riscos":{"source":"iana"},"application/rlmi+xml":{"source":"iana","compressible":true},"application/rls-services+xml":{"source":"iana","compressible":true,"extensions":["rs"]},"application/route-apd+xml":{"source":"iana","compressible":true,"extensions":["rapd"]},"application/route-s-tsid+xml":{"source":"iana","compressible":true,"extensions":["sls"]},"application/route-usd+xml":{"source":"iana","compressible":true,"extensions":["rusd"]},"application/rpki-ghostbusters":{"source":"iana","extensions":["gbr"]},"application/rpki-manifest":{"source":"iana","extensions":["mft"]},"application/rpki-publication":{"source":"iana"},"application/rpki-roa":{"source":"iana","extensions":["roa"]},"application/rpki-updown":{"source":"iana"},"application/rsd+xml":{"source":"apache","compressible":true,"extensions":["rsd"]},"application/rss+xml":{"source":"apache","compressible":true,"extensions":["rss"]},"application/rtf":{"source":"iana","compressible":true,"extensions":["rtf"]},"application/rtploopback":{"source":"iana"},"application/rtx":{"source":"iana"},"application/samlassertion+xml":{"source":"iana","compressible":true},"application/samlmetadata+xml":{"source":"iana","compressible":true},"application/sarif+json":{"source":"iana","compressible":true},"application/sarif-external-properties+json":{"source":"iana","compressible":true},"application/sbe":{"source":"iana"},"application/sbml+xml":{"source":"iana","compressible":true,"extensions":["sbml"]},"application/scaip+xml":{"source":"iana","compressible":true},"application/scim+json":{"source":"iana","compressible":true},"application/scvp-cv-request":{"source":"iana","extensions":["scq"]},"application/scvp-cv-response":{"source":"iana","extensions":["scs"]},"application/scvp-vp-request":{"source":"iana","extensions":["spq"]},"application/scvp-vp-response":{"source":"iana","extensions":["spp"]},"application/sdp":{"source":"iana","extensions":["sdp"]},"application/secevent+jwt":{"source":"iana"},"application/senml+cbor":{"source":"iana"},"application/senml+json":{"source":"iana","compressible":true},"application/senml+xml":{"source":"iana","compressible":true,"extensions":["senmlx"]},"application/senml-etch+cbor":{"source":"iana"},"application/senml-etch+json":{"source":"iana","compressible":true},"application/senml-exi":{"source":"iana"},"application/sensml+cbor":{"source":"iana"},"application/sensml+json":{"source":"iana","compressible":true},"application/sensml+xml":{"source":"iana","compressible":true,"extensions":["sensmlx"]},"application/sensml-exi":{"source":"iana"},"application/sep+xml":{"source":"iana","compressible":true},"application/sep-exi":{"source":"iana"},"application/session-info":{"source":"iana"},"application/set-payment":{"source":"iana"},"application/set-payment-initiation":{"source":"iana","extensions":["setpay"]},"application/set-registration":{"source":"iana"},"application/set-registration-initiation":{"source":"iana","extensions":["setreg"]},"application/sgml":{"source":"iana"},"application/sgml-open-catalog":{"source":"iana"},"application/shf+xml":{"source":"iana","compressible":true,"extensions":["shf"]},"application/sieve":{"source":"iana","extensions":["siv","sieve"]},"application/simple-filter+xml":{"source":"iana","compressible":true},"application/simple-message-summary":{"source":"iana"},"application/simplesymbolcontainer":{"source":"iana"},"application/sipc":{"source":"iana"},"application/slate":{"source":"iana"},"application/smil":{"source":"iana"},"application/smil+xml":{"source":"iana","compressible":true,"extensions":["smi","smil"]},"application/smpte336m":{"source":"iana"},"application/soap+fastinfoset":{"source":"iana"},"application/soap+xml":{"source":"iana","compressible":true},"application/sparql-query":{"source":"iana","extensions":["rq"]},"application/sparql-results+xml":{"source":"iana","compressible":true,"extensions":["srx"]},"application/spdx+json":{"source":"iana","compressible":true},"application/spirits-event+xml":{"source":"iana","compressible":true},"application/sql":{"source":"iana"},"application/srgs":{"source":"iana","extensions":["gram"]},"application/srgs+xml":{"source":"iana","compressible":true,"extensions":["grxml"]},"application/sru+xml":{"source":"iana","compressible":true,"extensions":["sru"]},"application/ssdl+xml":{"source":"apache","compressible":true,"extensions":["ssdl"]},"application/ssml+xml":{"source":"iana","compressible":true,"extensions":["ssml"]},"application/stix+json":{"source":"iana","compressible":true},"application/swid+xml":{"source":"iana","compressible":true,"extensions":["swidtag"]},"application/tamp-apex-update":{"source":"iana"},"application/tamp-apex-update-confirm":{"source":"iana"},"application/tamp-community-update":{"source":"iana"},"application/tamp-community-update-confirm":{"source":"iana"},"application/tamp-error":{"source":"iana"},"application/tamp-sequence-adjust":{"source":"iana"},"application/tamp-sequence-adjust-confirm":{"source":"iana"},"application/tamp-status-query":{"source":"iana"},"application/tamp-status-response":{"source":"iana"},"application/tamp-update":{"source":"iana"},"application/tamp-update-confirm":{"source":"iana"},"application/tar":{"compressible":true},"application/taxii+json":{"source":"iana","compressible":true},"application/td+json":{"source":"iana","compressible":true},"application/tei+xml":{"source":"iana","compressible":true,"extensions":["tei","teicorpus"]},"application/tetra_isi":{"source":"iana"},"application/thraud+xml":{"source":"iana","compressible":true,"extensions":["tfi"]},"application/timestamp-query":{"source":"iana"},"application/timestamp-reply":{"source":"iana"},"application/timestamped-data":{"source":"iana","extensions":["tsd"]},"application/tlsrpt+gzip":{"source":"iana"},"application/tlsrpt+json":{"source":"iana","compressible":true},"application/tnauthlist":{"source":"iana"},"application/token-introspection+jwt":{"source":"iana"},"application/toml":{"compressible":true,"extensions":["toml"]},"application/trickle-ice-sdpfrag":{"source":"iana"},"application/trig":{"source":"iana","extensions":["trig"]},"application/ttml+xml":{"source":"iana","compressible":true,"extensions":["ttml"]},"application/tve-trigger":{"source":"iana"},"application/tzif":{"source":"iana"},"application/tzif-leap":{"source":"iana"},"application/ubjson":{"compressible":false,"extensions":["ubj"]},"application/ulpfec":{"source":"iana"},"application/urc-grpsheet+xml":{"source":"iana","compressible":true},"application/urc-ressheet+xml":{"source":"iana","compressible":true,"extensions":["rsheet"]},"application/urc-targetdesc+xml":{"source":"iana","compressible":true,"extensions":["td"]},"application/urc-uisocketdesc+xml":{"source":"iana","compressible":true},"application/vcard+json":{"source":"iana","compressible":true},"application/vcard+xml":{"source":"iana","compressible":true},"application/vemmi":{"source":"iana"},"application/vividence.scriptfile":{"source":"apache"},"application/vnd.1000minds.decision-model+xml":{"source":"iana","compressible":true,"extensions":["1km"]},"application/vnd.3gpp-prose+xml":{"source":"iana","compressible":true},"application/vnd.3gpp-prose-pc3ch+xml":{"source":"iana","compressible":true},"application/vnd.3gpp-v2x-local-service-information":{"source":"iana"},"application/vnd.3gpp.5gnas":{"source":"iana"},"application/vnd.3gpp.access-transfer-events+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.bsf+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.gmop+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.gtpc":{"source":"iana"},"application/vnd.3gpp.interworking-data":{"source":"iana"},"application/vnd.3gpp.lpp":{"source":"iana"},"application/vnd.3gpp.mc-signalling-ear":{"source":"iana"},"application/vnd.3gpp.mcdata-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-payload":{"source":"iana"},"application/vnd.3gpp.mcdata-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-signalling":{"source":"iana"},"application/vnd.3gpp.mcdata-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcdata-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-floor-request+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-location-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-mbms-usage-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-signed+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-ue-init-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcptt-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-affiliation-command+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-affiliation-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-location-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-mbms-usage-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-service-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-transmission-request+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-ue-config+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mcvideo-user-profile+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.mid-call+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.ngap":{"source":"iana"},"application/vnd.3gpp.pfcp":{"source":"iana"},"application/vnd.3gpp.pic-bw-large":{"source":"iana","extensions":["plb"]},"application/vnd.3gpp.pic-bw-small":{"source":"iana","extensions":["psb"]},"application/vnd.3gpp.pic-bw-var":{"source":"iana","extensions":["pvb"]},"application/vnd.3gpp.s1ap":{"source":"iana"},"application/vnd.3gpp.sms":{"source":"iana"},"application/vnd.3gpp.sms+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.srvcc-ext+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.srvcc-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.state-and-event-info+xml":{"source":"iana","compressible":true},"application/vnd.3gpp.ussd+xml":{"source":"iana","compressible":true},"application/vnd.3gpp2.bcmcsinfo+xml":{"source":"iana","compressible":true},"application/vnd.3gpp2.sms":{"source":"iana"},"application/vnd.3gpp2.tcap":{"source":"iana","extensions":["tcap"]},"application/vnd.3lightssoftware.imagescal":{"source":"iana"},"application/vnd.3m.post-it-notes":{"source":"iana","extensions":["pwn"]},"application/vnd.accpac.simply.aso":{"source":"iana","extensions":["aso"]},"application/vnd.accpac.simply.imp":{"source":"iana","extensions":["imp"]},"application/vnd.acucobol":{"source":"iana","extensions":["acu"]},"application/vnd.acucorp":{"source":"iana","extensions":["atc","acutc"]},"application/vnd.adobe.air-application-installer-package+zip":{"source":"apache","compressible":false,"extensions":["air"]},"application/vnd.adobe.flash.movie":{"source":"iana"},"application/vnd.adobe.formscentral.fcdt":{"source":"iana","extensions":["fcdt"]},"application/vnd.adobe.fxp":{"source":"iana","extensions":["fxp","fxpl"]},"application/vnd.adobe.partial-upload":{"source":"iana"},"application/vnd.adobe.xdp+xml":{"source":"iana","compressible":true,"extensions":["xdp"]},"application/vnd.adobe.xfdf":{"source":"iana","extensions":["xfdf"]},"application/vnd.aether.imp":{"source":"iana"},"application/vnd.afpc.afplinedata":{"source":"iana"},"application/vnd.afpc.afplinedata-pagedef":{"source":"iana"},"application/vnd.afpc.cmoca-cmresource":{"source":"iana"},"application/vnd.afpc.foca-charset":{"source":"iana"},"application/vnd.afpc.foca-codedfont":{"source":"iana"},"application/vnd.afpc.foca-codepage":{"source":"iana"},"application/vnd.afpc.modca":{"source":"iana"},"application/vnd.afpc.modca-cmtable":{"source":"iana"},"application/vnd.afpc.modca-formdef":{"source":"iana"},"application/vnd.afpc.modca-mediummap":{"source":"iana"},"application/vnd.afpc.modca-objectcontainer":{"source":"iana"},"application/vnd.afpc.modca-overlay":{"source":"iana"},"application/vnd.afpc.modca-pagesegment":{"source":"iana"},"application/vnd.age":{"source":"iana","extensions":["age"]},"application/vnd.ah-barcode":{"source":"iana"},"application/vnd.ahead.space":{"source":"iana","extensions":["ahead"]},"application/vnd.airzip.filesecure.azf":{"source":"iana","extensions":["azf"]},"application/vnd.airzip.filesecure.azs":{"source":"iana","extensions":["azs"]},"application/vnd.amadeus+json":{"source":"iana","compressible":true},"application/vnd.amazon.ebook":{"source":"apache","extensions":["azw"]},"application/vnd.amazon.mobi8-ebook":{"source":"iana"},"application/vnd.americandynamics.acc":{"source":"iana","extensions":["acc"]},"application/vnd.amiga.ami":{"source":"iana","extensions":["ami"]},"application/vnd.amundsen.maze+xml":{"source":"iana","compressible":true},"application/vnd.android.ota":{"source":"iana"},"application/vnd.android.package-archive":{"source":"apache","compressible":false,"extensions":["apk"]},"application/vnd.anki":{"source":"iana"},"application/vnd.anser-web-certificate-issue-initiation":{"source":"iana","extensions":["cii"]},"application/vnd.anser-web-funds-transfer-initiation":{"source":"apache","extensions":["fti"]},"application/vnd.antix.game-component":{"source":"iana","extensions":["atx"]},"application/vnd.apache.arrow.file":{"source":"iana"},"application/vnd.apache.arrow.stream":{"source":"iana"},"application/vnd.apache.thrift.binary":{"source":"iana"},"application/vnd.apache.thrift.compact":{"source":"iana"},"application/vnd.apache.thrift.json":{"source":"iana"},"application/vnd.api+json":{"source":"iana","compressible":true},"application/vnd.aplextor.warrp+json":{"source":"iana","compressible":true},"application/vnd.apothekende.reservation+json":{"source":"iana","compressible":true},"application/vnd.apple.installer+xml":{"source":"iana","compressible":true,"extensions":["mpkg"]},"application/vnd.apple.keynote":{"source":"iana","extensions":["key"]},"application/vnd.apple.mpegurl":{"source":"iana","extensions":["m3u8"]},"application/vnd.apple.numbers":{"source":"iana","extensions":["numbers"]},"application/vnd.apple.pages":{"source":"iana","extensions":["pages"]},"application/vnd.apple.pkpass":{"compressible":false,"extensions":["pkpass"]},"application/vnd.arastra.swi":{"source":"iana"},"application/vnd.aristanetworks.swi":{"source":"iana","extensions":["swi"]},"application/vnd.artisan+json":{"source":"iana","compressible":true},"application/vnd.artsquare":{"source":"iana"},"application/vnd.astraea-software.iota":{"source":"iana","extensions":["iota"]},"application/vnd.audiograph":{"source":"iana","extensions":["aep"]},"application/vnd.autopackage":{"source":"iana"},"application/vnd.avalon+json":{"source":"iana","compressible":true},"application/vnd.avistar+xml":{"source":"iana","compressible":true},"application/vnd.balsamiq.bmml+xml":{"source":"iana","compressible":true,"extensions":["bmml"]},"application/vnd.balsamiq.bmpr":{"source":"iana"},"application/vnd.banana-accounting":{"source":"iana"},"application/vnd.bbf.usp.error":{"source":"iana"},"application/vnd.bbf.usp.msg":{"source":"iana"},"application/vnd.bbf.usp.msg+json":{"source":"iana","compressible":true},"application/vnd.bekitzur-stech+json":{"source":"iana","compressible":true},"application/vnd.bint.med-content":{"source":"iana"},"application/vnd.biopax.rdf+xml":{"source":"iana","compressible":true},"application/vnd.blink-idb-value-wrapper":{"source":"iana"},"application/vnd.blueice.multipass":{"source":"iana","extensions":["mpm"]},"application/vnd.bluetooth.ep.oob":{"source":"iana"},"application/vnd.bluetooth.le.oob":{"source":"iana"},"application/vnd.bmi":{"source":"iana","extensions":["bmi"]},"application/vnd.bpf":{"source":"iana"},"application/vnd.bpf3":{"source":"iana"},"application/vnd.businessobjects":{"source":"iana","extensions":["rep"]},"application/vnd.byu.uapi+json":{"source":"iana","compressible":true},"application/vnd.cab-jscript":{"source":"iana"},"application/vnd.canon-cpdl":{"source":"iana"},"application/vnd.canon-lips":{"source":"iana"},"application/vnd.capasystems-pg+json":{"source":"iana","compressible":true},"application/vnd.cendio.thinlinc.clientconf":{"source":"iana"},"application/vnd.century-systems.tcp_stream":{"source":"iana"},"application/vnd.chemdraw+xml":{"source":"iana","compressible":true,"extensions":["cdxml"]},"application/vnd.chess-pgn":{"source":"iana"},"application/vnd.chipnuts.karaoke-mmd":{"source":"iana","extensions":["mmd"]},"application/vnd.ciedi":{"source":"iana"},"application/vnd.cinderella":{"source":"iana","extensions":["cdy"]},"application/vnd.cirpack.isdn-ext":{"source":"iana"},"application/vnd.citationstyles.style+xml":{"source":"iana","compressible":true,"extensions":["csl"]},"application/vnd.claymore":{"source":"iana","extensions":["cla"]},"application/vnd.cloanto.rp9":{"source":"iana","extensions":["rp9"]},"application/vnd.clonk.c4group":{"source":"iana","extensions":["c4g","c4d","c4f","c4p","c4u"]},"application/vnd.cluetrust.cartomobile-config":{"source":"iana","extensions":["c11amc"]},"application/vnd.cluetrust.cartomobile-config-pkg":{"source":"iana","extensions":["c11amz"]},"application/vnd.coffeescript":{"source":"iana"},"application/vnd.collabio.xodocuments.document":{"source":"iana"},"application/vnd.collabio.xodocuments.document-template":{"source":"iana"},"application/vnd.collabio.xodocuments.presentation":{"source":"iana"},"application/vnd.collabio.xodocuments.presentation-template":{"source":"iana"},"application/vnd.collabio.xodocuments.spreadsheet":{"source":"iana"},"application/vnd.collabio.xodocuments.spreadsheet-template":{"source":"iana"},"application/vnd.collection+json":{"source":"iana","compressible":true},"application/vnd.collection.doc+json":{"source":"iana","compressible":true},"application/vnd.collection.next+json":{"source":"iana","compressible":true},"application/vnd.comicbook+zip":{"source":"iana","compressible":false},"application/vnd.comicbook-rar":{"source":"iana"},"application/vnd.commerce-battelle":{"source":"iana"},"application/vnd.commonspace":{"source":"iana","extensions":["csp"]},"application/vnd.contact.cmsg":{"source":"iana","extensions":["cdbcmsg"]},"application/vnd.coreos.ignition+json":{"source":"iana","compressible":true},"application/vnd.cosmocaller":{"source":"iana","extensions":["cmc"]},"application/vnd.crick.clicker":{"source":"iana","extensions":["clkx"]},"application/vnd.crick.clicker.keyboard":{"source":"iana","extensions":["clkk"]},"application/vnd.crick.clicker.palette":{"source":"iana","extensions":["clkp"]},"application/vnd.crick.clicker.template":{"source":"iana","extensions":["clkt"]},"application/vnd.crick.clicker.wordbank":{"source":"iana","extensions":["clkw"]},"application/vnd.criticaltools.wbs+xml":{"source":"iana","compressible":true,"extensions":["wbs"]},"application/vnd.cryptii.pipe+json":{"source":"iana","compressible":true},"application/vnd.crypto-shade-file":{"source":"iana"},"application/vnd.cryptomator.encrypted":{"source":"iana"},"application/vnd.cryptomator.vault":{"source":"iana"},"application/vnd.ctc-posml":{"source":"iana","extensions":["pml"]},"application/vnd.ctct.ws+xml":{"source":"iana","compressible":true},"application/vnd.cups-pdf":{"source":"iana"},"application/vnd.cups-postscript":{"source":"iana"},"application/vnd.cups-ppd":{"source":"iana","extensions":["ppd"]},"application/vnd.cups-raster":{"source":"iana"},"application/vnd.cups-raw":{"source":"iana"},"application/vnd.curl":{"source":"iana"},"application/vnd.curl.car":{"source":"apache","extensions":["car"]},"application/vnd.curl.pcurl":{"source":"apache","extensions":["pcurl"]},"application/vnd.cyan.dean.root+xml":{"source":"iana","compressible":true},"application/vnd.cybank":{"source":"iana"},"application/vnd.cyclonedx+json":{"source":"iana","compressible":true},"application/vnd.cyclonedx+xml":{"source":"iana","compressible":true},"application/vnd.d2l.coursepackage1p0+zip":{"source":"iana","compressible":false},"application/vnd.d3m-dataset":{"source":"iana"},"application/vnd.d3m-problem":{"source":"iana"},"application/vnd.dart":{"source":"iana","compressible":true,"extensions":["dart"]},"application/vnd.data-vision.rdz":{"source":"iana","extensions":["rdz"]},"application/vnd.datapackage+json":{"source":"iana","compressible":true},"application/vnd.dataresource+json":{"source":"iana","compressible":true},"application/vnd.dbf":{"source":"iana","extensions":["dbf"]},"application/vnd.debian.binary-package":{"source":"iana"},"application/vnd.dece.data":{"source":"iana","extensions":["uvf","uvvf","uvd","uvvd"]},"application/vnd.dece.ttml+xml":{"source":"iana","compressible":true,"extensions":["uvt","uvvt"]},"application/vnd.dece.unspecified":{"source":"iana","extensions":["uvx","uvvx"]},"application/vnd.dece.zip":{"source":"iana","extensions":["uvz","uvvz"]},"application/vnd.denovo.fcselayout-link":{"source":"iana","extensions":["fe_launch"]},"application/vnd.desmume.movie":{"source":"iana"},"application/vnd.dir-bi.plate-dl-nosuffix":{"source":"iana"},"application/vnd.dm.delegation+xml":{"source":"iana","compressible":true},"application/vnd.dna":{"source":"iana","extensions":["dna"]},"application/vnd.document+json":{"source":"iana","compressible":true},"application/vnd.dolby.mlp":{"source":"apache","extensions":["mlp"]},"application/vnd.dolby.mobile.1":{"source":"iana"},"application/vnd.dolby.mobile.2":{"source":"iana"},"application/vnd.doremir.scorecloud-binary-document":{"source":"iana"},"application/vnd.dpgraph":{"source":"iana","extensions":["dpg"]},"application/vnd.dreamfactory":{"source":"iana","extensions":["dfac"]},"application/vnd.drive+json":{"source":"iana","compressible":true},"application/vnd.ds-keypoint":{"source":"apache","extensions":["kpxx"]},"application/vnd.dtg.local":{"source":"iana"},"application/vnd.dtg.local.flash":{"source":"iana"},"application/vnd.dtg.local.html":{"source":"iana"},"application/vnd.dvb.ait":{"source":"iana","extensions":["ait"]},"application/vnd.dvb.dvbisl+xml":{"source":"iana","compressible":true},"application/vnd.dvb.dvbj":{"source":"iana"},"application/vnd.dvb.esgcontainer":{"source":"iana"},"application/vnd.dvb.ipdcdftnotifaccess":{"source":"iana"},"application/vnd.dvb.ipdcesgaccess":{"source":"iana"},"application/vnd.dvb.ipdcesgaccess2":{"source":"iana"},"application/vnd.dvb.ipdcesgpdd":{"source":"iana"},"application/vnd.dvb.ipdcroaming":{"source":"iana"},"application/vnd.dvb.iptv.alfec-base":{"source":"iana"},"application/vnd.dvb.iptv.alfec-enhancement":{"source":"iana"},"application/vnd.dvb.notif-aggregate-root+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-container+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-generic+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-msglist+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-registration-request+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-ia-registration-response+xml":{"source":"iana","compressible":true},"application/vnd.dvb.notif-init+xml":{"source":"iana","compressible":true},"application/vnd.dvb.pfr":{"source":"iana"},"application/vnd.dvb.service":{"source":"iana","extensions":["svc"]},"application/vnd.dxr":{"source":"iana"},"application/vnd.dynageo":{"source":"iana","extensions":["geo"]},"application/vnd.dzr":{"source":"iana"},"application/vnd.easykaraoke.cdgdownload":{"source":"iana"},"application/vnd.ecdis-update":{"source":"iana"},"application/vnd.ecip.rlp":{"source":"iana"},"application/vnd.eclipse.ditto+json":{"source":"iana","compressible":true},"application/vnd.ecowin.chart":{"source":"iana","extensions":["mag"]},"application/vnd.ecowin.filerequest":{"source":"iana"},"application/vnd.ecowin.fileupdate":{"source":"iana"},"application/vnd.ecowin.series":{"source":"iana"},"application/vnd.ecowin.seriesrequest":{"source":"iana"},"application/vnd.ecowin.seriesupdate":{"source":"iana"},"application/vnd.efi.img":{"source":"iana"},"application/vnd.efi.iso":{"source":"iana"},"application/vnd.emclient.accessrequest+xml":{"source":"iana","compressible":true},"application/vnd.enliven":{"source":"iana","extensions":["nml"]},"application/vnd.enphase.envoy":{"source":"iana"},"application/vnd.eprints.data+xml":{"source":"iana","compressible":true},"application/vnd.epson.esf":{"source":"iana","extensions":["esf"]},"application/vnd.epson.msf":{"source":"iana","extensions":["msf"]},"application/vnd.epson.quickanime":{"source":"iana","extensions":["qam"]},"application/vnd.epson.salt":{"source":"iana","extensions":["slt"]},"application/vnd.epson.ssf":{"source":"iana","extensions":["ssf"]},"application/vnd.ericsson.quickcall":{"source":"iana"},"application/vnd.espass-espass+zip":{"source":"iana","compressible":false},"application/vnd.eszigno3+xml":{"source":"iana","compressible":true,"extensions":["es3","et3"]},"application/vnd.etsi.aoc+xml":{"source":"iana","compressible":true},"application/vnd.etsi.asic-e+zip":{"source":"iana","compressible":false},"application/vnd.etsi.asic-s+zip":{"source":"iana","compressible":false},"application/vnd.etsi.cug+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvcommand+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvdiscovery+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvprofile+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-bc+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-cod+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsad-npvr+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvservice+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvsync+xml":{"source":"iana","compressible":true},"application/vnd.etsi.iptvueprofile+xml":{"source":"iana","compressible":true},"application/vnd.etsi.mcid+xml":{"source":"iana","compressible":true},"application/vnd.etsi.mheg5":{"source":"iana"},"application/vnd.etsi.overload-control-policy-dataset+xml":{"source":"iana","compressible":true},"application/vnd.etsi.pstn+xml":{"source":"iana","compressible":true},"application/vnd.etsi.sci+xml":{"source":"iana","compressible":true},"application/vnd.etsi.simservs+xml":{"source":"iana","compressible":true},"application/vnd.etsi.timestamp-token":{"source":"iana"},"application/vnd.etsi.tsl+xml":{"source":"iana","compressible":true},"application/vnd.etsi.tsl.der":{"source":"iana"},"application/vnd.eu.kasparian.car+json":{"source":"iana","compressible":true},"application/vnd.eudora.data":{"source":"iana"},"application/vnd.evolv.ecig.profile":{"source":"iana"},"application/vnd.evolv.ecig.settings":{"source":"iana"},"application/vnd.evolv.ecig.theme":{"source":"iana"},"application/vnd.exstream-empower+zip":{"source":"iana","compressible":false},"application/vnd.exstream-package":{"source":"iana"},"application/vnd.ezpix-album":{"source":"iana","extensions":["ez2"]},"application/vnd.ezpix-package":{"source":"iana","extensions":["ez3"]},"application/vnd.f-secure.mobile":{"source":"iana"},"application/vnd.familysearch.gedcom+zip":{"source":"iana","compressible":false},"application/vnd.fastcopy-disk-image":{"source":"iana"},"application/vnd.fdf":{"source":"iana","extensions":["fdf"]},"application/vnd.fdsn.mseed":{"source":"iana","extensions":["mseed"]},"application/vnd.fdsn.seed":{"source":"iana","extensions":["seed","dataless"]},"application/vnd.ffsns":{"source":"iana"},"application/vnd.ficlab.flb+zip":{"source":"iana","compressible":false},"application/vnd.filmit.zfc":{"source":"iana"},"application/vnd.fints":{"source":"iana"},"application/vnd.firemonkeys.cloudcell":{"source":"iana"},"application/vnd.flographit":{"source":"iana","extensions":["gph"]},"application/vnd.fluxtime.clip":{"source":"iana","extensions":["ftc"]},"application/vnd.font-fontforge-sfd":{"source":"iana"},"application/vnd.framemaker":{"source":"iana","extensions":["fm","frame","maker","book"]},"application/vnd.frogans.fnc":{"source":"iana","extensions":["fnc"]},"application/vnd.frogans.ltf":{"source":"iana","extensions":["ltf"]},"application/vnd.fsc.weblaunch":{"source":"iana","extensions":["fsc"]},"application/vnd.fujifilm.fb.docuworks":{"source":"iana"},"application/vnd.fujifilm.fb.docuworks.binder":{"source":"iana"},"application/vnd.fujifilm.fb.docuworks.container":{"source":"iana"},"application/vnd.fujifilm.fb.jfi+xml":{"source":"iana","compressible":true},"application/vnd.fujitsu.oasys":{"source":"iana","extensions":["oas"]},"application/vnd.fujitsu.oasys2":{"source":"iana","extensions":["oa2"]},"application/vnd.fujitsu.oasys3":{"source":"iana","extensions":["oa3"]},"application/vnd.fujitsu.oasysgp":{"source":"iana","extensions":["fg5"]},"application/vnd.fujitsu.oasysprs":{"source":"iana","extensions":["bh2"]},"application/vnd.fujixerox.art-ex":{"source":"iana"},"application/vnd.fujixerox.art4":{"source":"iana"},"application/vnd.fujixerox.ddd":{"source":"iana","extensions":["ddd"]},"application/vnd.fujixerox.docuworks":{"source":"iana","extensions":["xdw"]},"application/vnd.fujixerox.docuworks.binder":{"source":"iana","extensions":["xbd"]},"application/vnd.fujixerox.docuworks.container":{"source":"iana"},"application/vnd.fujixerox.hbpl":{"source":"iana"},"application/vnd.fut-misnet":{"source":"iana"},"application/vnd.futoin+cbor":{"source":"iana"},"application/vnd.futoin+json":{"source":"iana","compressible":true},"application/vnd.fuzzysheet":{"source":"iana","extensions":["fzs"]},"application/vnd.genomatix.tuxedo":{"source":"iana","extensions":["txd"]},"application/vnd.gentics.grd+json":{"source":"iana","compressible":true},"application/vnd.geo+json":{"source":"iana","compressible":true},"application/vnd.geocube+xml":{"source":"iana","compressible":true},"application/vnd.geogebra.file":{"source":"iana","extensions":["ggb"]},"application/vnd.geogebra.slides":{"source":"iana"},"application/vnd.geogebra.tool":{"source":"iana","extensions":["ggt"]},"application/vnd.geometry-explorer":{"source":"iana","extensions":["gex","gre"]},"application/vnd.geonext":{"source":"iana","extensions":["gxt"]},"application/vnd.geoplan":{"source":"iana","extensions":["g2w"]},"application/vnd.geospace":{"source":"iana","extensions":["g3w"]},"application/vnd.gerber":{"source":"iana"},"application/vnd.globalplatform.card-content-mgt":{"source":"iana"},"application/vnd.globalplatform.card-content-mgt-response":{"source":"iana"},"application/vnd.gmx":{"source":"iana","extensions":["gmx"]},"application/vnd.google-apps.document":{"compressible":false,"extensions":["gdoc"]},"application/vnd.google-apps.presentation":{"compressible":false,"extensions":["gslides"]},"application/vnd.google-apps.spreadsheet":{"compressible":false,"extensions":["gsheet"]},"application/vnd.google-earth.kml+xml":{"source":"iana","compressible":true,"extensions":["kml"]},"application/vnd.google-earth.kmz":{"source":"iana","compressible":false,"extensions":["kmz"]},"application/vnd.gov.sk.e-form+xml":{"source":"iana","compressible":true},"application/vnd.gov.sk.e-form+zip":{"source":"iana","compressible":false},"application/vnd.gov.sk.xmldatacontainer+xml":{"source":"iana","compressible":true},"application/vnd.grafeq":{"source":"iana","extensions":["gqf","gqs"]},"application/vnd.gridmp":{"source":"iana"},"application/vnd.groove-account":{"source":"iana","extensions":["gac"]},"application/vnd.groove-help":{"source":"iana","extensions":["ghf"]},"application/vnd.groove-identity-message":{"source":"iana","extensions":["gim"]},"application/vnd.groove-injector":{"source":"iana","extensions":["grv"]},"application/vnd.groove-tool-message":{"source":"iana","extensions":["gtm"]},"application/vnd.groove-tool-template":{"source":"iana","extensions":["tpl"]},"application/vnd.groove-vcard":{"source":"iana","extensions":["vcg"]},"application/vnd.hal+json":{"source":"iana","compressible":true},"application/vnd.hal+xml":{"source":"iana","compressible":true,"extensions":["hal"]},"application/vnd.handheld-entertainment+xml":{"source":"iana","compressible":true,"extensions":["zmm"]},"application/vnd.hbci":{"source":"iana","extensions":["hbci"]},"application/vnd.hc+json":{"source":"iana","compressible":true},"application/vnd.hcl-bireports":{"source":"iana"},"application/vnd.hdt":{"source":"iana"},"application/vnd.heroku+json":{"source":"iana","compressible":true},"application/vnd.hhe.lesson-player":{"source":"iana","extensions":["les"]},"application/vnd.hl7cda+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.hl7v2+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.hp-hpgl":{"source":"iana","extensions":["hpgl"]},"application/vnd.hp-hpid":{"source":"iana","extensions":["hpid"]},"application/vnd.hp-hps":{"source":"iana","extensions":["hps"]},"application/vnd.hp-jlyt":{"source":"iana","extensions":["jlt"]},"application/vnd.hp-pcl":{"source":"iana","extensions":["pcl"]},"application/vnd.hp-pclxl":{"source":"iana","extensions":["pclxl"]},"application/vnd.httphone":{"source":"iana"},"application/vnd.hydrostatix.sof-data":{"source":"iana","extensions":["sfd-hdstx"]},"application/vnd.hyper+json":{"source":"iana","compressible":true},"application/vnd.hyper-item+json":{"source":"iana","compressible":true},"application/vnd.hyperdrive+json":{"source":"iana","compressible":true},"application/vnd.hzn-3d-crossword":{"source":"iana"},"application/vnd.ibm.afplinedata":{"source":"iana"},"application/vnd.ibm.electronic-media":{"source":"iana"},"application/vnd.ibm.minipay":{"source":"iana","extensions":["mpy"]},"application/vnd.ibm.modcap":{"source":"iana","extensions":["afp","listafp","list3820"]},"application/vnd.ibm.rights-management":{"source":"iana","extensions":["irm"]},"application/vnd.ibm.secure-container":{"source":"iana","extensions":["sc"]},"application/vnd.iccprofile":{"source":"iana","extensions":["icc","icm"]},"application/vnd.ieee.1905":{"source":"iana"},"application/vnd.igloader":{"source":"iana","extensions":["igl"]},"application/vnd.imagemeter.folder+zip":{"source":"iana","compressible":false},"application/vnd.imagemeter.image+zip":{"source":"iana","compressible":false},"application/vnd.immervision-ivp":{"source":"iana","extensions":["ivp"]},"application/vnd.immervision-ivu":{"source":"iana","extensions":["ivu"]},"application/vnd.ims.imsccv1p1":{"source":"iana"},"application/vnd.ims.imsccv1p2":{"source":"iana"},"application/vnd.ims.imsccv1p3":{"source":"iana"},"application/vnd.ims.lis.v2.result+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolconsumerprofile+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolproxy+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolproxy.id+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolsettings+json":{"source":"iana","compressible":true},"application/vnd.ims.lti.v2.toolsettings.simple+json":{"source":"iana","compressible":true},"application/vnd.informedcontrol.rms+xml":{"source":"iana","compressible":true},"application/vnd.informix-visionary":{"source":"iana"},"application/vnd.infotech.project":{"source":"iana"},"application/vnd.infotech.project+xml":{"source":"iana","compressible":true},"application/vnd.innopath.wamp.notification":{"source":"iana"},"application/vnd.insors.igm":{"source":"iana","extensions":["igm"]},"application/vnd.intercon.formnet":{"source":"iana","extensions":["xpw","xpx"]},"application/vnd.intergeo":{"source":"iana","extensions":["i2g"]},"application/vnd.intertrust.digibox":{"source":"iana"},"application/vnd.intertrust.nncp":{"source":"iana"},"application/vnd.intu.qbo":{"source":"iana","extensions":["qbo"]},"application/vnd.intu.qfx":{"source":"iana","extensions":["qfx"]},"application/vnd.iptc.g2.catalogitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.conceptitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.knowledgeitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.newsitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.newsmessage+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.packageitem+xml":{"source":"iana","compressible":true},"application/vnd.iptc.g2.planningitem+xml":{"source":"iana","compressible":true},"application/vnd.ipunplugged.rcprofile":{"source":"iana","extensions":["rcprofile"]},"application/vnd.irepository.package+xml":{"source":"iana","compressible":true,"extensions":["irp"]},"application/vnd.is-xpr":{"source":"iana","extensions":["xpr"]},"application/vnd.isac.fcs":{"source":"iana","extensions":["fcs"]},"application/vnd.iso11783-10+zip":{"source":"iana","compressible":false},"application/vnd.jam":{"source":"iana","extensions":["jam"]},"application/vnd.japannet-directory-service":{"source":"iana"},"application/vnd.japannet-jpnstore-wakeup":{"source":"iana"},"application/vnd.japannet-payment-wakeup":{"source":"iana"},"application/vnd.japannet-registration":{"source":"iana"},"application/vnd.japannet-registration-wakeup":{"source":"iana"},"application/vnd.japannet-setstore-wakeup":{"source":"iana"},"application/vnd.japannet-verification":{"source":"iana"},"application/vnd.japannet-verification-wakeup":{"source":"iana"},"application/vnd.jcp.javame.midlet-rms":{"source":"iana","extensions":["rms"]},"application/vnd.jisp":{"source":"iana","extensions":["jisp"]},"application/vnd.joost.joda-archive":{"source":"iana","extensions":["joda"]},"application/vnd.jsk.isdn-ngn":{"source":"iana"},"application/vnd.kahootz":{"source":"iana","extensions":["ktz","ktr"]},"application/vnd.kde.karbon":{"source":"iana","extensions":["karbon"]},"application/vnd.kde.kchart":{"source":"iana","extensions":["chrt"]},"application/vnd.kde.kformula":{"source":"iana","extensions":["kfo"]},"application/vnd.kde.kivio":{"source":"iana","extensions":["flw"]},"application/vnd.kde.kontour":{"source":"iana","extensions":["kon"]},"application/vnd.kde.kpresenter":{"source":"iana","extensions":["kpr","kpt"]},"application/vnd.kde.kspread":{"source":"iana","extensions":["ksp"]},"application/vnd.kde.kword":{"source":"iana","extensions":["kwd","kwt"]},"application/vnd.kenameaapp":{"source":"iana","extensions":["htke"]},"application/vnd.kidspiration":{"source":"iana","extensions":["kia"]},"application/vnd.kinar":{"source":"iana","extensions":["kne","knp"]},"application/vnd.koan":{"source":"iana","extensions":["skp","skd","skt","skm"]},"application/vnd.kodak-descriptor":{"source":"iana","extensions":["sse"]},"application/vnd.las":{"source":"iana"},"application/vnd.las.las+json":{"source":"iana","compressible":true},"application/vnd.las.las+xml":{"source":"iana","compressible":true,"extensions":["lasxml"]},"application/vnd.laszip":{"source":"iana"},"application/vnd.leap+json":{"source":"iana","compressible":true},"application/vnd.liberty-request+xml":{"source":"iana","compressible":true},"application/vnd.llamagraphics.life-balance.desktop":{"source":"iana","extensions":["lbd"]},"application/vnd.llamagraphics.life-balance.exchange+xml":{"source":"iana","compressible":true,"extensions":["lbe"]},"application/vnd.logipipe.circuit+zip":{"source":"iana","compressible":false},"application/vnd.loom":{"source":"iana"},"application/vnd.lotus-1-2-3":{"source":"iana","extensions":["123"]},"application/vnd.lotus-approach":{"source":"iana","extensions":["apr"]},"application/vnd.lotus-freelance":{"source":"iana","extensions":["pre"]},"application/vnd.lotus-notes":{"source":"iana","extensions":["nsf"]},"application/vnd.lotus-organizer":{"source":"iana","extensions":["org"]},"application/vnd.lotus-screencam":{"source":"iana","extensions":["scm"]},"application/vnd.lotus-wordpro":{"source":"iana","extensions":["lwp"]},"application/vnd.macports.portpkg":{"source":"iana","extensions":["portpkg"]},"application/vnd.mapbox-vector-tile":{"source":"iana","extensions":["mvt"]},"application/vnd.marlin.drm.actiontoken+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.conftoken+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.license+xml":{"source":"iana","compressible":true},"application/vnd.marlin.drm.mdcf":{"source":"iana"},"application/vnd.mason+json":{"source":"iana","compressible":true},"application/vnd.maxar.archive.3tz+zip":{"source":"iana","compressible":false},"application/vnd.maxmind.maxmind-db":{"source":"iana"},"application/vnd.mcd":{"source":"iana","extensions":["mcd"]},"application/vnd.medcalcdata":{"source":"iana","extensions":["mc1"]},"application/vnd.mediastation.cdkey":{"source":"iana","extensions":["cdkey"]},"application/vnd.meridian-slingshot":{"source":"iana"},"application/vnd.mfer":{"source":"iana","extensions":["mwf"]},"application/vnd.mfmp":{"source":"iana","extensions":["mfm"]},"application/vnd.micro+json":{"source":"iana","compressible":true},"application/vnd.micrografx.flo":{"source":"iana","extensions":["flo"]},"application/vnd.micrografx.igx":{"source":"iana","extensions":["igx"]},"application/vnd.microsoft.portable-executable":{"source":"iana"},"application/vnd.microsoft.windows.thumbnail-cache":{"source":"iana"},"application/vnd.miele+json":{"source":"iana","compressible":true},"application/vnd.mif":{"source":"iana","extensions":["mif"]},"application/vnd.minisoft-hp3000-save":{"source":"iana"},"application/vnd.mitsubishi.misty-guard.trustweb":{"source":"iana"},"application/vnd.mobius.daf":{"source":"iana","extensions":["daf"]},"application/vnd.mobius.dis":{"source":"iana","extensions":["dis"]},"application/vnd.mobius.mbk":{"source":"iana","extensions":["mbk"]},"application/vnd.mobius.mqy":{"source":"iana","extensions":["mqy"]},"application/vnd.mobius.msl":{"source":"iana","extensions":["msl"]},"application/vnd.mobius.plc":{"source":"iana","extensions":["plc"]},"application/vnd.mobius.txf":{"source":"iana","extensions":["txf"]},"application/vnd.mophun.application":{"source":"iana","extensions":["mpn"]},"application/vnd.mophun.certificate":{"source":"iana","extensions":["mpc"]},"application/vnd.motorola.flexsuite":{"source":"iana"},"application/vnd.motorola.flexsuite.adsi":{"source":"iana"},"application/vnd.motorola.flexsuite.fis":{"source":"iana"},"application/vnd.motorola.flexsuite.gotap":{"source":"iana"},"application/vnd.motorola.flexsuite.kmr":{"source":"iana"},"application/vnd.motorola.flexsuite.ttc":{"source":"iana"},"application/vnd.motorola.flexsuite.wem":{"source":"iana"},"application/vnd.motorola.iprm":{"source":"iana"},"application/vnd.mozilla.xul+xml":{"source":"iana","compressible":true,"extensions":["xul"]},"application/vnd.ms-3mfdocument":{"source":"iana"},"application/vnd.ms-artgalry":{"source":"iana","extensions":["cil"]},"application/vnd.ms-asf":{"source":"iana"},"application/vnd.ms-cab-compressed":{"source":"iana","extensions":["cab"]},"application/vnd.ms-color.iccprofile":{"source":"apache"},"application/vnd.ms-excel":{"source":"iana","compressible":false,"extensions":["xls","xlm","xla","xlc","xlt","xlw"]},"application/vnd.ms-excel.addin.macroenabled.12":{"source":"iana","extensions":["xlam"]},"application/vnd.ms-excel.sheet.binary.macroenabled.12":{"source":"iana","extensions":["xlsb"]},"application/vnd.ms-excel.sheet.macroenabled.12":{"source":"iana","extensions":["xlsm"]},"application/vnd.ms-excel.template.macroenabled.12":{"source":"iana","extensions":["xltm"]},"application/vnd.ms-fontobject":{"source":"iana","compressible":true,"extensions":["eot"]},"application/vnd.ms-htmlhelp":{"source":"iana","extensions":["chm"]},"application/vnd.ms-ims":{"source":"iana","extensions":["ims"]},"application/vnd.ms-lrm":{"source":"iana","extensions":["lrm"]},"application/vnd.ms-office.activex+xml":{"source":"iana","compressible":true},"application/vnd.ms-officetheme":{"source":"iana","extensions":["thmx"]},"application/vnd.ms-opentype":{"source":"apache","compressible":true},"application/vnd.ms-outlook":{"compressible":false,"extensions":["msg"]},"application/vnd.ms-package.obfuscated-opentype":{"source":"apache"},"application/vnd.ms-pki.seccat":{"source":"apache","extensions":["cat"]},"application/vnd.ms-pki.stl":{"source":"apache","extensions":["stl"]},"application/vnd.ms-playready.initiator+xml":{"source":"iana","compressible":true},"application/vnd.ms-powerpoint":{"source":"iana","compressible":false,"extensions":["ppt","pps","pot"]},"application/vnd.ms-powerpoint.addin.macroenabled.12":{"source":"iana","extensions":["ppam"]},"application/vnd.ms-powerpoint.presentation.macroenabled.12":{"source":"iana","extensions":["pptm"]},"application/vnd.ms-powerpoint.slide.macroenabled.12":{"source":"iana","extensions":["sldm"]},"application/vnd.ms-powerpoint.slideshow.macroenabled.12":{"source":"iana","extensions":["ppsm"]},"application/vnd.ms-powerpoint.template.macroenabled.12":{"source":"iana","extensions":["potm"]},"application/vnd.ms-printdevicecapabilities+xml":{"source":"iana","compressible":true},"application/vnd.ms-printing.printticket+xml":{"source":"apache","compressible":true},"application/vnd.ms-printschematicket+xml":{"source":"iana","compressible":true},"application/vnd.ms-project":{"source":"iana","extensions":["mpp","mpt"]},"application/vnd.ms-tnef":{"source":"iana"},"application/vnd.ms-windows.devicepairing":{"source":"iana"},"application/vnd.ms-windows.nwprinting.oob":{"source":"iana"},"application/vnd.ms-windows.printerpairing":{"source":"iana"},"application/vnd.ms-windows.wsd.oob":{"source":"iana"},"application/vnd.ms-wmdrm.lic-chlg-req":{"source":"iana"},"application/vnd.ms-wmdrm.lic-resp":{"source":"iana"},"application/vnd.ms-wmdrm.meter-chlg-req":{"source":"iana"},"application/vnd.ms-wmdrm.meter-resp":{"source":"iana"},"application/vnd.ms-word.document.macroenabled.12":{"source":"iana","extensions":["docm"]},"application/vnd.ms-word.template.macroenabled.12":{"source":"iana","extensions":["dotm"]},"application/vnd.ms-works":{"source":"iana","extensions":["wps","wks","wcm","wdb"]},"application/vnd.ms-wpl":{"source":"iana","extensions":["wpl"]},"application/vnd.ms-xpsdocument":{"source":"iana","compressible":false,"extensions":["xps"]},"application/vnd.msa-disk-image":{"source":"iana"},"application/vnd.mseq":{"source":"iana","extensions":["mseq"]},"application/vnd.msign":{"source":"iana"},"application/vnd.multiad.creator":{"source":"iana"},"application/vnd.multiad.creator.cif":{"source":"iana"},"application/vnd.music-niff":{"source":"iana"},"application/vnd.musician":{"source":"iana","extensions":["mus"]},"application/vnd.muvee.style":{"source":"iana","extensions":["msty"]},"application/vnd.mynfc":{"source":"iana","extensions":["taglet"]},"application/vnd.nacamar.ybrid+json":{"source":"iana","compressible":true},"application/vnd.ncd.control":{"source":"iana"},"application/vnd.ncd.reference":{"source":"iana"},"application/vnd.nearst.inv+json":{"source":"iana","compressible":true},"application/vnd.nebumind.line":{"source":"iana"},"application/vnd.nervana":{"source":"iana"},"application/vnd.netfpx":{"source":"iana"},"application/vnd.neurolanguage.nlu":{"source":"iana","extensions":["nlu"]},"application/vnd.nimn":{"source":"iana"},"application/vnd.nintendo.nitro.rom":{"source":"iana"},"application/vnd.nintendo.snes.rom":{"source":"iana"},"application/vnd.nitf":{"source":"iana","extensions":["ntf","nitf"]},"application/vnd.noblenet-directory":{"source":"iana","extensions":["nnd"]},"application/vnd.noblenet-sealer":{"source":"iana","extensions":["nns"]},"application/vnd.noblenet-web":{"source":"iana","extensions":["nnw"]},"application/vnd.nokia.catalogs":{"source":"iana"},"application/vnd.nokia.conml+wbxml":{"source":"iana"},"application/vnd.nokia.conml+xml":{"source":"iana","compressible":true},"application/vnd.nokia.iptv.config+xml":{"source":"iana","compressible":true},"application/vnd.nokia.isds-radio-presets":{"source":"iana"},"application/vnd.nokia.landmark+wbxml":{"source":"iana"},"application/vnd.nokia.landmark+xml":{"source":"iana","compressible":true},"application/vnd.nokia.landmarkcollection+xml":{"source":"iana","compressible":true},"application/vnd.nokia.n-gage.ac+xml":{"source":"iana","compressible":true,"extensions":["ac"]},"application/vnd.nokia.n-gage.data":{"source":"iana","extensions":["ngdat"]},"application/vnd.nokia.n-gage.symbian.install":{"source":"iana","extensions":["n-gage"]},"application/vnd.nokia.ncd":{"source":"iana"},"application/vnd.nokia.pcd+wbxml":{"source":"iana"},"application/vnd.nokia.pcd+xml":{"source":"iana","compressible":true},"application/vnd.nokia.radio-preset":{"source":"iana","extensions":["rpst"]},"application/vnd.nokia.radio-presets":{"source":"iana","extensions":["rpss"]},"application/vnd.novadigm.edm":{"source":"iana","extensions":["edm"]},"application/vnd.novadigm.edx":{"source":"iana","extensions":["edx"]},"application/vnd.novadigm.ext":{"source":"iana","extensions":["ext"]},"application/vnd.ntt-local.content-share":{"source":"iana"},"application/vnd.ntt-local.file-transfer":{"source":"iana"},"application/vnd.ntt-local.ogw_remote-access":{"source":"iana"},"application/vnd.ntt-local.sip-ta_remote":{"source":"iana"},"application/vnd.ntt-local.sip-ta_tcp_stream":{"source":"iana"},"application/vnd.oasis.opendocument.chart":{"source":"iana","extensions":["odc"]},"application/vnd.oasis.opendocument.chart-template":{"source":"iana","extensions":["otc"]},"application/vnd.oasis.opendocument.database":{"source":"iana","extensions":["odb"]},"application/vnd.oasis.opendocument.formula":{"source":"iana","extensions":["odf"]},"application/vnd.oasis.opendocument.formula-template":{"source":"iana","extensions":["odft"]},"application/vnd.oasis.opendocument.graphics":{"source":"iana","compressible":false,"extensions":["odg"]},"application/vnd.oasis.opendocument.graphics-template":{"source":"iana","extensions":["otg"]},"application/vnd.oasis.opendocument.image":{"source":"iana","extensions":["odi"]},"application/vnd.oasis.opendocument.image-template":{"source":"iana","extensions":["oti"]},"application/vnd.oasis.opendocument.presentation":{"source":"iana","compressible":false,"extensions":["odp"]},"application/vnd.oasis.opendocument.presentation-template":{"source":"iana","extensions":["otp"]},"application/vnd.oasis.opendocument.spreadsheet":{"source":"iana","compressible":false,"extensions":["ods"]},"application/vnd.oasis.opendocument.spreadsheet-template":{"source":"iana","extensions":["ots"]},"application/vnd.oasis.opendocument.text":{"source":"iana","compressible":false,"extensions":["odt"]},"application/vnd.oasis.opendocument.text-master":{"source":"iana","extensions":["odm"]},"application/vnd.oasis.opendocument.text-template":{"source":"iana","extensions":["ott"]},"application/vnd.oasis.opendocument.text-web":{"source":"iana","extensions":["oth"]},"application/vnd.obn":{"source":"iana"},"application/vnd.ocf+cbor":{"source":"iana"},"application/vnd.oci.image.manifest.v1+json":{"source":"iana","compressible":true},"application/vnd.oftn.l10n+json":{"source":"iana","compressible":true},"application/vnd.oipf.contentaccessdownload+xml":{"source":"iana","compressible":true},"application/vnd.oipf.contentaccessstreaming+xml":{"source":"iana","compressible":true},"application/vnd.oipf.cspg-hexbinary":{"source":"iana"},"application/vnd.oipf.dae.svg+xml":{"source":"iana","compressible":true},"application/vnd.oipf.dae.xhtml+xml":{"source":"iana","compressible":true},"application/vnd.oipf.mippvcontrolmessage+xml":{"source":"iana","compressible":true},"application/vnd.oipf.pae.gem":{"source":"iana"},"application/vnd.oipf.spdiscovery+xml":{"source":"iana","compressible":true},"application/vnd.oipf.spdlist+xml":{"source":"iana","compressible":true},"application/vnd.oipf.ueprofile+xml":{"source":"iana","compressible":true},"application/vnd.oipf.userprofile+xml":{"source":"iana","compressible":true},"application/vnd.olpc-sugar":{"source":"iana","extensions":["xo"]},"application/vnd.oma-scws-config":{"source":"iana"},"application/vnd.oma-scws-http-request":{"source":"iana"},"application/vnd.oma-scws-http-response":{"source":"iana"},"application/vnd.oma.bcast.associated-procedure-parameter+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.drm-trigger+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.imd+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.ltkm":{"source":"iana"},"application/vnd.oma.bcast.notification+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.provisioningtrigger":{"source":"iana"},"application/vnd.oma.bcast.sgboot":{"source":"iana"},"application/vnd.oma.bcast.sgdd+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.sgdu":{"source":"iana"},"application/vnd.oma.bcast.simple-symbol-container":{"source":"iana"},"application/vnd.oma.bcast.smartcard-trigger+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.sprov+xml":{"source":"iana","compressible":true},"application/vnd.oma.bcast.stkm":{"source":"iana"},"application/vnd.oma.cab-address-book+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-feature-handler+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-pcc+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-subs-invite+xml":{"source":"iana","compressible":true},"application/vnd.oma.cab-user-prefs+xml":{"source":"iana","compressible":true},"application/vnd.oma.dcd":{"source":"iana"},"application/vnd.oma.dcdc":{"source":"iana"},"application/vnd.oma.dd2+xml":{"source":"iana","compressible":true,"extensions":["dd2"]},"application/vnd.oma.drm.risd+xml":{"source":"iana","compressible":true},"application/vnd.oma.group-usage-list+xml":{"source":"iana","compressible":true},"application/vnd.oma.lwm2m+cbor":{"source":"iana"},"application/vnd.oma.lwm2m+json":{"source":"iana","compressible":true},"application/vnd.oma.lwm2m+tlv":{"source":"iana"},"application/vnd.oma.pal+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.detailed-progress-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.final-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.groups+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.invocation-descriptor+xml":{"source":"iana","compressible":true},"application/vnd.oma.poc.optimized-progress-report+xml":{"source":"iana","compressible":true},"application/vnd.oma.push":{"source":"iana"},"application/vnd.oma.scidm.messages+xml":{"source":"iana","compressible":true},"application/vnd.oma.xcap-directory+xml":{"source":"iana","compressible":true},"application/vnd.omads-email+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.omads-file+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.omads-folder+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.omaloc-supl-init":{"source":"iana"},"application/vnd.onepager":{"source":"iana"},"application/vnd.onepagertamp":{"source":"iana"},"application/vnd.onepagertamx":{"source":"iana"},"application/vnd.onepagertat":{"source":"iana"},"application/vnd.onepagertatp":{"source":"iana"},"application/vnd.onepagertatx":{"source":"iana"},"application/vnd.openblox.game+xml":{"source":"iana","compressible":true,"extensions":["obgx"]},"application/vnd.openblox.game-binary":{"source":"iana"},"application/vnd.openeye.oeb":{"source":"iana"},"application/vnd.openofficeorg.extension":{"source":"apache","extensions":["oxt"]},"application/vnd.openstreetmap.data+xml":{"source":"iana","compressible":true,"extensions":["osm"]},"application/vnd.opentimestamps.ots":{"source":"iana"},"application/vnd.openxmlformats-officedocument.custom-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.customxmlproperties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawing+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.chart+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.extended-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.presentation":{"source":"iana","compressible":false,"extensions":["pptx"]},"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.presprops+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slide":{"source":"iana","extensions":["sldx"]},"application/vnd.openxmlformats-officedocument.presentationml.slide+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slideshow":{"source":"iana","extensions":["ppsx"]},"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.tags+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.template":{"source":"iana","extensions":["potx"]},"application/vnd.openxmlformats-officedocument.presentationml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{"source":"iana","compressible":false,"extensions":["xlsx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.template":{"source":"iana","extensions":["xltx"]},"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.theme+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.themeoverride+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.vmldrawing":{"source":"iana"},"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.document":{"source":"iana","compressible":false,"extensions":["docx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.template":{"source":"iana","extensions":["dotx"]},"application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.core-properties+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml":{"source":"iana","compressible":true},"application/vnd.openxmlformats-package.relationships+xml":{"source":"iana","compressible":true},"application/vnd.oracle.resource+json":{"source":"iana","compressible":true},"application/vnd.orange.indata":{"source":"iana"},"application/vnd.osa.netdeploy":{"source":"iana"},"application/vnd.osgeo.mapguide.package":{"source":"iana","extensions":["mgp"]},"application/vnd.osgi.bundle":{"source":"iana"},"application/vnd.osgi.dp":{"source":"iana","extensions":["dp"]},"application/vnd.osgi.subsystem":{"source":"iana","extensions":["esa"]},"application/vnd.otps.ct-kip+xml":{"source":"iana","compressible":true},"application/vnd.oxli.countgraph":{"source":"iana"},"application/vnd.pagerduty+json":{"source":"iana","compressible":true},"application/vnd.palm":{"source":"iana","extensions":["pdb","pqa","oprc"]},"application/vnd.panoply":{"source":"iana"},"application/vnd.paos.xml":{"source":"iana"},"application/vnd.patentdive":{"source":"iana"},"application/vnd.patientecommsdoc":{"source":"iana"},"application/vnd.pawaafile":{"source":"iana","extensions":["paw"]},"application/vnd.pcos":{"source":"iana"},"application/vnd.pg.format":{"source":"iana","extensions":["str"]},"application/vnd.pg.osasli":{"source":"iana","extensions":["ei6"]},"application/vnd.piaccess.application-licence":{"source":"iana"},"application/vnd.picsel":{"source":"iana","extensions":["efif"]},"application/vnd.pmi.widget":{"source":"iana","extensions":["wg"]},"application/vnd.poc.group-advertisement+xml":{"source":"iana","compressible":true},"application/vnd.pocketlearn":{"source":"iana","extensions":["plf"]},"application/vnd.powerbuilder6":{"source":"iana","extensions":["pbd"]},"application/vnd.powerbuilder6-s":{"source":"iana"},"application/vnd.powerbuilder7":{"source":"iana"},"application/vnd.powerbuilder7-s":{"source":"iana"},"application/vnd.powerbuilder75":{"source":"iana"},"application/vnd.powerbuilder75-s":{"source":"iana"},"application/vnd.preminet":{"source":"iana"},"application/vnd.previewsystems.box":{"source":"iana","extensions":["box"]},"application/vnd.proteus.magazine":{"source":"iana","extensions":["mgz"]},"application/vnd.psfs":{"source":"iana"},"application/vnd.publishare-delta-tree":{"source":"iana","extensions":["qps"]},"application/vnd.pvi.ptid1":{"source":"iana","extensions":["ptid"]},"application/vnd.pwg-multiplexed":{"source":"iana"},"application/vnd.pwg-xhtml-print+xml":{"source":"iana","compressible":true},"application/vnd.qualcomm.brew-app-res":{"source":"iana"},"application/vnd.quarantainenet":{"source":"iana"},"application/vnd.quark.quarkxpress":{"source":"iana","extensions":["qxd","qxt","qwd","qwt","qxl","qxb"]},"application/vnd.quobject-quoxdocument":{"source":"iana"},"application/vnd.radisys.moml+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-conf+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-conn+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-dialog+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-audit-stream+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-conf+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-base+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-fax-detect+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-fax-sendrecv+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-group+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-speech+xml":{"source":"iana","compressible":true},"application/vnd.radisys.msml-dialog-transform+xml":{"source":"iana","compressible":true},"application/vnd.rainstor.data":{"source":"iana"},"application/vnd.rapid":{"source":"iana"},"application/vnd.rar":{"source":"iana","extensions":["rar"]},"application/vnd.realvnc.bed":{"source":"iana","extensions":["bed"]},"application/vnd.recordare.musicxml":{"source":"iana","extensions":["mxl"]},"application/vnd.recordare.musicxml+xml":{"source":"iana","compressible":true,"extensions":["musicxml"]},"application/vnd.renlearn.rlprint":{"source":"iana"},"application/vnd.resilient.logic":{"source":"iana"},"application/vnd.restful+json":{"source":"iana","compressible":true},"application/vnd.rig.cryptonote":{"source":"iana","extensions":["cryptonote"]},"application/vnd.rim.cod":{"source":"apache","extensions":["cod"]},"application/vnd.rn-realmedia":{"source":"apache","extensions":["rm"]},"application/vnd.rn-realmedia-vbr":{"source":"apache","extensions":["rmvb"]},"application/vnd.route66.link66+xml":{"source":"iana","compressible":true,"extensions":["link66"]},"application/vnd.rs-274x":{"source":"iana"},"application/vnd.ruckus.download":{"source":"iana"},"application/vnd.s3sms":{"source":"iana"},"application/vnd.sailingtracker.track":{"source":"iana","extensions":["st"]},"application/vnd.sar":{"source":"iana"},"application/vnd.sbm.cid":{"source":"iana"},"application/vnd.sbm.mid2":{"source":"iana"},"application/vnd.scribus":{"source":"iana"},"application/vnd.sealed.3df":{"source":"iana"},"application/vnd.sealed.csf":{"source":"iana"},"application/vnd.sealed.doc":{"source":"iana"},"application/vnd.sealed.eml":{"source":"iana"},"application/vnd.sealed.mht":{"source":"iana"},"application/vnd.sealed.net":{"source":"iana"},"application/vnd.sealed.ppt":{"source":"iana"},"application/vnd.sealed.tiff":{"source":"iana"},"application/vnd.sealed.xls":{"source":"iana"},"application/vnd.sealedmedia.softseal.html":{"source":"iana"},"application/vnd.sealedmedia.softseal.pdf":{"source":"iana"},"application/vnd.seemail":{"source":"iana","extensions":["see"]},"application/vnd.seis+json":{"source":"iana","compressible":true},"application/vnd.sema":{"source":"iana","extensions":["sema"]},"application/vnd.semd":{"source":"iana","extensions":["semd"]},"application/vnd.semf":{"source":"iana","extensions":["semf"]},"application/vnd.shade-save-file":{"source":"iana"},"application/vnd.shana.informed.formdata":{"source":"iana","extensions":["ifm"]},"application/vnd.shana.informed.formtemplate":{"source":"iana","extensions":["itp"]},"application/vnd.shana.informed.interchange":{"source":"iana","extensions":["iif"]},"application/vnd.shana.informed.package":{"source":"iana","extensions":["ipk"]},"application/vnd.shootproof+json":{"source":"iana","compressible":true},"application/vnd.shopkick+json":{"source":"iana","compressible":true},"application/vnd.shp":{"source":"iana"},"application/vnd.shx":{"source":"iana"},"application/vnd.sigrok.session":{"source":"iana"},"application/vnd.simtech-mindmapper":{"source":"iana","extensions":["twd","twds"]},"application/vnd.siren+json":{"source":"iana","compressible":true},"application/vnd.smaf":{"source":"iana","extensions":["mmf"]},"application/vnd.smart.notebook":{"source":"iana"},"application/vnd.smart.teacher":{"source":"iana","extensions":["teacher"]},"application/vnd.snesdev-page-table":{"source":"iana"},"application/vnd.software602.filler.form+xml":{"source":"iana","compressible":true,"extensions":["fo"]},"application/vnd.software602.filler.form-xml-zip":{"source":"iana"},"application/vnd.solent.sdkm+xml":{"source":"iana","compressible":true,"extensions":["sdkm","sdkd"]},"application/vnd.spotfire.dxp":{"source":"iana","extensions":["dxp"]},"application/vnd.spotfire.sfs":{"source":"iana","extensions":["sfs"]},"application/vnd.sqlite3":{"source":"iana"},"application/vnd.sss-cod":{"source":"iana"},"application/vnd.sss-dtf":{"source":"iana"},"application/vnd.sss-ntf":{"source":"iana"},"application/vnd.stardivision.calc":{"source":"apache","extensions":["sdc"]},"application/vnd.stardivision.draw":{"source":"apache","extensions":["sda"]},"application/vnd.stardivision.impress":{"source":"apache","extensions":["sdd"]},"application/vnd.stardivision.math":{"source":"apache","extensions":["smf"]},"application/vnd.stardivision.writer":{"source":"apache","extensions":["sdw","vor"]},"application/vnd.stardivision.writer-global":{"source":"apache","extensions":["sgl"]},"application/vnd.stepmania.package":{"source":"iana","extensions":["smzip"]},"application/vnd.stepmania.stepchart":{"source":"iana","extensions":["sm"]},"application/vnd.street-stream":{"source":"iana"},"application/vnd.sun.wadl+xml":{"source":"iana","compressible":true,"extensions":["wadl"]},"application/vnd.sun.xml.calc":{"source":"apache","extensions":["sxc"]},"application/vnd.sun.xml.calc.template":{"source":"apache","extensions":["stc"]},"application/vnd.sun.xml.draw":{"source":"apache","extensions":["sxd"]},"application/vnd.sun.xml.draw.template":{"source":"apache","extensions":["std"]},"application/vnd.sun.xml.impress":{"source":"apache","extensions":["sxi"]},"application/vnd.sun.xml.impress.template":{"source":"apache","extensions":["sti"]},"application/vnd.sun.xml.math":{"source":"apache","extensions":["sxm"]},"application/vnd.sun.xml.writer":{"source":"apache","extensions":["sxw"]},"application/vnd.sun.xml.writer.global":{"source":"apache","extensions":["sxg"]},"application/vnd.sun.xml.writer.template":{"source":"apache","extensions":["stw"]},"application/vnd.sus-calendar":{"source":"iana","extensions":["sus","susp"]},"application/vnd.svd":{"source":"iana","extensions":["svd"]},"application/vnd.swiftview-ics":{"source":"iana"},"application/vnd.sycle+xml":{"source":"iana","compressible":true},"application/vnd.syft+json":{"source":"iana","compressible":true},"application/vnd.symbian.install":{"source":"apache","extensions":["sis","sisx"]},"application/vnd.syncml+xml":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["xsm"]},"application/vnd.syncml.dm+wbxml":{"source":"iana","charset":"UTF-8","extensions":["bdm"]},"application/vnd.syncml.dm+xml":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["xdm"]},"application/vnd.syncml.dm.notification":{"source":"iana"},"application/vnd.syncml.dmddf+wbxml":{"source":"iana"},"application/vnd.syncml.dmddf+xml":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["ddf"]},"application/vnd.syncml.dmtnds+wbxml":{"source":"iana"},"application/vnd.syncml.dmtnds+xml":{"source":"iana","charset":"UTF-8","compressible":true},"application/vnd.syncml.ds.notification":{"source":"iana"},"application/vnd.tableschema+json":{"source":"iana","compressible":true},"application/vnd.tao.intent-module-archive":{"source":"iana","extensions":["tao"]},"application/vnd.tcpdump.pcap":{"source":"iana","extensions":["pcap","cap","dmp"]},"application/vnd.think-cell.ppttc+json":{"source":"iana","compressible":true},"application/vnd.tmd.mediaflex.api+xml":{"source":"iana","compressible":true},"application/vnd.tml":{"source":"iana"},"application/vnd.tmobile-livetv":{"source":"iana","extensions":["tmo"]},"application/vnd.tri.onesource":{"source":"iana"},"application/vnd.trid.tpt":{"source":"iana","extensions":["tpt"]},"application/vnd.triscape.mxs":{"source":"iana","extensions":["mxs"]},"application/vnd.trueapp":{"source":"iana","extensions":["tra"]},"application/vnd.truedoc":{"source":"iana"},"application/vnd.ubisoft.webplayer":{"source":"iana"},"application/vnd.ufdl":{"source":"iana","extensions":["ufd","ufdl"]},"application/vnd.uiq.theme":{"source":"iana","extensions":["utz"]},"application/vnd.umajin":{"source":"iana","extensions":["umj"]},"application/vnd.unity":{"source":"iana","extensions":["unityweb"]},"application/vnd.uoml+xml":{"source":"iana","compressible":true,"extensions":["uoml"]},"application/vnd.uplanet.alert":{"source":"iana"},"application/vnd.uplanet.alert-wbxml":{"source":"iana"},"application/vnd.uplanet.bearer-choice":{"source":"iana"},"application/vnd.uplanet.bearer-choice-wbxml":{"source":"iana"},"application/vnd.uplanet.cacheop":{"source":"iana"},"application/vnd.uplanet.cacheop-wbxml":{"source":"iana"},"application/vnd.uplanet.channel":{"source":"iana"},"application/vnd.uplanet.channel-wbxml":{"source":"iana"},"application/vnd.uplanet.list":{"source":"iana"},"application/vnd.uplanet.list-wbxml":{"source":"iana"},"application/vnd.uplanet.listcmd":{"source":"iana"},"application/vnd.uplanet.listcmd-wbxml":{"source":"iana"},"application/vnd.uplanet.signal":{"source":"iana"},"application/vnd.uri-map":{"source":"iana"},"application/vnd.valve.source.material":{"source":"iana"},"application/vnd.vcx":{"source":"iana","extensions":["vcx"]},"application/vnd.vd-study":{"source":"iana"},"application/vnd.vectorworks":{"source":"iana"},"application/vnd.vel+json":{"source":"iana","compressible":true},"application/vnd.verimatrix.vcas":{"source":"iana"},"application/vnd.veritone.aion+json":{"source":"iana","compressible":true},"application/vnd.veryant.thin":{"source":"iana"},"application/vnd.ves.encrypted":{"source":"iana"},"application/vnd.vidsoft.vidconference":{"source":"iana"},"application/vnd.visio":{"source":"iana","extensions":["vsd","vst","vss","vsw"]},"application/vnd.visionary":{"source":"iana","extensions":["vis"]},"application/vnd.vividence.scriptfile":{"source":"iana"},"application/vnd.vsf":{"source":"iana","extensions":["vsf"]},"application/vnd.wap.sic":{"source":"iana"},"application/vnd.wap.slc":{"source":"iana"},"application/vnd.wap.wbxml":{"source":"iana","charset":"UTF-8","extensions":["wbxml"]},"application/vnd.wap.wmlc":{"source":"iana","extensions":["wmlc"]},"application/vnd.wap.wmlscriptc":{"source":"iana","extensions":["wmlsc"]},"application/vnd.webturbo":{"source":"iana","extensions":["wtb"]},"application/vnd.wfa.dpp":{"source":"iana"},"application/vnd.wfa.p2p":{"source":"iana"},"application/vnd.wfa.wsc":{"source":"iana"},"application/vnd.windows.devicepairing":{"source":"iana"},"application/vnd.wmc":{"source":"iana"},"application/vnd.wmf.bootstrap":{"source":"iana"},"application/vnd.wolfram.mathematica":{"source":"iana"},"application/vnd.wolfram.mathematica.package":{"source":"iana"},"application/vnd.wolfram.player":{"source":"iana","extensions":["nbp"]},"application/vnd.wordperfect":{"source":"iana","extensions":["wpd"]},"application/vnd.wqd":{"source":"iana","extensions":["wqd"]},"application/vnd.wrq-hp3000-labelled":{"source":"iana"},"application/vnd.wt.stf":{"source":"iana","extensions":["stf"]},"application/vnd.wv.csp+wbxml":{"source":"iana"},"application/vnd.wv.csp+xml":{"source":"iana","compressible":true},"application/vnd.wv.ssp+xml":{"source":"iana","compressible":true},"application/vnd.xacml+json":{"source":"iana","compressible":true},"application/vnd.xara":{"source":"iana","extensions":["xar"]},"application/vnd.xfdl":{"source":"iana","extensions":["xfdl"]},"application/vnd.xfdl.webform":{"source":"iana"},"application/vnd.xmi+xml":{"source":"iana","compressible":true},"application/vnd.xmpie.cpkg":{"source":"iana"},"application/vnd.xmpie.dpkg":{"source":"iana"},"application/vnd.xmpie.plan":{"source":"iana"},"application/vnd.xmpie.ppkg":{"source":"iana"},"application/vnd.xmpie.xlim":{"source":"iana"},"application/vnd.yamaha.hv-dic":{"source":"iana","extensions":["hvd"]},"application/vnd.yamaha.hv-script":{"source":"iana","extensions":["hvs"]},"application/vnd.yamaha.hv-voice":{"source":"iana","extensions":["hvp"]},"application/vnd.yamaha.openscoreformat":{"source":"iana","extensions":["osf"]},"application/vnd.yamaha.openscoreformat.osfpvg+xml":{"source":"iana","compressible":true,"extensions":["osfpvg"]},"application/vnd.yamaha.remote-setup":{"source":"iana"},"application/vnd.yamaha.smaf-audio":{"source":"iana","extensions":["saf"]},"application/vnd.yamaha.smaf-phrase":{"source":"iana","extensions":["spf"]},"application/vnd.yamaha.through-ngn":{"source":"iana"},"application/vnd.yamaha.tunnel-udpencap":{"source":"iana"},"application/vnd.yaoweme":{"source":"iana"},"application/vnd.yellowriver-custom-menu":{"source":"iana","extensions":["cmp"]},"application/vnd.youtube.yt":{"source":"iana"},"application/vnd.zul":{"source":"iana","extensions":["zir","zirz"]},"application/vnd.zzazz.deck+xml":{"source":"iana","compressible":true,"extensions":["zaz"]},"application/voicexml+xml":{"source":"iana","compressible":true,"extensions":["vxml"]},"application/voucher-cms+json":{"source":"iana","compressible":true},"application/vq-rtcpxr":{"source":"iana"},"application/wasm":{"source":"iana","compressible":true,"extensions":["wasm"]},"application/watcherinfo+xml":{"source":"iana","compressible":true,"extensions":["wif"]},"application/webpush-options+json":{"source":"iana","compressible":true},"application/whoispp-query":{"source":"iana"},"application/whoispp-response":{"source":"iana"},"application/widget":{"source":"iana","extensions":["wgt"]},"application/winhlp":{"source":"apache","extensions":["hlp"]},"application/wita":{"source":"iana"},"application/wordperfect5.1":{"source":"iana"},"application/wsdl+xml":{"source":"iana","compressible":true,"extensions":["wsdl"]},"application/wspolicy+xml":{"source":"iana","compressible":true,"extensions":["wspolicy"]},"application/x-7z-compressed":{"source":"apache","compressible":false,"extensions":["7z"]},"application/x-abiword":{"source":"apache","extensions":["abw"]},"application/x-ace-compressed":{"source":"apache","extensions":["ace"]},"application/x-amf":{"source":"apache"},"application/x-apple-diskimage":{"source":"apache","extensions":["dmg"]},"application/x-arj":{"compressible":false,"extensions":["arj"]},"application/x-authorware-bin":{"source":"apache","extensions":["aab","x32","u32","vox"]},"application/x-authorware-map":{"source":"apache","extensions":["aam"]},"application/x-authorware-seg":{"source":"apache","extensions":["aas"]},"application/x-bcpio":{"source":"apache","extensions":["bcpio"]},"application/x-bdoc":{"compressible":false,"extensions":["bdoc"]},"application/x-bittorrent":{"source":"apache","extensions":["torrent"]},"application/x-blorb":{"source":"apache","extensions":["blb","blorb"]},"application/x-bzip":{"source":"apache","compressible":false,"extensions":["bz"]},"application/x-bzip2":{"source":"apache","compressible":false,"extensions":["bz2","boz"]},"application/x-cbr":{"source":"apache","extensions":["cbr","cba","cbt","cbz","cb7"]},"application/x-cdlink":{"source":"apache","extensions":["vcd"]},"application/x-cfs-compressed":{"source":"apache","extensions":["cfs"]},"application/x-chat":{"source":"apache","extensions":["chat"]},"application/x-chess-pgn":{"source":"apache","extensions":["pgn"]},"application/x-chrome-extension":{"extensions":["crx"]},"application/x-cocoa":{"source":"nginx","extensions":["cco"]},"application/x-compress":{"source":"apache"},"application/x-conference":{"source":"apache","extensions":["nsc"]},"application/x-cpio":{"source":"apache","extensions":["cpio"]},"application/x-csh":{"source":"apache","extensions":["csh"]},"application/x-deb":{"compressible":false},"application/x-debian-package":{"source":"apache","extensions":["deb","udeb"]},"application/x-dgc-compressed":{"source":"apache","extensions":["dgc"]},"application/x-director":{"source":"apache","extensions":["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"]},"application/x-doom":{"source":"apache","extensions":["wad"]},"application/x-dtbncx+xml":{"source":"apache","compressible":true,"extensions":["ncx"]},"application/x-dtbook+xml":{"source":"apache","compressible":true,"extensions":["dtb"]},"application/x-dtbresource+xml":{"source":"apache","compressible":true,"extensions":["res"]},"application/x-dvi":{"source":"apache","compressible":false,"extensions":["dvi"]},"application/x-envoy":{"source":"apache","extensions":["evy"]},"application/x-eva":{"source":"apache","extensions":["eva"]},"application/x-font-bdf":{"source":"apache","extensions":["bdf"]},"application/x-font-dos":{"source":"apache"},"application/x-font-framemaker":{"source":"apache"},"application/x-font-ghostscript":{"source":"apache","extensions":["gsf"]},"application/x-font-libgrx":{"source":"apache"},"application/x-font-linux-psf":{"source":"apache","extensions":["psf"]},"application/x-font-pcf":{"source":"apache","extensions":["pcf"]},"application/x-font-snf":{"source":"apache","extensions":["snf"]},"application/x-font-speedo":{"source":"apache"},"application/x-font-sunos-news":{"source":"apache"},"application/x-font-type1":{"source":"apache","extensions":["pfa","pfb","pfm","afm"]},"application/x-font-vfont":{"source":"apache"},"application/x-freearc":{"source":"apache","extensions":["arc"]},"application/x-futuresplash":{"source":"apache","extensions":["spl"]},"application/x-gca-compressed":{"source":"apache","extensions":["gca"]},"application/x-glulx":{"source":"apache","extensions":["ulx"]},"application/x-gnumeric":{"source":"apache","extensions":["gnumeric"]},"application/x-gramps-xml":{"source":"apache","extensions":["gramps"]},"application/x-gtar":{"source":"apache","extensions":["gtar"]},"application/x-gzip":{"source":"apache"},"application/x-hdf":{"source":"apache","extensions":["hdf"]},"application/x-httpd-php":{"compressible":true,"extensions":["php"]},"application/x-install-instructions":{"source":"apache","extensions":["install"]},"application/x-iso9660-image":{"source":"apache","extensions":["iso"]},"application/x-iwork-keynote-sffkey":{"extensions":["key"]},"application/x-iwork-numbers-sffnumbers":{"extensions":["numbers"]},"application/x-iwork-pages-sffpages":{"extensions":["pages"]},"application/x-java-archive-diff":{"source":"nginx","extensions":["jardiff"]},"application/x-java-jnlp-file":{"source":"apache","compressible":false,"extensions":["jnlp"]},"application/x-javascript":{"compressible":true},"application/x-keepass2":{"extensions":["kdbx"]},"application/x-latex":{"source":"apache","compressible":false,"extensions":["latex"]},"application/x-lua-bytecode":{"extensions":["luac"]},"application/x-lzh-compressed":{"source":"apache","extensions":["lzh","lha"]},"application/x-makeself":{"source":"nginx","extensions":["run"]},"application/x-mie":{"source":"apache","extensions":["mie"]},"application/x-mobipocket-ebook":{"source":"apache","extensions":["prc","mobi"]},"application/x-mpegurl":{"compressible":false},"application/x-ms-application":{"source":"apache","extensions":["application"]},"application/x-ms-shortcut":{"source":"apache","extensions":["lnk"]},"application/x-ms-wmd":{"source":"apache","extensions":["wmd"]},"application/x-ms-wmz":{"source":"apache","extensions":["wmz"]},"application/x-ms-xbap":{"source":"apache","extensions":["xbap"]},"application/x-msaccess":{"source":"apache","extensions":["mdb"]},"application/x-msbinder":{"source":"apache","extensions":["obd"]},"application/x-mscardfile":{"source":"apache","extensions":["crd"]},"application/x-msclip":{"source":"apache","extensions":["clp"]},"application/x-msdos-program":{"extensions":["exe"]},"application/x-msdownload":{"source":"apache","extensions":["exe","dll","com","bat","msi"]},"application/x-msmediaview":{"source":"apache","extensions":["mvb","m13","m14"]},"application/x-msmetafile":{"source":"apache","extensions":["wmf","wmz","emf","emz"]},"application/x-msmoney":{"source":"apache","extensions":["mny"]},"application/x-mspublisher":{"source":"apache","extensions":["pub"]},"application/x-msschedule":{"source":"apache","extensions":["scd"]},"application/x-msterminal":{"source":"apache","extensions":["trm"]},"application/x-mswrite":{"source":"apache","extensions":["wri"]},"application/x-netcdf":{"source":"apache","extensions":["nc","cdf"]},"application/x-ns-proxy-autoconfig":{"compressible":true,"extensions":["pac"]},"application/x-nzb":{"source":"apache","extensions":["nzb"]},"application/x-perl":{"source":"nginx","extensions":["pl","pm"]},"application/x-pilot":{"source":"nginx","extensions":["prc","pdb"]},"application/x-pkcs12":{"source":"apache","compressible":false,"extensions":["p12","pfx"]},"application/x-pkcs7-certificates":{"source":"apache","extensions":["p7b","spc"]},"application/x-pkcs7-certreqresp":{"source":"apache","extensions":["p7r"]},"application/x-pki-message":{"source":"iana"},"application/x-rar-compressed":{"source":"apache","compressible":false,"extensions":["rar"]},"application/x-redhat-package-manager":{"source":"nginx","extensions":["rpm"]},"application/x-research-info-systems":{"source":"apache","extensions":["ris"]},"application/x-sea":{"source":"nginx","extensions":["sea"]},"application/x-sh":{"source":"apache","compressible":true,"extensions":["sh"]},"application/x-shar":{"source":"apache","extensions":["shar"]},"application/x-shockwave-flash":{"source":"apache","compressible":false,"extensions":["swf"]},"application/x-silverlight-app":{"source":"apache","extensions":["xap"]},"application/x-sql":{"source":"apache","extensions":["sql"]},"application/x-stuffit":{"source":"apache","compressible":false,"extensions":["sit"]},"application/x-stuffitx":{"source":"apache","extensions":["sitx"]},"application/x-subrip":{"source":"apache","extensions":["srt"]},"application/x-sv4cpio":{"source":"apache","extensions":["sv4cpio"]},"application/x-sv4crc":{"source":"apache","extensions":["sv4crc"]},"application/x-t3vm-image":{"source":"apache","extensions":["t3"]},"application/x-tads":{"source":"apache","extensions":["gam"]},"application/x-tar":{"source":"apache","compressible":true,"extensions":["tar"]},"application/x-tcl":{"source":"apache","extensions":["tcl","tk"]},"application/x-tex":{"source":"apache","extensions":["tex"]},"application/x-tex-tfm":{"source":"apache","extensions":["tfm"]},"application/x-texinfo":{"source":"apache","extensions":["texinfo","texi"]},"application/x-tgif":{"source":"apache","extensions":["obj"]},"application/x-ustar":{"source":"apache","extensions":["ustar"]},"application/x-virtualbox-hdd":{"compressible":true,"extensions":["hdd"]},"application/x-virtualbox-ova":{"compressible":true,"extensions":["ova"]},"application/x-virtualbox-ovf":{"compressible":true,"extensions":["ovf"]},"application/x-virtualbox-vbox":{"compressible":true,"extensions":["vbox"]},"application/x-virtualbox-vbox-extpack":{"compressible":false,"extensions":["vbox-extpack"]},"application/x-virtualbox-vdi":{"compressible":true,"extensions":["vdi"]},"application/x-virtualbox-vhd":{"compressible":true,"extensions":["vhd"]},"application/x-virtualbox-vmdk":{"compressible":true,"extensions":["vmdk"]},"application/x-wais-source":{"source":"apache","extensions":["src"]},"application/x-web-app-manifest+json":{"compressible":true,"extensions":["webapp"]},"application/x-www-form-urlencoded":{"source":"iana","compressible":true},"application/x-x509-ca-cert":{"source":"iana","extensions":["der","crt","pem"]},"application/x-x509-ca-ra-cert":{"source":"iana"},"application/x-x509-next-ca-cert":{"source":"iana"},"application/x-xfig":{"source":"apache","extensions":["fig"]},"application/x-xliff+xml":{"source":"apache","compressible":true,"extensions":["xlf"]},"application/x-xpinstall":{"source":"apache","compressible":false,"extensions":["xpi"]},"application/x-xz":{"source":"apache","extensions":["xz"]},"application/x-zmachine":{"source":"apache","extensions":["z1","z2","z3","z4","z5","z6","z7","z8"]},"application/x400-bp":{"source":"iana"},"application/xacml+xml":{"source":"iana","compressible":true},"application/xaml+xml":{"source":"apache","compressible":true,"extensions":["xaml"]},"application/xcap-att+xml":{"source":"iana","compressible":true,"extensions":["xav"]},"application/xcap-caps+xml":{"source":"iana","compressible":true,"extensions":["xca"]},"application/xcap-diff+xml":{"source":"iana","compressible":true,"extensions":["xdf"]},"application/xcap-el+xml":{"source":"iana","compressible":true,"extensions":["xel"]},"application/xcap-error+xml":{"source":"iana","compressible":true},"application/xcap-ns+xml":{"source":"iana","compressible":true,"extensions":["xns"]},"application/xcon-conference-info+xml":{"source":"iana","compressible":true},"application/xcon-conference-info-diff+xml":{"source":"iana","compressible":true},"application/xenc+xml":{"source":"iana","compressible":true,"extensions":["xenc"]},"application/xhtml+xml":{"source":"iana","compressible":true,"extensions":["xhtml","xht"]},"application/xhtml-voice+xml":{"source":"apache","compressible":true},"application/xliff+xml":{"source":"iana","compressible":true,"extensions":["xlf"]},"application/xml":{"source":"iana","compressible":true,"extensions":["xml","xsl","xsd","rng"]},"application/xml-dtd":{"source":"iana","compressible":true,"extensions":["dtd"]},"application/xml-external-parsed-entity":{"source":"iana"},"application/xml-patch+xml":{"source":"iana","compressible":true},"application/xmpp+xml":{"source":"iana","compressible":true},"application/xop+xml":{"source":"iana","compressible":true,"extensions":["xop"]},"application/xproc+xml":{"source":"apache","compressible":true,"extensions":["xpl"]},"application/xslt+xml":{"source":"iana","compressible":true,"extensions":["xsl","xslt"]},"application/xspf+xml":{"source":"apache","compressible":true,"extensions":["xspf"]},"application/xv+xml":{"source":"iana","compressible":true,"extensions":["mxml","xhvml","xvml","xvm"]},"application/yang":{"source":"iana","extensions":["yang"]},"application/yang-data+json":{"source":"iana","compressible":true},"application/yang-data+xml":{"source":"iana","compressible":true},"application/yang-patch+json":{"source":"iana","compressible":true},"application/yang-patch+xml":{"source":"iana","compressible":true},"application/yin+xml":{"source":"iana","compressible":true,"extensions":["yin"]},"application/zip":{"source":"iana","compressible":false,"extensions":["zip"]},"application/zlib":{"source":"iana"},"application/zstd":{"source":"iana"},"audio/1d-interleaved-parityfec":{"source":"iana"},"audio/32kadpcm":{"source":"iana"},"audio/3gpp":{"source":"iana","compressible":false,"extensions":["3gpp"]},"audio/3gpp2":{"source":"iana"},"audio/aac":{"source":"iana"},"audio/ac3":{"source":"iana"},"audio/adpcm":{"source":"apache","extensions":["adp"]},"audio/amr":{"source":"iana","extensions":["amr"]},"audio/amr-wb":{"source":"iana"},"audio/amr-wb+":{"source":"iana"},"audio/aptx":{"source":"iana"},"audio/asc":{"source":"iana"},"audio/atrac-advanced-lossless":{"source":"iana"},"audio/atrac-x":{"source":"iana"},"audio/atrac3":{"source":"iana"},"audio/basic":{"source":"iana","compressible":false,"extensions":["au","snd"]},"audio/bv16":{"source":"iana"},"audio/bv32":{"source":"iana"},"audio/clearmode":{"source":"iana"},"audio/cn":{"source":"iana"},"audio/dat12":{"source":"iana"},"audio/dls":{"source":"iana"},"audio/dsr-es201108":{"source":"iana"},"audio/dsr-es202050":{"source":"iana"},"audio/dsr-es202211":{"source":"iana"},"audio/dsr-es202212":{"source":"iana"},"audio/dv":{"source":"iana"},"audio/dvi4":{"source":"iana"},"audio/eac3":{"source":"iana"},"audio/encaprtp":{"source":"iana"},"audio/evrc":{"source":"iana"},"audio/evrc-qcp":{"source":"iana"},"audio/evrc0":{"source":"iana"},"audio/evrc1":{"source":"iana"},"audio/evrcb":{"source":"iana"},"audio/evrcb0":{"source":"iana"},"audio/evrcb1":{"source":"iana"},"audio/evrcnw":{"source":"iana"},"audio/evrcnw0":{"source":"iana"},"audio/evrcnw1":{"source":"iana"},"audio/evrcwb":{"source":"iana"},"audio/evrcwb0":{"source":"iana"},"audio/evrcwb1":{"source":"iana"},"audio/evs":{"source":"iana"},"audio/flexfec":{"source":"iana"},"audio/fwdred":{"source":"iana"},"audio/g711-0":{"source":"iana"},"audio/g719":{"source":"iana"},"audio/g722":{"source":"iana"},"audio/g7221":{"source":"iana"},"audio/g723":{"source":"iana"},"audio/g726-16":{"source":"iana"},"audio/g726-24":{"source":"iana"},"audio/g726-32":{"source":"iana"},"audio/g726-40":{"source":"iana"},"audio/g728":{"source":"iana"},"audio/g729":{"source":"iana"},"audio/g7291":{"source":"iana"},"audio/g729d":{"source":"iana"},"audio/g729e":{"source":"iana"},"audio/gsm":{"source":"iana"},"audio/gsm-efr":{"source":"iana"},"audio/gsm-hr-08":{"source":"iana"},"audio/ilbc":{"source":"iana"},"audio/ip-mr_v2.5":{"source":"iana"},"audio/isac":{"source":"apache"},"audio/l16":{"source":"iana"},"audio/l20":{"source":"iana"},"audio/l24":{"source":"iana","compressible":false},"audio/l8":{"source":"iana"},"audio/lpc":{"source":"iana"},"audio/melp":{"source":"iana"},"audio/melp1200":{"source":"iana"},"audio/melp2400":{"source":"iana"},"audio/melp600":{"source":"iana"},"audio/mhas":{"source":"iana"},"audio/midi":{"source":"apache","extensions":["mid","midi","kar","rmi"]},"audio/mobile-xmf":{"source":"iana","extensions":["mxmf"]},"audio/mp3":{"compressible":false,"extensions":["mp3"]},"audio/mp4":{"source":"iana","compressible":false,"extensions":["m4a","mp4a"]},"audio/mp4a-latm":{"source":"iana"},"audio/mpa":{"source":"iana"},"audio/mpa-robust":{"source":"iana"},"audio/mpeg":{"source":"iana","compressible":false,"extensions":["mpga","mp2","mp2a","mp3","m2a","m3a"]},"audio/mpeg4-generic":{"source":"iana"},"audio/musepack":{"source":"apache"},"audio/ogg":{"source":"iana","compressible":false,"extensions":["oga","ogg","spx","opus"]},"audio/opus":{"source":"iana"},"audio/parityfec":{"source":"iana"},"audio/pcma":{"source":"iana"},"audio/pcma-wb":{"source":"iana"},"audio/pcmu":{"source":"iana"},"audio/pcmu-wb":{"source":"iana"},"audio/prs.sid":{"source":"iana"},"audio/qcelp":{"source":"iana"},"audio/raptorfec":{"source":"iana"},"audio/red":{"source":"iana"},"audio/rtp-enc-aescm128":{"source":"iana"},"audio/rtp-midi":{"source":"iana"},"audio/rtploopback":{"source":"iana"},"audio/rtx":{"source":"iana"},"audio/s3m":{"source":"apache","extensions":["s3m"]},"audio/scip":{"source":"iana"},"audio/silk":{"source":"apache","extensions":["sil"]},"audio/smv":{"source":"iana"},"audio/smv-qcp":{"source":"iana"},"audio/smv0":{"source":"iana"},"audio/sofa":{"source":"iana"},"audio/sp-midi":{"source":"iana"},"audio/speex":{"source":"iana"},"audio/t140c":{"source":"iana"},"audio/t38":{"source":"iana"},"audio/telephone-event":{"source":"iana"},"audio/tetra_acelp":{"source":"iana"},"audio/tetra_acelp_bb":{"source":"iana"},"audio/tone":{"source":"iana"},"audio/tsvcis":{"source":"iana"},"audio/uemclip":{"source":"iana"},"audio/ulpfec":{"source":"iana"},"audio/usac":{"source":"iana"},"audio/vdvi":{"source":"iana"},"audio/vmr-wb":{"source":"iana"},"audio/vnd.3gpp.iufp":{"source":"iana"},"audio/vnd.4sb":{"source":"iana"},"audio/vnd.audiokoz":{"source":"iana"},"audio/vnd.celp":{"source":"iana"},"audio/vnd.cisco.nse":{"source":"iana"},"audio/vnd.cmles.radio-events":{"source":"iana"},"audio/vnd.cns.anp1":{"source":"iana"},"audio/vnd.cns.inf1":{"source":"iana"},"audio/vnd.dece.audio":{"source":"iana","extensions":["uva","uvva"]},"audio/vnd.digital-winds":{"source":"iana","extensions":["eol"]},"audio/vnd.dlna.adts":{"source":"iana"},"audio/vnd.dolby.heaac.1":{"source":"iana"},"audio/vnd.dolby.heaac.2":{"source":"iana"},"audio/vnd.dolby.mlp":{"source":"iana"},"audio/vnd.dolby.mps":{"source":"iana"},"audio/vnd.dolby.pl2":{"source":"iana"},"audio/vnd.dolby.pl2x":{"source":"iana"},"audio/vnd.dolby.pl2z":{"source":"iana"},"audio/vnd.dolby.pulse.1":{"source":"iana"},"audio/vnd.dra":{"source":"iana","extensions":["dra"]},"audio/vnd.dts":{"source":"iana","extensions":["dts"]},"audio/vnd.dts.hd":{"source":"iana","extensions":["dtshd"]},"audio/vnd.dts.uhd":{"source":"iana"},"audio/vnd.dvb.file":{"source":"iana"},"audio/vnd.everad.plj":{"source":"iana"},"audio/vnd.hns.audio":{"source":"iana"},"audio/vnd.lucent.voice":{"source":"iana","extensions":["lvp"]},"audio/vnd.ms-playready.media.pya":{"source":"iana","extensions":["pya"]},"audio/vnd.nokia.mobile-xmf":{"source":"iana"},"audio/vnd.nortel.vbk":{"source":"iana"},"audio/vnd.nuera.ecelp4800":{"source":"iana","extensions":["ecelp4800"]},"audio/vnd.nuera.ecelp7470":{"source":"iana","extensions":["ecelp7470"]},"audio/vnd.nuera.ecelp9600":{"source":"iana","extensions":["ecelp9600"]},"audio/vnd.octel.sbc":{"source":"iana"},"audio/vnd.presonus.multitrack":{"source":"iana"},"audio/vnd.qcelp":{"source":"iana"},"audio/vnd.rhetorex.32kadpcm":{"source":"iana"},"audio/vnd.rip":{"source":"iana","extensions":["rip"]},"audio/vnd.rn-realaudio":{"compressible":false},"audio/vnd.sealedmedia.softseal.mpeg":{"source":"iana"},"audio/vnd.vmx.cvsd":{"source":"iana"},"audio/vnd.wave":{"compressible":false},"audio/vorbis":{"source":"iana","compressible":false},"audio/vorbis-config":{"source":"iana"},"audio/wav":{"compressible":false,"extensions":["wav"]},"audio/wave":{"compressible":false,"extensions":["wav"]},"audio/webm":{"source":"apache","compressible":false,"extensions":["weba"]},"audio/x-aac":{"source":"apache","compressible":false,"extensions":["aac"]},"audio/x-aiff":{"source":"apache","extensions":["aif","aiff","aifc"]},"audio/x-caf":{"source":"apache","compressible":false,"extensions":["caf"]},"audio/x-flac":{"source":"apache","extensions":["flac"]},"audio/x-m4a":{"source":"nginx","extensions":["m4a"]},"audio/x-matroska":{"source":"apache","extensions":["mka"]},"audio/x-mpegurl":{"source":"apache","extensions":["m3u"]},"audio/x-ms-wax":{"source":"apache","extensions":["wax"]},"audio/x-ms-wma":{"source":"apache","extensions":["wma"]},"audio/x-pn-realaudio":{"source":"apache","extensions":["ram","ra"]},"audio/x-pn-realaudio-plugin":{"source":"apache","extensions":["rmp"]},"audio/x-realaudio":{"source":"nginx","extensions":["ra"]},"audio/x-tta":{"source":"apache"},"audio/x-wav":{"source":"apache","extensions":["wav"]},"audio/xm":{"source":"apache","extensions":["xm"]},"chemical/x-cdx":{"source":"apache","extensions":["cdx"]},"chemical/x-cif":{"source":"apache","extensions":["cif"]},"chemical/x-cmdf":{"source":"apache","extensions":["cmdf"]},"chemical/x-cml":{"source":"apache","extensions":["cml"]},"chemical/x-csml":{"source":"apache","extensions":["csml"]},"chemical/x-pdb":{"source":"apache"},"chemical/x-xyz":{"source":"apache","extensions":["xyz"]},"font/collection":{"source":"iana","extensions":["ttc"]},"font/otf":{"source":"iana","compressible":true,"extensions":["otf"]},"font/sfnt":{"source":"iana"},"font/ttf":{"source":"iana","compressible":true,"extensions":["ttf"]},"font/woff":{"source":"iana","extensions":["woff"]},"font/woff2":{"source":"iana","extensions":["woff2"]},"image/aces":{"source":"iana","extensions":["exr"]},"image/apng":{"compressible":false,"extensions":["apng"]},"image/avci":{"source":"iana","extensions":["avci"]},"image/avcs":{"source":"iana","extensions":["avcs"]},"image/avif":{"source":"iana","compressible":false,"extensions":["avif"]},"image/bmp":{"source":"iana","compressible":true,"extensions":["bmp"]},"image/cgm":{"source":"iana","extensions":["cgm"]},"image/dicom-rle":{"source":"iana","extensions":["drle"]},"image/emf":{"source":"iana","extensions":["emf"]},"image/fits":{"source":"iana","extensions":["fits"]},"image/g3fax":{"source":"iana","extensions":["g3"]},"image/gif":{"source":"iana","compressible":false,"extensions":["gif"]},"image/heic":{"source":"iana","extensions":["heic"]},"image/heic-sequence":{"source":"iana","extensions":["heics"]},"image/heif":{"source":"iana","extensions":["heif"]},"image/heif-sequence":{"source":"iana","extensions":["heifs"]},"image/hej2k":{"source":"iana","extensions":["hej2"]},"image/hsj2":{"source":"iana","extensions":["hsj2"]},"image/ief":{"source":"iana","extensions":["ief"]},"image/jls":{"source":"iana","extensions":["jls"]},"image/jp2":{"source":"iana","compressible":false,"extensions":["jp2","jpg2"]},"image/jpeg":{"source":"iana","compressible":false,"extensions":["jpeg","jpg","jpe"]},"image/jph":{"source":"iana","extensions":["jph"]},"image/jphc":{"source":"iana","extensions":["jhc"]},"image/jpm":{"source":"iana","compressible":false,"extensions":["jpm"]},"image/jpx":{"source":"iana","compressible":false,"extensions":["jpx","jpf"]},"image/jxr":{"source":"iana","extensions":["jxr"]},"image/jxra":{"source":"iana","extensions":["jxra"]},"image/jxrs":{"source":"iana","extensions":["jxrs"]},"image/jxs":{"source":"iana","extensions":["jxs"]},"image/jxsc":{"source":"iana","extensions":["jxsc"]},"image/jxsi":{"source":"iana","extensions":["jxsi"]},"image/jxss":{"source":"iana","extensions":["jxss"]},"image/ktx":{"source":"iana","extensions":["ktx"]},"image/ktx2":{"source":"iana","extensions":["ktx2"]},"image/naplps":{"source":"iana"},"image/pjpeg":{"compressible":false},"image/png":{"source":"iana","compressible":false,"extensions":["png"]},"image/prs.btif":{"source":"iana","extensions":["btif"]},"image/prs.pti":{"source":"iana","extensions":["pti"]},"image/pwg-raster":{"source":"iana"},"image/sgi":{"source":"apache","extensions":["sgi"]},"image/svg+xml":{"source":"iana","compressible":true,"extensions":["svg","svgz"]},"image/t38":{"source":"iana","extensions":["t38"]},"image/tiff":{"source":"iana","compressible":false,"extensions":["tif","tiff"]},"image/tiff-fx":{"source":"iana","extensions":["tfx"]},"image/vnd.adobe.photoshop":{"source":"iana","compressible":true,"extensions":["psd"]},"image/vnd.airzip.accelerator.azv":{"source":"iana","extensions":["azv"]},"image/vnd.cns.inf2":{"source":"iana"},"image/vnd.dece.graphic":{"source":"iana","extensions":["uvi","uvvi","uvg","uvvg"]},"image/vnd.djvu":{"source":"iana","extensions":["djvu","djv"]},"image/vnd.dvb.subtitle":{"source":"iana","extensions":["sub"]},"image/vnd.dwg":{"source":"iana","extensions":["dwg"]},"image/vnd.dxf":{"source":"iana","extensions":["dxf"]},"image/vnd.fastbidsheet":{"source":"iana","extensions":["fbs"]},"image/vnd.fpx":{"source":"iana","extensions":["fpx"]},"image/vnd.fst":{"source":"iana","extensions":["fst"]},"image/vnd.fujixerox.edmics-mmr":{"source":"iana","extensions":["mmr"]},"image/vnd.fujixerox.edmics-rlc":{"source":"iana","extensions":["rlc"]},"image/vnd.globalgraphics.pgb":{"source":"iana"},"image/vnd.microsoft.icon":{"source":"iana","compressible":true,"extensions":["ico"]},"image/vnd.mix":{"source":"iana"},"image/vnd.mozilla.apng":{"source":"iana"},"image/vnd.ms-dds":{"compressible":true,"extensions":["dds"]},"image/vnd.ms-modi":{"source":"iana","extensions":["mdi"]},"image/vnd.ms-photo":{"source":"apache","extensions":["wdp"]},"image/vnd.net-fpx":{"source":"iana","extensions":["npx"]},"image/vnd.pco.b16":{"source":"iana","extensions":["b16"]},"image/vnd.radiance":{"source":"iana"},"image/vnd.sealed.png":{"source":"iana"},"image/vnd.sealedmedia.softseal.gif":{"source":"iana"},"image/vnd.sealedmedia.softseal.jpg":{"source":"iana"},"image/vnd.svf":{"source":"iana"},"image/vnd.tencent.tap":{"source":"iana","extensions":["tap"]},"image/vnd.valve.source.texture":{"source":"iana","extensions":["vtf"]},"image/vnd.wap.wbmp":{"source":"iana","extensions":["wbmp"]},"image/vnd.xiff":{"source":"iana","extensions":["xif"]},"image/vnd.zbrush.pcx":{"source":"iana","extensions":["pcx"]},"image/webp":{"source":"apache","extensions":["webp"]},"image/wmf":{"source":"iana","extensions":["wmf"]},"image/x-3ds":{"source":"apache","extensions":["3ds"]},"image/x-cmu-raster":{"source":"apache","extensions":["ras"]},"image/x-cmx":{"source":"apache","extensions":["cmx"]},"image/x-freehand":{"source":"apache","extensions":["fh","fhc","fh4","fh5","fh7"]},"image/x-icon":{"source":"apache","compressible":true,"extensions":["ico"]},"image/x-jng":{"source":"nginx","extensions":["jng"]},"image/x-mrsid-image":{"source":"apache","extensions":["sid"]},"image/x-ms-bmp":{"source":"nginx","compressible":true,"extensions":["bmp"]},"image/x-pcx":{"source":"apache","extensions":["pcx"]},"image/x-pict":{"source":"apache","extensions":["pic","pct"]},"image/x-portable-anymap":{"source":"apache","extensions":["pnm"]},"image/x-portable-bitmap":{"source":"apache","extensions":["pbm"]},"image/x-portable-graymap":{"source":"apache","extensions":["pgm"]},"image/x-portable-pixmap":{"source":"apache","extensions":["ppm"]},"image/x-rgb":{"source":"apache","extensions":["rgb"]},"image/x-tga":{"source":"apache","extensions":["tga"]},"image/x-xbitmap":{"source":"apache","extensions":["xbm"]},"image/x-xcf":{"compressible":false},"image/x-xpixmap":{"source":"apache","extensions":["xpm"]},"image/x-xwindowdump":{"source":"apache","extensions":["xwd"]},"message/cpim":{"source":"iana"},"message/delivery-status":{"source":"iana"},"message/disposition-notification":{"source":"iana","extensions":["disposition-notification"]},"message/external-body":{"source":"iana"},"message/feedback-report":{"source":"iana"},"message/global":{"source":"iana","extensions":["u8msg"]},"message/global-delivery-status":{"source":"iana","extensions":["u8dsn"]},"message/global-disposition-notification":{"source":"iana","extensions":["u8mdn"]},"message/global-headers":{"source":"iana","extensions":["u8hdr"]},"message/http":{"source":"iana","compressible":false},"message/imdn+xml":{"source":"iana","compressible":true},"message/news":{"source":"iana"},"message/partial":{"source":"iana","compressible":false},"message/rfc822":{"source":"iana","compressible":true,"extensions":["eml","mime"]},"message/s-http":{"source":"iana"},"message/sip":{"source":"iana"},"message/sipfrag":{"source":"iana"},"message/tracking-status":{"source":"iana"},"message/vnd.si.simp":{"source":"iana"},"message/vnd.wfa.wsc":{"source":"iana","extensions":["wsc"]},"model/3mf":{"source":"iana","extensions":["3mf"]},"model/e57":{"source":"iana"},"model/gltf+json":{"source":"iana","compressible":true,"extensions":["gltf"]},"model/gltf-binary":{"source":"iana","compressible":true,"extensions":["glb"]},"model/iges":{"source":"iana","compressible":false,"extensions":["igs","iges"]},"model/mesh":{"source":"iana","compressible":false,"extensions":["msh","mesh","silo"]},"model/mtl":{"source":"iana","extensions":["mtl"]},"model/obj":{"source":"iana","extensions":["obj"]},"model/step":{"source":"iana"},"model/step+xml":{"source":"iana","compressible":true,"extensions":["stpx"]},"model/step+zip":{"source":"iana","compressible":false,"extensions":["stpz"]},"model/step-xml+zip":{"source":"iana","compressible":false,"extensions":["stpxz"]},"model/stl":{"source":"iana","extensions":["stl"]},"model/vnd.collada+xml":{"source":"iana","compressible":true,"extensions":["dae"]},"model/vnd.dwf":{"source":"iana","extensions":["dwf"]},"model/vnd.flatland.3dml":{"source":"iana"},"model/vnd.gdl":{"source":"iana","extensions":["gdl"]},"model/vnd.gs-gdl":{"source":"apache"},"model/vnd.gs.gdl":{"source":"iana"},"model/vnd.gtw":{"source":"iana","extensions":["gtw"]},"model/vnd.moml+xml":{"source":"iana","compressible":true},"model/vnd.mts":{"source":"iana","extensions":["mts"]},"model/vnd.opengex":{"source":"iana","extensions":["ogex"]},"model/vnd.parasolid.transmit.binary":{"source":"iana","extensions":["x_b"]},"model/vnd.parasolid.transmit.text":{"source":"iana","extensions":["x_t"]},"model/vnd.pytha.pyox":{"source":"iana"},"model/vnd.rosette.annotated-data-model":{"source":"iana"},"model/vnd.sap.vds":{"source":"iana","extensions":["vds"]},"model/vnd.usdz+zip":{"source":"iana","compressible":false,"extensions":["usdz"]},"model/vnd.valve.source.compiled-map":{"source":"iana","extensions":["bsp"]},"model/vnd.vtu":{"source":"iana","extensions":["vtu"]},"model/vrml":{"source":"iana","compressible":false,"extensions":["wrl","vrml"]},"model/x3d+binary":{"source":"apache","compressible":false,"extensions":["x3db","x3dbz"]},"model/x3d+fastinfoset":{"source":"iana","extensions":["x3db"]},"model/x3d+vrml":{"source":"apache","compressible":false,"extensions":["x3dv","x3dvz"]},"model/x3d+xml":{"source":"iana","compressible":true,"extensions":["x3d","x3dz"]},"model/x3d-vrml":{"source":"iana","extensions":["x3dv"]},"multipart/alternative":{"source":"iana","compressible":false},"multipart/appledouble":{"source":"iana"},"multipart/byteranges":{"source":"iana"},"multipart/digest":{"source":"iana"},"multipart/encrypted":{"source":"iana","compressible":false},"multipart/form-data":{"source":"iana","compressible":false},"multipart/header-set":{"source":"iana"},"multipart/mixed":{"source":"iana"},"multipart/multilingual":{"source":"iana"},"multipart/parallel":{"source":"iana"},"multipart/related":{"source":"iana","compressible":false},"multipart/report":{"source":"iana"},"multipart/signed":{"source":"iana","compressible":false},"multipart/vnd.bint.med-plus":{"source":"iana"},"multipart/voice-message":{"source":"iana"},"multipart/x-mixed-replace":{"source":"iana"},"text/1d-interleaved-parityfec":{"source":"iana"},"text/cache-manifest":{"source":"iana","compressible":true,"extensions":["appcache","manifest"]},"text/calendar":{"source":"iana","extensions":["ics","ifb"]},"text/calender":{"compressible":true},"text/cmd":{"compressible":true},"text/coffeescript":{"extensions":["coffee","litcoffee"]},"text/cql":{"source":"iana"},"text/cql-expression":{"source":"iana"},"text/cql-identifier":{"source":"iana"},"text/css":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["css"]},"text/csv":{"source":"iana","compressible":true,"extensions":["csv"]},"text/csv-schema":{"source":"iana"},"text/directory":{"source":"iana"},"text/dns":{"source":"iana"},"text/ecmascript":{"source":"iana"},"text/encaprtp":{"source":"iana"},"text/enriched":{"source":"iana"},"text/fhirpath":{"source":"iana"},"text/flexfec":{"source":"iana"},"text/fwdred":{"source":"iana"},"text/gff3":{"source":"iana"},"text/grammar-ref-list":{"source":"iana"},"text/html":{"source":"iana","compressible":true,"extensions":["html","htm","shtml"]},"text/jade":{"extensions":["jade"]},"text/javascript":{"source":"iana","compressible":true},"text/jcr-cnd":{"source":"iana"},"text/jsx":{"compressible":true,"extensions":["jsx"]},"text/less":{"compressible":true,"extensions":["less"]},"text/markdown":{"source":"iana","compressible":true,"extensions":["markdown","md"]},"text/mathml":{"source":"nginx","extensions":["mml"]},"text/mdx":{"compressible":true,"extensions":["mdx"]},"text/mizar":{"source":"iana"},"text/n3":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["n3"]},"text/parameters":{"source":"iana","charset":"UTF-8"},"text/parityfec":{"source":"iana"},"text/plain":{"source":"iana","compressible":true,"extensions":["txt","text","conf","def","list","log","in","ini"]},"text/provenance-notation":{"source":"iana","charset":"UTF-8"},"text/prs.fallenstein.rst":{"source":"iana"},"text/prs.lines.tag":{"source":"iana","extensions":["dsc"]},"text/prs.prop.logic":{"source":"iana"},"text/raptorfec":{"source":"iana"},"text/red":{"source":"iana"},"text/rfc822-headers":{"source":"iana"},"text/richtext":{"source":"iana","compressible":true,"extensions":["rtx"]},"text/rtf":{"source":"iana","compressible":true,"extensions":["rtf"]},"text/rtp-enc-aescm128":{"source":"iana"},"text/rtploopback":{"source":"iana"},"text/rtx":{"source":"iana"},"text/sgml":{"source":"iana","extensions":["sgml","sgm"]},"text/shaclc":{"source":"iana"},"text/shex":{"source":"iana","extensions":["shex"]},"text/slim":{"extensions":["slim","slm"]},"text/spdx":{"source":"iana","extensions":["spdx"]},"text/strings":{"source":"iana"},"text/stylus":{"extensions":["stylus","styl"]},"text/t140":{"source":"iana"},"text/tab-separated-values":{"source":"iana","compressible":true,"extensions":["tsv"]},"text/troff":{"source":"iana","extensions":["t","tr","roff","man","me","ms"]},"text/turtle":{"source":"iana","charset":"UTF-8","extensions":["ttl"]},"text/ulpfec":{"source":"iana"},"text/uri-list":{"source":"iana","compressible":true,"extensions":["uri","uris","urls"]},"text/vcard":{"source":"iana","compressible":true,"extensions":["vcard"]},"text/vnd.a":{"source":"iana"},"text/vnd.abc":{"source":"iana"},"text/vnd.ascii-art":{"source":"iana"},"text/vnd.curl":{"source":"iana","extensions":["curl"]},"text/vnd.curl.dcurl":{"source":"apache","extensions":["dcurl"]},"text/vnd.curl.mcurl":{"source":"apache","extensions":["mcurl"]},"text/vnd.curl.scurl":{"source":"apache","extensions":["scurl"]},"text/vnd.debian.copyright":{"source":"iana","charset":"UTF-8"},"text/vnd.dmclientscript":{"source":"iana"},"text/vnd.dvb.subtitle":{"source":"iana","extensions":["sub"]},"text/vnd.esmertec.theme-descriptor":{"source":"iana","charset":"UTF-8"},"text/vnd.familysearch.gedcom":{"source":"iana","extensions":["ged"]},"text/vnd.ficlab.flt":{"source":"iana"},"text/vnd.fly":{"source":"iana","extensions":["fly"]},"text/vnd.fmi.flexstor":{"source":"iana","extensions":["flx"]},"text/vnd.gml":{"source":"iana"},"text/vnd.graphviz":{"source":"iana","extensions":["gv"]},"text/vnd.hans":{"source":"iana"},"text/vnd.hgl":{"source":"iana"},"text/vnd.in3d.3dml":{"source":"iana","extensions":["3dml"]},"text/vnd.in3d.spot":{"source":"iana","extensions":["spot"]},"text/vnd.iptc.newsml":{"source":"iana"},"text/vnd.iptc.nitf":{"source":"iana"},"text/vnd.latex-z":{"source":"iana"},"text/vnd.motorola.reflex":{"source":"iana"},"text/vnd.ms-mediapackage":{"source":"iana"},"text/vnd.net2phone.commcenter.command":{"source":"iana"},"text/vnd.radisys.msml-basic-layout":{"source":"iana"},"text/vnd.senx.warpscript":{"source":"iana"},"text/vnd.si.uricatalogue":{"source":"iana"},"text/vnd.sosi":{"source":"iana"},"text/vnd.sun.j2me.app-descriptor":{"source":"iana","charset":"UTF-8","extensions":["jad"]},"text/vnd.trolltech.linguist":{"source":"iana","charset":"UTF-8"},"text/vnd.wap.si":{"source":"iana"},"text/vnd.wap.sl":{"source":"iana"},"text/vnd.wap.wml":{"source":"iana","extensions":["wml"]},"text/vnd.wap.wmlscript":{"source":"iana","extensions":["wmls"]},"text/vtt":{"source":"iana","charset":"UTF-8","compressible":true,"extensions":["vtt"]},"text/x-asm":{"source":"apache","extensions":["s","asm"]},"text/x-c":{"source":"apache","extensions":["c","cc","cxx","cpp","h","hh","dic"]},"text/x-component":{"source":"nginx","extensions":["htc"]},"text/x-fortran":{"source":"apache","extensions":["f","for","f77","f90"]},"text/x-gwt-rpc":{"compressible":true},"text/x-handlebars-template":{"extensions":["hbs"]},"text/x-java-source":{"source":"apache","extensions":["java"]},"text/x-jquery-tmpl":{"compressible":true},"text/x-lua":{"extensions":["lua"]},"text/x-markdown":{"compressible":true,"extensions":["mkd"]},"text/x-nfo":{"source":"apache","extensions":["nfo"]},"text/x-opml":{"source":"apache","extensions":["opml"]},"text/x-org":{"compressible":true,"extensions":["org"]},"text/x-pascal":{"source":"apache","extensions":["p","pas"]},"text/x-processing":{"compressible":true,"extensions":["pde"]},"text/x-sass":{"extensions":["sass"]},"text/x-scss":{"extensions":["scss"]},"text/x-setext":{"source":"apache","extensions":["etx"]},"text/x-sfv":{"source":"apache","extensions":["sfv"]},"text/x-suse-ymp":{"compressible":true,"extensions":["ymp"]},"text/x-uuencode":{"source":"apache","extensions":["uu"]},"text/x-vcalendar":{"source":"apache","extensions":["vcs"]},"text/x-vcard":{"source":"apache","extensions":["vcf"]},"text/xml":{"source":"iana","compressible":true,"extensions":["xml"]},"text/xml-external-parsed-entity":{"source":"iana"},"text/yaml":{"compressible":true,"extensions":["yaml","yml"]},"video/1d-interleaved-parityfec":{"source":"iana"},"video/3gpp":{"source":"iana","extensions":["3gp","3gpp"]},"video/3gpp-tt":{"source":"iana"},"video/3gpp2":{"source":"iana","extensions":["3g2"]},"video/av1":{"source":"iana"},"video/bmpeg":{"source":"iana"},"video/bt656":{"source":"iana"},"video/celb":{"source":"iana"},"video/dv":{"source":"iana"},"video/encaprtp":{"source":"iana"},"video/ffv1":{"source":"iana"},"video/flexfec":{"source":"iana"},"video/h261":{"source":"iana","extensions":["h261"]},"video/h263":{"source":"iana","extensions":["h263"]},"video/h263-1998":{"source":"iana"},"video/h263-2000":{"source":"iana"},"video/h264":{"source":"iana","extensions":["h264"]},"video/h264-rcdo":{"source":"iana"},"video/h264-svc":{"source":"iana"},"video/h265":{"source":"iana"},"video/iso.segment":{"source":"iana","extensions":["m4s"]},"video/jpeg":{"source":"iana","extensions":["jpgv"]},"video/jpeg2000":{"source":"iana"},"video/jpm":{"source":"apache","extensions":["jpm","jpgm"]},"video/jxsv":{"source":"iana"},"video/mj2":{"source":"iana","extensions":["mj2","mjp2"]},"video/mp1s":{"source":"iana"},"video/mp2p":{"source":"iana"},"video/mp2t":{"source":"iana","extensions":["ts"]},"video/mp4":{"source":"iana","compressible":false,"extensions":["mp4","mp4v","mpg4"]},"video/mp4v-es":{"source":"iana"},"video/mpeg":{"source":"iana","compressible":false,"extensions":["mpeg","mpg","mpe","m1v","m2v"]},"video/mpeg4-generic":{"source":"iana"},"video/mpv":{"source":"iana"},"video/nv":{"source":"iana"},"video/ogg":{"source":"iana","compressible":false,"extensions":["ogv"]},"video/parityfec":{"source":"iana"},"video/pointer":{"source":"iana"},"video/quicktime":{"source":"iana","compressible":false,"extensions":["qt","mov"]},"video/raptorfec":{"source":"iana"},"video/raw":{"source":"iana"},"video/rtp-enc-aescm128":{"source":"iana"},"video/rtploopback":{"source":"iana"},"video/rtx":{"source":"iana"},"video/scip":{"source":"iana"},"video/smpte291":{"source":"iana"},"video/smpte292m":{"source":"iana"},"video/ulpfec":{"source":"iana"},"video/vc1":{"source":"iana"},"video/vc2":{"source":"iana"},"video/vnd.cctv":{"source":"iana"},"video/vnd.dece.hd":{"source":"iana","extensions":["uvh","uvvh"]},"video/vnd.dece.mobile":{"source":"iana","extensions":["uvm","uvvm"]},"video/vnd.dece.mp4":{"source":"iana"},"video/vnd.dece.pd":{"source":"iana","extensions":["uvp","uvvp"]},"video/vnd.dece.sd":{"source":"iana","extensions":["uvs","uvvs"]},"video/vnd.dece.video":{"source":"iana","extensions":["uvv","uvvv"]},"video/vnd.directv.mpeg":{"source":"iana"},"video/vnd.directv.mpeg-tts":{"source":"iana"},"video/vnd.dlna.mpeg-tts":{"source":"iana"},"video/vnd.dvb.file":{"source":"iana","extensions":["dvb"]},"video/vnd.fvt":{"source":"iana","extensions":["fvt"]},"video/vnd.hns.video":{"source":"iana"},"video/vnd.iptvforum.1dparityfec-1010":{"source":"iana"},"video/vnd.iptvforum.1dparityfec-2005":{"source":"iana"},"video/vnd.iptvforum.2dparityfec-1010":{"source":"iana"},"video/vnd.iptvforum.2dparityfec-2005":{"source":"iana"},"video/vnd.iptvforum.ttsavc":{"source":"iana"},"video/vnd.iptvforum.ttsmpeg2":{"source":"iana"},"video/vnd.motorola.video":{"source":"iana"},"video/vnd.motorola.videop":{"source":"iana"},"video/vnd.mpegurl":{"source":"iana","extensions":["mxu","m4u"]},"video/vnd.ms-playready.media.pyv":{"source":"iana","extensions":["pyv"]},"video/vnd.nokia.interleaved-multimedia":{"source":"iana"},"video/vnd.nokia.mp4vr":{"source":"iana"},"video/vnd.nokia.videovoip":{"source":"iana"},"video/vnd.objectvideo":{"source":"iana"},"video/vnd.radgamettools.bink":{"source":"iana"},"video/vnd.radgamettools.smacker":{"source":"iana"},"video/vnd.sealed.mpeg1":{"source":"iana"},"video/vnd.sealed.mpeg4":{"source":"iana"},"video/vnd.sealed.swf":{"source":"iana"},"video/vnd.sealedmedia.softseal.mov":{"source":"iana"},"video/vnd.uvvu.mp4":{"source":"iana","extensions":["uvu","uvvu"]},"video/vnd.vivo":{"source":"iana","extensions":["viv"]},"video/vnd.youtube.yt":{"source":"iana"},"video/vp8":{"source":"iana"},"video/vp9":{"source":"iana"},"video/webm":{"source":"apache","compressible":false,"extensions":["webm"]},"video/x-f4v":{"source":"apache","extensions":["f4v"]},"video/x-fli":{"source":"apache","extensions":["fli"]},"video/x-flv":{"source":"apache","compressible":false,"extensions":["flv"]},"video/x-m4v":{"source":"apache","extensions":["m4v"]},"video/x-matroska":{"source":"apache","compressible":false,"extensions":["mkv","mk3d","mks"]},"video/x-mng":{"source":"apache","extensions":["mng"]},"video/x-ms-asf":{"source":"apache","extensions":["asf","asx"]},"video/x-ms-vob":{"source":"apache","extensions":["vob"]},"video/x-ms-wm":{"source":"apache","extensions":["wm"]},"video/x-ms-wmv":{"source":"apache","compressible":false,"extensions":["wmv"]},"video/x-ms-wmx":{"source":"apache","extensions":["wmx"]},"video/x-ms-wvx":{"source":"apache","extensions":["wvx"]},"video/x-msvideo":{"source":"apache","extensions":["avi"]},"video/x-sgi-movie":{"source":"apache","extensions":["movie"]},"video/x-smv":{"source":"apache","extensions":["smv"]},"x-conference/x-cooltalk":{"source":"apache","extensions":["ice"]},"x-shader/x-fragment":{"compressible":true},"x-shader/x-vertex":{"compressible":true}}')}};var __webpack_module_cache__={};function __nccwpck_require__(e){var t=__webpack_module_cache__[e];if(t!==undefined){return t.exports}var n=__webpack_module_cache__[e]={exports:{}};var s=true;try{__webpack_modules__[e].call(n.exports,n,n.exports,__nccwpck_require__);s=false}finally{if(s)delete __webpack_module_cache__[e]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var __webpack_exports__=__nccwpck_require__(4822);module.exports=__webpack_exports__})(); \ No newline at end of file diff --git a/.github/actions/invite-user/package-lock.json b/.github/actions/invite-user/package-lock.json deleted file mode 100644 index 3818ce69..00000000 --- a/.github/actions/invite-user/package-lock.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "name": "invite-user", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "invite-user", - "version": "1.0.0", - "dependencies": { - "@actions/core": "^1.10.0", - "@vercel/ncc": "^0.34.0", - "auth0": "^4.1.0", - "axios": "^1.6.0" - }, - "devDependencies": { - "typescript": "^5.2.2" - } - }, - "node_modules/@actions/core": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", - "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", - "dependencies": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "node_modules/@actions/http-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz", - "integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==", - "dependencies": { - "tunnel": "^0.0.6", - "undici": "^5.25.4" - } - }, - "node_modules/@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@vercel/ncc": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz", - "integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==", - "bin": { - "ncc": "dist/ncc/cli.js" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/auth0": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/auth0/-/auth0-4.1.0.tgz", - "integrity": "sha512-1CpjWPOuWPAhQZy46/T/jOViy1WXhytmdlZji693ZpBfugYw181+JXfKLzjea59oKmo4HFctD05cec7xGdysfQ==", - "dependencies": { - "jose": "^4.13.2", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/auth0/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jose": { - "version": "4.15.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", - "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.0.tgz", - "integrity": "sha512-l3ydWhlhOJzMVOYkymLykcRRXqbUaQriERtR70B9LzNkZ4bX52Fc8wbTDneMiwo8T+AemZXvXaTx+9o5ROxrXg==", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - } - } -} diff --git a/.github/actions/invite-user/package.json b/.github/actions/invite-user/package.json deleted file mode 100644 index 6e9525e6..00000000 --- a/.github/actions/invite-user/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "invite-user", - "version": "1.0.0", - "description": "Invite a user to Shape Docs", - "main": "lib/index.js", - "author": "Shape", - "scripts": { - "build": "tsc && ncc build --minify", - "test": "jest" - }, - "dependencies": { - "@actions/core": "^1.10.0", - "@vercel/ncc": "^0.34.0", - "auth0": "^4.1.0", - "axios": "^1.6.0" - }, - "devDependencies": { - "typescript": "^5.2.2" - } -} diff --git a/.github/actions/invite-user/src/Action.ts b/.github/actions/invite-user/src/Action.ts deleted file mode 100644 index 8ff1604c..00000000 --- a/.github/actions/invite-user/src/Action.ts +++ /dev/null @@ -1,87 +0,0 @@ -import IStateStore from "./StateStore/IStateStore" -import ILogger from "./Logger/ILogger" -import IUserClient from "./UserClient/IUserClient" -import IPasswordGenerator from "./PasswordGenerator/IPasswordGenerator" -import IRoleNameParser from "./RoleNameParser/IRoleNameParser" - -export interface ActionOptions { - readonly name: string - readonly email: string - readonly roles: string -} - -export interface ActionConfig { - readonly stateStore: IStateStore - readonly logger: ILogger - readonly userClient: IUserClient - readonly passwordGenerator: IPasswordGenerator - readonly roleNameParser: IRoleNameParser -} - -export default class Action { - private readonly stateStore: IStateStore - private readonly logger: ILogger - private readonly userClient: IUserClient - private readonly passwordGenerator: IPasswordGenerator - private readonly roleNameParser: IRoleNameParser - - constructor(config: ActionConfig) { - this.stateStore = config.stateStore - this.logger = config.logger - this.userClient = config.userClient - this.passwordGenerator = config.passwordGenerator - this.roleNameParser = config.roleNameParser - } - - async run(options: ActionOptions) { - if (!this.stateStore.isPost) { - await this.runMain(options) - this.stateStore.isPost = true - } - } - - private async runMain(options: ActionOptions) { - if (!options.name || options.name.length == 0) { - throw new Error("No name supplied.") - } - if (!options.email || options.email.length == 0) { - throw new Error("No e-mail supplied.") - } - if (!options.roles || options.roles.length == 0) { - throw new Error("No roles supplied.") - } - const roleNames = this.roleNameParser.parse(options.roles) - if (roleNames.length == 0) { - throw new Error("No roles supplied.") - } - const existingUser = await this.userClient.getUser({ email: options.email }) - let user = existingUser - if (!existingUser) { - const password = this.passwordGenerator.generatePassword() - const newUser = await this.userClient.createUser({ - name: options.name, - email: options.email, - password: password - }) - user = newUser - } - if (!user) { - throw new Error("Could not get an existing user or create a new user.") - } - const roles = await this.userClient.createRoles({ roleNames }) - if (roles.length == 0) { - throw new Error("Received an empty set of roles.") - } - const roleIDs = roles.map(e => e.id) - await this.userClient.assignRolesToUser({ - userID: user.id, - roleIDs: roleIDs - }) - if (!existingUser) { - await this.userClient.sendChangePasswordEmail({ email: user.email }) - this.logger.log(`${options.name} (${user.email}) has been invited.`) - } else { - this.logger.log(`${options.name} (${user.email}) has been updated.`) - } - } -} diff --git a/.github/actions/invite-user/src/Logger/ILogger.ts b/.github/actions/invite-user/src/Logger/ILogger.ts deleted file mode 100644 index 5093d452..00000000 --- a/.github/actions/invite-user/src/Logger/ILogger.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface ILogger { - log(message: string): void - error(message: string): void -} diff --git a/.github/actions/invite-user/src/Logger/Logger.ts b/.github/actions/invite-user/src/Logger/Logger.ts deleted file mode 100644 index e4ea571c..00000000 --- a/.github/actions/invite-user/src/Logger/Logger.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as core from "@actions/core" -import ILogger from "./ILogger" - -export default class Logger implements ILogger { - log(message: string) { - console.log(message) - } - - error(message: string) { - core.setFailed(message) - } -} diff --git a/.github/actions/invite-user/src/PasswordGenerator/IPasswordGenerator.ts b/.github/actions/invite-user/src/PasswordGenerator/IPasswordGenerator.ts deleted file mode 100644 index 5aa88129..00000000 --- a/.github/actions/invite-user/src/PasswordGenerator/IPasswordGenerator.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IPasswordGenerator { - generatePassword(): string -} diff --git a/.github/actions/invite-user/src/PasswordGenerator/PasswordGenerator.ts b/.github/actions/invite-user/src/PasswordGenerator/PasswordGenerator.ts deleted file mode 100644 index 69ec2793..00000000 --- a/.github/actions/invite-user/src/PasswordGenerator/PasswordGenerator.ts +++ /dev/null @@ -1,14 +0,0 @@ -import IPasswordGenerator from "./IPasswordGenerator" - -export default class PasswordGenerator implements IPasswordGenerator { - generatePassword(): string { - let pass = "" - const str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$" - const length = 18 - for (let i = 1; i <= length; i++) { - const char = Math.floor(Math.random() * str.length + 1) - pass += str.charAt(char) - } - return pass - } -} diff --git a/.github/actions/invite-user/src/RoleNameParser/IRoleNameParser.ts b/.github/actions/invite-user/src/RoleNameParser/IRoleNameParser.ts deleted file mode 100644 index c235ecef..00000000 --- a/.github/actions/invite-user/src/RoleNameParser/IRoleNameParser.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IRoleNameParser { - parse(roleNames: string): string[] -} diff --git a/.github/actions/invite-user/src/RoleNameParser/RoleNameParser.ts b/.github/actions/invite-user/src/RoleNameParser/RoleNameParser.ts deleted file mode 100644 index 94621838..00000000 --- a/.github/actions/invite-user/src/RoleNameParser/RoleNameParser.ts +++ /dev/null @@ -1,9 +0,0 @@ -import IRoleNameParser from "./IRoleNameParser" - -export default class RoleNameParser implements IRoleNameParser { - parse(roleNames: string): string[] { - return roleNames.split(",") - .map(e => e.trim().toLowerCase()) - .filter(e => e.length > 0) - } -} diff --git a/.github/actions/invite-user/src/StateStore/IStateStore.ts b/.github/actions/invite-user/src/StateStore/IStateStore.ts deleted file mode 100644 index 8dad4e68..00000000 --- a/.github/actions/invite-user/src/StateStore/IStateStore.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IStateStore { - isPost: boolean -} diff --git a/.github/actions/invite-user/src/StateStore/KeyValueStateStore.ts b/.github/actions/invite-user/src/StateStore/KeyValueStateStore.ts deleted file mode 100644 index 1d883334..00000000 --- a/.github/actions/invite-user/src/StateStore/KeyValueStateStore.ts +++ /dev/null @@ -1,27 +0,0 @@ -import IStateStore from "./IStateStore" - -export interface KeyValueStateWriterReader { - getState(name: string): string | null - saveState(name: string, value: any | null): void -} - -const KEY = { - IS_POST: "isPost" -} - -export default class KeyValueStateStore implements IStateStore { - private writerReader: KeyValueStateWriterReader - - constructor(writerReader: KeyValueStateWriterReader) { - this.writerReader = writerReader - this.isPost = false - } - - get isPost(): boolean { - return !!this.writerReader.getState(KEY.IS_POST) - } - - set isPost(isPost: boolean) { - this.writerReader.saveState(KEY.IS_POST, isPost) - } -} diff --git a/.github/actions/invite-user/src/UserClient/Auth0UserClient.ts b/.github/actions/invite-user/src/UserClient/Auth0UserClient.ts deleted file mode 100644 index a81e01ce..00000000 --- a/.github/actions/invite-user/src/UserClient/Auth0UserClient.ts +++ /dev/null @@ -1,119 +0,0 @@ -import axios from "axios" -import { ManagementClient } from "auth0" -import IUserClient, { User, Role } from "./IUserClient" - -export interface Auth0UserClientConfig { - domain: string - clientId: string - clientSecret: string -} - -export default class Auth0UserClient implements IUserClient { - private readonly config: Auth0UserClientConfig - private readonly managementClient: ManagementClient - - constructor(config: Auth0UserClientConfig) { - this.config = config - this.managementClient = new ManagementClient({ - domain: config.domain, - clientId: config.clientId, - clientSecret: config.clientSecret - }) - } - - async getUser(request: { email: string }): Promise { - const response = await this.managementClient.usersByEmail.getByEmail({ - email: request.email - }) - if (response.data.length == 0) { - return null - } - const user = response.data[0] - return { - id: user.user_id, - email: user.email - } - } - - async createUser(request: { name: string, email: string, password: string }): Promise { - const response = await this.managementClient.users.create({ - connection: "Username-Password-Authentication", - name: request.name, - email: request.email, - email_verified: true, - password: request.password, - app_metadata: { - has_pending_invitation: true - } - }) - return { - id: response.data.user_id, - email: response.data.email - } - } - - async createRoles(request: { roleNames: string[] }): Promise { - const allRolesResponse = await this.managementClient.roles.getAll() - const allRoles = allRolesResponse.data - const existingRoles = allRoles.filter((role: Role) => { - return request.roleNames.includes(role.name) - }).map(role => { - return { id: role.id, name: role.name } - }) - const roleNamesToAdd = request.roleNames.filter(roleName => { - const existingRole = existingRoles.find(role => { - return role.name == roleName - }) - return existingRole == null - }) - const responses = await Promise.all(roleNamesToAdd.map(roleName => { - return this.managementClient.roles.create({ name: roleName }) - })) - const addedRoles = responses.map(response => { - return { - id: response.data.id, - name: response.data.name - } - }) - return existingRoles.concat(addedRoles) - } - - async assignRolesToUser(request: { userID: string, roleIDs: string[] }): Promise { - await this.managementClient.users.assignRoles({ - id: request.userID - }, { - roles: request.roleIDs - }) - } - - async sendChangePasswordEmail(request: { email: string }): Promise { - const token = await this.getToken() - await axios.post(this.getURL("/dbconnections/change_password"), { - email: request.email, - connection: "Username-Password-Authentication" - }, { - headers: { - "Authorization": `Bearer ${token}`, - "Content-Type": "application/json" - } - }) - } - - private async getToken(): Promise { - const response = await axios.post(this.getURL("/oauth/token"), { - "grant_type": "client_credentials", - "client_id": this.config.clientId, - "client_secret": this.config.clientSecret, - "audience": `https://${this.config.domain}/api/v2/` - }, { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } - }) - return response.data.access_token - } - - private getURL(path: string): string { - return `https://${this.config.domain}${path}` - } -} diff --git a/.github/actions/invite-user/src/UserClient/IUserClient.ts b/.github/actions/invite-user/src/UserClient/IUserClient.ts deleted file mode 100644 index 7e0b6613..00000000 --- a/.github/actions/invite-user/src/UserClient/IUserClient.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type User = { - readonly id: string - readonly email: string -} - -export type Role = { - readonly id: string - readonly name: string -} - -export default interface IUserClient { - getUser(request: { email: string }): Promise - createUser(request: { name: string, email: string, password: string }): Promise - createRoles(request: { roleNames: string[] }): Promise - assignRolesToUser(request: { userID: string, roleIDs: string[] }): Promise - sendChangePasswordEmail(request: { email: string }): Promise -} diff --git a/.github/actions/invite-user/src/getOptions.ts b/.github/actions/invite-user/src/getOptions.ts deleted file mode 100644 index ae6dcd20..00000000 --- a/.github/actions/invite-user/src/getOptions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as core from "@actions/core" -import { ActionOptions } from "./Action" - -export default function getOptions(): ActionOptions { - return { - name: core.getInput("name"), - email: core.getInput("email"), - roles: core.getInput("roles") - } -} diff --git a/.github/actions/invite-user/src/index.ts b/.github/actions/invite-user/src/index.ts deleted file mode 100644 index 71bd61b3..00000000 --- a/.github/actions/invite-user/src/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as core from "@actions/core" -import Action from "./Action" -import Auth0UserClient from "./UserClient/Auth0UserClient" -import getOptions from "./getOptions" -import Logger from "./Logger/Logger" -import KeyValueStateStore from "./StateStore/KeyValueStateStore" -import PasswordGenerator from "./PasswordGenerator/PasswordGenerator" -import RoleNameParser from "./RoleNameParser/RoleNameParser" - -const { - AUTH0_MANAGEMENT_CLIENT_ID, - AUTH0_MANAGEMENT_CLIENT_SECRET, - AUTH0_MANAGEMENT_CLIENT_DOMAIN -} = process.env - -if (!AUTH0_MANAGEMENT_CLIENT_ID || AUTH0_MANAGEMENT_CLIENT_ID.length == 0) { - throw new Error("AUTH0_MANAGEMENT_CLIENT_ID environment variable not set.") -} else if (!AUTH0_MANAGEMENT_CLIENT_SECRET || AUTH0_MANAGEMENT_CLIENT_SECRET.length == 0) { - throw new Error("AUTH0_MANAGEMENT_CLIENT_ID environment variable not set.") -} else if (!AUTH0_MANAGEMENT_CLIENT_DOMAIN || AUTH0_MANAGEMENT_CLIENT_DOMAIN.length == 0) { - throw new Error("AUTH0_MANAGEMENT_CLIENT_ID environment variable not set.") -} - -const stateStore = new KeyValueStateStore(core) -const logger = new Logger() -const userClient = new Auth0UserClient({ - clientId: AUTH0_MANAGEMENT_CLIENT_ID, - clientSecret: AUTH0_MANAGEMENT_CLIENT_SECRET, - domain: AUTH0_MANAGEMENT_CLIENT_DOMAIN -}) -const passwordGenerator = new PasswordGenerator() -const roleNameParser = new RoleNameParser() -const action = new Action({ - stateStore, - logger, - userClient, - passwordGenerator, - roleNameParser -}) -action.run(getOptions()).catch((err: Error) => { - logger.error(err.toString()) -}) diff --git a/.github/actions/invite-user/tsconfig.json b/.github/actions/invite-user/tsconfig.json deleted file mode 100644 index 816d6560..00000000 --- a/.github/actions/invite-user/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "lib": ["es6"], - "outDir": "./lib", - "rootDir": "./src", - "declaration": true, - "strict": true, - "noImplicitAny": false, - "esModuleInterop": true, - "skipLibCheck": true - }, - "exclude": ["__test__", "lib", "node_modules"] -} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 216f787f..40bd1a85 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,14 +7,8 @@ concurrency: cancel-in-progress: true env: NEXT_TELEMETRY_DISABLED: 1 - AUTH0_SECRET: 336f7a926310cff425cea29556dce2a98859b8d234aa27968696c2e6f1cb7d34 - AUTH0_BASE_URL: http://dev.local:3000 - AUTH0_ISSUER_BASE_URL: https://shape-docs-dev.eu.auth0.com - AUTH0_CLIENT_ID: this-is-our-client-id - AUTH0_CLIENT_SECRET: this-is-our-client-secret - AUTH0_MANAGEMENT_DOMAIN: shape-docs-dev.eu.auth0.com - AUTH0_MANAGEMENT_CLIENT_ID: this-is-our-management-client-id - AUTH0_MANAGEMENT_CLIENT_SECRET: this-is-our-management-client-secret + NEXTAUTH_URL: http://dev.local:3000 + NEXTAUTH_SECRET: 336f7a926310cff425cea29556dce2a98859b8d234aa27968696c2e6f1cb7d34 GITHUB_CLIENT_ID: this-is-our-github-client-id GITHUB_CLIENT_SECRET: this-is-our-github-client-secret GITHUB_APP_ID: 12345 diff --git a/.github/workflows/invite-guest.yml b/.github/workflows/invite-guest.yml deleted file mode 100644 index b1e4904b..00000000 --- a/.github/workflows/invite-guest.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Invite Guest -on: - workflow_dispatch: - inputs: - environment: - type: choice - description: Environment - options: - - production - - staging - required: true - name: - description: Name of user - required: true - email: - description: E-mail address to send invitation to - required: true - roles: - description: Comma-separated list of repositories user needs access to - required: true -env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_SHAPE_DOCS }} -jobs: - build: - name: Invite Guest - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - name: Install 1Password CLI - uses: 1password/install-cli-action@v1 - - name: Configure for Production - if: "${{ github.event.inputs.environment == 'production' }}" - run: | - echo "AUTH0_MANAGEMENT_CLIENT_ID_OP_KEY=op://Shape Docs GitHub Actions/Auth0 Management API Client ID/password" >> $GITHUB_ENV - echo "AUTH0_MANAGEMENT_CLIENT_SECRET_OP_KEY=op://Shape Docs GitHub Actions/Auth0 Management API Client Secret/password" >> $GITHUB_ENV - echo "AUTH0_MANAGEMENT_CLIENT_DOMAIN=shape-docs.eu.auth0.com" >> $GITHUB_ENV - - name: Configure for Staging - if: "${{ github.event.inputs.environment == 'staging' }}" - run: | - echo "AUTH0_MANAGEMENT_CLIENT_ID_OP_KEY=op://Shape Docs GitHub Actions/Auth0 Management API Client ID Staging/password" >> $GITHUB_ENV - echo "AUTH0_MANAGEMENT_CLIENT_SECRET_OP_KEY=op://Shape Docs GitHub Actions/Auth0 Management API Client Secret Staging/password" >> $GITHUB_ENV - echo "AUTH0_MANAGEMENT_CLIENT_DOMAIN=shape-docs-dev.eu.auth0.com" >> $GITHUB_ENV - - name: Install Secrets - run: | - AUTH0_MANAGEMENT_CLIENT_ID=$(op read "${AUTH0_MANAGEMENT_CLIENT_ID_OP_KEY}") - AUTH0_MANAGEMENT_CLIENT_SECRET=$(op read "${AUTH0_MANAGEMENT_CLIENT_SECRET_OP_KEY}") - echo "AUTH0_MANAGEMENT_CLIENT_ID=${AUTH0_MANAGEMENT_CLIENT_ID}" >> $GITHUB_ENV - echo "AUTH0_MANAGEMENT_CLIENT_SECRET=${AUTH0_MANAGEMENT_CLIENT_SECRET}" >> $GITHUB_ENV - echo "::add-mask::${AUTH0_MANAGEMENT_CLIENT_ID}" - echo "::add-mask::${AUTH0_MANAGEMENT_CLIENT_SECRET}" - - name: Send Invitation - uses: ./.github/actions/invite-user - with: - name: ${{ github.event.inputs.name }} - email: ${{ github.event.inputs.email }} - roles: ${{ github.event.inputs.roles }} From 7e73c0f1fa55bdd997229f7cbb08c744799fd72b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 5 Feb 2024 15:59:23 +0100 Subject: [PATCH 002/191] Replaces Auth0 with Auth.js --- README.md | 34 +--- package-lock.json | 176 +++++++----------- package.json | 3 +- src/app/api/auth/[...nextauth]/route.ts | 14 ++ src/app/api/auth/[auth0]/route.ts | 34 ---- src/app/api/auth/forceLogout/route.ts | 22 --- src/app/layout.tsx | 6 +- src/common/errors/client/ErrorHandler.tsx | 2 +- .../{Auth0Session.ts => AuthSession.ts} | 18 +- src/composition.ts | 4 +- .../auth/view/client/SessionProvider.tsx | 15 ++ src/features/user/view/SettingsList.tsx | 3 +- src/features/user/view/UserButton.tsx | 9 +- src/features/user/view/UserFooter.tsx | 7 +- src/middleware.ts | 6 +- types/env.d.ts | 10 +- 16 files changed, 130 insertions(+), 233 deletions(-) create mode 100644 src/app/api/auth/[...nextauth]/route.ts delete mode 100644 src/app/api/auth/[auth0]/route.ts delete mode 100644 src/app/api/auth/forceLogout/route.ts rename src/common/session/{Auth0Session.ts => AuthSession.ts} (58%) create mode 100644 src/features/auth/view/client/SessionProvider.tsx diff --git a/README.md b/README.md index be3c1c4f..178baf2d 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,8 @@ Create a file named `.env.local` in the root of the project with the following c ``` NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' SHAPE_DOCS_BASE_URL='https://docs.shapetools.io' -AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value' -AUTH0_BASE_URL='http://dev.local:3000' -AUTH0_ISSUER_BASE_URL='https://shape-docs-dev.eu.auth0.com' -AUTH0_CLIENT_ID='Your client ID' -AUTH0_CLIENT_SECRET='Your client secret' -AUTH0_MANAGEMENT_DOMAIN='shape-docs-dev.eu.auth0.com' -AUTH0_MANAGEMENT_CLIENT_ID='Your client ID' -AUTH0_MANAGEMENT_CLIENT_SECRET='Your client secret' +NEXTAUTH_URL='https://docs.shapetools.io' +NEXTAUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' GITHUB_CLIENT_ID='GitHub App client ID' GITHUB_CLIENT_SECRET='GitHub App client secret' GITHUB_APP_ID='the GitHub App id' @@ -42,13 +36,8 @@ Each environment variable is described in the table below. |-|-| |NEXT_PUBLIC_SHAPE_DOCS_TITLE|Title of the portal. Displayed to the user in the browser.| |SHAPE_DOCS_BASE_URL|The URL where Shape Docs is hosted.| -|AUTH0_SECRET|A long secret value used to encrypt the session cookie. Generate it using `openssl rand -hex 32`.|AUTH0_BASE_URL|The base URL of your Auth0 application. `http://dev.local:3000` during development.| -|AUTH0_ISSUER_BASE_URL|The URL of your Auth0 tenant domain.| -|AUTH0_CLIENT_ID|The client ID of your default Auth0 application.| -|AUTH0_CLIENT_SECRET|The client secret of your default Auth0 application.| -|AUTH0_MANAGEMENT_DOMAIN|The URL of your Auth0 tenant domain. It is key that this does not contain "http" or "https".| -|AUTH0_MANAGEMENT_CLIENT_ID|The client ID of your Auth0 Machine to Machine application.| -|AUTH0_MANAGEMENT_CLIENT_SECRET|The client secret of your Machine to Machine Auth0 application.| +|NEXTAUTH_URL|The URL where Shape Docs is hosted.| +|NEXTAUTH_SECRET|A long secret value used to encrypt the session cookie. Generate it using `openssl rand -base64 32`.| |GITHUB_CLIENT_ID|The client ID of your GitHub app.| |GITHUB_CLIENT_SECRET|The client secret of your GitHub app.| |GITHUB_APP_ID|The ID of your GitHub app.| @@ -59,21 +48,6 @@ Each environment variable is described in the table below. |GITHUB_ORGANIZATION_NAME|Name of the organization to show repositories for.| |REDIS_URL|The URL to the Redis store.| -You need the following two Auth0 apps. - -| |Type|Description| -|-|-|-| -|Default|Generic|Used to authenticate the user when they log in.| -|Management|Machine to Machine|Used for making requests to [Auth0's Management API](https://auth0.com/docs/api/management/v2) to retrieve the access token for the identity provider that the user authorized with.| - -Modify your `/etc/hosts` file to add the following entry: - -``` -127.0.0.1 dev.local -``` - -We visit our local website by opening https://dev.local:3000 instead of https://localhost:3000 as this ensures that Auth0's flow will work correctly. Auth0 does some extra checks when localhost is included in the URL and we are generally not interested in those as they give a false impression of the flow the user will see. - Run the app using the following command: ``` diff --git a/package-lock.json b/package-lock.json index 8b84c090..1d8ee0cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "name": "shape-docs", "version": "0.1.0", "dependencies": { - "@auth0/nextjs-auth0": "^3.3.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -20,7 +19,6 @@ "@octokit/core": "^5.1.0", "@octokit/webhooks": "^12.0.3", "@stoplight/elements": "^7.15.3", - "auth0": "^4.1.0", "core-js": "^3.33.3", "encoding": "^0.1.13", "figma-squircle": "^0.3.1", @@ -28,6 +26,7 @@ "ioredis": "^5.3.2", "mobx": "^6.12.0", "next": "14.0.2", + "next-auth": "^4.24.5", "npm": "^10.2.4", "octokit": "^3.1.2", "react": "^18.2.0", @@ -97,36 +96,6 @@ "node": ">=6.0.0" } }, - "node_modules/@auth0/nextjs-auth0": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@auth0/nextjs-auth0/-/nextjs-auth0-3.3.0.tgz", - "integrity": "sha512-zIBeb2OuLIv0SyNEJaqfBbmwsaPLi1CAlnwOIRloABPI2nX0k0Sp959FiI7uUfJkUzqW8xtCyE56MDl4bs9WLQ==", - "dependencies": { - "@panva/hkdf": "^1.0.2", - "cookie": "^0.6.0", - "debug": "^4.3.4", - "joi": "^17.6.0", - "jose": "^4.9.2", - "oauth4webapi": "^2.3.0", - "openid-client": "^5.2.1", - "tslib": "^2.4.0", - "url-join": "^4.0.1" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "next": ">=10" - } - }, - "node_modules/@auth0/nextjs-auth0/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -1147,19 +1116,6 @@ "react": ">=16.3" } }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -2768,24 +2724,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -4856,18 +4794,6 @@ "node": ">= 4.0.0" } }, - "node_modules/auth0": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/auth0/-/auth0-4.1.0.tgz", - "integrity": "sha512-1CpjWPOuWPAhQZy46/T/jOViy1WXhytmdlZji693ZpBfugYw181+JXfKLzjea59oKmo4HFctD05cec7xGdysfQ==", - "dependencies": { - "jose": "^4.13.2", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/autolinker": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz", @@ -9174,18 +9100,6 @@ "jiti": "bin/jiti.js" } }, - "node_modules/joi": { - "version": "17.11.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", - "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", - "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, "node_modules/jose": { "version": "4.15.2", "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.2.tgz", @@ -10485,6 +10399,41 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.5.tgz", + "integrity": "sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.5.0", + "jose": "^4.11.4", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "next": "^12.2.5 || ^13 || ^14", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/node-abi": { "version": "3.50.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.50.0.tgz", @@ -13463,13 +13412,10 @@ "node": ">= 6" } }, - "node_modules/oauth4webapi": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.3.0.tgz", - "integrity": "sha512-JGkb5doGrwzVDuHwgrR4nHJayzN4h59VCed6EW8Tql6iHDfZIabCJvg6wtbn5q6pyB2hZruI3b77Nudvq7NmvA==", - "funding": { - "url": "https://github.com/sponsors/panva" - } + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -14230,6 +14176,31 @@ "node": ">=10" } }, + "node_modules/preact": { + "version": "10.19.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", + "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/preact-render-to-string/node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", @@ -16835,11 +16806,6 @@ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" - }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -16913,18 +16879,6 @@ "node": ">= 4" } }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-to-istanbul": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", diff --git a/package.json b/package.json index 22e34782..6d65a00f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "test": "jest" }, "dependencies": { - "@auth0/nextjs-auth0": "^3.3.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -26,7 +25,6 @@ "@octokit/core": "^5.1.0", "@octokit/webhooks": "^12.0.3", "@stoplight/elements": "^7.15.3", - "auth0": "^4.1.0", "core-js": "^3.33.3", "encoding": "^0.1.13", "figma-squircle": "^0.3.1", @@ -34,6 +32,7 @@ "ioredis": "^5.3.2", "mobx": "^6.12.0", "next": "14.0.2", + "next-auth": "^4.24.5", "npm": "^10.2.4", "octokit": "^3.1.2", "react": "^18.2.0", diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..ec521255 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,14 @@ +import NextAuth from "next-auth" +import GithubProvider from "next-auth/providers/github" + +const handler = NextAuth({ + secret: process.env.NEXTAUTH_SECRET, + providers: [ + GithubProvider({ + clientId: process.env.GITHUB_CLIENT_ID, + clientSecret: process.env.GITHUB_CLIENT_SECRET + }) + ] +}) + +export { handler as GET, handler as POST } diff --git a/src/app/api/auth/[auth0]/route.ts b/src/app/api/auth/[auth0]/route.ts deleted file mode 100644 index 2023b606..00000000 --- a/src/app/api/auth/[auth0]/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NextResponse } from "next/server" -import { - handleAuth, - handleCallback, - handleLogout, - AfterCallbackAppRoute, - NextAppRouterHandler, - AppRouterOnError -} from "@auth0/nextjs-auth0" -import { logInHandler, logOutHandler } from "@/composition" - -const { SHAPE_DOCS_BASE_URL } = process.env - -const afterCallback: AfterCallbackAppRoute = async (_req, session) => { - await logInHandler.handleLogIn(session.user.sub) - return session -} - -const onError: AppRouterOnError = async (_req, error) => { - console.error(error) - const url = new URL(SHAPE_DOCS_BASE_URL + "/api/auth/forceLogout") - return NextResponse.redirect(url) -} - -const onLogout: NextAppRouterHandler = async (req, ctx) => { - await logOutHandler.handleLogOut() - return await handleLogout(req, ctx) -} - -export const GET = handleAuth({ - callback: handleCallback({ afterCallback }), - logout: onLogout, - onError -}) diff --git a/src/app/api/auth/forceLogout/route.ts b/src/app/api/auth/forceLogout/route.ts deleted file mode 100644 index bc902b44..00000000 --- a/src/app/api/auth/forceLogout/route.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NextRequest, NextResponse } from "next/server" - -const { - AUTH0_ISSUER_BASE_URL -} = process.env - -export async function GET(req: NextRequest) { - // If we encounter an error during login then we force the user - // to logout using Auth0's /oidc/logout endpoint as documented here: - // https://auth0.com/docs/authenticate/login/logout/log-users-out-of-auth0 - // While the documentation states that an id_token_hint or logout_hint - // should be provided that is not needed as the user is not fully logged - // in at this point. - const url = new URL(AUTH0_ISSUER_BASE_URL + "/oidc/logout") - const host = req.headers.get('host') - const redirectURI = req.nextUrl.protocol + "//" + host - url.searchParams.append("post_logout_redirect_uri", redirectURI) - - const response = NextResponse.redirect(url) - response.cookies.delete("appSession") - return response -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 48ad9fee..a382399e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,6 @@ import "./globals.css" import type { Metadata } from "next" -import { UserProvider } from "@auth0/nextjs-auth0/client" +import SessionProvider from "@/features/auth/view/client/SessionProvider" import { config as fontAwesomeConfig } from "@fortawesome/fontawesome-svg-core" import { CssBaseline } from "@mui/material" import ThemeRegistry from "../common/theme/ThemeRegistry" @@ -18,14 +18,14 @@ export default function RootLayout({ children }: { children: React.ReactNode }) return ( - + {children} - + ) diff --git a/src/common/errors/client/ErrorHandler.tsx b/src/common/errors/client/ErrorHandler.tsx index 3626459a..7e928289 100644 --- a/src/common/errors/client/ErrorHandler.tsx +++ b/src/common/errors/client/ErrorHandler.tsx @@ -13,7 +13,7 @@ export default function ErrorHandler({ return } if (error.status == 401) { - window.location.href = "/api/auth/logout" + window.location.href = "/api/auth/signout" } } return ( diff --git a/src/common/session/Auth0Session.ts b/src/common/session/AuthSession.ts similarity index 58% rename from src/common/session/Auth0Session.ts rename to src/common/session/AuthSession.ts index 10c9e02c..23770b82 100644 --- a/src/common/session/Auth0Session.ts +++ b/src/common/session/AuthSession.ts @@ -1,29 +1,31 @@ -import { getSession } from "@auth0/nextjs-auth0" +import { getServerSession } from "next-auth/next" import { UnauthorizedError } from "../../common" import ISession from "./ISession" import IIsUserGuestReader from "@/features/auth/domain/userIdentityProvider/IsUserGuestReader" -export type Auth0SessionConfig = { +export type AuthSessionConfig = { readonly isUserGuestReader: IIsUserGuestReader } -export default class Auth0Session implements ISession { +export default class AuthSession implements ISession { private readonly isUserGuestReader: IIsUserGuestReader - constructor(config: Auth0SessionConfig) { + constructor(config: AuthSessionConfig) { this.isUserGuestReader = config.isUserGuestReader } async getUserId(): Promise { - const session = await getSession() + const session = await getServerSession() if (!session) { throw new UnauthorizedError("User ID is unavailable because the user is not authenticated.") } - return session.user.sub + // return session.user.sub + return "foo" } async getIsGuest(): Promise { - const userId = await this.getUserId() - return await this.isUserGuestReader.getIsUserGuest(userId) + return false + // const userId = await this.getUserId() + // return await this.isUserGuestReader.getIsUserGuest(userId) } } diff --git a/src/composition.ts b/src/composition.ts index 926d2396..a5336d6e 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -1,4 +1,4 @@ -import Auth0Session from "@/common/session/Auth0Session" +import AuthSession from "@/common/session/AuthSession" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" import { @@ -85,7 +85,7 @@ export const userIdentityProviderReader = new CachingUserIdentityProviderReader( new Auth0UserIdentityProviderReader(auth0ManagementCredentials) ) -export const session = new Auth0Session({ +export const session = new AuthSession({ isUserGuestReader: new IsUserGuestReader(userIdentityProviderReader) }) diff --git a/src/features/auth/view/client/SessionProvider.tsx b/src/features/auth/view/client/SessionProvider.tsx new file mode 100644 index 00000000..37c3aee7 --- /dev/null +++ b/src/features/auth/view/client/SessionProvider.tsx @@ -0,0 +1,15 @@ +"use client" + +import { SessionProvider as NextAuthSessionProvider } from "next-auth/react" + +export default function SessionProvider({ + children +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/src/features/user/view/SettingsList.tsx b/src/features/user/view/SettingsList.tsx index 765d9694..a62d6883 100644 --- a/src/features/user/view/SettingsList.tsx +++ b/src/features/user/view/SettingsList.tsx @@ -1,6 +1,7 @@ import { List, Button } from "@mui/material" import ThickDivider from "@/common/ui/ThickDivider" import DocumentationVisualizationPicker from "./DocumentationVisualizationPicker" +import { signOut } from "next-auth/react" const SettingsList = () => { return ( @@ -17,8 +18,8 @@ const SettingsList = () => { variant="text" fullWidth={true} style={{justifyContent: "flex-start"}} - href="/api/auth/logout" sx={{ marginTop: 1.3 }} + onClick={() => signOut()} > Log out diff --git a/src/features/user/view/UserButton.tsx b/src/features/user/view/UserButton.tsx index f50fdce6..2d8597e0 100644 --- a/src/features/user/view/UserButton.tsx +++ b/src/features/user/view/UserButton.tsx @@ -1,5 +1,5 @@ import { useState } from "react" -import { UserProfile } from "@auth0/nextjs-auth0/client" +import { Session } from "next-auth" import { Avatar, Box, @@ -13,7 +13,7 @@ import { faEllipsis } from "@fortawesome/free-solid-svg-icons" import MenuItemHover from "@/common/ui/MenuItemHover" import SettingsList from "./SettingsList" -const UserButton = ({ user }: { user: UserProfile }) => { +const UserButton = ({ session }: { session: Session }) => { const [popoverAnchorElement, setPopoverAnchorElement] = useState(null) const handlePopoverClick = (event: React.MouseEvent) => { setPopoverAnchorElement(event.currentTarget) @@ -23,6 +23,7 @@ const UserButton = ({ user }: { user: UserProfile }) => { } const isPopoverOpen = Boolean(popoverAnchorElement) const id = isPopoverOpen ? "settings-popover" : undefined + const user = session.user return ( <> { > - {user.picture && - + {user?.image && + } {user && diff --git a/src/features/user/view/UserFooter.tsx b/src/features/user/view/UserFooter.tsx index ef69430e..a5b750c6 100644 --- a/src/features/user/view/UserFooter.tsx +++ b/src/features/user/view/UserFooter.tsx @@ -1,14 +1,15 @@ -import { useUser } from "@auth0/nextjs-auth0/client" +import { useSession } from "next-auth/react" import { List, ListItem } from "@mui/material" import UserButton from "./UserButton" import UserSkeleton from "./UserSkeleton" const UserFooter = () => { - const { user, isLoading } = useUser() + const { data: session, status } = useSession() + const isLoading = status == "loading" return ( - {!isLoading && user && } + {!isLoading && session && } {isLoading && } diff --git a/src/middleware.ts b/src/middleware.ts index ab6c8d87..ab978fb1 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,7 +1,5 @@ -import { withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge" +export { default } from "next-auth/middleware" export const config = { - matcher: "/((?!api/hooks|api/auth/logout|api/auth/forceLogout|_next/static|_next/image|images|favicon.ico).*)" + matcher: "/((?!api/hooks|api/auth/signout|_next/static|_next/image|images|favicon.ico).*)" } - -export default withMiddlewareAuthRequired() diff --git a/types/env.d.ts b/types/env.d.ts index 290e4fe3..4dbcf968 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -2,14 +2,8 @@ namespace NodeJS { interface ProcessEnv { NEXT_PUBLIC_SHAPE_DOCS_TITLE: string SHAPE_DOCS_BASE_URL: string - AUTH0_SECRET: string - AUTH0_BASE_URL: string - AUTH0_ISSUER_BASE_URL: string - AUTH0_CLIENT_ID: string - AUTH0_CLIENT_SECRET: string - AUTH0_MANAGEMENT_DOMAIN: string - AUTH0_MANAGEMENT_CLIENT_ID: string - AUTH0_MANAGEMENT_CLIENT_SECRET: string + NEXTAUTH_URL: string + NEXTAUTH_SECRET: string GITHUB_CLIENT_ID: string GITHUB_CLIENT_SECRET: string GITHUB_APP_ID: string From 1e910b57891705b896aca78330d3aabb64dc490a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 5 Feb 2024 15:59:31 +0100 Subject: [PATCH 003/191] Removes script for updating Auth0 connection --- update-github-connection.sh | 51 ------------------------------------- 1 file changed, 51 deletions(-) delete mode 100755 update-github-connection.sh diff --git a/update-github-connection.sh b/update-github-connection.sh deleted file mode 100755 index 4fad95eb..00000000 --- a/update-github-connection.sh +++ /dev/null @@ -1,51 +0,0 @@ -GITHUB_CONNECTION_NAME="github" -GITHUB_CONNECTION_DISPLAY_NAME="GitHub" -GITHUB_CONNECTION_ICON_URL="https://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/GitHub_Invertocat_Logo.svg/400px-GitHub_Invertocat_Logo.svg.png" - -if [ -z ${AUTH0_MANAGEMENT_DOMAIN} ]; then - echo "AUTH0_MANAGEMENT_DOMAIN must be set to the Auth0 domain, e.g. shape-docs.eu.auth0.com" - exit 1 -fi -if [ -z ${AUTH0_MANAGEMENT_CLIENT_ID} ]; then - echo "AUTH0_MANAGEMENT_CLIENT_ID must be to the client ID of the app used to communicate with Auth0's Management API." - exit 1 -fi -if [ -z ${AUTH0_MANAGEMENT_CLIENT_SECRET} ]; then - echo "AUTH0_MANAGEMENT_CLIENT_SECRET must be to the client secret of the app used to communicate with Auth0's Management API." - exit 1 -fi - -# Get an access token for the Management API. -TOKEN_RESPONSE=$( - curl -s --request POST \ - --url "https://${AUTH0_MANAGEMENT_DOMAIN}/oauth/token" \ - --header "Content-Type: application/x-www-form-urlencoded" \ - --data grant_type=client_credentials \ - --data "client_id=${AUTH0_MANAGEMENT_CLIENT_ID}" \ - --data "client_secret=${AUTH0_MANAGEMENT_CLIENT_SECRET}" \ - --data "audience=https://${AUTH0_MANAGEMENT_DOMAIN}/api/v2/" -) -TOKEN=$(echo $TOKEN_RESPONSE | jq -r .access_token) - -# Fetch all connections. -CONNECTIONS_RESPONSE=$( - curl -s --request GET \ - --url "https://${AUTH0_MANAGEMENT_DOMAIN}/api/v2/connections" \ - --header "Authorization: Bearer ${TOKEN}" -) -SAFE_CONNECTIONS_RESPONSE=${CONNECTIONS_RESPONSE//\\n/\\\\n} - -# Modify the GitHub Connection. -GITHUB_CONNECTION_ID=$( - echo $SAFE_CONNECTIONS_RESPONSE | jq -r ".[] | select(.name == \"${GITHUB_CONNECTION_NAME}\") | .id" -) -GITHUB_CONNECTION_OPTIONS=$( - echo $SAFE_CONNECTIONS_RESPONSE | jq -r ".[] | select(.name == \"${GITHUB_CONNECTION_NAME}\") | .options += {\"display_name\": \"${GITHUB_CONNECTION_DISPLAY_NAME}\", \"icon_url\": \"${GITHUB_CONNECTION_ICON_URL}\"} | .options" -) - -# Post the updated options. -curl -s --request PATCH \ - --url "https://${AUTH0_MANAGEMENT_DOMAIN}/api/v2/connections/${GITHUB_CONNECTION_ID}" \ - --header "Content-Type: application/json" \ - --header "Authorization: Bearer ${TOKEN}" \ - --data "{ \"options\": ${GITHUB_CONNECTION_OPTIONS} }" From 4b7c6f8f9af0e0ddf598a096d8cf763ff562f682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 5 Feb 2024 16:27:46 +0100 Subject: [PATCH 004/191] Removes last mentions of Auth0 --- .../RemoveInvitedFlagLogInHandler.test.ts | 18 -------- .../{AuthSession.ts => AuthjsSession.ts} | 2 +- src/common/session/index.ts | 1 + src/composition.ts | 46 ++++--------------- .../auth/data/Auth0MetadataUpdater.ts | 28 ----------- .../auth/data/Auth0RefreshTokenReader.ts | 34 -------------- .../auth/data/Auth0RepositoryAccessReader.ts | 24 ---------- .../data/Auth0UserIdentityProviderReader.ts | 40 ---------------- .../auth/data/AuthjsRefreshTokenReader.ts | 17 +++++++ .../auth/data/AuthjsRepositoryAccessReader.ts | 7 +++ src/features/auth/data/index.ts | 6 +-- .../IIsUserGuestReader.ts | 0 .../auth/domain/guest/IsUserGuestReader.ts | 9 ++++ src/features/auth/domain/guest/index.ts | 1 + src/features/auth/domain/index.ts | 2 +- .../logIn/RemoveInvitedFlagLogInHandler.ts | 20 -------- src/features/auth/domain/logIn/index.ts | 1 - .../CachingUserIdentityProviderReader.ts | 26 ----------- .../IUserIdentityProviderReader.ts | 5 -- .../userIdentityProvider/IsUserGuestReader.ts | 16 ------- .../UserIdentityProvider.ts | 6 --- .../auth/domain/userIdentityProvider/index.ts | 3 -- 22 files changed, 48 insertions(+), 264 deletions(-) delete mode 100644 __test__/auth/RemoveInvitedFlagLogInHandler.test.ts rename src/common/session/{AuthSession.ts => AuthjsSession.ts} (94%) delete mode 100644 src/features/auth/data/Auth0MetadataUpdater.ts delete mode 100644 src/features/auth/data/Auth0RefreshTokenReader.ts delete mode 100644 src/features/auth/data/Auth0RepositoryAccessReader.ts delete mode 100644 src/features/auth/data/Auth0UserIdentityProviderReader.ts create mode 100644 src/features/auth/data/AuthjsRefreshTokenReader.ts create mode 100644 src/features/auth/data/AuthjsRepositoryAccessReader.ts rename src/features/auth/domain/{userIdentityProvider => guest}/IIsUserGuestReader.ts (100%) create mode 100644 src/features/auth/domain/guest/IsUserGuestReader.ts create mode 100644 src/features/auth/domain/guest/index.ts delete mode 100644 src/features/auth/domain/logIn/RemoveInvitedFlagLogInHandler.ts delete mode 100644 src/features/auth/domain/userIdentityProvider/CachingUserIdentityProviderReader.ts delete mode 100644 src/features/auth/domain/userIdentityProvider/IUserIdentityProviderReader.ts delete mode 100644 src/features/auth/domain/userIdentityProvider/IsUserGuestReader.ts delete mode 100644 src/features/auth/domain/userIdentityProvider/UserIdentityProvider.ts delete mode 100644 src/features/auth/domain/userIdentityProvider/index.ts diff --git a/__test__/auth/RemoveInvitedFlagLogInHandler.test.ts b/__test__/auth/RemoveInvitedFlagLogInHandler.test.ts deleted file mode 100644 index e4a276cd..00000000 --- a/__test__/auth/RemoveInvitedFlagLogInHandler.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { RemoveInvitedFlagLogInHandler } from "../../src/features/auth/domain" - -test("It removes invited flag from specified user", async () => { - let updatedUserId: string | undefined - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - let updatedMetadata: {[key: string]: any} | undefined - const sut = new RemoveInvitedFlagLogInHandler({ - async updateMetadata(userId, metadata) { - updatedUserId = userId - updatedMetadata = metadata - } - }) - await sut.handleLogIn("1234") - expect(updatedUserId).toEqual("1234") - expect(updatedMetadata).toEqual({ - has_pending_invitation: false - }) -}) diff --git a/src/common/session/AuthSession.ts b/src/common/session/AuthjsSession.ts similarity index 94% rename from src/common/session/AuthSession.ts rename to src/common/session/AuthjsSession.ts index 23770b82..e41444ab 100644 --- a/src/common/session/AuthSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -7,7 +7,7 @@ export type AuthSessionConfig = { readonly isUserGuestReader: IIsUserGuestReader } -export default class AuthSession implements ISession { +export default class AuthjsSession implements ISession { private readonly isUserGuestReader: IIsUserGuestReader constructor(config: AuthSessionConfig) { diff --git a/src/common/session/index.ts b/src/common/session/index.ts index bf010ed7..5ba5fb15 100644 --- a/src/common/session/index.ts +++ b/src/common/session/index.ts @@ -1 +1,2 @@ +export { default as AuthjsSession } from "./AuthjsSession" export type { default as ISession } from "./ISession" diff --git a/src/composition.ts b/src/composition.ts index a5336d6e..d8a1df32 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -1,4 +1,4 @@ -import AuthSession from "@/common/session/AuthSession" +import { AuthjsSession } from "@/common/session" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" import { @@ -18,16 +18,13 @@ import { import { GitHubOAuthTokenRefresher, GitHubInstallationAccessTokenDataSource, - Auth0MetadataUpdater, - Auth0RefreshTokenReader, - Auth0RepositoryAccessReader, - Auth0UserIdentityProviderReader + AuthjsRefreshTokenReader, + AuthjsRepositoryAccessReader } from "@/features/auth/data" import { AccessTokenService, AccessTokenSessionValidator, CachingRepositoryAccessReader, - CachingUserIdentityProviderReader, CompositeLogInHandler, CompositeLogOutHandler, CredentialsTransferringLogInHandler, @@ -43,15 +40,11 @@ import { LockingAccessTokenService, OAuthTokenRepository, OnlyStaleRefreshingAccessTokenService, - RemoveInvitedFlagLogInHandler, RepositoryRestrictingAccessTokenDataSource, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" const { - AUTH0_MANAGEMENT_DOMAIN, - AUTH0_MANAGEMENT_CLIENT_ID, - AUTH0_MANAGEMENT_CLIENT_SECRET, GITHUB_APP_ID, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, @@ -60,12 +53,6 @@ const { REDIS_URL } = process.env -const auth0ManagementCredentials = { - domain: AUTH0_MANAGEMENT_DOMAIN, - clientId: AUTH0_MANAGEMENT_CLIENT_ID, - clientSecret: AUTH0_MANAGEMENT_CLIENT_SECRET -} - const gitHubAppCredentials = { appId: GITHUB_APP_ID, clientId: GITHUB_CLIENT_ID, @@ -80,13 +67,8 @@ const userIdentityProviderRepository = new KeyValueUserDataRepository( "userIdentityProvider" ) -export const userIdentityProviderReader = new CachingUserIdentityProviderReader( - userIdentityProviderRepository, - new Auth0UserIdentityProviderReader(auth0ManagementCredentials) -) - -export const session = new AuthSession({ - isUserGuestReader: new IsUserGuestReader(userIdentityProviderReader) +export const session = new AuthjsSession({ + isUserGuestReader: new IsUserGuestReader() }) const oAuthTokenRepository = new OAuthTokenRepository( @@ -115,9 +97,7 @@ const guestRepositoryAccessRepository = new KeyValueUserDataRepository( export const guestRepositoryAccessReader = new CachingRepositoryAccessReader({ repository: guestRepositoryAccessRepository, - repositoryAccessReader: new Auth0RepositoryAccessReader({ - ...auth0ManagementCredentials - }) + repositoryAccessReader: new AuthjsRepositoryAccessReader() }) const guestAccessTokenDataSource = new RepositoryRestrictingAccessTokenDataSource({ @@ -194,25 +174,17 @@ export const projectDataSource = new CachingProjectDataSource({ export const logInHandler = new CompositeLogInHandler([ new CredentialsTransferringLogInHandler({ - isUserGuestReader: new IsUserGuestReader( - userIdentityProviderReader - ), + isUserGuestReader: new IsUserGuestReader(), guestCredentialsTransferrer: new GuestCredentialsTransferrer({ dataSource: guestAccessTokenDataSource, repository: guestAccessTokenRepository }), hostCredentialsTransferrer: new HostCredentialsTransferrer({ - refreshTokenReader: new Auth0RefreshTokenReader({ - ...auth0ManagementCredentials, - connection: "github" - }), + refreshTokenReader: new AuthjsRefreshTokenReader(), oAuthTokenRefresher: gitHubOAuthTokenRefresher, oAuthTokenRepository: oAuthTokenRepository }) - }), - new RemoveInvitedFlagLogInHandler( - new Auth0MetadataUpdater({ ...auth0ManagementCredentials }) - ) + }) ]) export const logOutHandler = new ErrorIgnoringLogOutHandler( diff --git a/src/features/auth/data/Auth0MetadataUpdater.ts b/src/features/auth/data/Auth0MetadataUpdater.ts deleted file mode 100644 index 88ffb524..00000000 --- a/src/features/auth/data/Auth0MetadataUpdater.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ManagementClient } from "auth0" - -type Auth0MetadataUpdaterConfig = { - readonly domain: string - readonly clientId: string - readonly clientSecret: string -} - -export default class Auth0MetadataUpdater { - private readonly managementClient: ManagementClient - - constructor(config: Auth0MetadataUpdaterConfig) { - this.managementClient = new ManagementClient({ - domain: config.domain, - clientId: config.clientId, - clientSecret: config.clientSecret - }) - } - - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - async updateMetadata(userId: string, metadata: {[key: string]: any}): Promise { - await this.managementClient.users.update({ - id: userId - }, { - app_metadata: metadata - }) - } -} diff --git a/src/features/auth/data/Auth0RefreshTokenReader.ts b/src/features/auth/data/Auth0RefreshTokenReader.ts deleted file mode 100644 index cafe3edc..00000000 --- a/src/features/auth/data/Auth0RefreshTokenReader.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ManagementClient } from "auth0" -import { UnauthorizedError } from "@/common" - -interface Auth0RefreshTokenReaderConfig { - readonly domain: string - readonly clientId: string - readonly clientSecret: string - readonly connection: string -} - -export default class Auth0RefreshTokenReader { - private readonly managementClient: ManagementClient - private readonly connection: string - - constructor(config: Auth0RefreshTokenReaderConfig) { - this.connection = config.connection - this.managementClient = new ManagementClient({ - domain: config.domain, - clientId: config.clientId, - clientSecret: config.clientSecret - }) - } - - async getRefreshToken(userId: string): Promise { - const userResponse = await this.managementClient.users.get({ id: userId }) - const identity = userResponse.data.identities.find(identity => { - return identity.connection.toLowerCase() == this.connection.toLowerCase() - }) - if (!identity) { - throw new UnauthorizedError(`No identity found for connection "${this.connection}"`) - } - return identity.refresh_token - } -} diff --git a/src/features/auth/data/Auth0RepositoryAccessReader.ts b/src/features/auth/data/Auth0RepositoryAccessReader.ts deleted file mode 100644 index 1d923d61..00000000 --- a/src/features/auth/data/Auth0RepositoryAccessReader.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ManagementClient } from "auth0" - -type Auth0RepositoryAccessReaderConfig = { - readonly domain: string - readonly clientId: string - readonly clientSecret: string -} - -export default class Auth0RepositoryAccessReader { - private readonly managementClient: ManagementClient - - constructor(config: Auth0RepositoryAccessReaderConfig) { - this.managementClient = new ManagementClient({ - domain: config.domain, - clientId: config.clientId, - clientSecret: config.clientSecret - }) - } - - async getRepositoryNames(userId: string): Promise { - const response = await this.managementClient.users.getRoles({ id: userId }) - return response.data.map(e => e.name) - } -} diff --git a/src/features/auth/data/Auth0UserIdentityProviderReader.ts b/src/features/auth/data/Auth0UserIdentityProviderReader.ts deleted file mode 100644 index dfd56b98..00000000 --- a/src/features/auth/data/Auth0UserIdentityProviderReader.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ManagementClient } from "auth0" -import UserIdentityProvider from "../domain/userIdentityProvider/UserIdentityProvider" -import IUserIdentityProviderReader from "../domain/userIdentityProvider/IUserIdentityProviderReader" -import { UnauthorizedError } from "@/common" - -interface Auth0UserIdentityProviderReaderConfig { - readonly domain: string - readonly clientId: string - readonly clientSecret: string -} - -export default class Auth0UserIdentityProviderReader implements IUserIdentityProviderReader { - private readonly managementClient: ManagementClient - - constructor(config: Auth0UserIdentityProviderReaderConfig) { - this.managementClient = new ManagementClient({ - domain: config.domain, - clientId: config.clientId, - clientSecret: config.clientSecret - }) - } - - async getUserIdentityProvider(userId: string): Promise { - const response = await this.managementClient.users.get({ id: userId }) - const identities = response.data.identities - const gitHubIdentity = identities.find(e => { - return e.connection.toLowerCase() === "github" - }) - const usernamePasswordIdentity = identities.find(e => { - return e.connection.toLowerCase() === "username-password-authentication" - }) - if (gitHubIdentity) { - return UserIdentityProvider.GITHUB - } else if (usernamePasswordIdentity) { - return UserIdentityProvider.USERNAME_PASSWORD - } else { - throw new UnauthorizedError() - } - } -} diff --git a/src/features/auth/data/AuthjsRefreshTokenReader.ts b/src/features/auth/data/AuthjsRefreshTokenReader.ts new file mode 100644 index 00000000..bee3dd78 --- /dev/null +++ b/src/features/auth/data/AuthjsRefreshTokenReader.ts @@ -0,0 +1,17 @@ +import { UnauthorizedError } from "@/common" + +export default class AuthjsRefreshTokenReader { + constructor() {} + + async getRefreshToken(userId: string): Promise { + // const userResponse = await this.managementClient.users.get({ id: userId }) + // const identity = userResponse.data.identities.find(identity => { + // return identity.connection.toLowerCase() == this.connection.toLowerCase() + // }) + // if (!identity) { + // throw new UnauthorizedError(`No identity found for connection "${this.connection}"`) + // } + // return identity.refresh_token + throw new UnauthorizedError("Not implemented") + } +} diff --git a/src/features/auth/data/AuthjsRepositoryAccessReader.ts b/src/features/auth/data/AuthjsRepositoryAccessReader.ts new file mode 100644 index 00000000..03cf0288 --- /dev/null +++ b/src/features/auth/data/AuthjsRepositoryAccessReader.ts @@ -0,0 +1,7 @@ +export default class AuthjsRepositoryAccessReader { + constructor() {} + + async getRepositoryNames(userId: string): Promise { + return [] + } +} diff --git a/src/features/auth/data/index.ts b/src/features/auth/data/index.ts index 8090466b..16c7437b 100644 --- a/src/features/auth/data/index.ts +++ b/src/features/auth/data/index.ts @@ -1,6 +1,4 @@ -export { default as Auth0MetadataUpdater } from "./Auth0MetadataUpdater" -export { default as Auth0RefreshTokenReader } from "./Auth0RefreshTokenReader" -export { default as Auth0RepositoryAccessReader } from "./Auth0RepositoryAccessReader" -export { default as Auth0UserIdentityProviderReader } from "./Auth0UserIdentityProviderReader" +export { default as AuthjsRefreshTokenReader } from "./AuthjsRefreshTokenReader" +export { default as AuthjsRepositoryAccessReader } from "./AuthjsRepositoryAccessReader" export { default as GitHubInstallationAccessTokenDataSource } from "./GitHubInstallationAccessTokenDataSource" export { default as GitHubOAuthTokenRefresher } from "./GitHubOAuthTokenRefresher" diff --git a/src/features/auth/domain/userIdentityProvider/IIsUserGuestReader.ts b/src/features/auth/domain/guest/IIsUserGuestReader.ts similarity index 100% rename from src/features/auth/domain/userIdentityProvider/IIsUserGuestReader.ts rename to src/features/auth/domain/guest/IIsUserGuestReader.ts diff --git a/src/features/auth/domain/guest/IsUserGuestReader.ts b/src/features/auth/domain/guest/IsUserGuestReader.ts new file mode 100644 index 00000000..1fa77881 --- /dev/null +++ b/src/features/auth/domain/guest/IsUserGuestReader.ts @@ -0,0 +1,9 @@ +import IIsUserGuestReader from "./IIsUserGuestReader" + +export default class IsUserGuestReader implements IIsUserGuestReader { + constructor() {} + + async getIsUserGuest(userId: string): Promise { + return false + } +} diff --git a/src/features/auth/domain/guest/index.ts b/src/features/auth/domain/guest/index.ts new file mode 100644 index 00000000..aa88c39f --- /dev/null +++ b/src/features/auth/domain/guest/index.ts @@ -0,0 +1 @@ +export { default as IsUserGuestReader } from "./IsUserGuestReader" diff --git a/src/features/auth/domain/index.ts b/src/features/auth/domain/index.ts index 1c1961cb..b1be6778 100644 --- a/src/features/auth/domain/index.ts +++ b/src/features/auth/domain/index.ts @@ -5,4 +5,4 @@ export * from "./logOut" export * from "./oAuthToken" export * from "./repositoryAccess" export * from "./sessionValidity" -export * from "./userIdentityProvider" +export * from "./guest" diff --git a/src/features/auth/domain/logIn/RemoveInvitedFlagLogInHandler.ts b/src/features/auth/domain/logIn/RemoveInvitedFlagLogInHandler.ts deleted file mode 100644 index c1565e9d..00000000 --- a/src/features/auth/domain/logIn/RemoveInvitedFlagLogInHandler.ts +++ /dev/null @@ -1,20 +0,0 @@ -import ILogInHandler from "./ILogInHandler" - -export interface IMetadataUpdater { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - updateMetadata(userId: string, metadata: {[key: string]: any}): Promise -} - -export default class RemoveInvitedFlagLogInHandler implements ILogInHandler { - private readonly metadataUpdater: IMetadataUpdater - - constructor(metadataUpdater: IMetadataUpdater) { - this.metadataUpdater = metadataUpdater - } - - async handleLogIn(userId: string): Promise { - await this.metadataUpdater.updateMetadata(userId, { - has_pending_invitation: false - }) - } -} diff --git a/src/features/auth/domain/logIn/index.ts b/src/features/auth/domain/logIn/index.ts index ec771fa7..21cfa0ce 100644 --- a/src/features/auth/domain/logIn/index.ts +++ b/src/features/auth/domain/logIn/index.ts @@ -1,4 +1,3 @@ export { default as CompositeLogInHandler } from "./CompositeLogInHandler" export { default as CredentialsTransferringLogInHandler } from "./CredentialsTransferringLogInHandler" export type { default as ILogInHandler } from "./ILogInHandler" -export { default as RemoveInvitedFlagLogInHandler } from "./RemoveInvitedFlagLogInHandler" diff --git a/src/features/auth/domain/userIdentityProvider/CachingUserIdentityProviderReader.ts b/src/features/auth/domain/userIdentityProvider/CachingUserIdentityProviderReader.ts deleted file mode 100644 index 86456036..00000000 --- a/src/features/auth/domain/userIdentityProvider/CachingUserIdentityProviderReader.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { IUserDataRepository } from "@/common" -import IUserIdentityProviderReader from "./IUserIdentityProviderReader" -import UserIdentityProvider from "./UserIdentityProvider" - -type Repository = IUserDataRepository - -export default class CachingUserIdentityProviderReader implements IUserIdentityProviderReader { - private readonly repository: Repository - private readonly reader: IUserIdentityProviderReader - - constructor(repository: Repository, reader: IUserIdentityProviderReader) { - this.repository = repository - this.reader = reader - } - - async getUserIdentityProvider(userId: string): Promise { - const cachedValue = await this.repository.get(userId) - if (cachedValue) { - return cachedValue as UserIdentityProvider - } else { - const userIdentity = await this.reader.getUserIdentityProvider(userId) - await this.repository.setExpiring(userId, userIdentity.toString(), 7 * 24 * 3600) - return userIdentity - } - } -} diff --git a/src/features/auth/domain/userIdentityProvider/IUserIdentityProviderReader.ts b/src/features/auth/domain/userIdentityProvider/IUserIdentityProviderReader.ts deleted file mode 100644 index 7b70ffec..00000000 --- a/src/features/auth/domain/userIdentityProvider/IUserIdentityProviderReader.ts +++ /dev/null @@ -1,5 +0,0 @@ -import UserIdentityProvider from "./UserIdentityProvider" - -export default interface IUserIdentityProviderReader { - getUserIdentityProvider(userId: string): Promise -} diff --git a/src/features/auth/domain/userIdentityProvider/IsUserGuestReader.ts b/src/features/auth/domain/userIdentityProvider/IsUserGuestReader.ts deleted file mode 100644 index d2e00648..00000000 --- a/src/features/auth/domain/userIdentityProvider/IsUserGuestReader.ts +++ /dev/null @@ -1,16 +0,0 @@ -import IIsUserGuestReader from "./IIsUserGuestReader" -import IUserIdentityProviderReader from "./IUserIdentityProviderReader" -import UserIdentityProvider from "./UserIdentityProvider" - -export default class IsUserGuestReader implements IIsUserGuestReader { - private readonly userIdentityProviderReader: IUserIdentityProviderReader - - constructor(userIdentityProviderReader: IUserIdentityProviderReader) { - this.userIdentityProviderReader = userIdentityProviderReader - } - - async getIsUserGuest(userId: string): Promise { - const userIdentityProvider = await this.userIdentityProviderReader.getUserIdentityProvider(userId) - return userIdentityProvider == UserIdentityProvider.USERNAME_PASSWORD - } -} \ No newline at end of file diff --git a/src/features/auth/domain/userIdentityProvider/UserIdentityProvider.ts b/src/features/auth/domain/userIdentityProvider/UserIdentityProvider.ts deleted file mode 100644 index 394cafa4..00000000 --- a/src/features/auth/domain/userIdentityProvider/UserIdentityProvider.ts +++ /dev/null @@ -1,6 +0,0 @@ -enum UserIdentityProvider { - GITHUB = "github", - USERNAME_PASSWORD = "username_password" -} - -export default UserIdentityProvider diff --git a/src/features/auth/domain/userIdentityProvider/index.ts b/src/features/auth/domain/userIdentityProvider/index.ts deleted file mode 100644 index 74b56b57..00000000 --- a/src/features/auth/domain/userIdentityProvider/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default as CachingUserIdentityProviderReader } from "./CachingUserIdentityProviderReader" -export { default as IsUserGuestReader } from "./IsUserGuestReader" -export { default as UserIdentityProvider } from "./UserIdentityProvider" From dcf8c05cc110e2570fb85180f779b593f5a6ee31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 5 Feb 2024 16:38:16 +0100 Subject: [PATCH 005/191] Removes unneeded store --- src/composition.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index d8a1df32..d070548e 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -62,11 +62,6 @@ const gitHubAppCredentials = { .toString("utf-8") } -const userIdentityProviderRepository = new KeyValueUserDataRepository( - new RedisKeyValueStore(REDIS_URL), - "userIdentityProvider" -) - export const session = new AuthjsSession({ isUserGuestReader: new IsUserGuestReader() }) @@ -190,7 +185,6 @@ export const logInHandler = new CompositeLogInHandler([ export const logOutHandler = new ErrorIgnoringLogOutHandler( new CompositeLogOutHandler([ new UserDataCleanUpLogOutHandler(session, projectUserDataRepository), - new UserDataCleanUpLogOutHandler(session, userIdentityProviderRepository), new UserDataCleanUpLogOutHandler(session, guestRepositoryAccessRepository), new UserDataCleanUpLogOutHandler(session, oAuthTokenRepository), new UserDataCleanUpLogOutHandler(session, guestAccessTokenRepository) From 850e8f62a4f1862f5e880d4689764b60e18d0478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 5 Feb 2024 16:38:30 +0100 Subject: [PATCH 006/191] Fixes imports --- src/common/session/AuthjsSession.ts | 2 +- src/features/auth/domain/guest/index.ts | 1 + .../auth/domain/logIn/CredentialsTransferringLogInHandler.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index e41444ab..d6da700a 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -1,7 +1,7 @@ import { getServerSession } from "next-auth/next" import { UnauthorizedError } from "../../common" import ISession from "./ISession" -import IIsUserGuestReader from "@/features/auth/domain/userIdentityProvider/IsUserGuestReader" +import { IIsUserGuestReader } from "@/features/auth/domain" export type AuthSessionConfig = { readonly isUserGuestReader: IIsUserGuestReader diff --git a/src/features/auth/domain/guest/index.ts b/src/features/auth/domain/guest/index.ts index aa88c39f..238abe7c 100644 --- a/src/features/auth/domain/guest/index.ts +++ b/src/features/auth/domain/guest/index.ts @@ -1 +1,2 @@ +export type { default as IIsUserGuestReader } from "./IIsUserGuestReader" export { default as IsUserGuestReader } from "./IsUserGuestReader" diff --git a/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts b/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts index 112171ee..552cbd15 100644 --- a/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts +++ b/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts @@ -1,5 +1,5 @@ import ICredentialsTransferrer from "../credentialsTransfer/ICredentialsTransferrer" -import IIsUserGuestReader from "../userIdentityProvider/IIsUserGuestReader" +import IIsUserGuestReader from "../guest/IIsUserGuestReader" import ILogInHandler from "./ILogInHandler" export interface IRefreshTokenReader { From 296a251f0206968384f50002d6093dd6bd715a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 5 Feb 2024 16:38:34 +0100 Subject: [PATCH 007/191] Removes unneeded unit tests --- .../CachingUserIdentityProviderReader.test.ts | 63 ------------------- __test__/auth/IsUserGuestReader.test.ts | 27 +++----- 2 files changed, 8 insertions(+), 82 deletions(-) delete mode 100644 __test__/auth/CachingUserIdentityProviderReader.test.ts diff --git a/__test__/auth/CachingUserIdentityProviderReader.test.ts b/__test__/auth/CachingUserIdentityProviderReader.test.ts deleted file mode 100644 index 0075f9a3..00000000 --- a/__test__/auth/CachingUserIdentityProviderReader.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { CachingUserIdentityProviderReader } from "../../src/features/auth/domain" -import { UserIdentityProvider } from "../../src/features/auth/domain" - -test("It fetches user identity provider if it is not cached", async () => { - let didFetchUserIdentityProvider = false - const sut = new CachingUserIdentityProviderReader({ - async get() { - return null - }, - async set() {}, - async setExpiring() {}, - async delete() {} - }, { - async getUserIdentityProvider() { - didFetchUserIdentityProvider = true - return UserIdentityProvider.GITHUB - }, - }) - await sut.getUserIdentityProvider("foo") - expect(didFetchUserIdentityProvider).toBeTruthy() -}) - -test("It does not fetch user identity provider if it is cached", async () => { - let didFetchUserIdentityProvider = false - const sut = new CachingUserIdentityProviderReader({ - async get() { - return UserIdentityProvider.GITHUB - }, - async set() {}, - async setExpiring() {}, - async delete() {} - }, { - async getUserIdentityProvider() { - didFetchUserIdentityProvider = true - return UserIdentityProvider.GITHUB - }, - }) - await sut.getUserIdentityProvider("foo") - expect(didFetchUserIdentityProvider).toBeFalsy() -}) - -test("It caches fetched user identity provider for user", async () => { - let cachedUserId: string | undefined - let cachedUserIdentityProvider: string | undefined - const sut = new CachingUserIdentityProviderReader({ - async get() { - return null - }, - async set() {}, - async setExpiring(userId, userIdentityProvider) { - cachedUserId = userId - cachedUserIdentityProvider = userIdentityProvider - }, - async delete() {} - }, { - async getUserIdentityProvider() { - return UserIdentityProvider.USERNAME_PASSWORD - }, - }) - await sut.getUserIdentityProvider("1234") - expect(cachedUserId).toBe("1234") - expect(cachedUserIdentityProvider).toBe(UserIdentityProvider.USERNAME_PASSWORD.toString()) -}) diff --git a/__test__/auth/IsUserGuestReader.test.ts b/__test__/auth/IsUserGuestReader.test.ts index 2d604818..426c7814 100644 --- a/__test__/auth/IsUserGuestReader.test.ts +++ b/__test__/auth/IsUserGuestReader.test.ts @@ -1,22 +1,11 @@ import { IsUserGuestReader } from "../../src/features/auth/domain" -import { UserIdentityProvider } from "../../src/features/auth/domain" -test("It does not consider a user to be a guest if they are logged in with GitHub", async () => { - const sut = new IsUserGuestReader({ - async getUserIdentityProvider() { - return UserIdentityProvider.GITHUB - } - }) - const isGuest = await sut.getIsUserGuest("foo") - expect(isGuest).toBeFalsy() -}) - -test("It considers user a to be a guest if they are logged in with username and password", async () => { - const sut = new IsUserGuestReader({ - async getUserIdentityProvider() { - return UserIdentityProvider.USERNAME_PASSWORD - } - }) - const isGuest = await sut.getIsUserGuest("foo") - expect(isGuest).toBeTruthy() +test("It considers no user to be a guest", async () => { + const sut = new IsUserGuestReader() + const isGuestA = await sut.getIsUserGuest("foo") + const isGuestB = await sut.getIsUserGuest("bar") + const isGuestC = await sut.getIsUserGuest("hello") + expect(isGuestA).toBeFalsy() + expect(isGuestB).toBeFalsy() + expect(isGuestC).toBeFalsy() }) From e64f16cc1bd509881e8eaadaa58e177ebb9c8f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 7 Feb 2024 15:45:29 +0100 Subject: [PATCH 008/191] Renames IAccessTokenService to IAccessTokenReader --- .../sessionValidity/AccessTokenSessionValidator.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts index 05ef4dc4..c46d9356 100644 --- a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts @@ -1,23 +1,23 @@ import SessionValidity from "./SessionValidity" -interface IAccessTokenService { +interface IAccessTokenReader { getAccessToken(): Promise } type AccessTokenSessionValidatorConfig = { - readonly accessTokenService: IAccessTokenService + readonly accessTokenReader: IAccessTokenReader } export default class AccessTokenSessionValidator { - private readonly accessTokenService: IAccessTokenService + private readonly accessTokenReader: IAccessTokenReader constructor(config: AccessTokenSessionValidatorConfig) { - this.accessTokenService = config.accessTokenService + this.accessTokenReader = config.accessTokenReader } async validateSession(): Promise { try { - await this.accessTokenService.getAccessToken() + await this.accessTokenReader.getAccessToken() return SessionValidity.VALID } catch { return SessionValidity.INVALID_ACCESS_TOKEN From 56f1f98c5e96e2737144b29de646da29b90308f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 14:45:04 +0100 Subject: [PATCH 009/191] WIP authenticating GitHub accounts with Auth.js --- __test__/auth/AccessTokenRefresher.test.ts | 166 +++++++++ __test__/auth/AccessTokenService.test.ts | 121 ------ .../auth/AuthjsOAuthTokenRepository.test.ts | 75 ++++ __test__/auth/CompositeLogInHandler.test.ts | 24 -- ...redentialsTransferringLogInHandler.test.ts | 43 --- .../auth/GuestAccessTokenRepository.test.ts | 52 --- __test__/auth/GuestAccessTokenService.test.ts | 27 -- __test__/auth/HostAccessTokenService.test.ts | 86 ----- .../auth/HostCredentialsTransferrer.test.ts | 85 ----- __test__/auth/IsUserGuestReader.test.ts | 11 - .../auth/LockingAccessTokenRefresher.test.ts | 69 ++++ .../auth/LockingAccessTokenService.test.ts | 91 ----- __test__/auth/OAuthTokenRepository.test.ts | 86 +++-- ...yStaleRefreshingAccessTokenService.test.ts | 46 --- .../TransferringAccessTokenReader.test.ts | 141 +++++++ .../AccessTokenRefreshingGitHubClient.test.ts | 293 ++++++++------- create-tables.sql | 56 +++ drop-tables.sql | 5 + dump.rdb | Bin 0 -> 9241 bytes package-lock.json | 350 ++++++++++++++++++ package.json | 3 + src/app/api/auth/[...nextauth]/route.ts | 12 +- .../[owner]/[repository]/[...path]/route.ts | 7 +- src/app/api/proxy/route.ts | 7 +- src/app/api/user/projects/route.ts | 11 +- src/app/api/user/repository-access/route.ts | 6 +- src/app/api/user/session-validity/route.ts | 6 +- src/app/layout.tsx | 5 +- src/app/page.tsx | 7 +- src/common/db/IDB.ts | 15 + src/common/db/PostgreSQLDB.ts | 47 +++ src/common/db/index.ts | 2 + src/common/errors/index.ts | 3 +- src/common/errors/makeAPIErrorResponse.ts | 4 + .../AccessTokenRefreshingGitHubClient.ts | 28 +- src/common/index.ts | 1 + src/common/mutex/SessionMutexFactory.ts | 13 +- src/common/session/AuthjsSession.ts | 25 +- src/common/session/ISession.ts | 4 - src/common/session/index.ts | 1 - src/composition.ts | 172 +++++---- .../auth/data/AuthjsOAuthTokenRepository.ts | 41 ++ .../auth/data/AuthjsRefreshTokenReader.ts | 17 - .../auth/data/AuthjsRepositoryAccessReader.ts | 2 +- .../auth/data/GitHubOAuthTokenRefresher.ts | 2 +- src/features/auth/data/index.ts | 2 +- .../accessToken/AccessTokenRefresher.ts | 50 +++ .../domain/accessToken/AccessTokenService.ts | 42 --- .../accessToken/GuestAccessTokenRepository.ts | 25 -- .../accessToken/GuestAccessTokenService.ts | 49 --- .../accessToken/HostAccessTokenService.ts | 39 -- .../accessToken/IAccessTokenRefresher.ts | 3 + .../domain/accessToken/IAccessTokenService.ts | 4 - .../LockingAccessTokenRefresher.ts | 25 ++ .../accessToken/LockingAccessTokenService.ts | 27 -- .../OnlyStaleRefreshingAccessTokenService.ts | 23 -- .../TransferringAccessTokenReader.ts | 37 ++ src/features/auth/domain/accessToken/index.ts | 10 +- .../GuestCredentialsTransferrer.ts | 29 -- .../HostCredentialsTransferrer.ts | 31 -- .../ICredentialsTransferrer.ts | 3 - .../auth/domain/credentialsTransfer/index.ts | 2 - .../auth/domain/guest/IIsUserGuestReader.ts | 3 - .../auth/domain/guest/IsUserGuestReader.ts | 9 - src/features/auth/domain/guest/index.ts | 2 - src/features/auth/domain/index.ts | 3 - .../domain/logIn/CompositeLogInHandler.ts | 14 - .../CredentialsTransferringLogInHandler.ts | 42 --- .../auth/domain/logIn/ILogInHandler.ts | 3 - src/features/auth/domain/logIn/index.ts | 3 - .../domain/oAuthToken/OAuthTokenRepository.ts | 37 +- .../AccessTokenSessionValidator.ts | 3 +- src/features/auth/view/SessionBarrier.tsx | 5 + src/middleware.ts | 5 - types/@next-auth.d.ts | 9 + types/env.d.ts | 4 +- 76 files changed, 1522 insertions(+), 1289 deletions(-) create mode 100644 __test__/auth/AccessTokenRefresher.test.ts delete mode 100644 __test__/auth/AccessTokenService.test.ts create mode 100644 __test__/auth/AuthjsOAuthTokenRepository.test.ts delete mode 100644 __test__/auth/CompositeLogInHandler.test.ts delete mode 100644 __test__/auth/CredentialsTransferringLogInHandler.test.ts delete mode 100644 __test__/auth/GuestAccessTokenRepository.test.ts delete mode 100644 __test__/auth/GuestAccessTokenService.test.ts delete mode 100644 __test__/auth/HostAccessTokenService.test.ts delete mode 100644 __test__/auth/HostCredentialsTransferrer.test.ts delete mode 100644 __test__/auth/IsUserGuestReader.test.ts create mode 100644 __test__/auth/LockingAccessTokenRefresher.test.ts delete mode 100644 __test__/auth/LockingAccessTokenService.test.ts delete mode 100644 __test__/auth/OnlyStaleRefreshingAccessTokenService.test.ts create mode 100644 __test__/auth/TransferringAccessTokenReader.test.ts create mode 100644 create-tables.sql create mode 100644 drop-tables.sql create mode 100644 dump.rdb create mode 100644 src/common/db/IDB.ts create mode 100644 src/common/db/PostgreSQLDB.ts create mode 100644 src/common/db/index.ts delete mode 100644 src/common/session/ISession.ts create mode 100644 src/features/auth/data/AuthjsOAuthTokenRepository.ts delete mode 100644 src/features/auth/data/AuthjsRefreshTokenReader.ts create mode 100644 src/features/auth/domain/accessToken/AccessTokenRefresher.ts delete mode 100644 src/features/auth/domain/accessToken/AccessTokenService.ts delete mode 100644 src/features/auth/domain/accessToken/GuestAccessTokenRepository.ts delete mode 100644 src/features/auth/domain/accessToken/GuestAccessTokenService.ts delete mode 100644 src/features/auth/domain/accessToken/HostAccessTokenService.ts create mode 100644 src/features/auth/domain/accessToken/IAccessTokenRefresher.ts delete mode 100644 src/features/auth/domain/accessToken/IAccessTokenService.ts create mode 100644 src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts delete mode 100644 src/features/auth/domain/accessToken/LockingAccessTokenService.ts delete mode 100644 src/features/auth/domain/accessToken/OnlyStaleRefreshingAccessTokenService.ts create mode 100644 src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts delete mode 100644 src/features/auth/domain/credentialsTransfer/GuestCredentialsTransferrer.ts delete mode 100644 src/features/auth/domain/credentialsTransfer/HostCredentialsTransferrer.ts delete mode 100644 src/features/auth/domain/credentialsTransfer/ICredentialsTransferrer.ts delete mode 100644 src/features/auth/domain/credentialsTransfer/index.ts delete mode 100644 src/features/auth/domain/guest/IIsUserGuestReader.ts delete mode 100644 src/features/auth/domain/guest/IsUserGuestReader.ts delete mode 100644 src/features/auth/domain/guest/index.ts delete mode 100644 src/features/auth/domain/logIn/CompositeLogInHandler.ts delete mode 100644 src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts delete mode 100644 src/features/auth/domain/logIn/ILogInHandler.ts delete mode 100644 src/features/auth/domain/logIn/index.ts delete mode 100644 src/middleware.ts create mode 100644 types/@next-auth.d.ts diff --git a/__test__/auth/AccessTokenRefresher.test.ts b/__test__/auth/AccessTokenRefresher.test.ts new file mode 100644 index 00000000..b156fc41 --- /dev/null +++ b/__test__/auth/AccessTokenRefresher.test.ts @@ -0,0 +1,166 @@ +import { AccessTokenRefresher, OAuthToken } from "../../src/features/auth/domain" + +test("It refreshes OAuth token using stored refresh token", async () => { + let usedRefreshToken: string | undefined + const sut = new AccessTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oAuthTokenRefresher: { + async refreshOAuthToken(refreshToken) { + usedRefreshToken = refreshToken + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + oAuthTokenRepository: { + async get() { + return { + accessToken: "oldAccessToken", + refreshToken: "oldRefreshToken" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshAccessToken("oldAccessToken") + expect(usedRefreshToken).toBe("oldRefreshToken") +}) + +test("It stores the new OAuth token for the user", async () => { + let storedUserId: string | undefined + let storedOAuthToken: OAuthToken | undefined + const sut = new AccessTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oAuthTokenRefresher: { + async refreshOAuthToken() { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } + } + }, + oAuthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(userId, oAuthToken) { + storedUserId = userId + storedOAuthToken = oAuthToken + }, + async delete() {} + } + }) + await sut.refreshAccessToken("foo") + expect(storedUserId).toBe("1234") + expect(storedOAuthToken?.accessToken).toBe("newAccessToken") + expect(storedOAuthToken?.refreshToken).toBe("newRefreshToken") +}) + +test("It refreshes the access token when the input access token is equal to the stored access token", async () => { + let didRefreshAccessToken = false + const sut = new AccessTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oAuthTokenRefresher: { + async refreshOAuthToken() { + didRefreshAccessToken = true + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + oAuthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshAccessToken("foo") + expect(didRefreshAccessToken).toBeTruthy() +}) + +test("It skips refreshing the access token when the input access token is not equal to the stored access token", async () => { + let didRefreshAccessToken = false + const sut = new AccessTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oAuthTokenRefresher: { + async refreshOAuthToken() { + didRefreshAccessToken = true + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + oAuthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshAccessToken("outdated") + expect(didRefreshAccessToken).toBeFalsy() +}) + +test("It reads user ID", async () => { + let didReadUserId = false + const sut = new AccessTokenRefresher({ + userIdReader: { + async getUserId() { + didReadUserId = true + return "1234" + } + }, + oAuthTokenRefresher: { + async refreshOAuthToken() { + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + oAuthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshAccessToken("foo") + expect(didReadUserId).toBeTruthy() +}) diff --git a/__test__/auth/AccessTokenService.test.ts b/__test__/auth/AccessTokenService.test.ts deleted file mode 100644 index 9c0024c5..00000000 --- a/__test__/auth/AccessTokenService.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { AccessTokenService } from "../../src/features/auth/domain" - -test("It reads the access token for a guest user", async () => { - let didReadAccessToken = false - const sut = new AccessTokenService({ - isGuestReader: { - async getIsGuest() { - return true - } - }, - guestAccessTokenService: { - async getAccessToken() { - didReadAccessToken = true - return "oldAccessToken" - }, - async refreshAccessToken() { - return "newAccessToken" - } - }, - hostAccessTokenService: { - async getAccessToken() { - return "oldAccessToken" - }, - async refreshAccessToken() { - return "newAccessToken" - } - } - }) - await sut.getAccessToken() - expect(didReadAccessToken).toBeTruthy() -}) - -test("It refreshes the access token for a guest user", async () => { - let didRefreshAccessToken = false - const sut = new AccessTokenService({ - isGuestReader: { - async getIsGuest() { - return true - } - }, - guestAccessTokenService: { - async getAccessToken() { - return "oldAccessToken" - }, - async refreshAccessToken() { - didRefreshAccessToken = true - return "newAccessToken" - } - }, - hostAccessTokenService: { - async getAccessToken() { - return "oldAccessToken" - }, - async refreshAccessToken() { - return "newAccessToken" - } - } - }) - await sut.refreshAccessToken("oldAccessToken") - expect(didRefreshAccessToken).toBeTruthy() -}) - -test("It reads the access token for a host user", async () => { - let didReadAccessToken = false - const sut = new AccessTokenService({ - isGuestReader: { - async getIsGuest() { - return false - } - }, - guestAccessTokenService: { - async getAccessToken() { - return "oldAccessToken" - }, - async refreshAccessToken() { - return "newAccessToken" - } - }, - hostAccessTokenService: { - async getAccessToken() { - didReadAccessToken = true - return "oldAccessToken" - }, - async refreshAccessToken() { - return "newAccessToken" - } - } - }) - await sut.getAccessToken() - expect(didReadAccessToken).toBeTruthy() -}) - -test("It refreshes the access token for a host user", async () => { - let didRefreshAccessToken = false - const sut = new AccessTokenService({ - isGuestReader: { - async getIsGuest() { - return false - } - }, - guestAccessTokenService: { - async getAccessToken() { - return "oldAccessToken" - }, - async refreshAccessToken() { - return "newAccessToken" - } - }, - hostAccessTokenService: { - async getAccessToken() { - return "oldAccessToken" - }, - async refreshAccessToken() { - didRefreshAccessToken = true - return "newAccessToken" - } - } - }) - await sut.refreshAccessToken("oldAccessToken") - expect(didRefreshAccessToken).toBeTruthy() -}) diff --git a/__test__/auth/AuthjsOAuthTokenRepository.test.ts b/__test__/auth/AuthjsOAuthTokenRepository.test.ts new file mode 100644 index 00000000..5579aee4 --- /dev/null +++ b/__test__/auth/AuthjsOAuthTokenRepository.test.ts @@ -0,0 +1,75 @@ +import { AuthjsOAuthTokenRepository } from "../../src/features/auth/data" + +test("It queries token for user ID and provider", async () => { + let queriedUserId: string | undefined + let queriedProvider: string | undefined + const sut = new AuthjsOAuthTokenRepository({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query, values: any[] = []) { + queriedUserId = values[0] + queriedProvider = values[1] + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + await sut.get("1234") + expect(queriedUserId).toEqual("1234") + expect(queriedProvider).toEqual("github") +}) + +test("It cannot update tokens", async () => { + const sut = new AuthjsOAuthTokenRepository({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query() { + return { rows: [] } + } + } + }) + expect(sut.set("1234", { + accessToken: "foo", + refreshToken: "bar" + })).rejects.toThrow() +}) + +test("It cannot delete tokens", async () => { + const sut = new AuthjsOAuthTokenRepository({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query() { + return { rows: [] } + } + } + }) + expect(sut.delete("1234")).rejects.toThrow() +}) diff --git a/__test__/auth/CompositeLogInHandler.test.ts b/__test__/auth/CompositeLogInHandler.test.ts deleted file mode 100644 index c342ffbb..00000000 --- a/__test__/auth/CompositeLogInHandler.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { CompositeLogInHandler } from "../../src/features/auth/domain" - -test("It invokes all log in handlers for user", async () => { - let userId1: string | undefined - let userId2: string | undefined - let userId3: string | undefined - const sut = new CompositeLogInHandler([{ - async handleLogIn(userId) { - userId1 = userId - } - }, { - async handleLogIn(userId) { - userId2 = userId - } - }, { - async handleLogIn(userId) { - userId3 = userId - } - }]) - await sut.handleLogIn("1234") - expect(userId1).toEqual("1234") - expect(userId2).toEqual("1234") - expect(userId3).toEqual("1234") -}) diff --git a/__test__/auth/CredentialsTransferringLogInHandler.test.ts b/__test__/auth/CredentialsTransferringLogInHandler.test.ts deleted file mode 100644 index f1061b40..00000000 --- a/__test__/auth/CredentialsTransferringLogInHandler.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { CredentialsTransferringLogInHandler } from "../../src/features/auth/domain" - -test("It transfers credentials for guest", async () => { - let didTransferGuestCredentials = false - const sut = new CredentialsTransferringLogInHandler({ - isUserGuestReader: { - async getIsUserGuest() { - return true - } - }, - guestCredentialsTransferrer: { - async transferCredentials() { - didTransferGuestCredentials = true - } - }, - hostCredentialsTransferrer: { - async transferCredentials() {} - } - }) - await sut.handleLogIn("1234") - expect(didTransferGuestCredentials).toBeTruthy() -}) - -test("It transfers credentials for host", async () => { - let didTransferHostCredentials = false - const sut = new CredentialsTransferringLogInHandler({ - isUserGuestReader: { - async getIsUserGuest() { - return false - } - }, - guestCredentialsTransferrer: { - async transferCredentials() {} - }, - hostCredentialsTransferrer: { - async transferCredentials() { - didTransferHostCredentials = true - } - } - }) - await sut.handleLogIn("1234") - expect(didTransferHostCredentials).toBeTruthy() -}) diff --git a/__test__/auth/GuestAccessTokenRepository.test.ts b/__test__/auth/GuestAccessTokenRepository.test.ts deleted file mode 100644 index 5c089cd3..00000000 --- a/__test__/auth/GuestAccessTokenRepository.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { GuestAccessTokenRepository } from "../../src/features/auth/domain" - -test("It reads access token for user", async () => { - let readUserId: string | undefined - const sut = new GuestAccessTokenRepository({ - async get(userId) { - readUserId = userId - return "foo" - }, - async setExpiring() {}, - async delete() {} - }) - const accessToken = await sut.get("1234") - expect(readUserId).toBe("1234") - expect(accessToken).toBe("foo") -}) - -test("It stores access token for user", async () => { - let storedUserId: string | undefined - let storedToken: string | undefined - let storedTimeToLive: number | undefined - const sut = new GuestAccessTokenRepository({ - async get() { - return "foo" - }, - async setExpiring(userId, token, timeToLive) { - storedUserId = userId - storedToken = token - storedTimeToLive = timeToLive - }, - async delete(userId) {} - }) - await sut.set("1234", "bar") - expect(storedUserId).toBe("1234") - expect(storedToken).toBe("bar") - expect(storedTimeToLive).toBeGreaterThan(0) -}) - -test("It deletes access token for user", async () => { - let deletedUserId: string | undefined - const sut = new GuestAccessTokenRepository({ - async get() { - return "foo" - }, - async setExpiring() {}, - async delete(userId) { - deletedUserId = userId - } - }) - await sut.delete("1234") - expect(deletedUserId).toBe("1234") -}) diff --git a/__test__/auth/GuestAccessTokenService.test.ts b/__test__/auth/GuestAccessTokenService.test.ts deleted file mode 100644 index 407ac26e..00000000 --- a/__test__/auth/GuestAccessTokenService.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { GuestAccessTokenService } from "../../src/features/auth/domain" - -test("It gets the access token for the user", async () => { - let readUserId: string | undefined - const sut = new GuestAccessTokenService({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - repository: { - async get(userId) { - readUserId = userId - return "foo" - }, - async set() {} - }, - dataSource: { - async getAccessToken() { - return "foo" - } - } - }) - const accessToken = await sut.getAccessToken() - expect(readUserId).toBe("1234") - expect(accessToken).toBe("foo") -}) diff --git a/__test__/auth/HostAccessTokenService.test.ts b/__test__/auth/HostAccessTokenService.test.ts deleted file mode 100644 index 79fcaf72..00000000 --- a/__test__/auth/HostAccessTokenService.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { HostAccessTokenService } from "../../src/features/auth/domain" -import { OAuthToken } from "../../src/features/auth/domain" - -test("It gets the access token for the user", async () => { - let readUserID: string | undefined - const sut = new HostAccessTokenService({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - repository: { - async get(userId) { - readUserID = userId - return { accessToken: "foo", refreshToken: "bar" } - }, - async set() {}, - async delete() {}, - }, - refresher: { - async refreshOAuthToken() { - return { accessToken: "foo", refreshToken: "bar" } - } - } - }) - const accessToken = await sut.getAccessToken() - expect(readUserID).toBe("1234") - expect(accessToken).toBe("foo") -}) - -test("It refreshes OAuth using stored refresh token", async () => { - let usedRefreshToken: string | undefined - const sut = new HostAccessTokenService({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - repository: { - async get() { - return { accessToken: "oldAccessToken", refreshToken: "oldRefreshToken" } - }, - async set() {}, - async delete() {}, - }, - refresher: { - async refreshOAuthToken(refreshToken) { - usedRefreshToken = refreshToken - return { accessToken: "newAccessToken", refreshToken: "newRefreshToken" } - } - } - }) - await sut.refreshAccessToken("oldAccessToken") - expect(usedRefreshToken).toBe("oldRefreshToken") -}) - -test("It stores the new OAuth token for the user", async () => { - let storedUserId: string | undefined - let storedOAuthToken: OAuthToken | undefined - const sut = new HostAccessTokenService({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - repository: { - async get() { - return { accessToken: "oldAccessToken", refreshToken: "oldRefreshToken" } - }, - async set(userId, oAuthToken) { - storedUserId = userId - storedOAuthToken = oAuthToken - }, - async delete() {}, - }, - refresher: { - async refreshOAuthToken() { - return { accessToken: "newAccessToken", refreshToken: "newRefreshToken" } - } - } - }) - await sut.refreshAccessToken("foo") - expect(storedUserId).toBe("1234") - expect(storedOAuthToken?.accessToken).toBe("newAccessToken") - expect(storedOAuthToken?.refreshToken).toBe("newRefreshToken") -}) diff --git a/__test__/auth/HostCredentialsTransferrer.test.ts b/__test__/auth/HostCredentialsTransferrer.test.ts deleted file mode 100644 index a5988aa8..00000000 --- a/__test__/auth/HostCredentialsTransferrer.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { HostCredentialsTransferrer } from "../../src/features/auth/domain" -import { OAuthToken } from "../../src/features/auth/domain" - -test("It fetches refresh token for specified user", async () => { - let fetchedUserId: string | undefined - const sut = new HostCredentialsTransferrer({ - refreshTokenReader: { - async getRefreshToken(userId) { - fetchedUserId = userId - return "" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken() { - return { accessToken: "foo", refreshToken: "bar" } - } - }, - oAuthTokenRepository: { - async get() { - return { accessToken: "foo", refreshToken: "bar" } - }, - async set() {}, - async delete() {} - } - }) - await sut.transferCredentials("123") - expect(fetchedUserId).toBe("123") -}) - -test("It refreshes the fetched refresh token", async () => { - let refreshedRefreshToken: string | undefined - const sut = new HostCredentialsTransferrer({ - refreshTokenReader: { - async getRefreshToken() { - return "helloworld" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken(refreshToken) { - refreshedRefreshToken = refreshToken - return { accessToken: "foo", refreshToken: "bar" } - } - }, - oAuthTokenRepository: { - async get() { - return { accessToken: "foo", refreshToken: "bar" } - }, - async set() {}, - async delete() {} - } - }) - await sut.transferCredentials("123") - expect(refreshedRefreshToken).toBe("helloworld") -}) - -test("It stores the refreshed auth token for the user", async () => { - let storedAuthToken: OAuthToken | undefined - let storedUserId: string | undefined - const sut = new HostCredentialsTransferrer({ - refreshTokenReader: { - async getRefreshToken() { - return "helloworld" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken() { - return { accessToken: "foo", refreshToken: "bar" } - } - }, - oAuthTokenRepository: { - async get() { - return { accessToken: "foo", refreshToken: "bar" } - }, - async set(userId, token) { - storedAuthToken = token - storedUserId = userId - }, - async delete() {} - } - }) - await sut.transferCredentials("123") - expect(storedAuthToken?.accessToken).toBe("foo") - expect(storedAuthToken?.refreshToken).toBe("bar") - expect(storedUserId).toBe("123") -}) \ No newline at end of file diff --git a/__test__/auth/IsUserGuestReader.test.ts b/__test__/auth/IsUserGuestReader.test.ts deleted file mode 100644 index 426c7814..00000000 --- a/__test__/auth/IsUserGuestReader.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsUserGuestReader } from "../../src/features/auth/domain" - -test("It considers no user to be a guest", async () => { - const sut = new IsUserGuestReader() - const isGuestA = await sut.getIsUserGuest("foo") - const isGuestB = await sut.getIsUserGuest("bar") - const isGuestC = await sut.getIsUserGuest("hello") - expect(isGuestA).toBeFalsy() - expect(isGuestB).toBeFalsy() - expect(isGuestC).toBeFalsy() -}) diff --git a/__test__/auth/LockingAccessTokenRefresher.test.ts b/__test__/auth/LockingAccessTokenRefresher.test.ts new file mode 100644 index 00000000..f70640b0 --- /dev/null +++ b/__test__/auth/LockingAccessTokenRefresher.test.ts @@ -0,0 +1,69 @@ +import { LockingAccessTokenRefresher } from "../../src/features/auth/domain" + +test("It acquires a lock", async () => { + let didAcquireLock = false + const sut = new LockingAccessTokenRefresher({ + mutexFactory: { + async makeMutex() { + return { + async acquire() { + didAcquireLock = true + }, + async release() {} + } + } + }, + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } + } + }) + await sut.refreshAccessToken("bar") + expect(didAcquireLock).toBeTruthy() +}) + +test("It releases the acquired lock", async () => { + let didReleaseLock = false + const sut = new LockingAccessTokenRefresher({ + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() { + didReleaseLock = true + } + } + } + }, + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } + } + }) + await sut.refreshAccessToken("bar") + expect(didReleaseLock).toBeTruthy() +}) + +test("It refreshes access token", async () => { + let didRefreshAccessToken = false + const sut = new LockingAccessTokenRefresher({ + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() {} + } + } + }, + accessTokenRefresher: { + async refreshAccessToken() { + didRefreshAccessToken = true + return "foo" + } + } + }) + await sut.refreshAccessToken("foo") + expect(didRefreshAccessToken).toBeTruthy() +}) diff --git a/__test__/auth/LockingAccessTokenService.test.ts b/__test__/auth/LockingAccessTokenService.test.ts deleted file mode 100644 index 30996afb..00000000 --- a/__test__/auth/LockingAccessTokenService.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { LockingAccessTokenService } from "../../src/features/auth/domain" - -test("It reads access token", async () => { - let didReadAccessToken = false - const sut = new LockingAccessTokenService({ - async makeMutex() { - return { - async acquire() {}, - async release() {} - } - } - }, { - async getAccessToken() { - didReadAccessToken = true - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }) - await sut.getAccessToken() - expect(didReadAccessToken).toBeTruthy() -}) - -test("It acquires a lock", async () => { - let didAcquireLock = false - const sut = new LockingAccessTokenService({ - async makeMutex() { - return { - async acquire() { - didAcquireLock = true - }, - async release() {} - } - } - }, { - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }) - await sut.refreshAccessToken("bar") - expect(didAcquireLock).toBeTruthy() -}) - -test("It releases the acquired lock", async () => { - let didReleaseLock = false - const sut = new LockingAccessTokenService({ - async makeMutex() { - return { - async acquire() {}, - async release() { - didReleaseLock = true - } - } - } - }, { - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }) - await sut.refreshAccessToken("bar") - expect(didReleaseLock).toBeTruthy() -}) - -test("It refreshes access token", async () => { - let didRefreshAccessToken = false - const sut = new LockingAccessTokenService({ - async makeMutex() { - return { - async acquire() {}, - async release() {} - } - } - }, { - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - didRefreshAccessToken = true - return "foo" - } - }) - await sut.refreshAccessToken("foo") - expect(didRefreshAccessToken).toBeTruthy() -}) diff --git a/__test__/auth/OAuthTokenRepository.test.ts b/__test__/auth/OAuthTokenRepository.test.ts index 2bcac8a0..baefd102 100644 --- a/__test__/auth/OAuthTokenRepository.test.ts +++ b/__test__/auth/OAuthTokenRepository.test.ts @@ -3,56 +3,80 @@ import { OAuthTokenRepository } from "../../src/features/auth/domain" test("It reads the auth token for the specified user", async () => { let readUserId: string | undefined const sut = new OAuthTokenRepository({ - async get(userId) { - readUserId = userId - return JSON.stringify({ - accessToken: "foo", - refreshToken: "bar" - }) - }, - async set() {}, - async setExpiring() {}, - async delete() {} + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query: string, values: any[] = []) { + readUserId = values[0] + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } }) - await sut.get("1234") + const token = await sut.get("1234") expect(readUserId).toBe("1234") + expect(token.accessToken).toBe("foo") + expect(token.refreshToken).toBe("bar") }) test("It stores the auth token for the specified user", async () => { let storedUserId: string | undefined - let storedJSON: any | undefined + let storedAccessToken: any | undefined + let storedRefreshToken: any | undefined const sut = new OAuthTokenRepository({ - async get() { - return "" - }, - async set() {}, - async setExpiring(userId, data) { - storedUserId = userId - storedJSON = data - }, - async delete() {} + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query: string, values: any[] = []) { + storedUserId = values[0] + storedAccessToken = values[1] + storedRefreshToken = values[2] + return { rows: [] } + } + } }) const authToken = { accessToken: "foo", refreshToken: "bar" } await sut.set("1234", authToken) - const storedObj = JSON.parse(storedJSON) expect(storedUserId).toBe("1234") - expect(storedObj.accessToken).toBe(authToken.accessToken) - expect(storedObj.refreshToken).toBe(authToken.refreshToken) + expect(storedAccessToken).toBe(authToken.accessToken) + expect(storedRefreshToken).toBe(authToken.refreshToken) }) test("It deletes the auth token for the specified user", async () => { let deletedUserId: string | undefined const sut = new OAuthTokenRepository({ - async get() { - return "" - }, - async set() {}, - async setExpiring() {}, - async delete(userId) { - deletedUserId = userId + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query: string, values: any[] = []) { + deletedUserId = values[0] + return { rows: [] } + } } }) await sut.delete("1234") diff --git a/__test__/auth/OnlyStaleRefreshingAccessTokenService.test.ts b/__test__/auth/OnlyStaleRefreshingAccessTokenService.test.ts deleted file mode 100644 index f6dedfda..00000000 --- a/__test__/auth/OnlyStaleRefreshingAccessTokenService.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { OnlyStaleRefreshingAccessTokenService } from "../../src/features/auth/domain" - -test("It refreshes the access token when the input access token is equal to the stored access token", async () => { - let didRefreshAccessToken = false - const sut = new OnlyStaleRefreshingAccessTokenService({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - didRefreshAccessToken = true - return "foo" - } - }) - await sut.refreshAccessToken("foo") - expect(didRefreshAccessToken).toBeTruthy() -}) - -test("It skips refreshing the access token when the input access token is not equal to the stored access token", async () => { - let didRefreshAccessToken = false - const sut = new OnlyStaleRefreshingAccessTokenService({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - didRefreshAccessToken = true - return "foo" - } - }) - await sut.refreshAccessToken("outdated") - expect(didRefreshAccessToken).toBeFalsy() -}) - -test("It reads access token", async () => { - let didReadAccessToken = false - const sut = new OnlyStaleRefreshingAccessTokenService({ - async getAccessToken() { - didReadAccessToken = true - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }) - await sut.getAccessToken() - expect(didReadAccessToken).toBeTruthy() -}) \ No newline at end of file diff --git a/__test__/auth/TransferringAccessTokenReader.test.ts b/__test__/auth/TransferringAccessTokenReader.test.ts new file mode 100644 index 00000000..03636e6c --- /dev/null +++ b/__test__/auth/TransferringAccessTokenReader.test.ts @@ -0,0 +1,141 @@ +import { TransferringAccessTokenReader } from "../../src/features/auth/domain" + +test("It reads user ID", async () => { + let didReadUserId = false + const sut = new TransferringAccessTokenReader({ + userIdReader: { + async getUserId() { + didReadUserId = true + return "1234" + }, + }, + sourceOAuthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + }, + destinationOAuthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.getAccessToken() + expect(didReadUserId).toBeTruthy() +}) + +test("It skips reading from source repository if a token was found in the destination repository", async () => { + let didReadOAuthTokenFromSource = false + let didReadOAuthTokenFromDestination = false + const sut = new TransferringAccessTokenReader({ + userIdReader: { + async getUserId() { + return "1234" + }, + }, + sourceOAuthTokenRepository: { + async get() { + didReadOAuthTokenFromSource = true + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + }, + destinationOAuthTokenRepository: { + async get() { + didReadOAuthTokenFromDestination = true + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.getAccessToken() + expect(didReadOAuthTokenFromSource).toBeFalsy() + expect(didReadOAuthTokenFromDestination).toBeTruthy() +}) + +test("It reads from source repository if no token was found in destination repository", async () => { + let didReadOAuthTokenFromSource = false + let didReadOAuthTokenFromDestination = false + const sut = new TransferringAccessTokenReader({ + userIdReader: { + async getUserId() { + return "1234" + }, + }, + sourceOAuthTokenRepository: { + async get() { + didReadOAuthTokenFromSource = true + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + }, + destinationOAuthTokenRepository: { + async get() { + didReadOAuthTokenFromDestination = true + throw new Error("No token found") + }, + async set() {}, + async delete() {} + } + }) + await sut.getAccessToken() + expect(didReadOAuthTokenFromSource).toBeTruthy() + expect(didReadOAuthTokenFromDestination).toBeTruthy() +}) + +test("It stores token read from source repository in destination repository", async () => { + let storedAccessToken: string | undefined + let storedRefreshToken: string | undefined + const sut = new TransferringAccessTokenReader({ + userIdReader: { + async getUserId() { + return "1234" + }, + }, + sourceOAuthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + }, + destinationOAuthTokenRepository: { + async get() { + throw new Error("No token found") + }, + async set(_userId, token) { + storedAccessToken = token.accessToken + storedRefreshToken = token.refreshToken + }, + async delete() {} + } + }) + await sut.getAccessToken() + expect(storedAccessToken).toEqual("foo") + expect(storedRefreshToken).toEqual("bar") +}) diff --git a/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts b/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts index 0345ea9c..42c405d1 100644 --- a/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts +++ b/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts @@ -9,26 +9,31 @@ import { test("It forwards a GraphQL request", async () => { let forwardedRequest: GraphQLQueryRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }, { - async graphql(request: GraphQLQueryRequest) { - forwardedRequest = request - return {} - }, - async getRepositoryContent() { - return { downloadURL: "https://example.com" } + accessTokenReader: { + async getAccessToken() { + return "foo" + } }, - async getPullRequestComments() { - return [] + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } }, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } + gitHubClient: { + async graphql(request: GraphQLQueryRequest) { + forwardedRequest = request + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } } }) const request: GraphQLQueryRequest = { @@ -42,26 +47,31 @@ test("It forwards a GraphQL request", async () => { test("It forwards a request to get the repository content", async () => { let forwardedRequest: GetRepositoryContentRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }, { - async graphql() { - return {} - }, - async getRepositoryContent(request: GetRepositoryContentRequest) { - forwardedRequest = request - return { downloadURL: "https://example.com" } + accessTokenReader: { + async getAccessToken() { + return "foo" + } }, - async getPullRequestComments() { - return [] + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } }, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent(request: GetRepositoryContentRequest) { + forwardedRequest = request + return { downloadURL: "https://example.com" } + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } } }) const request: GetRepositoryContentRequest = { @@ -77,26 +87,31 @@ test("It forwards a request to get the repository content", async () => { test("It forwards a request to get comments to a pull request", async () => { let forwardedRequest: GetPullRequestCommentsRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }, { - async graphql() { - return {} - }, - async getRepositoryContent() { - return { downloadURL: "https://example.com" } + accessTokenReader: { + async getAccessToken() { + return "foo" + } }, - async getPullRequestComments(request: GetPullRequestCommentsRequest) { - forwardedRequest = request - return [] + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } }, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestComments(request: GetPullRequestCommentsRequest) { + forwardedRequest = request + return [] + }, + async addCommentToPullRequest() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } } }) const request: GetPullRequestCommentsRequest = { @@ -112,27 +127,32 @@ test("It forwards a request to get comments to a pull request", async () => { test("It forwards a request to add a comment to a pull request", async () => { let forwardedRequest: AddCommentToPullRequestRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }, { - async graphql() { - return {} - }, - async getRepositoryContent() { - return { downloadURL: "https://example.com" } - }, - async getPullRequestComments() { - return [] + accessTokenReader: { + async getAccessToken() { + return "foo" + } }, - async addCommentToPullRequest(request: AddCommentToPullRequestRequest) { - forwardedRequest = request + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } }, - async getOrganizationMembershipStatus() { - return { state: "active" } + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest(request: AddCommentToPullRequestRequest) { + forwardedRequest = request + }, + async getOrganizationMembershipStatus() { + return { state: "active" } + } } }) const request: AddCommentToPullRequestRequest = { @@ -150,30 +170,35 @@ test("It retries with a refreshed access token when receiving HTTP 401", async ( let didRefreshAccessToken = false let didRespondWithAuthorizationError = false const sut = new AccessTokenRefreshingGitHubClient({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - didRefreshAccessToken = true - return "foo" - } - }, { - async graphql() { - if (!didRespondWithAuthorizationError) { - didRespondWithAuthorizationError = true - throw { status: 401 } + accessTokenReader: { + async getAccessToken() { + return "foo" } - return [] }, - async getRepositoryContent() { - return { downloadURL: "https://example.com" } - }, - async getPullRequestComments() { - return [] + accessTokenRefresher: { + async refreshAccessToken() { + didRefreshAccessToken = true + return "foo" + } }, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } + gitHubClient: { + async graphql() { + if (!didRespondWithAuthorizationError) { + didRespondWithAuthorizationError = true + throw { status: 401 } + } + return [] + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } } }) const request: GraphQLQueryRequest = { @@ -187,26 +212,31 @@ test("It retries with a refreshed access token when receiving HTTP 401", async ( test("It only retries a request once when receiving HTTP 401", async () => { let requestCount = 0 const sut = new AccessTokenRefreshingGitHubClient({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }, { - async graphql() { - requestCount += 1 - throw { status: 401 } - }, - async getRepositoryContent() { - return { downloadURL: "https://example.com" } + accessTokenReader: { + async getAccessToken() { + return "foo" + } }, - async getPullRequestComments() { - return [] + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } }, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } + gitHubClient: { + async graphql() { + requestCount += 1 + throw { status: 401 } + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } } }) const request: GraphQLQueryRequest = { @@ -224,25 +254,30 @@ test("It only retries a request once when receiving HTTP 401", async () => { test("It does not refresh an access token when the initial request was successful", async () => { let didRefreshAccesstoken = false const sut = new AccessTokenRefreshingGitHubClient({ - async getAccessToken() { - return "foo" - }, - async refreshAccessToken() { - return "foo" - } - }, { - async graphql() { - return [] - }, - async getRepositoryContent() { - return { downloadURL: "https://example.com" } + accessTokenReader: { + async getAccessToken() { + return "foo" + } }, - async getPullRequestComments() { - return [] + accessTokenRefresher: { + async refreshAccessToken() { + return "foo" + } }, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } + gitHubClient: { + async graphql() { + return [] + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } } }) const request: GraphQLQueryRequest = { diff --git a/create-tables.sql b/create-tables.sql new file mode 100644 index 00000000..78fbbd92 --- /dev/null +++ b/create-tables.sql @@ -0,0 +1,56 @@ +CREATE TABLE verification_token +( + identifier TEXT NOT NULL, + expires TIMESTAMPTZ NOT NULL, + token TEXT NOT NULL, + + PRIMARY KEY (identifier, token) +); + +CREATE TABLE accounts +( + id SERIAL, + "userId" INTEGER NOT NULL, + type VARCHAR(255) NOT NULL, + provider VARCHAR(255) NOT NULL, + "providerAccountId" VARCHAR(255) NOT NULL, + refresh_token TEXT, + access_token TEXT, + expires_at BIGINT, + id_token TEXT, + scope TEXT, + session_state TEXT, + token_type TEXT, + + PRIMARY KEY (id) +); + +CREATE TABLE sessions +( + id SERIAL, + "userId" INTEGER NOT NULL, + expires TIMESTAMPTZ NOT NULL, + "sessionToken" VARCHAR(255) NOT NULL, + + PRIMARY KEY (id) +); + +CREATE TABLE users +( + id SERIAL, + name VARCHAR(255), + email VARCHAR(255), + "emailVerified" TIMESTAMPTZ, + image TEXT, + + PRIMARY KEY (id) +); + +CREATE TABLE access_tokens +( + user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, + access_token VARCHAR(255) NOT NULL, + refresh_token VARCHAR(255) NOT NULL, + + PRIMARY KEY (user_id) +); diff --git a/drop-tables.sql b/drop-tables.sql new file mode 100644 index 00000000..35bf3d95 --- /dev/null +++ b/drop-tables.sql @@ -0,0 +1,5 @@ +DROP TABLE access_tokens; +DROP TABLE verification_token; +DROP TABLE accounts; +DROP TABLE sessions; +DROP TABLE users; diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 0000000000000000000000000000000000000000..681fbf7717462d79e47c59c8b1cc70c5ac6ebdaf GIT binary patch literal 9241 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.0.0" } }, + "node_modules/@auth/core": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.26.2.tgz", + "integrity": "sha512-BK+NaUDLcX8AMW/O32wxzk+SE9lr/xvjO4PsTgla9ToHHanoPMl+6pYu2/WoNKnZomdHBWOl/00LFfajuulIVA==", + "dev": true, + "dependencies": { + "@panva/hkdf": "^1.1.1", + "@types/cookie": "0.6.0", + "cookie": "0.6.0", + "jose": "^5.1.3", + "oauth4webapi": "^2.4.0", + "preact": "10.11.3", + "preact-render-to-string": "5.2.3" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.1", + "nodemailer": "^6.8.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/@auth/core/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@auth/core/node_modules/jose": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.1.tgz", + "integrity": "sha512-qiaQhtQRw6YrOaOj0v59h3R6hUY9NvxBmmnMfKemkqYmBB0tEc97NbLP7ix44VP5p9/0YHG8Vyhzuo5YBNwviA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@auth/core/node_modules/preact": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", + "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@auth/core/node_modules/preact-render-to-string": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", + "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", + "dev": true, + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/@auth/core/node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", + "dev": true + }, + "node_modules/@auth/pg-adapter": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@auth/pg-adapter/-/pg-adapter-0.5.2.tgz", + "integrity": "sha512-QEC9ZbBAl1jeP0Uf9kdJa717ToH+yIKfWQj1/q7CavnQVFkKX+vGx9mIOHmU5rlFRzG+PwXXT/PKNyXxDuFTRA==", + "dev": true, + "dependencies": { + "@auth/core": "0.26.2" + }, + "peerDependencies": { + "pg": "^8" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -3988,6 +4080,12 @@ "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz", "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==" }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", @@ -4109,6 +4207,74 @@ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==" }, + "node_modules/@types/pg": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.0.tgz", + "integrity": "sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@types/pg/node_modules/pg-types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "dev": true, + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/pg/node_modules/postgres-array": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "dev": true, + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/pg/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@types/prop-types": { "version": "15.7.10", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", @@ -5226,6 +5392,15 @@ "dev": true, "peer": true }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -13417,6 +13592,15 @@ "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, + "node_modules/oauth4webapi": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.10.2.tgz", + "integrity": "sha512-ib0x1f4tCaZkTEEnRpkt96D8F2e38AFWzTOwpha1Wmme5kD+RFFgDVkrXyBSxBefFeQUoODVaieS/w9QmkZbnQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -13547,6 +13731,12 @@ "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, "node_modules/octokit": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz", @@ -13726,6 +13916,12 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13885,6 +14081,106 @@ "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz", "integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==" }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dev": true, + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "dev": true, + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "dev": true + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "dev": true, + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==", + "dev": true + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dev": true, + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dev": true, + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -14136,6 +14432,51 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-range": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "dev": true + }, "node_modules/postman-collection": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.3.0.tgz", @@ -15582,6 +15923,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", diff --git a/package.json b/package.json index 6d65a00f..c9f2eb1b 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,10 @@ "zod": "^3.22.4" }, "devDependencies": { + "@auth/pg-adapter": "^0.5.2", "@types/jest": "^29.5.8", "@types/node": "^20.9.2", + "@types/pg": "^8.11.0", "@types/react": "^18", "@types/react-dom": "^18", "@types/swagger-ui-react": "^4.18.2", @@ -57,6 +59,7 @@ "autoprefixer": "^10", "eslint": "^8.56.0", "eslint-config-next": "14.0.3", + "pg": "^8.11.3", "postcss": "^8", "tailwindcss": "^3", "ts-jest": "^29.1.1", diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index ec521255..2d251c9c 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,14 +1,6 @@ import NextAuth from "next-auth" -import GithubProvider from "next-auth/providers/github" +import { authOptions } from "@/composition" -const handler = NextAuth({ - secret: process.env.NEXTAUTH_SECRET, - providers: [ - GithubProvider({ - clientId: process.env.GITHUB_CLIENT_ID, - clientSecret: process.env.GITHUB_CLIENT_SECRET - }) - ] -}) +const handler = NextAuth(authOptions) export { handler as GET, handler as POST } diff --git a/src/app/api/blob/[owner]/[repository]/[...path]/route.ts b/src/app/api/blob/[owner]/[repository]/[...path]/route.ts index 2dd2d532..ad91bf3d 100644 --- a/src/app/api/blob/[owner]/[repository]/[...path]/route.ts +++ b/src/app/api/blob/[owner]/[repository]/[...path]/route.ts @@ -1,5 +1,6 @@ import { NextRequest, NextResponse } from "next/server" -import { userGitHubClient } from "@/composition" +import { session, userGitHubClient } from "@/composition" +import { makeUnauthenticatedAPIErrorResponse } from "@/common" interface GetBlobParams { owner: string @@ -8,6 +9,10 @@ interface GetBlobParams { } export async function GET(req: NextRequest, { params }: { params: GetBlobParams }) { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return makeUnauthenticatedAPIErrorResponse() + } const path = params.path.join("/") const item = await userGitHubClient.getRepositoryContent({ repositoryOwner: params.owner, diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts index 7245898c..22226634 100644 --- a/src/app/api/proxy/route.ts +++ b/src/app/api/proxy/route.ts @@ -1,7 +1,12 @@ import { NextRequest, NextResponse } from "next/server" -import { makeAPIErrorResponse } from "@/common" +import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" +import { session } from "@/composition" export async function GET(req: NextRequest) { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return makeUnauthenticatedAPIErrorResponse() + } const rawURL = req.nextUrl.searchParams.get("url") if (!rawURL) { return makeAPIErrorResponse(400, "Missing \"url\" query parameter.") diff --git a/src/app/api/user/projects/route.ts b/src/app/api/user/projects/route.ts index 8adfab66..93290bda 100644 --- a/src/app/api/user/projects/route.ts +++ b/src/app/api/user/projects/route.ts @@ -1,8 +1,17 @@ import { NextResponse } from "next/server" -import { makeAPIErrorResponse, UnauthorizedError } from "../../../../common" +import { + makeAPIErrorResponse, + UnauthorizedError, + makeUnauthenticatedAPIErrorResponse +} from "../../../../common" import { projectDataSource } from "@/composition" +import { session } from "@/composition" export async function GET() { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return makeUnauthenticatedAPIErrorResponse() + } try { const projects = await projectDataSource.getProjects() return NextResponse.json({projects}) diff --git a/src/app/api/user/repository-access/route.ts b/src/app/api/user/repository-access/route.ts index 697d4ee0..0e0b8983 100644 --- a/src/app/api/user/repository-access/route.ts +++ b/src/app/api/user/repository-access/route.ts @@ -1,8 +1,12 @@ import { NextResponse } from "next/server" -import { makeAPIErrorResponse } from "@/common" +import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" import { session, guestRepositoryAccessReader } from "@/composition" export async function GET() { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return makeUnauthenticatedAPIErrorResponse() + } let userId: string try { userId = await session.getUserId() diff --git a/src/app/api/user/session-validity/route.ts b/src/app/api/user/session-validity/route.ts index aa5d2e97..c1084311 100644 --- a/src/app/api/user/session-validity/route.ts +++ b/src/app/api/user/session-validity/route.ts @@ -1,8 +1,12 @@ import { NextResponse } from "next/server" -import { makeAPIErrorResponse } from "@/common" +import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" import { session, delayedSessionValidator } from "@/composition" export async function GET() { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return makeUnauthenticatedAPIErrorResponse() + } try { await session.getUserId() } catch { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a382399e..9bc5e417 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,6 +5,7 @@ import { config as fontAwesomeConfig } from "@fortawesome/fontawesome-svg-core" import { CssBaseline } from "@mui/material" import ThemeRegistry from "../common/theme/ThemeRegistry" import ErrorHandler from "../common/errors/client/ErrorHandler" +import SessionBarrier from "@/features/auth/view/SessionBarrier" import "@fortawesome/fontawesome-svg-core/styles.css" fontAwesomeConfig.autoAddCss = false @@ -22,7 +23,9 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - {children} + + {children} + diff --git a/src/app/page.tsx b/src/app/page.tsx index 0d97feea..17050857 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,11 +1,6 @@ -import SessionBarrier from "@/features/auth/view/SessionBarrier" import ProjectsPage from "@/features/projects/view/ProjectsPage" import { projectRepository } from "@/composition" export default async function Page() { - return ( - - - - ) + return } diff --git a/src/common/db/IDB.ts b/src/common/db/IDB.ts new file mode 100644 index 00000000..70d29f20 --- /dev/null +++ b/src/common/db/IDB.ts @@ -0,0 +1,15 @@ +export interface IDBQueryResult { + readonly rows: any[] +} + +export interface IDBConnection { + query(query: string, values: any[]): Promise + query(query: string): Promise + disconnect(): Promise +} + +export default interface IDB { + connect(): Promise + query(query: string, values: any[]): Promise + query(query: string): Promise +} diff --git a/src/common/db/PostgreSQLDB.ts b/src/common/db/PostgreSQLDB.ts new file mode 100644 index 00000000..1b16afb8 --- /dev/null +++ b/src/common/db/PostgreSQLDB.ts @@ -0,0 +1,47 @@ +import { Pool, PoolClient } from "pg" +import IDB, { IDBConnection, IDBQueryResult } from "./IDB" + +type PostgreSQLDBConnectionConfig = { + readonly client: PoolClient +} + +export class PostgreSQLDBConnection implements IDBConnection { + private readonly client: PoolClient + + constructor(config: PostgreSQLDBConnectionConfig) { + this.client = config.client + } + + async disconnect(): Promise { + this.client.release() + } + + async query(query: string, values: any[] = []): Promise { + const result = await this.client.query(query, values) + return { rows: result.rows } + } +} + +interface PostgreSQLDBConfig { + readonly pool: Pool +} + +export default class PostgreSQLDB implements IDB { + private readonly pool: Pool + + constructor(config: PostgreSQLDBConfig) { + this.pool = config.pool + } + + async connect(): Promise { + const client = await this.pool.connect() + return new PostgreSQLDBConnection({ client }) + } + + async query(query: string, values: any[] = []): Promise { + const connection = await this.connect() + const result = await connection.query(query, values) + connection.disconnect() + return result + } +} diff --git a/src/common/db/index.ts b/src/common/db/index.ts new file mode 100644 index 00000000..08da8c6a --- /dev/null +++ b/src/common/db/index.ts @@ -0,0 +1,2 @@ +export type { default as IDB, IDBConnection } from "./IDB" +export { default as PostgreSQLDB, PostgreSQLDBConnection } from "./PostgreSQLDB" diff --git a/src/common/errors/index.ts b/src/common/errors/index.ts index 8ad94893..0f1a98d8 100644 --- a/src/common/errors/index.ts +++ b/src/common/errors/index.ts @@ -1,2 +1,3 @@ export { default as makeAPIErrorResponse } from "./makeAPIErrorResponse" -export class UnauthorizedError extends Error {} \ No newline at end of file +export { makeUnauthenticatedAPIErrorResponse } from "./makeAPIErrorResponse" +export class UnauthorizedError extends Error {} diff --git a/src/common/errors/makeAPIErrorResponse.ts b/src/common/errors/makeAPIErrorResponse.ts index af62969f..766a5141 100644 --- a/src/common/errors/makeAPIErrorResponse.ts +++ b/src/common/errors/makeAPIErrorResponse.ts @@ -6,3 +6,7 @@ export default function makeAPIErrorResponse( ): NextResponse { return NextResponse.json({ status, message }, { status }) } + +export function makeUnauthenticatedAPIErrorResponse(): NextResponse { + return makeAPIErrorResponse(401, "Unauthenticated") +} diff --git a/src/common/github/AccessTokenRefreshingGitHubClient.ts b/src/common/github/AccessTokenRefreshingGitHubClient.ts index 4a250d5c..d8a51f3b 100644 --- a/src/common/github/AccessTokenRefreshingGitHubClient.ts +++ b/src/common/github/AccessTokenRefreshingGitHubClient.ts @@ -15,21 +15,29 @@ const HttpErrorSchema = z.object({ status: z.number() }) -interface IGitHubAccessTokenService { +interface IGitHubAccessTokenReader { getAccessToken(): Promise +} + +interface IGitHubAccessTokenRefresher { refreshAccessToken(accessToken: string): Promise } +type AccessTokenRefreshingGitHubClientConfig = { + readonly accessTokenReader: IGitHubAccessTokenReader + readonly accessTokenRefresher: IGitHubAccessTokenRefresher + readonly gitHubClient: IGitHubClient +} + export default class AccessTokenRefreshingGitHubClient implements IGitHubClient { - private readonly accessTokenService: IGitHubAccessTokenService + private readonly accessTokenReader: IGitHubAccessTokenReader + private readonly accessTokenRefresher: IGitHubAccessTokenRefresher private readonly gitHubClient: IGitHubClient - constructor( - accessTokenService: IGitHubAccessTokenService, - gitHubClient: IGitHubClient - ) { - this.accessTokenService = accessTokenService - this.gitHubClient = gitHubClient + constructor(config: AccessTokenRefreshingGitHubClientConfig) { + this.accessTokenReader = config.accessTokenReader + this.accessTokenRefresher = config.accessTokenRefresher + this.gitHubClient = config.gitHubClient } async graphql(request: GraphQLQueryRequest): Promise { @@ -65,7 +73,7 @@ export default class AccessTokenRefreshingGitHubClient implements IGitHubClient } private async send(fn: () => Promise): Promise { - const accessToken = await this.accessTokenService.getAccessToken() + const accessToken = await this.accessTokenReader.getAccessToken() try { return await fn() } catch (e) { @@ -73,7 +81,7 @@ export default class AccessTokenRefreshingGitHubClient implements IGitHubClient const error = HttpErrorSchema.parse(e) if (error.status == 401) { // Refresh access token and try the request one last time. - await this.accessTokenService.refreshAccessToken(accessToken) + await this.accessTokenRefresher.refreshAccessToken(accessToken) return await fn() } else { // Not an error we can handle so forward it. diff --git a/src/common/index.ts b/src/common/index.ts index c7086817..4be9c4a1 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,3 +1,4 @@ +export * from "./db" export * from "./errors" export * from "./github" export * from "./keyValueStore" diff --git a/src/common/mutex/SessionMutexFactory.ts b/src/common/mutex/SessionMutexFactory.ts index c84b02de..3ae5f13b 100644 --- a/src/common/mutex/SessionMutexFactory.ts +++ b/src/common/mutex/SessionMutexFactory.ts @@ -1,25 +1,28 @@ import IKeyedMutexFactory from "./IKeyedMutexFactory" import IMutex from "./IMutex" import IMutexFactory from "./IMutexFactory" -import { ISession } from "@/common" + +interface IUserIDReader { + getUserId(): Promise +} export default class SessionMutexFactory implements IMutexFactory { private readonly mutexFactory: IKeyedMutexFactory - private readonly session: ISession + private readonly userIdReader: IUserIDReader private readonly baseKey: string constructor( mutexFactory: IKeyedMutexFactory, - session: ISession, + userIdReader: IUserIDReader, baseKey: string ) { - this.session = session + this.userIdReader = userIdReader this.baseKey = baseKey this.mutexFactory = mutexFactory } async makeMutex(): Promise { - const userId = await this.session.getUserId() + const userId = await this.userIdReader.getUserId() const key = `${this.baseKey}[${userId}]` return this.mutexFactory.makeMutex(key) } diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index d6da700a..be926b01 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -1,31 +1,32 @@ +import { NextAuthOptions } from "next-auth" import { getServerSession } from "next-auth/next" import { UnauthorizedError } from "../../common" -import ISession from "./ISession" -import { IIsUserGuestReader } from "@/features/auth/domain" export type AuthSessionConfig = { - readonly isUserGuestReader: IIsUserGuestReader + readonly authOptions: NextAuthOptions } -export default class AuthjsSession implements ISession { - private readonly isUserGuestReader: IIsUserGuestReader +export default class AuthjsSession { + private readonly authOptions: NextAuthOptions constructor(config: AuthSessionConfig) { - this.isUserGuestReader = config.isUserGuestReader + this.authOptions = config.authOptions + } + + async getIsAuthenticated(): Promise { + const session = await getServerSession(this.authOptions) + return session != null } async getUserId(): Promise { - const session = await getServerSession() - if (!session) { + const session = await getServerSession(this.authOptions) + if (!session || !session.user || !session.user.id) { throw new UnauthorizedError("User ID is unavailable because the user is not authenticated.") } - // return session.user.sub - return "foo" + return session.user.id } async getIsGuest(): Promise { return false - // const userId = await this.getUserId() - // return await this.isUserGuestReader.getIsUserGuest(userId) } } diff --git a/src/common/session/ISession.ts b/src/common/session/ISession.ts deleted file mode 100644 index 2496223f..00000000 --- a/src/common/session/ISession.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface ISession { - getUserId(): Promise - getIsGuest(): Promise -} diff --git a/src/common/session/index.ts b/src/common/session/index.ts index 5ba5fb15..791ee839 100644 --- a/src/common/session/index.ts +++ b/src/common/session/index.ts @@ -1,2 +1 @@ export { default as AuthjsSession } from "./AuthjsSession" -export type { default as ISession } from "./ISession" diff --git a/src/composition.ts b/src/composition.ts index d070548e..5c792061 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -1,3 +1,8 @@ +import { Pool } from "pg" +import { NextAuthOptions } from "next-auth" +import GithubProvider from "next-auth/providers/github" +import PostgresAdapter from "@auth/pg-adapter" +import { Adapter } from "next-auth/adapters" import { AuthjsSession } from "@/common/session" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" @@ -5,6 +10,7 @@ import { AccessTokenRefreshingGitHubClient, GitHubClient, KeyValueUserDataRepository, + PostgreSQLDB, SessionMutexFactory } from "@/common" import { @@ -17,30 +23,22 @@ import { } from "@/features/projects/domain" import { GitHubOAuthTokenRefresher, - GitHubInstallationAccessTokenDataSource, - AuthjsRefreshTokenReader, + // GitHubInstallationAccessTokenDataSource, + AuthjsOAuthTokenRepository, AuthjsRepositoryAccessReader } from "@/features/auth/data" import { - AccessTokenService, + AccessTokenRefresher, AccessTokenSessionValidator, CachingRepositoryAccessReader, - CompositeLogInHandler, CompositeLogOutHandler, - CredentialsTransferringLogInHandler, ErrorIgnoringLogOutHandler, GitHubOrganizationSessionValidator, - GuestAccessTokenRepository, - GuestAccessTokenService, - GuestCredentialsTransferrer, - HostAccessTokenService, - HostCredentialsTransferrer, HostOnlySessionValidator, - IsUserGuestReader, - LockingAccessTokenService, + LockingAccessTokenRefresher, OAuthTokenRepository, - OnlyStaleRefreshingAccessTokenService, - RepositoryRestrictingAccessTokenDataSource, + TransferringAccessTokenReader, + // RepositoryRestrictingAccessTokenDataSource, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" @@ -50,9 +48,53 @@ const { GITHUB_CLIENT_SECRET, GITHUB_PRIVATE_KEY_BASE_64, GITHUB_ORGANIZATION_NAME, - REDIS_URL + REDIS_URL, + POSTGRESQL_HOST, + POSTGRESQL_USER, + POSTGRESQL_DB } = process.env +const pool = new Pool({ + host: POSTGRESQL_HOST, + user: POSTGRESQL_USER, + database: POSTGRESQL_DB, + max: 20, + idleTimeoutMillis: 30000, + connectionTimeoutMillis: 2000 +}) + +const db = new PostgreSQLDB({ pool }) + +export const authOptions: NextAuthOptions = { + adapter: PostgresAdapter(pool) as Adapter, + secret: process.env.NEXTAUTH_SECRET, + providers: [ + GithubProvider({ + clientId: process.env.GITHUB_CLIENT_ID, + clientSecret: process.env.GITHUB_CLIENT_SECRET, + authorization: { + params: { + scope: "repo" + } + } + }) + ], + session: { + strategy: "database" + }, + callbacks: { + async signIn({ account, user }) { + console.log(account) + console.log(user) + return true + }, + async session({ session, user }) { + session.user.id = user.id + return session + } + } +} + const gitHubAppCredentials = { appId: GITHUB_APP_ID, clientId: GITHUB_CLIENT_ID, @@ -62,29 +104,14 @@ const gitHubAppCredentials = { .toString("utf-8") } -export const session = new AuthjsSession({ - isUserGuestReader: new IsUserGuestReader() -}) - -const oAuthTokenRepository = new OAuthTokenRepository( - new KeyValueUserDataRepository( - new RedisKeyValueStore(REDIS_URL), - "authToken" - ) -) +export const session = new AuthjsSession({ authOptions }) -const gitHubOAuthTokenRefresher = new GitHubOAuthTokenRefresher({ - clientId: gitHubAppCredentials.clientId, - clientSecret: gitHubAppCredentials.clientSecret +const accessTokenReader = new TransferringAccessTokenReader({ + userIdReader: session, + sourceOAuthTokenRepository: new AuthjsOAuthTokenRepository({ provider: "github", db }), + destinationOAuthTokenRepository: new OAuthTokenRepository({ db }) }) -const guestAccessTokenRepository = new GuestAccessTokenRepository( - new KeyValueUserDataRepository( - new RedisKeyValueStore(REDIS_URL), - "accessToken" - ) -) - const guestRepositoryAccessRepository = new KeyValueUserDataRepository( new RedisKeyValueStore(REDIS_URL), "guestRepositoryAccess" @@ -95,49 +122,33 @@ export const guestRepositoryAccessReader = new CachingRepositoryAccessReader({ repositoryAccessReader: new AuthjsRepositoryAccessReader() }) -const guestAccessTokenDataSource = new RepositoryRestrictingAccessTokenDataSource({ - repositoryAccessReader: guestRepositoryAccessReader, - dataSource: new GitHubInstallationAccessTokenDataSource({ - ...gitHubAppCredentials, - organization: GITHUB_ORGANIZATION_NAME - }) +export const gitHubClient = new GitHubClient({ + ...gitHubAppCredentials, + accessTokenReader }) -export const accessTokenService = new LockingAccessTokenService( - new SessionMutexFactory( - new RedisKeyedMutexFactory(REDIS_URL), - session, - "mutexAccessToken" - ), - new OnlyStaleRefreshingAccessTokenService( - new AccessTokenService({ - isGuestReader: session, - guestAccessTokenService: new GuestAccessTokenService({ - userIdReader: session, - repository: guestAccessTokenRepository, - dataSource: guestAccessTokenDataSource - }), - hostAccessTokenService: new HostAccessTokenService({ - userIdReader: session, - repository: oAuthTokenRepository, - refresher: gitHubOAuthTokenRefresher +export const userGitHubClient = new AccessTokenRefreshingGitHubClient({ + gitHubClient, + accessTokenReader, + accessTokenRefresher: new LockingAccessTokenRefresher({ + mutexFactory: new SessionMutexFactory( + new RedisKeyedMutexFactory(REDIS_URL), + session, + "mutexAccessToken" + ), + accessTokenRefresher: new AccessTokenRefresher({ + userIdReader: session, + oAuthTokenRepository: new OAuthTokenRepository({ db }), + oAuthTokenRefresher: new GitHubOAuthTokenRefresher({ + clientId: gitHubAppCredentials.clientId, + clientSecret: gitHubAppCredentials.clientSecret }) }) - ) -) - -export const gitHubClient = new GitHubClient({ - ...gitHubAppCredentials, - accessTokenReader: accessTokenService + }) }) -export const userGitHubClient = new AccessTokenRefreshingGitHubClient( - accessTokenService, - gitHubClient -) - export const blockingSessionValidator = new AccessTokenSessionValidator({ - accessTokenService: accessTokenService + accessTokenReader }) export const delayedSessionValidator = new HostOnlySessionValidator({ isGuestReader: session, @@ -167,26 +178,9 @@ export const projectDataSource = new CachingProjectDataSource({ repository: projectRepository }) -export const logInHandler = new CompositeLogInHandler([ - new CredentialsTransferringLogInHandler({ - isUserGuestReader: new IsUserGuestReader(), - guestCredentialsTransferrer: new GuestCredentialsTransferrer({ - dataSource: guestAccessTokenDataSource, - repository: guestAccessTokenRepository - }), - hostCredentialsTransferrer: new HostCredentialsTransferrer({ - refreshTokenReader: new AuthjsRefreshTokenReader(), - oAuthTokenRefresher: gitHubOAuthTokenRefresher, - oAuthTokenRepository: oAuthTokenRepository - }) - }) -]) - export const logOutHandler = new ErrorIgnoringLogOutHandler( new CompositeLogOutHandler([ new UserDataCleanUpLogOutHandler(session, projectUserDataRepository), - new UserDataCleanUpLogOutHandler(session, guestRepositoryAccessRepository), - new UserDataCleanUpLogOutHandler(session, oAuthTokenRepository), - new UserDataCleanUpLogOutHandler(session, guestAccessTokenRepository) + new UserDataCleanUpLogOutHandler(session, guestRepositoryAccessRepository) ]) ) diff --git a/src/features/auth/data/AuthjsOAuthTokenRepository.ts b/src/features/auth/data/AuthjsOAuthTokenRepository.ts new file mode 100644 index 00000000..b92e50d0 --- /dev/null +++ b/src/features/auth/data/AuthjsOAuthTokenRepository.ts @@ -0,0 +1,41 @@ +import { IDB, UnauthorizedError } from "../../../common" +import { IOAuthTokenRepository, OAuthToken } from "../domain" + +type AuthjsOAuthTokenRepositoryConfig = { + readonly db: IDB + readonly provider: string +} + +export default class AuthjsOAuthTokenRepository implements IOAuthTokenRepository { + private readonly db: IDB + private readonly provider: string + + constructor(config: AuthjsOAuthTokenRepositoryConfig) { + this.db = config.db + this.provider = config.provider + } + + async get(userId: string): Promise { + const result = await this.db.query( + "SELECT access_token, refresh_token FROM accounts WHERE \"userId\" = $1 AND provider = $2", + [userId, this.provider] + ) + if (result.rows.length == 0) { + throw new UnauthorizedError("The access token was not found. It appears that the user is not authenticated.") + } + const accessToken = result.rows[0].access_token + const refreshToken = result.rows[0].refresh_token + if (!accessToken || !refreshToken) { + throw new UnauthorizedError("") + } + return { accessToken, refreshToken } + } + + async set(_userId: string, _token: OAuthToken): Promise { + throw new Error("Not implemented. We do not support modifying data owned by Auth.js. Use our access_tokens table instead.") + } + + async delete(_userId: string): Promise { + throw new Error("Not implemented. We do not support modifying data owned by Auth.js. Use our access_tokens table instead.") + } +} diff --git a/src/features/auth/data/AuthjsRefreshTokenReader.ts b/src/features/auth/data/AuthjsRefreshTokenReader.ts deleted file mode 100644 index bee3dd78..00000000 --- a/src/features/auth/data/AuthjsRefreshTokenReader.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { UnauthorizedError } from "@/common" - -export default class AuthjsRefreshTokenReader { - constructor() {} - - async getRefreshToken(userId: string): Promise { - // const userResponse = await this.managementClient.users.get({ id: userId }) - // const identity = userResponse.data.identities.find(identity => { - // return identity.connection.toLowerCase() == this.connection.toLowerCase() - // }) - // if (!identity) { - // throw new UnauthorizedError(`No identity found for connection "${this.connection}"`) - // } - // return identity.refresh_token - throw new UnauthorizedError("Not implemented") - } -} diff --git a/src/features/auth/data/AuthjsRepositoryAccessReader.ts b/src/features/auth/data/AuthjsRepositoryAccessReader.ts index 03cf0288..4d524d8f 100644 --- a/src/features/auth/data/AuthjsRepositoryAccessReader.ts +++ b/src/features/auth/data/AuthjsRepositoryAccessReader.ts @@ -2,6 +2,6 @@ export default class AuthjsRepositoryAccessReader { constructor() {} async getRepositoryNames(userId: string): Promise { - return [] + throw new Error("Not implemented") } } diff --git a/src/features/auth/data/GitHubOAuthTokenRefresher.ts b/src/features/auth/data/GitHubOAuthTokenRefresher.ts index 08a2a998..6ef11c5e 100644 --- a/src/features/auth/data/GitHubOAuthTokenRefresher.ts +++ b/src/features/auth/data/GitHubOAuthTokenRefresher.ts @@ -1,4 +1,4 @@ -import { UnauthorizedError } from "@/common/errors" +import { UnauthorizedError } from "../../../common" import { OAuthToken, IOAuthTokenRefresher } from "../domain" export interface GitHubOAuthTokenRefresherConfig { diff --git a/src/features/auth/data/index.ts b/src/features/auth/data/index.ts index 16c7437b..b17c6f92 100644 --- a/src/features/auth/data/index.ts +++ b/src/features/auth/data/index.ts @@ -1,4 +1,4 @@ -export { default as AuthjsRefreshTokenReader } from "./AuthjsRefreshTokenReader" +export { default as AuthjsOAuthTokenRepository } from "./AuthjsOAuthTokenRepository" export { default as AuthjsRepositoryAccessReader } from "./AuthjsRepositoryAccessReader" export { default as GitHubInstallationAccessTokenDataSource } from "./GitHubInstallationAccessTokenDataSource" export { default as GitHubOAuthTokenRefresher } from "./GitHubOAuthTokenRefresher" diff --git a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts new file mode 100644 index 00000000..17315fcb --- /dev/null +++ b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts @@ -0,0 +1,50 @@ +import IAccessTokenRefresher from "./IAccessTokenRefresher" +import IOAuthTokenRepository from "../oAuthToken/IOAuthTokenRepository" +import IOAuthTokenRefresher from "../oAuthToken/IOAuthTokenRefresher" +import OAuthToken from "../oAuthToken/OAuthToken" +import { UnauthorizedError } from "@/common" + +interface IUserIDReader { + getUserId(): Promise +} + +type HostAccessTokenServiceConfig = { + readonly userIdReader: IUserIDReader + readonly oAuthTokenRepository: IOAuthTokenRepository + readonly oAuthTokenRefresher: IOAuthTokenRefresher +} + +export default class AccessTokenRefresher implements IAccessTokenRefresher { + private readonly userIdReader: IUserIDReader + private readonly oAuthTokenRepository: IOAuthTokenRepository + private readonly oAuthTokenRefresher: IOAuthTokenRefresher + + constructor(config: HostAccessTokenServiceConfig) { + this.userIdReader = config.userIdReader + this.oAuthTokenRepository = config.oAuthTokenRepository + this.oAuthTokenRefresher = config.oAuthTokenRefresher + } + + async refreshAccessToken(accessToken: string): Promise { + const userId = await this.userIdReader.getUserId() + const oAuthToken = await this.oAuthTokenRepository.get(userId) + if (accessToken != oAuthToken.accessToken) { + // Given access token is outdated so we use our stored access token. + return oAuthToken.accessToken + } + // Given access token is stale so we refresh it. + console.log("♻️ Refresh: " + oAuthToken.refreshToken) + let newOAuthToken: OAuthToken | undefined + try { + newOAuthToken = await this.oAuthTokenRefresher.refreshOAuthToken(oAuthToken.refreshToken) + } catch (error) { + // console.error(error) + // await this.oAuthTokenRepository.delete(userId) + throw new UnauthorizedError("Refresh token is invalid.") + } + console.log("💾 Store new token") + console.log(newOAuthToken) + await this.oAuthTokenRepository.set(userId, newOAuthToken) + return newOAuthToken.accessToken + } +} \ No newline at end of file diff --git a/src/features/auth/domain/accessToken/AccessTokenService.ts b/src/features/auth/domain/accessToken/AccessTokenService.ts deleted file mode 100644 index 095aef31..00000000 --- a/src/features/auth/domain/accessToken/AccessTokenService.ts +++ /dev/null @@ -1,42 +0,0 @@ -import IAccessTokenService from "./IAccessTokenService" - -export interface IIsGuestReader { - getIsGuest(): Promise -} - -interface AccessTokenServiceConfig { - readonly isGuestReader: IIsGuestReader - readonly guestAccessTokenService: IAccessTokenService - readonly hostAccessTokenService: IAccessTokenService -} - -export default class AccessTokenService implements IAccessTokenService { - private readonly isGuestReader: IIsGuestReader - private readonly guestAccessTokenService: IAccessTokenService - private readonly hostAccessTokenService: IAccessTokenService - - constructor(config: AccessTokenServiceConfig) { - this.isGuestReader = config.isGuestReader - this.guestAccessTokenService = config.guestAccessTokenService - this.hostAccessTokenService = config.hostAccessTokenService - } - - async getAccessToken(): Promise { - const service = await this.getService() - return await service.getAccessToken() - } - - async refreshAccessToken(accessToken: string): Promise { - const service = await this.getService() - return await service.refreshAccessToken(accessToken) - } - - private async getService() { - const isGuest = await this.isGuestReader.getIsGuest() - if (isGuest) { - return this.guestAccessTokenService - } else { - return this.hostAccessTokenService - } - } -} \ No newline at end of file diff --git a/src/features/auth/domain/accessToken/GuestAccessTokenRepository.ts b/src/features/auth/domain/accessToken/GuestAccessTokenRepository.ts deleted file mode 100644 index 7a93cef0..00000000 --- a/src/features/auth/domain/accessToken/GuestAccessTokenRepository.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface IRepository { - get(userId: string): Promise - setExpiring(userId: string, token: string, timeToLive: number): Promise - delete(userId: string): Promise -} - -export default class GuestAccessTokenRepository { - private readonly repository: IRepository - - constructor(repository: IRepository) { - this.repository = repository - } - - async get(userId: string): Promise { - return await this.repository.get(userId) - } - - async set(userId: string, accessToken: string): Promise { - await this.repository.setExpiring(userId, accessToken, 7 * 24 * 3600) - } - - async delete(userId: string): Promise { - await this.repository.delete(userId) - } -} \ No newline at end of file diff --git a/src/features/auth/domain/accessToken/GuestAccessTokenService.ts b/src/features/auth/domain/accessToken/GuestAccessTokenService.ts deleted file mode 100644 index 6ac68c0c..00000000 --- a/src/features/auth/domain/accessToken/GuestAccessTokenService.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { UnauthorizedError } from "../../../../common" -import IAccessTokenService from "./IAccessTokenService" - -export interface IUserIDReader { - getUserId(): Promise -} - -export interface Repository { - get(userId: string): Promise - set(userId: string, token: string): Promise -} - -export interface DataSource { - getAccessToken(userId: string): Promise -} - -export type GuestAccessTokenServiceConfig = { - readonly userIdReader: IUserIDReader - readonly repository: Repository - readonly dataSource: DataSource -} - -export default class GuestAccessTokenService implements IAccessTokenService { - private readonly userIdReader: IUserIDReader - private readonly repository: Repository - private readonly dataSource: DataSource - - constructor(config: GuestAccessTokenServiceConfig) { - this.userIdReader = config.userIdReader - this.repository = config.repository - this.dataSource = config.dataSource - } - - async getAccessToken(): Promise { - const userId = await this.userIdReader.getUserId() - const accessToken = await this.repository.get(userId) - if (!accessToken) { - throw new UnauthorizedError(`No access token stored for user with ID ${userId}.`) - } - return accessToken - } - - async refreshAccessToken(_accessToken: string): Promise { - const userId = await this.userIdReader.getUserId() - const newAccessToken = await this.dataSource.getAccessToken(userId) - await this.repository.set(userId, newAccessToken) - return newAccessToken - } -} \ No newline at end of file diff --git a/src/features/auth/domain/accessToken/HostAccessTokenService.ts b/src/features/auth/domain/accessToken/HostAccessTokenService.ts deleted file mode 100644 index 7b400082..00000000 --- a/src/features/auth/domain/accessToken/HostAccessTokenService.ts +++ /dev/null @@ -1,39 +0,0 @@ -import IAccessTokenService from "./IAccessTokenService" -import IOAuthTokenRepository from "../oAuthToken/IOAuthTokenRepository" -import IOAuthTokenRefresher from "../oAuthToken/IOAuthTokenRefresher" - -export interface IUserIDReader { - getUserId(): Promise -} - -type HostAccessTokenServiceConfig = { - readonly userIdReader: IUserIDReader - readonly repository: IOAuthTokenRepository - readonly refresher: IOAuthTokenRefresher -} - -export default class HostAccessTokenService implements IAccessTokenService { - private readonly userIdReader: IUserIDReader - private readonly repository: IOAuthTokenRepository - private readonly refresher: IOAuthTokenRefresher - - constructor(config: HostAccessTokenServiceConfig) { - this.userIdReader = config.userIdReader - this.repository = config.repository - this.refresher = config.refresher - } - - async getAccessToken(): Promise { - const userId = await this.userIdReader.getUserId() - const oAuthToken = await this.repository.get(userId) - return oAuthToken.accessToken - } - - async refreshAccessToken(_accessToken: string): Promise { - const userId = await this.userIdReader.getUserId() - const oAuthToken = await this.repository.get(userId) - const newOAuthToken = await this.refresher.refreshOAuthToken(oAuthToken.refreshToken) - await this.repository.set(userId, newOAuthToken) - return newOAuthToken.accessToken - } -} \ No newline at end of file diff --git a/src/features/auth/domain/accessToken/IAccessTokenRefresher.ts b/src/features/auth/domain/accessToken/IAccessTokenRefresher.ts new file mode 100644 index 00000000..3e1da196 --- /dev/null +++ b/src/features/auth/domain/accessToken/IAccessTokenRefresher.ts @@ -0,0 +1,3 @@ +export default interface IAccessTokenRefresher { + refreshAccessToken(accessToken: string): Promise +} diff --git a/src/features/auth/domain/accessToken/IAccessTokenService.ts b/src/features/auth/domain/accessToken/IAccessTokenService.ts deleted file mode 100644 index 579211c1..00000000 --- a/src/features/auth/domain/accessToken/IAccessTokenService.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface IAccessTokenService { - getAccessToken(): Promise - refreshAccessToken(accessToken: string): Promise -} diff --git a/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts b/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts new file mode 100644 index 00000000..e72d9bc4 --- /dev/null +++ b/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts @@ -0,0 +1,25 @@ +import { IMutexFactory } from "@/common" +import withMutex from "../../../../common/mutex/withMutex" +import IAccessTokenRefresher from "./IAccessTokenRefresher" + +type LockingAccessTokenRefresherConfig = { + readonly mutexFactory: IMutexFactory + readonly accessTokenRefresher: IAccessTokenRefresher +} + +export default class LockingAccessTokenRefresher implements IAccessTokenRefresher { + private readonly mutexFactory: IMutexFactory + private readonly accessTokenRefresher: IAccessTokenRefresher + + constructor(config: LockingAccessTokenRefresherConfig) { + this.mutexFactory = config.mutexFactory + this.accessTokenRefresher = config.accessTokenRefresher + } + + async refreshAccessToken(accessToken: string): Promise { + const mutex = await this.mutexFactory.makeMutex() + return await withMutex(mutex, async () => { + return await this.accessTokenRefresher.refreshAccessToken(accessToken) + }) + } +} diff --git a/src/features/auth/domain/accessToken/LockingAccessTokenService.ts b/src/features/auth/domain/accessToken/LockingAccessTokenService.ts deleted file mode 100644 index 5717bdcc..00000000 --- a/src/features/auth/domain/accessToken/LockingAccessTokenService.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { IMutexFactory } from "@/common" -import IAccessTokenService from "./IAccessTokenService" -import withMutex from "../../../../common/mutex/withMutex" - -export default class LockingAccessTokenService implements IAccessTokenService { - private readonly mutexFactory: IMutexFactory - private readonly accessTokenService: IAccessTokenService - - constructor( - mutexFactory: IMutexFactory, - accessTokenService: IAccessTokenService - ) { - this.mutexFactory = mutexFactory - this.accessTokenService = accessTokenService - } - - async getAccessToken(): Promise { - return await this.accessTokenService.getAccessToken() - } - - async refreshAccessToken(accessToken: string): Promise { - const mutex = await this.mutexFactory.makeMutex() - return await withMutex(mutex, async () => { - return await this.accessTokenService.refreshAccessToken(accessToken) - }) - } -} diff --git a/src/features/auth/domain/accessToken/OnlyStaleRefreshingAccessTokenService.ts b/src/features/auth/domain/accessToken/OnlyStaleRefreshingAccessTokenService.ts deleted file mode 100644 index 9b3aabce..00000000 --- a/src/features/auth/domain/accessToken/OnlyStaleRefreshingAccessTokenService.ts +++ /dev/null @@ -1,23 +0,0 @@ -import IAccessTokenService from "./IAccessTokenService" - -export default class OnlyStaleRefreshingAccessTokenService implements IAccessTokenService { - private readonly service: IAccessTokenService - - constructor(service: IAccessTokenService) { - this.service = service - } - - async getAccessToken(): Promise { - return await this.service.getAccessToken() - } - - async refreshAccessToken(accessToken: string): Promise { - const storedAccessToken = await this.getAccessToken() - if (accessToken != storedAccessToken) { - // Given access token is outdated so we use our stored access token. - return storedAccessToken - } - // Given access token is stale so we refresh it. - return await this.service.refreshAccessToken(accessToken) - } -} \ No newline at end of file diff --git a/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts b/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts new file mode 100644 index 00000000..00a1a96d --- /dev/null +++ b/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts @@ -0,0 +1,37 @@ +import { IOAuthTokenRepository, OAuthToken } from ".." + +interface IUserIDReader { + getUserId(): Promise +} + +type TransferringAccessTokenReaderConfig = { + readonly userIdReader: IUserIDReader + readonly sourceOAuthTokenRepository: IOAuthTokenRepository + readonly destinationOAuthTokenRepository: IOAuthTokenRepository +} + +export default class TransferringAccessTokenReader { + private readonly userIdReader: IUserIDReader + private readonly sourceOAuthTokenRepository: IOAuthTokenRepository + private readonly destinationOAuthTokenRepository: IOAuthTokenRepository + + constructor(config: TransferringAccessTokenReaderConfig) { + this.userIdReader = config.userIdReader + this.sourceOAuthTokenRepository = config.sourceOAuthTokenRepository + this.destinationOAuthTokenRepository = config.destinationOAuthTokenRepository + } + + async getAccessToken(): Promise { + const userId = await this.userIdReader.getUserId() + let destinationOAuthToken: OAuthToken | undefined + try { + destinationOAuthToken = await this.destinationOAuthTokenRepository.get(userId) + } catch {} + if (destinationOAuthToken) { + return destinationOAuthToken.accessToken + } + const sourceOAuthToken = await this.sourceOAuthTokenRepository.get(userId) + await this.destinationOAuthTokenRepository.set(userId, sourceOAuthToken) + return sourceOAuthToken.accessToken + } +} diff --git a/src/features/auth/domain/accessToken/index.ts b/src/features/auth/domain/accessToken/index.ts index b575dda2..c497aba8 100644 --- a/src/features/auth/domain/accessToken/index.ts +++ b/src/features/auth/domain/accessToken/index.ts @@ -1,6 +1,4 @@ -export { default as GuestAccessTokenRepository } from "./GuestAccessTokenRepository" -export { default as GuestAccessTokenService } from "./GuestAccessTokenService" -export { default as HostAccessTokenService } from "./HostAccessTokenService" -export { default as LockingAccessTokenService } from "./LockingAccessTokenService" -export { default as OnlyStaleRefreshingAccessTokenService } from "./OnlyStaleRefreshingAccessTokenService" -export { default as AccessTokenService } from "./AccessTokenService" +export { default as AccessTokenRefresher } from "./AccessTokenRefresher" +export type { default as IAccessTokenRefresher } from "./IAccessTokenRefresher" +export { default as LockingAccessTokenRefresher } from "./LockingAccessTokenRefresher" +export { default as TransferringAccessTokenReader } from "./TransferringAccessTokenReader" diff --git a/src/features/auth/domain/credentialsTransfer/GuestCredentialsTransferrer.ts b/src/features/auth/domain/credentialsTransfer/GuestCredentialsTransferrer.ts deleted file mode 100644 index 05a39f7f..00000000 --- a/src/features/auth/domain/credentialsTransfer/GuestCredentialsTransferrer.ts +++ /dev/null @@ -1,29 +0,0 @@ -import ICredentialsTransferrer from "./ICredentialsTransferrer" - -export interface IDataSource { - getAccessToken(userId: string): Promise -} - -export interface IRepository { - set(userId: string, token: string): Promise -} - -export type GuestCredentialsTransferrerConfig = { - readonly dataSource: IDataSource - readonly repository: IRepository -} - -export default class GuestCredentialsTransferrer implements ICredentialsTransferrer { - private readonly dataSource: IDataSource - private readonly repository: IRepository - - constructor(config: GuestCredentialsTransferrerConfig) { - this.dataSource = config.dataSource - this.repository = config.repository - } - - async transferCredentials(userId: string): Promise { - const newAccessToken = await this.dataSource.getAccessToken(userId) - await this.repository.set(userId, newAccessToken) - } -} diff --git a/src/features/auth/domain/credentialsTransfer/HostCredentialsTransferrer.ts b/src/features/auth/domain/credentialsTransfer/HostCredentialsTransferrer.ts deleted file mode 100644 index d66a0534..00000000 --- a/src/features/auth/domain/credentialsTransfer/HostCredentialsTransferrer.ts +++ /dev/null @@ -1,31 +0,0 @@ -import IOAuthTokenRefresher from "../oAuthToken/IOAuthTokenRefresher" -import IOAuthTokenRepository from "../oAuthToken/IOAuthTokenRepository" -import ICredentialsTransferrer from "./ICredentialsTransferrer" - -export interface IRefreshTokenReader { - getRefreshToken(userId: string): Promise -} - -type HostCredentialsTransferrerConfig = { - readonly refreshTokenReader: IRefreshTokenReader - readonly oAuthTokenRefresher: IOAuthTokenRefresher - readonly oAuthTokenRepository: IOAuthTokenRepository -} - -export default class HostCredentialsTransferrer implements ICredentialsTransferrer { - private readonly refreshTokenReader: IRefreshTokenReader - private readonly oAuthTokenRefresher: IOAuthTokenRefresher - private readonly oAuthTokenRepository: IOAuthTokenRepository - - constructor(config: HostCredentialsTransferrerConfig) { - this.refreshTokenReader = config.refreshTokenReader - this.oAuthTokenRefresher = config.oAuthTokenRefresher - this.oAuthTokenRepository = config.oAuthTokenRepository - } - - async transferCredentials(userId: string): Promise { - const refreshToken = await this.refreshTokenReader.getRefreshToken(userId) - const authToken = await this.oAuthTokenRefresher.refreshOAuthToken(refreshToken) - await this.oAuthTokenRepository.set(userId, authToken) - } -} diff --git a/src/features/auth/domain/credentialsTransfer/ICredentialsTransferrer.ts b/src/features/auth/domain/credentialsTransfer/ICredentialsTransferrer.ts deleted file mode 100644 index 7bea8973..00000000 --- a/src/features/auth/domain/credentialsTransfer/ICredentialsTransferrer.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface ICredentialsTransferrer { - transferCredentials(userId: string): Promise -} diff --git a/src/features/auth/domain/credentialsTransfer/index.ts b/src/features/auth/domain/credentialsTransfer/index.ts deleted file mode 100644 index b07aa4af..00000000 --- a/src/features/auth/domain/credentialsTransfer/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as GuestCredentialsTransferrer } from "./GuestCredentialsTransferrer" -export { default as HostCredentialsTransferrer } from "./HostCredentialsTransferrer" diff --git a/src/features/auth/domain/guest/IIsUserGuestReader.ts b/src/features/auth/domain/guest/IIsUserGuestReader.ts deleted file mode 100644 index da34aa0d..00000000 --- a/src/features/auth/domain/guest/IIsUserGuestReader.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IIsUserGuestReader { - getIsUserGuest(userId: string): Promise -} \ No newline at end of file diff --git a/src/features/auth/domain/guest/IsUserGuestReader.ts b/src/features/auth/domain/guest/IsUserGuestReader.ts deleted file mode 100644 index 1fa77881..00000000 --- a/src/features/auth/domain/guest/IsUserGuestReader.ts +++ /dev/null @@ -1,9 +0,0 @@ -import IIsUserGuestReader from "./IIsUserGuestReader" - -export default class IsUserGuestReader implements IIsUserGuestReader { - constructor() {} - - async getIsUserGuest(userId: string): Promise { - return false - } -} diff --git a/src/features/auth/domain/guest/index.ts b/src/features/auth/domain/guest/index.ts deleted file mode 100644 index 238abe7c..00000000 --- a/src/features/auth/domain/guest/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { default as IIsUserGuestReader } from "./IIsUserGuestReader" -export { default as IsUserGuestReader } from "./IsUserGuestReader" diff --git a/src/features/auth/domain/index.ts b/src/features/auth/domain/index.ts index b1be6778..0a7ff361 100644 --- a/src/features/auth/domain/index.ts +++ b/src/features/auth/domain/index.ts @@ -1,8 +1,5 @@ export * from "./accessToken" -export * from "./credentialsTransfer" -export * from "./logIn" export * from "./logOut" export * from "./oAuthToken" export * from "./repositoryAccess" export * from "./sessionValidity" -export * from "./guest" diff --git a/src/features/auth/domain/logIn/CompositeLogInHandler.ts b/src/features/auth/domain/logIn/CompositeLogInHandler.ts deleted file mode 100644 index 169be4c2..00000000 --- a/src/features/auth/domain/logIn/CompositeLogInHandler.ts +++ /dev/null @@ -1,14 +0,0 @@ -import ILogInHandler from "./ILogInHandler" - -export default class CompositeLogInHandler implements ILogInHandler { - private readonly handlers: ILogInHandler[] - - constructor(handlers: ILogInHandler[]) { - this.handlers = handlers - } - - async handleLogIn(userId: string): Promise { - const promises = this.handlers.map(e => e.handleLogIn(userId)) - await Promise.all(promises) - } -} diff --git a/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts b/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts deleted file mode 100644 index 552cbd15..00000000 --- a/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts +++ /dev/null @@ -1,42 +0,0 @@ -import ICredentialsTransferrer from "../credentialsTransfer/ICredentialsTransferrer" -import IIsUserGuestReader from "../guest/IIsUserGuestReader" -import ILogInHandler from "./ILogInHandler" - -export interface IRefreshTokenReader { - getRefreshToken(userId: string): Promise -} - -type CredentialsTransferringLogInHandlerConfig = { - readonly isUserGuestReader: IIsUserGuestReader - readonly guestCredentialsTransferrer: ICredentialsTransferrer - readonly hostCredentialsTransferrer: ICredentialsTransferrer -} - -export default class CredentialsTransferringLogInHandler implements ILogInHandler { - private readonly isUserGuestReader: IIsUserGuestReader - private readonly guestCredentialsTransferrer: ICredentialsTransferrer - private readonly hostCredentialsTransferrer: ICredentialsTransferrer - - constructor(config: CredentialsTransferringLogInHandlerConfig) { - this.isUserGuestReader = config.isUserGuestReader - this.guestCredentialsTransferrer = config.guestCredentialsTransferrer - this.hostCredentialsTransferrer = config.hostCredentialsTransferrer - } - - async handleLogIn(userId: string): Promise { - try { - const isGuest = await this.isUserGuestReader.getIsUserGuest(userId) - if (isGuest) { - await this.guestCredentialsTransferrer.transferCredentials(userId) - } else { - await this.hostCredentialsTransferrer.transferCredentials(userId) - } - } catch { - // It is safe to ignore the error. Transferring credentials is a - // "best-case scenario" that will always succeed unless the user - // is not a member of the GitHub organization or a guest user has - // been incorrectly configured. Either way, we allow the user to - // login an rely on the SessionBarrier to show an error later. - } - } -} diff --git a/src/features/auth/domain/logIn/ILogInHandler.ts b/src/features/auth/domain/logIn/ILogInHandler.ts deleted file mode 100644 index b0e0e0f4..00000000 --- a/src/features/auth/domain/logIn/ILogInHandler.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface ILogInHandler { - handleLogIn(userId: string): Promise -} diff --git a/src/features/auth/domain/logIn/index.ts b/src/features/auth/domain/logIn/index.ts deleted file mode 100644 index 21cfa0ce..00000000 --- a/src/features/auth/domain/logIn/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default as CompositeLogInHandler } from "./CompositeLogInHandler" -export { default as CredentialsTransferringLogInHandler } from "./CredentialsTransferringLogInHandler" -export type { default as ILogInHandler } from "./ILogInHandler" diff --git a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts index 678f83bd..5963aa53 100644 --- a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts +++ b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts @@ -1,28 +1,39 @@ -import { IUserDataRepository, UnauthorizedError, ZodJSONCoder } from "../../../../common" -import IOAuthTokenRepository from "./IOAuthTokenRepository" -import OAuthToken, { OAuthTokenSchema } from "./OAuthToken" +import { IDB, UnauthorizedError } from "../../../../common" +import { IOAuthTokenRepository, OAuthToken } from "." + +type OAuthTokenRepositoryConfig = { + readonly db: IDB +} export default class OAuthTokenRepository implements IOAuthTokenRepository { - private readonly repository: IUserDataRepository + private readonly db: IDB - constructor(repository: IUserDataRepository) { - this.repository = repository + constructor(config: OAuthTokenRepositoryConfig) { + this.db = config.db } async get(userId: string): Promise { - const string = await this.repository.get(userId) - if (!string) { - throw new UnauthorizedError(`No OAuthToken stored for user with ID ${userId}.`) + const query = "SELECT access_token, refresh_token FROM access_tokens WHERE user_id = $1" + const result = await this.db.query(query, [userId]) + if (result.rows.length == 0) { + throw new UnauthorizedError("The access token was not found. It appears that the user is not authenticated.") } - return ZodJSONCoder.decode(OAuthTokenSchema, string) + const row = result.rows[0] + const accessToken = row.access_token + const refreshToken = row.refresh_token + return { accessToken, refreshToken } } async set(userId: string, token: OAuthToken): Promise { - const string = ZodJSONCoder.encode(OAuthTokenSchema, token) - await this.repository.setExpiring(userId, string, 6 * 30 * 24 * 3600) + const query = ` + INSERT INTO access_tokens (user_id, access_token, refresh_token) + VALUES ($1, $2, $3) + ON CONFLICT (user_id) + DO UPDATE SET access_token = $2, refresh_token = $3;` + await this.db.query(query, [userId, token.accessToken, token.refreshToken]) } async delete(userId: string): Promise { - await this.repository.delete(userId) + await this.db.query("DELETE FROM access_tokens WHERE user_id = $1", [userId]) } } diff --git a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts index c46d9356..b64ea4bf 100644 --- a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts @@ -19,7 +19,8 @@ export default class AccessTokenSessionValidator { try { await this.accessTokenReader.getAccessToken() return SessionValidity.VALID - } catch { + } catch (error) { + console.error(error) return SessionValidity.INVALID_ACCESS_TOKEN } } diff --git a/src/features/auth/view/SessionBarrier.tsx b/src/features/auth/view/SessionBarrier.tsx index 15211ce9..d7c62839 100644 --- a/src/features/auth/view/SessionBarrier.tsx +++ b/src/features/auth/view/SessionBarrier.tsx @@ -1,4 +1,5 @@ import { ReactNode } from "react" +import { redirect } from "next/navigation" import { session, blockingSessionValidator } from "@/composition" import ClientSessionBarrier from "./client/SessionBarrier" @@ -12,6 +13,10 @@ export default async function SessionBarrier({ }: { children: ReactNode }) { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return redirect("/api/auth/signin") + } const getIsGuest = async () => { try { return await session.getIsGuest() diff --git a/src/middleware.ts b/src/middleware.ts deleted file mode 100644 index ab978fb1..00000000 --- a/src/middleware.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from "next-auth/middleware" - -export const config = { - matcher: "/((?!api/hooks|api/auth/signout|_next/static|_next/image|images|favicon.ico).*)" -} diff --git a/types/@next-auth.d.ts b/types/@next-auth.d.ts new file mode 100644 index 00000000..184b26d8 --- /dev/null +++ b/types/@next-auth.d.ts @@ -0,0 +1,9 @@ +import NextAuth from "next-auth" + +declare module "next-auth" { + interface Session { + user: { + id: string + } + } +} diff --git a/types/env.d.ts b/types/env.d.ts index 4dbcf968..dbb1daa8 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -13,6 +13,8 @@ namespace NodeJS { GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST?: string GITHUB_ORGANIZATION_NAME: string REDIS_URL: string - DATABASE_URL: string + POSTGRESQL_HOST: string + POSTGRESQL_USER: string + POSTGRESQL_DB: string } } From dcb525383c0d97f1bd1bad8a20ffd70c1bfecb38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 14:59:13 +0100 Subject: [PATCH 010/191] Clean up --- .../auth/domain/accessToken/AccessTokenRefresher.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts index 17315fcb..d49adcde 100644 --- a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts +++ b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts @@ -33,17 +33,7 @@ export default class AccessTokenRefresher implements IAccessTokenRefresher { return oAuthToken.accessToken } // Given access token is stale so we refresh it. - console.log("♻️ Refresh: " + oAuthToken.refreshToken) - let newOAuthToken: OAuthToken | undefined - try { - newOAuthToken = await this.oAuthTokenRefresher.refreshOAuthToken(oAuthToken.refreshToken) - } catch (error) { - // console.error(error) - // await this.oAuthTokenRepository.delete(userId) - throw new UnauthorizedError("Refresh token is invalid.") - } - console.log("💾 Store new token") - console.log(newOAuthToken) + const newOAuthToken = await this.oAuthTokenRefresher.refreshOAuthToken(oAuthToken.refreshToken) await this.oAuthTokenRepository.set(userId, newOAuthToken) return newOAuthToken.accessToken } From 61a51ecc7d2b86fd8edc253af5d5e73cd5da8c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 16:37:20 +0100 Subject: [PATCH 011/191] Removes config types --- src/common/db/PostgreSQLDB.ts | 5 +---- .../AccessTokenRefreshingGitHubClient.ts | 14 +++++++------- src/common/github/GitHubClient.ts | 18 +++++++++--------- src/common/session/AuthjsSession.ts | 6 +----- .../auth/data/AuthjsOAuthTokenRepository.ts | 7 +------ .../domain/accessToken/AccessTokenRefresher.ts | 16 +++++++--------- .../accessToken/LockingAccessTokenRefresher.ts | 12 ++++++------ .../TransferringAccessTokenReader.ts | 14 +++++++------- .../CachingRepositoryAccessReader.ts | 12 ++++++------ ...positoryRestrictingAccessTokenDataSource.ts | 12 ++++++------ .../AccessTokenSessionValidator.ts | 6 +----- .../GitHubOrganizationSessionValidator.ts | 12 ++++++------ .../HostOnlySessionValidator.ts | 12 ++++++------ .../projects/data/GitHubProjectDataSource.ts | 6 +----- .../data/GitHubProjectRepositoryDataSource.ts | 12 ++++++------ .../domain/CachingProjectDataSource.ts | 7 +------ .../projects/domain/ProjectNavigator.ts | 7 +------ 17 files changed, 73 insertions(+), 105 deletions(-) diff --git a/src/common/db/PostgreSQLDB.ts b/src/common/db/PostgreSQLDB.ts index 1b16afb8..cc2b1bd8 100644 --- a/src/common/db/PostgreSQLDB.ts +++ b/src/common/db/PostgreSQLDB.ts @@ -1,14 +1,11 @@ import { Pool, PoolClient } from "pg" import IDB, { IDBConnection, IDBQueryResult } from "./IDB" -type PostgreSQLDBConnectionConfig = { - readonly client: PoolClient -} export class PostgreSQLDBConnection implements IDBConnection { private readonly client: PoolClient - constructor(config: PostgreSQLDBConnectionConfig) { + constructor(config: { client: PoolClient }) { this.client = config.client } diff --git a/src/common/github/AccessTokenRefreshingGitHubClient.ts b/src/common/github/AccessTokenRefreshingGitHubClient.ts index d8a51f3b..dfe0b22c 100644 --- a/src/common/github/AccessTokenRefreshingGitHubClient.ts +++ b/src/common/github/AccessTokenRefreshingGitHubClient.ts @@ -23,18 +23,18 @@ interface IGitHubAccessTokenRefresher { refreshAccessToken(accessToken: string): Promise } -type AccessTokenRefreshingGitHubClientConfig = { - readonly accessTokenReader: IGitHubAccessTokenReader - readonly accessTokenRefresher: IGitHubAccessTokenRefresher - readonly gitHubClient: IGitHubClient -} - export default class AccessTokenRefreshingGitHubClient implements IGitHubClient { private readonly accessTokenReader: IGitHubAccessTokenReader private readonly accessTokenRefresher: IGitHubAccessTokenRefresher private readonly gitHubClient: IGitHubClient - constructor(config: AccessTokenRefreshingGitHubClientConfig) { + constructor( + config: { + accessTokenReader: IGitHubAccessTokenReader + accessTokenRefresher: IGitHubAccessTokenRefresher + gitHubClient: IGitHubClient + } + ) { this.accessTokenReader = config.accessTokenReader this.accessTokenRefresher = config.accessTokenRefresher this.gitHubClient = config.gitHubClient diff --git a/src/common/github/GitHubClient.ts b/src/common/github/GitHubClient.ts index cec899e9..3f41c0c2 100644 --- a/src/common/github/GitHubClient.ts +++ b/src/common/github/GitHubClient.ts @@ -12,14 +12,6 @@ import IGitHubClient, { PullRequestComment } from "./IGitHubClient" -type GitHubClientConfig = { - readonly appId: string - readonly clientId: string - readonly clientSecret: string - readonly privateKey: string - readonly accessTokenReader: IGitHubAccessTokenReader -} - interface IGitHubAccessTokenReader { getAccessToken(): Promise } @@ -32,7 +24,15 @@ export default class GitHubClient implements IGitHubClient { private readonly accessTokenReader: IGitHubAccessTokenReader private readonly installationAuthenticator: InstallationAuthenticator - constructor(config: GitHubClientConfig) { + constructor( + config: { + appId: string + clientId: string + clientSecret: string + privateKey: string + accessTokenReader: IGitHubAccessTokenReader + } + ) { this.accessTokenReader = config.accessTokenReader const appAuth = createAppAuth({ appId: config.appId, diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index be926b01..f6dd6a62 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -2,14 +2,10 @@ import { NextAuthOptions } from "next-auth" import { getServerSession } from "next-auth/next" import { UnauthorizedError } from "../../common" -export type AuthSessionConfig = { - readonly authOptions: NextAuthOptions -} - export default class AuthjsSession { private readonly authOptions: NextAuthOptions - constructor(config: AuthSessionConfig) { + constructor(config: { authOptions: NextAuthOptions }) { this.authOptions = config.authOptions } diff --git a/src/features/auth/data/AuthjsOAuthTokenRepository.ts b/src/features/auth/data/AuthjsOAuthTokenRepository.ts index b92e50d0..a80112bd 100644 --- a/src/features/auth/data/AuthjsOAuthTokenRepository.ts +++ b/src/features/auth/data/AuthjsOAuthTokenRepository.ts @@ -1,16 +1,11 @@ import { IDB, UnauthorizedError } from "../../../common" import { IOAuthTokenRepository, OAuthToken } from "../domain" -type AuthjsOAuthTokenRepositoryConfig = { - readonly db: IDB - readonly provider: string -} - export default class AuthjsOAuthTokenRepository implements IOAuthTokenRepository { private readonly db: IDB private readonly provider: string - constructor(config: AuthjsOAuthTokenRepositoryConfig) { + constructor(config: { db: IDB, provider: string }) { this.db = config.db this.provider = config.provider } diff --git a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts index d49adcde..add96af1 100644 --- a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts +++ b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts @@ -1,25 +1,23 @@ import IAccessTokenRefresher from "./IAccessTokenRefresher" import IOAuthTokenRepository from "../oAuthToken/IOAuthTokenRepository" import IOAuthTokenRefresher from "../oAuthToken/IOAuthTokenRefresher" -import OAuthToken from "../oAuthToken/OAuthToken" -import { UnauthorizedError } from "@/common" interface IUserIDReader { getUserId(): Promise } -type HostAccessTokenServiceConfig = { - readonly userIdReader: IUserIDReader - readonly oAuthTokenRepository: IOAuthTokenRepository - readonly oAuthTokenRefresher: IOAuthTokenRefresher -} - export default class AccessTokenRefresher implements IAccessTokenRefresher { private readonly userIdReader: IUserIDReader private readonly oAuthTokenRepository: IOAuthTokenRepository private readonly oAuthTokenRefresher: IOAuthTokenRefresher - constructor(config: HostAccessTokenServiceConfig) { + constructor( + config: { + userIdReader: IUserIDReader + oAuthTokenRepository: IOAuthTokenRepository + oAuthTokenRefresher: IOAuthTokenRefresher + } + ) { this.userIdReader = config.userIdReader this.oAuthTokenRepository = config.oAuthTokenRepository this.oAuthTokenRefresher = config.oAuthTokenRefresher diff --git a/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts b/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts index e72d9bc4..c8b61f2d 100644 --- a/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts +++ b/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts @@ -2,16 +2,16 @@ import { IMutexFactory } from "@/common" import withMutex from "../../../../common/mutex/withMutex" import IAccessTokenRefresher from "./IAccessTokenRefresher" -type LockingAccessTokenRefresherConfig = { - readonly mutexFactory: IMutexFactory - readonly accessTokenRefresher: IAccessTokenRefresher -} - export default class LockingAccessTokenRefresher implements IAccessTokenRefresher { private readonly mutexFactory: IMutexFactory private readonly accessTokenRefresher: IAccessTokenRefresher - constructor(config: LockingAccessTokenRefresherConfig) { + constructor( + config: { + mutexFactory: IMutexFactory + accessTokenRefresher: IAccessTokenRefresher + } + ) { this.mutexFactory = config.mutexFactory this.accessTokenRefresher = config.accessTokenRefresher } diff --git a/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts b/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts index 00a1a96d..ef98c790 100644 --- a/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts +++ b/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts @@ -4,18 +4,18 @@ interface IUserIDReader { getUserId(): Promise } -type TransferringAccessTokenReaderConfig = { - readonly userIdReader: IUserIDReader - readonly sourceOAuthTokenRepository: IOAuthTokenRepository - readonly destinationOAuthTokenRepository: IOAuthTokenRepository -} - export default class TransferringAccessTokenReader { private readonly userIdReader: IUserIDReader private readonly sourceOAuthTokenRepository: IOAuthTokenRepository private readonly destinationOAuthTokenRepository: IOAuthTokenRepository - constructor(config: TransferringAccessTokenReaderConfig) { + constructor( + config: { + userIdReader: IUserIDReader + sourceOAuthTokenRepository: IOAuthTokenRepository + destinationOAuthTokenRepository: IOAuthTokenRepository + } + ) { this.userIdReader = config.userIdReader this.sourceOAuthTokenRepository = config.sourceOAuthTokenRepository this.destinationOAuthTokenRepository = config.destinationOAuthTokenRepository diff --git a/src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts b/src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts index 02405b43..9bff92c6 100644 --- a/src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts +++ b/src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts @@ -7,18 +7,18 @@ interface IRepositoryAccessReader { getRepositoryNames(userId: string): Promise } -type CachingRepositoryAccessReaderConfig = { - readonly repository: IRepositoryNameRepository - readonly repositoryAccessReader: IRepositoryAccessReader -} - type IRepositoryNameRepository = IUserDataRepository export default class CachingRepositoryAccessReader { private readonly repository: IRepositoryNameRepository private readonly repositoryAccessReader: IRepositoryAccessReader - constructor(config: CachingRepositoryAccessReaderConfig) { + constructor( + config: { + repository: IRepositoryNameRepository + repositoryAccessReader: IRepositoryAccessReader + } + ) { this.repository = config.repository this.repositoryAccessReader = config.repositoryAccessReader } diff --git a/src/features/auth/domain/repositoryAccess/RepositoryRestrictingAccessTokenDataSource.ts b/src/features/auth/domain/repositoryAccess/RepositoryRestrictingAccessTokenDataSource.ts index 0bb4b4d2..c4f055b9 100644 --- a/src/features/auth/domain/repositoryAccess/RepositoryRestrictingAccessTokenDataSource.ts +++ b/src/features/auth/domain/repositoryAccess/RepositoryRestrictingAccessTokenDataSource.ts @@ -6,16 +6,16 @@ interface IAccessTokenDataSource { getAccessToken(repositoryNames: string[]): Promise } -type RepositoryRestrictingAccessTokenDataSourceConfig = { - readonly repositoryAccessReader: IRepositoryAccessReader - readonly dataSource: IAccessTokenDataSource -} - export default class RepositoryRestrictingAccessTokenDataSource { private readonly repositoryAccessReader: IRepositoryAccessReader private readonly dataSource: IAccessTokenDataSource - constructor(config: RepositoryRestrictingAccessTokenDataSourceConfig) { + constructor( + config: { + repositoryAccessReader: IRepositoryAccessReader + dataSource: IAccessTokenDataSource + } + ) { this.repositoryAccessReader = config.repositoryAccessReader this.dataSource = config.dataSource } diff --git a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts index b64ea4bf..f3fc7453 100644 --- a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts @@ -4,14 +4,10 @@ interface IAccessTokenReader { getAccessToken(): Promise } -type AccessTokenSessionValidatorConfig = { - readonly accessTokenReader: IAccessTokenReader -} - export default class AccessTokenSessionValidator { private readonly accessTokenReader: IAccessTokenReader - constructor(config: AccessTokenSessionValidatorConfig) { + constructor(config: { accessTokenReader: IAccessTokenReader }) { this.accessTokenReader = config.accessTokenReader } diff --git a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts index 7f8aba6b..9d1f6f62 100644 --- a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts @@ -10,16 +10,16 @@ interface IOrganizationMembershipStatusReader { ): Promise } -type GitHubOrganizationSessionValidatorConfig = { - readonly acceptedOrganization: string - readonly organizationMembershipStatusReader: IOrganizationMembershipStatusReader -} - export default class GitHubOrganizationSessionValidator { private readonly acceptedOrganization: string private readonly organizationMembershipStatusReader: IOrganizationMembershipStatusReader - constructor(config: GitHubOrganizationSessionValidatorConfig) { + constructor( + config: { + acceptedOrganization: string + organizationMembershipStatusReader: IOrganizationMembershipStatusReader + } + ) { this.acceptedOrganization = config.acceptedOrganization this.organizationMembershipStatusReader = config.organizationMembershipStatusReader } diff --git a/src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts b/src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts index 08220a67..2fee68a5 100644 --- a/src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts @@ -8,16 +8,16 @@ interface ISessionValidator { validateSession(): Promise } -type HostOnlySessionValidatorConfig = { - readonly isGuestReader: IIsGuestReader - readonly sessionValidator: ISessionValidator -} - export default class HostOnlySessionValidator { private readonly isGuestReader: IIsGuestReader private readonly sessionValidator: ISessionValidator - constructor(config: HostOnlySessionValidatorConfig) { + constructor( + config: { + isGuestReader: IIsGuestReader + sessionValidator: ISessionValidator + } + ) { this.isGuestReader = config.isGuestReader this.sessionValidator = config.sessionValidator } diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index 52580197..fe926269 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -14,14 +14,10 @@ interface IGitHubProjectRepositoryDataSource { getRepositories(): Promise } -type GitHubProjectDataSourceConfig = { - readonly dataSource: IGitHubProjectRepositoryDataSource -} - export default class GitHubProjectDataSource implements IProjectDataSource { private dataSource: IGitHubProjectRepositoryDataSource - constructor(config: GitHubProjectDataSourceConfig) { + constructor(config: { dataSource: IGitHubProjectRepositoryDataSource }) { this.dataSource = config.dataSource } diff --git a/src/features/projects/data/GitHubProjectRepositoryDataSource.ts b/src/features/projects/data/GitHubProjectRepositoryDataSource.ts index 2373df7a..b9cc67f0 100644 --- a/src/features/projects/data/GitHubProjectRepositoryDataSource.ts +++ b/src/features/projects/data/GitHubProjectRepositoryDataSource.ts @@ -15,16 +15,16 @@ interface IGitHubGraphQLClient { graphql(request: GitHubGraphQLClientRequest): Promise } -type GitHubProjectRepositoryDataSourceConfig = { - readonly graphQlClient: IGitHubGraphQLClient - readonly organizationName: string -} - export default class GitHubProjectRepositoryDataSource { private graphQlClient: IGitHubGraphQLClient private organizationName: string - constructor(config: GitHubProjectRepositoryDataSourceConfig) { + constructor( + config: { + graphQlClient: IGitHubGraphQLClient + organizationName: string + } + ) { this.graphQlClient = config.graphQlClient this.organizationName = config.organizationName } diff --git a/src/features/projects/domain/CachingProjectDataSource.ts b/src/features/projects/domain/CachingProjectDataSource.ts index f47f4160..48c6fb01 100644 --- a/src/features/projects/domain/CachingProjectDataSource.ts +++ b/src/features/projects/domain/CachingProjectDataSource.ts @@ -2,16 +2,11 @@ import Project from "./Project" import IProjectDataSource from "./IProjectDataSource" import IProjectRepository from "./IProjectRepository" -type CachingProjectDataSourceConfig = { - readonly dataSource: IProjectDataSource - readonly repository: IProjectRepository -} - export default class CachingProjectDataSource implements IProjectDataSource { private dataSource: IProjectDataSource private repository: IProjectRepository - constructor(config: CachingProjectDataSourceConfig) { + constructor(config: { dataSource: IProjectDataSource, repository: IProjectRepository }) { this.dataSource = config.dataSource this.repository = config.repository } diff --git a/src/features/projects/domain/ProjectNavigator.ts b/src/features/projects/domain/ProjectNavigator.ts index 1fcd8933..f7bf9e6f 100644 --- a/src/features/projects/domain/ProjectNavigator.ts +++ b/src/features/projects/domain/ProjectNavigator.ts @@ -9,16 +9,11 @@ export interface IRouter { replace(path: string): void } -type ProjectNavigatorConfig = { - readonly pathnameReader: IPathnameReader - readonly router: IRouter -} - export default class ProjectNavigator { private readonly pathnameReader: IPathnameReader private readonly router: IRouter - constructor(config: ProjectNavigatorConfig) { + constructor(config: { pathnameReader: IPathnameReader, router: IRouter }) { this.pathnameReader = config.pathnameReader this.router = config.router } From d532fa2921139add4ba5a8760bbb76810c8110a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 16:44:36 +0100 Subject: [PATCH 012/191] Stores initial OAuth credentials --- create-tables.sql | 6 +- dump.rdb | Bin 9241 -> 0 bytes src/composition.ts | 22 +++++--- src/features/auth/domain/index.ts | 1 + .../auth/domain/logIn/ILogInHandler.ts | 10 ++++ ...AccountCredentialPersistingLogInHandler.ts | 43 +++++++++++++++ src/features/auth/domain/logIn/index.ts | 2 + .../domain/oAuthToken/OAuthTokenRepository.ts | 52 ++++++++++++++---- 8 files changed, 114 insertions(+), 22 deletions(-) delete mode 100644 dump.rdb create mode 100644 src/features/auth/domain/logIn/ILogInHandler.ts create mode 100644 src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts create mode 100644 src/features/auth/domain/logIn/index.ts diff --git a/create-tables.sql b/create-tables.sql index 78fbbd92..6bed5983 100644 --- a/create-tables.sql +++ b/create-tables.sql @@ -48,9 +48,11 @@ CREATE TABLE users CREATE TABLE access_tokens ( - user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, + provider VARCHAR(255) NOT NULL, + provider_account_id VARCHAR(255) NOT NULL, access_token VARCHAR(255) NOT NULL, refresh_token VARCHAR(255) NOT NULL, + last_updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (user_id) + PRIMARY KEY (provider, provider_account_id) ); diff --git a/dump.rdb b/dump.rdb deleted file mode 100644 index 681fbf7717462d79e47c59c8b1cc70c5ac6ebdaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9241 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 +} diff --git a/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts b/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts new file mode 100644 index 00000000..a3284967 --- /dev/null +++ b/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts @@ -0,0 +1,43 @@ +import { IDB } from "@/common" +import { ILogInHandler, IAccount, ISession, IUser } from "." + +export default class OAuthAccountCredentialPersistingLogInHandler implements ILogInHandler { + private readonly db: IDB + private readonly provider: string + + constructor(config: { db: IDB, provider: string }) { + this.db = config.db + this.provider = config.provider + } + + async handleLogIn(_userId: string, account?: IAccount): Promise { + if (!account) { + return true + } + if (account.provider !== this.provider) { + return true + } + if (!account.providerAccountId || !account.access_token || !account.refresh_token) { + return false + } + const query = ` + INSERT INTO access_tokens ( + provider, + provider_account_id, + access_token, + refresh_token + ) + VALUES ($1, $2, $3, $4) + ON CONFLICT (provider, provider_account_id) + DO UPDATE SET access_token = $3, refresh_token = $4, last_updated_at = NOW(); + ` + await this.db.query(query, [ + account.provider, + account.providerAccountId, + account.access_token, + account.refresh_token + ]) + console.log("💾 DID PERSIST") + return true + } +} diff --git a/src/features/auth/domain/logIn/index.ts b/src/features/auth/domain/logIn/index.ts new file mode 100644 index 00000000..083d9540 --- /dev/null +++ b/src/features/auth/domain/logIn/index.ts @@ -0,0 +1,2 @@ +export type { default as ILogInHandler, IAccount, ISession, IUser } from "./ILogInHandler" +export { default as OAuthAccountCredentialPersistingLogInHandler } from "./OAuthAccountCredentialPersistingLogInHandler" diff --git a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts index 5963aa53..112e4ea9 100644 --- a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts +++ b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts @@ -1,20 +1,29 @@ import { IDB, UnauthorizedError } from "../../../../common" import { IOAuthTokenRepository, OAuthToken } from "." -type OAuthTokenRepositoryConfig = { - readonly db: IDB -} - export default class OAuthTokenRepository implements IOAuthTokenRepository { + private readonly provider: string private readonly db: IDB - constructor(config: OAuthTokenRepositoryConfig) { + constructor(config: { provider: string, db: IDB }) { + this.provider = config.provider this.db = config.db } async get(userId: string): Promise { - const query = "SELECT access_token, refresh_token FROM access_tokens WHERE user_id = $1" - const result = await this.db.query(query, [userId]) + const query = ` + SELECT + access_tokens.access_token, + access_tokens.refresh_token + FROM + accounts + INNER JOIN + access_tokens ON access_tokens.provider_account_id = accounts."providerAccountId" + WHERE + access_tokens.provider = $1 + AND accounts."userId" = $2; + ` + const result = await this.db.query(query, [this.provider, userId]) if (result.rows.length == 0) { throw new UnauthorizedError("The access token was not found. It appears that the user is not authenticated.") } @@ -26,11 +35,30 @@ export default class OAuthTokenRepository implements IOAuthTokenRepository { async set(userId: string, token: OAuthToken): Promise { const query = ` - INSERT INTO access_tokens (user_id, access_token, refresh_token) - VALUES ($1, $2, $3) - ON CONFLICT (user_id) - DO UPDATE SET access_token = $2, refresh_token = $3;` - await this.db.query(query, [userId, token.accessToken, token.refreshToken]) + INSERT INTO access_tokens ( + provider, + provider_account_id, + access_token, + refresh_token + ) + SELECT + $2, + "providerAccountId", + $3, + $4 + FROM + accounts + WHERE + accounts."userId" = $1 + ON CONFLICT (provider, provider_account_id) + DO UPDATE SET access_token = excluded.access_token, refresh_token = excluded.refresh_token, last_updated_at = NOW(); + ` + try { + await this.db.query(query, [userId, this.provider, token.accessToken, token.refreshToken]) + } catch (error) { + console.error(error) + throw error + } } async delete(userId: string): Promise { From deb7dd73980011441aa9595612059f356b6d6ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 16:47:55 +0100 Subject: [PATCH 013/191] Clean up --- .../logIn/OAuthAccountCredentialPersistingLogInHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts b/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts index a3284967..222e7aee 100644 --- a/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts +++ b/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts @@ -1,5 +1,5 @@ import { IDB } from "@/common" -import { ILogInHandler, IAccount, ISession, IUser } from "." +import { ILogInHandler, IAccount } from "." export default class OAuthAccountCredentialPersistingLogInHandler implements ILogInHandler { private readonly db: IDB From 69eaefaa10a6efc7cce318b747244f7aa67e9f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 16:55:25 +0100 Subject: [PATCH 014/191] Adds unit tests for OAuthAccountCredentialPersistingLogInHandler --- ...ntCredentialPersistingLogInHandler.test.ts | 173 ++++++++++++++++++ ...AccountCredentialPersistingLogInHandler.ts | 3 +- src/features/auth/domain/logIn/index.ts | 2 +- 3 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 __test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts diff --git a/__test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts b/__test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts new file mode 100644 index 00000000..25a00652 --- /dev/null +++ b/__test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts @@ -0,0 +1,173 @@ +import { OAuthAccountCredentialPersistingLogInHandler } from "../../src/features/auth/domain" + +test("It skips handling log in if account is unavailable", async () => { + let didQuery = false + const sut = new OAuthAccountCredentialPersistingLogInHandler({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query() { + didQuery = true + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + const allowLogin = await sut.handleLogIn("42") + expect(allowLogin).toBeTruthy() + expect(didQuery).toBeFalsy() +}) + +test("It skips handling log in if account does not come from the expected provider", async () => { + let didQuery = false + const sut = new OAuthAccountCredentialPersistingLogInHandler({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query() { + didQuery = true + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + const allowLogin = await sut.handleLogIn("42", { + provider: "twitter", + providerAccountId: "1234", + access_token: "foo", + refresh_token: "bar" + }) + expect(allowLogin).toBeTruthy() + expect(didQuery).toBeFalsy() +}) + +test("It disallows login if account has no access token", async () => { + let didQuery = false + const sut = new OAuthAccountCredentialPersistingLogInHandler({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query() { + didQuery = true + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + const allowLogin = await sut.handleLogIn("42", { + provider: "twitter", + providerAccountId: "1234", + refresh_token: "bar" + }) + expect(allowLogin).toBeTruthy() + expect(didQuery).toBeFalsy() +}) + +test("It disallows login if account has no refresh token", async () => { + let didQuery = false + const sut = new OAuthAccountCredentialPersistingLogInHandler({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query() { + didQuery = true + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + const allowLogin = await sut.handleLogIn("42", { + provider: "twitter", + providerAccountId: "1234", + access_token: "foo" + }) + expect(allowLogin).toBeTruthy() + expect(didQuery).toBeFalsy() +}) + +test("It stores OAuth account information", async () => { + let storedProvider: string | undefined + let storedProviderAccountID: string | undefined + let storedAccessToken: string | undefined + let storedRefreshToken: string | undefined + const sut = new OAuthAccountCredentialPersistingLogInHandler({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query: string, values: any[] = []) { + storedProvider = values[0] + storedProviderAccountID = values[1] + storedAccessToken = values[2] + storedRefreshToken = values[3] + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + const allowLogin = await sut.handleLogIn("42", { + provider: "github", + providerAccountId: "1234", + access_token: "foo", + refresh_token: "bar" + }) + expect(allowLogin).toBeTruthy() + expect(storedProvider).toBe("github") + expect(storedProviderAccountID).toBe("1234") + expect(storedAccessToken).toBe("foo") + expect(storedRefreshToken).toBe("bar") +}) diff --git a/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts b/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts index 222e7aee..4938932b 100644 --- a/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts +++ b/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts @@ -17,7 +17,7 @@ export default class OAuthAccountCredentialPersistingLogInHandler implements ILo if (account.provider !== this.provider) { return true } - if (!account.providerAccountId || !account.access_token || !account.refresh_token) { + if (!account.access_token || !account.refresh_token) { return false } const query = ` @@ -37,7 +37,6 @@ export default class OAuthAccountCredentialPersistingLogInHandler implements ILo account.access_token, account.refresh_token ]) - console.log("💾 DID PERSIST") return true } } diff --git a/src/features/auth/domain/logIn/index.ts b/src/features/auth/domain/logIn/index.ts index 083d9540..cef36bee 100644 --- a/src/features/auth/domain/logIn/index.ts +++ b/src/features/auth/domain/logIn/index.ts @@ -1,2 +1,2 @@ -export type { default as ILogInHandler, IAccount, ISession, IUser } from "./ILogInHandler" +export type { default as ILogInHandler, IAccount } from "./ILogInHandler" export { default as OAuthAccountCredentialPersistingLogInHandler } from "./OAuthAccountCredentialPersistingLogInHandler" From c8d8282a6d4d6c1e66b630701318853b27bc7d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 17:52:40 +0100 Subject: [PATCH 015/191] Clean up --- __test__/auth/HostOnlySessionValidator.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/__test__/auth/HostOnlySessionValidator.test.ts b/__test__/auth/HostOnlySessionValidator.test.ts index daf60611..c0f44838 100644 --- a/__test__/auth/HostOnlySessionValidator.test.ts +++ b/__test__/auth/HostOnlySessionValidator.test.ts @@ -22,7 +22,6 @@ test("It validates session when user is host", async () => { expect(didValidateSession).toBeTruthy() }) - test("It does not validate session when user is guest", async () => { let didValidateSession = false const sut = new HostOnlySessionValidator({ From 2017542f694423b2ccdf24089dda89a0754985de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 17:52:47 +0100 Subject: [PATCH 016/191] Fixes unit test --- __test__/auth/OAuthTokenRepository.test.ts | 17 +++++++++++++---- .../domain/oAuthToken/OAuthTokenRepository.ts | 6 +++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/__test__/auth/OAuthTokenRepository.test.ts b/__test__/auth/OAuthTokenRepository.test.ts index baefd102..06e2b664 100644 --- a/__test__/auth/OAuthTokenRepository.test.ts +++ b/__test__/auth/OAuthTokenRepository.test.ts @@ -1,8 +1,10 @@ import { OAuthTokenRepository } from "../../src/features/auth/domain" test("It reads the auth token for the specified user", async () => { + let readProvider: string | undefined let readUserId: string | undefined const sut = new OAuthTokenRepository({ + provider: "github", db: { async connect() { return { @@ -13,7 +15,8 @@ test("It reads the auth token for the specified user", async () => { } }, async query(_query: string, values: any[] = []) { - readUserId = values[0] + readProvider = values[0] + readUserId = values[1] return { rows: [{ access_token: "foo", @@ -24,16 +27,19 @@ test("It reads the auth token for the specified user", async () => { } }) const token = await sut.get("1234") + expect(readProvider).toBe("github") expect(readUserId).toBe("1234") expect(token.accessToken).toBe("foo") expect(token.refreshToken).toBe("bar") }) test("It stores the auth token for the specified user", async () => { + let storedProvider: string | undefined let storedUserId: string | undefined let storedAccessToken: any | undefined let storedRefreshToken: any | undefined const sut = new OAuthTokenRepository({ + provider: "github", db: { async connect() { return { @@ -44,9 +50,10 @@ test("It stores the auth token for the specified user", async () => { } }, async query(_query: string, values: any[] = []) { - storedUserId = values[0] - storedAccessToken = values[1] - storedRefreshToken = values[2] + storedProvider = values[0] + storedUserId = values[1] + storedAccessToken = values[2] + storedRefreshToken = values[3] return { rows: [] } } } @@ -56,6 +63,7 @@ test("It stores the auth token for the specified user", async () => { refreshToken: "bar" } await sut.set("1234", authToken) + expect(storedProvider).toBe("github") expect(storedUserId).toBe("1234") expect(storedAccessToken).toBe(authToken.accessToken) expect(storedRefreshToken).toBe(authToken.refreshToken) @@ -64,6 +72,7 @@ test("It stores the auth token for the specified user", async () => { test("It deletes the auth token for the specified user", async () => { let deletedUserId: string | undefined const sut = new OAuthTokenRepository({ + provider: "github", db: { async connect() { return { diff --git a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts index 112e4ea9..73d278a8 100644 --- a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts +++ b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts @@ -42,19 +42,19 @@ export default class OAuthTokenRepository implements IOAuthTokenRepository { refresh_token ) SELECT - $2, + $1, "providerAccountId", $3, $4 FROM accounts WHERE - accounts."userId" = $1 + accounts."userId" = $2 ON CONFLICT (provider, provider_account_id) DO UPDATE SET access_token = excluded.access_token, refresh_token = excluded.refresh_token, last_updated_at = NOW(); ` try { - await this.db.query(query, [userId, this.provider, token.accessToken, token.refreshToken]) + await this.db.query(query, [this.provider, userId, token.accessToken, token.refreshToken]) } catch (error) { console.error(error) throw error From c30cba23e7ec882cd420f6102b997b1534ebab35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 17:53:33 +0100 Subject: [PATCH 017/191] Removes GitHubProjectRepositoryDataSource --- .../projects/GitHubProjectDataSource.test.ts | 1738 +++++++++-------- .../GitHubProjectRepositoryDataSource.test.ts | 20 - src/composition.ts | 9 +- .../projects/data/GitHubProjectDataSource.ts | 101 +- .../data/GitHubProjectRepositoryDataSource.ts | 102 - src/features/projects/data/index.ts | 2 - 6 files changed, 1022 insertions(+), 950 deletions(-) delete mode 100644 __test__/projects/GitHubProjectRepositoryDataSource.test.ts delete mode 100644 src/features/projects/data/GitHubProjectRepositoryDataSource.ts diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index d0efbb3e..2be08923 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -5,10 +5,15 @@ import { test("It loads repositories from data source", async () => { let didLoadRepositories = false const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { + organizationName: "foo", + graphQlClient: { + async graphql() { didLoadRepositories = true - return [] + return { + search: { + results: [] + } + } } } }) @@ -18,50 +23,55 @@ test("It loads repositories from data source", async () => { test("It maps projects including branches and tags", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -99,50 +109,55 @@ test("It maps projects including branches and tags", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -154,54 +169,59 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It supports multiple OpenAPI specifications on a branch", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "foo-service.yml" - }, { - name: "bar-service.yml" - }, { - name: "baz-service.yml" - }] - } + oid: "12345678" } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "foo-service.yml" + }, { + name: "bar-service.yml" + }, { + name: "baz-service.yml" + }] + } + } } - } + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -249,50 +269,55 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -304,26 +329,31 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It filters away projects with no versions", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [] - }, - tags: { - edges: [] + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { + name: "main", + target: { + oid: "12345678" + } + }, + branches: { + edges: [] + }, + tags: { + edges: [] + } + }] } - }] + } } } }) @@ -333,50 +363,55 @@ test("It filters away projects with no versions", async () => { test("It filters away branches with no specifications", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }, { - node: { - name: "bugfix", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "foo.txt" - }] + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }, { + node: { + name: "bugfix", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "foo.txt" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -386,62 +421,67 @@ test("It filters away branches with no specifications", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }, { - node: { - name: "1.1", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "foo.txt" - }] + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }, { + node: { + name: "1.1", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "foo.txt" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -451,41 +491,46 @@ test("It filters away tags with no specifications", async () => { test("It reads image from .shape-config.yml", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - configYml: { - text: "image: icon.png" - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + configYml: { + text: "image: icon.png" + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -495,62 +540,67 @@ test("It reads image from .shape-config.yml", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }, { - node: { - name: "1.1", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "foo.txt" - }] + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }, { + node: { + name: "1.1", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "foo.txt" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -560,41 +610,46 @@ test("It filters away tags with no specifications", async () => { test("It reads display name from .shape-config.yml", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - configYml: { - text: "name: Hello World" - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + configYml: { + text: "name: Hello World" + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -606,41 +661,46 @@ test("It reads display name from .shape-config.yml", async () => { test("It reads image from .shape-config.yml", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - configYml: { - text: "image: icon.png" - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + configYml: { + text: "image: icon.png" + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -650,41 +710,46 @@ test("It reads image from .shape-config.yml", async () => { test("It reads display name from .shape-config.yaml", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - configYaml: { - text: "name: Hello World" - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + configYaml: { + text: "name: Hello World" + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -696,41 +761,46 @@ test("It reads display name from .shape-config.yaml", async () => { test("It reads image from .shape-config.yaml", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo-openapi", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - configYaml: { - text: "image: icon.png" - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo-openapi", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + configYaml: { + text: "image: icon.png" + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -740,96 +810,101 @@ test("It reads image from .shape-config.yaml", async () => { test("It sorts projects alphabetically", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "cathrine", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "cathrine", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } - }] - }, - tags: { - edges: [] - } - }, { - name: "anne", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + }, { + name: "anne", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } - }] - }, - tags: { - edges: [] - } - }, { - name: "bobby", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { + }, { + name: "bobby", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + branches: { + edges: [{ + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -841,74 +916,79 @@ test("It sorts projects alphabetically", async () => { test("It sorts versions alphabetically", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { - name: "bobby", + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { + name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }, { - node: { - name: "anne", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "bobby", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, { + node: { + name: "anne", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }, { - node: { - name: "cathrine", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }, { + node: { + name: "cathrine", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -921,98 +1001,103 @@ test("It sorts versions alphabetically", async () => { test("It prioritizes main, master, develop, and development branch names when sorting verisons", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { - name: "anne", + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { + name: "main", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }, { - node: { - name: "develop", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "anne", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }, { - node: { - name: "main", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, { + node: { + name: "develop", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }, { - node: { - name: "development", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, { + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }, { - node: { - name: "master", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, { + node: { + name: "development", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }] - }, - tags: { - edges: [{ - node: { - name: "1.0", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, { + node: { + name: "master", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }] + }, + tags: { + edges: [{ + node: { + name: "1.0", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] } }] } - }] + } } } }) @@ -1027,62 +1112,67 @@ test("It prioritizes main, master, develop, and development branch names when so test("It identifies the default branch in returned versions", async () => { const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "development", - target: { - oid: "12345678" - } - }, - branches: { - edges: [{ - node: { - name: "anne", + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { + name: "development", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } - } - }, { - node: { - name: "main", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, + branches: { + edges: [{ + node: { + name: "anne", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } - } - }, { - node: { - name: "development", - target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] + }, { + node: { + name: "main", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } } - } + }, { + node: { + name: "development", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -1109,29 +1199,34 @@ test("It adds remote versions from the project configuration", async () => { url: https://example.com/louie.yml ` const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "main", - target: { - oid: "12345678" - } - }, - configYml: { - text: rawProjectConfig - }, - branches: { - edges: [] - }, - tags: { - edges: [] + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { + name: "main", + target: { + oid: "12345678" + } + }, + configYml: { + text: rawProjectConfig + }, + branches: { + edges: [] + }, + tags: { + edges: [] + } + }] } - }] + } } } }) @@ -1174,41 +1269,46 @@ test("It modifies ID of remote version if the ID already exists", async () => { url: https://example.com/hello.yml ` const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "bar", - target: { - oid: "12345678" - } - }, - configYml: { - text: rawProjectConfig - }, - branches: { - edges: [{ - node: { + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { name: "bar", target: { - oid: "12345678", - tree: { - entries: [{ - name: "openapi.yml" - }] - } + oid: "12345678" } + }, + configYml: { + text: rawProjectConfig + }, + branches: { + edges: [{ + node: { + name: "bar", + target: { + oid: "12345678", + tree: { + entries: [{ + name: "openapi.yml" + }] + } + } + } + }] + }, + tags: { + edges: [] } }] - }, - tags: { - edges: [] } - }] + } } } }) @@ -1255,29 +1355,34 @@ test("It lets users specify the ID of a remote version", async () => { url: https://example.com/baz.yml ` const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "bar", - target: { - oid: "12345678" - } - }, - configYml: { - text: rawProjectConfig - }, - branches: { - edges: [] - }, - tags: { - edges: [] + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { + name: "bar", + target: { + oid: "12345678" + } + }, + configYml: { + text: rawProjectConfig + }, + branches: { + edges: [] + }, + tags: { + edges: [] + } + }] } - }] + } } } }) @@ -1304,29 +1409,34 @@ test("It lets users specify the ID of a remote specification", async () => { url: https://example.com/baz.yml ` const sut = new GitHubProjectDataSource({ - dataSource: { - async getRepositories() { - return [{ - name: "foo", - owner: { - login: "acme" - }, - defaultBranchRef: { - name: "bar", - target: { - oid: "12345678" - } - }, - configYml: { - text: rawProjectConfig - }, - branches: { - edges: [] - }, - tags: { - edges: [] + organizationName: "foo", + graphQlClient: { + async graphql() { + return { + search: { + results: [{ + name: "foo", + owner: { + login: "acme" + }, + defaultBranchRef: { + name: "bar", + target: { + oid: "12345678" + } + }, + configYml: { + text: rawProjectConfig + }, + branches: { + edges: [] + }, + tags: { + edges: [] + } + }] } - }] + } } } }) diff --git a/__test__/projects/GitHubProjectRepositoryDataSource.test.ts b/__test__/projects/GitHubProjectRepositoryDataSource.test.ts deleted file mode 100644 index 7d8d79fd..00000000 --- a/__test__/projects/GitHubProjectRepositoryDataSource.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - GitHubProjectRepositoryDataSource, - GitHubGraphQLClientRequest - } from "../../src/features/projects/data" - -test("It requests data for the specified organization", async () => { - let sentRequest: GitHubGraphQLClientRequest | undefined - const sut = new GitHubProjectRepositoryDataSource({ - organizationName: "foo", - graphQlClient: { - async graphql(request) { - sentRequest = request - return { search: { results: [] } } - } - } - }) - await sut.getRepositories() - expect(sentRequest).not.toBeUndefined() - expect(sentRequest?.variables.searchQuery).toContain("org:foo") -}) diff --git a/src/composition.ts b/src/composition.ts index db1cc7cd..18d47b49 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -14,8 +14,7 @@ import { SessionMutexFactory } from "@/common" import { - GitHubProjectDataSource, - GitHubProjectRepositoryDataSource + GitHubProjectDataSource } from "@/features/projects/data" import { CachingProjectDataSource, @@ -176,10 +175,8 @@ export const projectRepository = new ProjectRepository( export const projectDataSource = new CachingProjectDataSource({ dataSource: new GitHubProjectDataSource({ - dataSource: new GitHubProjectRepositoryDataSource({ - graphQlClient: userGitHubClient, - organizationName: GITHUB_ORGANIZATION_NAME - }) + graphQlClient: userGitHubClient, + organizationName: GITHUB_ORGANIZATION_NAME }), repository: projectRepository }) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index fe926269..568855e7 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -10,19 +10,37 @@ import { ProjectConfigRemoteVersion, } from "../domain" -interface IGitHubProjectRepositoryDataSource { - getRepositories(): Promise +export type GitHubGraphQLClientRequest = { + readonly query: string + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + readonly variables: {[key: string]: any} +} + +export type GitHubGraphQLClientResponse = { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + readonly [key: string]: any +} + +interface IGitHubGraphQLClient { + graphql(request: GitHubGraphQLClientRequest): Promise } export default class GitHubProjectDataSource implements IProjectDataSource { - private dataSource: IGitHubProjectRepositoryDataSource + private readonly graphQlClient: IGitHubGraphQLClient + private readonly organizationName: string - constructor(config: { dataSource: IGitHubProjectRepositoryDataSource }) { - this.dataSource = config.dataSource + constructor( + config: { + graphQlClient: IGitHubGraphQLClient, + organizationName: string + } + ) { + this.graphQlClient = config.graphQlClient + this.organizationName = config.organizationName } async getProjects(): Promise { - const repositories = await this.dataSource.getRepositories() + const repositories = await this.getRepositories() return repositories.map(repository => { return this.mapProject(repository) }) @@ -209,4 +227,75 @@ export default class GitHubProjectDataSource implements IProjectDataSource { .replace(/ /g, "-") .replace(/[^A-Za-z0-9-]/g, "") } + + private async getRepositories(): Promise { + const request = { + query: ` + query Repositories($searchQuery: String!) { + search(query: $searchQuery, type: REPOSITORY, first: 100) { + results: nodes { + ... on Repository { + name + owner { + login + } + defaultBranchRef { + name + target { + ...on Commit { + oid + } + } + } + configYml: object(expression: "HEAD:.shape-docs.yml") { + ...ConfigParts + } + configYaml: object(expression: "HEAD:.shape-docs.yaml") { + ...ConfigParts + } + branches: refs(refPrefix: "refs/heads/", first: 100) { + ...RefConnectionParts + } + tags: refs(refPrefix: "refs/tags/", first: 100) { + ...RefConnectionParts + } + } + } + } + } + + fragment RefConnectionParts on RefConnection { + edges { + node { + name + ... on Ref { + name + target { + ... on Commit { + oid + tree { + entries { + name + } + } + } + } + } + } + } + } + + fragment ConfigParts on GitObject { + ... on Blob { + text + } + } + `, + variables: { + searchQuery: `org:${this.organizationName} openapi in:name` + } + } + const response = await this.graphQlClient.graphql(request) + return response.search.results + } } diff --git a/src/features/projects/data/GitHubProjectRepositoryDataSource.ts b/src/features/projects/data/GitHubProjectRepositoryDataSource.ts deleted file mode 100644 index b9cc67f0..00000000 --- a/src/features/projects/data/GitHubProjectRepositoryDataSource.ts +++ /dev/null @@ -1,102 +0,0 @@ -import GitHubProjectRepository from "./GitHubProjectRepository" - -export type GitHubGraphQLClientRequest = { - readonly query: string - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - readonly variables: {[key: string]: any} -} - -export type GitHubGraphQLClientResponse = { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - readonly [key: string]: any -} - -interface IGitHubGraphQLClient { - graphql(request: GitHubGraphQLClientRequest): Promise -} - -export default class GitHubProjectRepositoryDataSource { - private graphQlClient: IGitHubGraphQLClient - private organizationName: string - - constructor( - config: { - graphQlClient: IGitHubGraphQLClient - organizationName: string - } - ) { - this.graphQlClient = config.graphQlClient - this.organizationName = config.organizationName - } - - async getRepositories(): Promise { - const request = { - query: ` - query Repositories($searchQuery: String!) { - search(query: $searchQuery, type: REPOSITORY, first: 100) { - results: nodes { - ... on Repository { - name - owner { - login - } - defaultBranchRef { - name - target { - ...on Commit { - oid - } - } - } - configYml: object(expression: "HEAD:.shape-docs.yml") { - ...ConfigParts - } - configYaml: object(expression: "HEAD:.shape-docs.yaml") { - ...ConfigParts - } - branches: refs(refPrefix: "refs/heads/", first: 100) { - ...RefConnectionParts - } - tags: refs(refPrefix: "refs/tags/", first: 100) { - ...RefConnectionParts - } - } - } - } - } - - fragment RefConnectionParts on RefConnection { - edges { - node { - name - ... on Ref { - name - target { - ... on Commit { - oid - tree { - entries { - name - } - } - } - } - } - } - } - } - - fragment ConfigParts on GitObject { - ... on Blob { - text - } - } - `, - variables: { - searchQuery: `org:${this.organizationName} openapi in:name` - } - } - const response = await this.graphQlClient.graphql(request) - return response.search.results - } -} diff --git a/src/features/projects/data/index.ts b/src/features/projects/data/index.ts index 936a7bd9..8199a31a 100644 --- a/src/features/projects/data/index.ts +++ b/src/features/projects/data/index.ts @@ -1,4 +1,2 @@ export { default as GitHubProjectDataSource } from "./GitHubProjectDataSource" -export { default as GitHubProjectRepositoryDataSource } from "./GitHubProjectRepositoryDataSource" -export * from "./GitHubProjectRepositoryDataSource" export { default as useProjects } from "./useProjects" From e0a2fd9abf3f299229d1a12a1ddbb4e6b18a9c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 18:10:43 +0100 Subject: [PATCH 018/191] Removes HostOnlySessionValidator --- ...GitHubOrganizationSessionValidator.test.ts | 48 +++++++++++++++++++ .../auth/HostOnlySessionValidator.test.ts | 42 ---------------- src/common/session/AuthjsSession.ts | 7 +-- src/common/session/ISession.ts | 7 +++ src/common/session/index.ts | 1 + src/composition.ts | 16 +++---- .../GitHubOrganizationSessionValidator.ts | 15 +++++- .../HostOnlySessionValidator.ts | 33 ------------- .../auth/domain/sessionValidity/index.ts | 1 - 9 files changed, 81 insertions(+), 89 deletions(-) delete mode 100644 __test__/auth/HostOnlySessionValidator.test.ts create mode 100644 src/common/session/ISession.ts delete mode 100644 src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts diff --git a/__test__/auth/GitHubOrganizationSessionValidator.test.ts b/__test__/auth/GitHubOrganizationSessionValidator.test.ts index 9287ae6c..a3aa5489 100644 --- a/__test__/auth/GitHubOrganizationSessionValidator.test.ts +++ b/__test__/auth/GitHubOrganizationSessionValidator.test.ts @@ -12,6 +12,11 @@ test("It requests organization membership status for the specified organization" queriedOrganizationName = request.organizationName return { state: "active" } } + }, + accountProviderTypeReader: { + async getAccountProviderType() { + return "github" + } } }) await sut.validateSession() @@ -25,6 +30,11 @@ test("It considers session valid when membership state is \"active\"", async () async getOrganizationMembershipStatus() { return { state: "active" } } + }, + accountProviderTypeReader: { + async getAccountProviderType() { + return "github" + } } }) const sessionValidity = await sut.validateSession() @@ -38,6 +48,11 @@ test("It considers user not to be part of the organization when membership state async getOrganizationMembershipStatus() { return { state: "pending" } } + }, + accountProviderTypeReader: { + async getAccountProviderType() { + return "github" + } } }) const sessionValidity = await sut.validateSession() @@ -51,6 +66,11 @@ test("It considers user not to be part of the organization when receiving HTTP 4 async getOrganizationMembershipStatus() { throw { status: 404, message: "User is not member of organization"} } + }, + accountProviderTypeReader: { + async getAccountProviderType() { + return "github" + } } }) const sessionValidity = await sut.validateSession() @@ -64,6 +84,11 @@ test("It considers organization to have blocked the GitHub app when receiving HT async getOrganizationMembershipStatus() { throw { status: 403, message: "Organization has blocked GitHub app"} } + }, + accountProviderTypeReader: { + async getAccountProviderType() { + return "github" + } } }) const sessionValidity = await sut.validateSession() @@ -77,7 +102,30 @@ test("It forwards error when getting membership status throws unknown error", as async getOrganizationMembershipStatus() { throw { status: 500 } } + }, + accountProviderTypeReader: { + async getAccountProviderType() { + return "github" + } } }) await expect(sut.validateSession()).rejects.toEqual({ status: 500 }) }) + +test("It considers session valid when the account provider is not \"github\"", async () => { + const sut = new GitHubOrganizationSessionValidator({ + acceptedOrganization: "foo", + organizationMembershipStatusReader: { + async getOrganizationMembershipStatus() { + throw { status: "pending" } + } + }, + accountProviderTypeReader: { + async getAccountProviderType() { + return "email" + } + } + }) + const sessionValidity = await sut.validateSession() + expect(sessionValidity).toEqual(SessionValidity.VALID) +}) diff --git a/__test__/auth/HostOnlySessionValidator.test.ts b/__test__/auth/HostOnlySessionValidator.test.ts deleted file mode 100644 index c0f44838..00000000 --- a/__test__/auth/HostOnlySessionValidator.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - HostOnlySessionValidator, - SessionValidity -} from "../../src/features/auth/domain" - -test("It validates session when user is host", async () => { - let didValidateSession = false - const sut = new HostOnlySessionValidator({ - isGuestReader: { - async getIsGuest() { - return false - } - }, - sessionValidator: { - async validateSession() { - didValidateSession = true - return SessionValidity.VALID - }, - } - }) - await sut.validateSession() - expect(didValidateSession).toBeTruthy() -}) - -test("It does not validate session when user is guest", async () => { - let didValidateSession = false - const sut = new HostOnlySessionValidator({ - isGuestReader: { - async getIsGuest() { - return true - } - }, - sessionValidator: { - async validateSession() { - didValidateSession = true - return SessionValidity.VALID - }, - } - }) - await sut.validateSession() - expect(didValidateSession).toBeFalsy() -}) diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index f6dd6a62..dffc47a7 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -1,8 +1,9 @@ import { NextAuthOptions } from "next-auth" import { getServerSession } from "next-auth/next" +import ISession, { AccountProviderType } from "./ISession" import { UnauthorizedError } from "../../common" -export default class AuthjsSession { +export default class AuthjsSession implements ISession { private readonly authOptions: NextAuthOptions constructor(config: { authOptions: NextAuthOptions }) { @@ -22,7 +23,7 @@ export default class AuthjsSession { return session.user.id } - async getIsGuest(): Promise { - return false + async getAccountProviderType(): Promise { + return "github" } } diff --git a/src/common/session/ISession.ts b/src/common/session/ISession.ts new file mode 100644 index 00000000..c4c85ed2 --- /dev/null +++ b/src/common/session/ISession.ts @@ -0,0 +1,7 @@ +export type AccountProviderType = "github" | "email" + +export default interface ISession { + getIsAuthenticated(): Promise + getUserId(): Promise + getAccountProviderType(): Promise +} diff --git a/src/common/session/index.ts b/src/common/session/index.ts index 791ee839..24ff8f62 100644 --- a/src/common/session/index.ts +++ b/src/common/session/index.ts @@ -1 +1,2 @@ export { default as AuthjsSession } from "./AuthjsSession" +export type { default as ISession, AccountProviderType } from "./ISession" diff --git a/src/composition.ts b/src/composition.ts index 18d47b49..a1a4b434 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -3,12 +3,13 @@ import { NextAuthOptions } from "next-auth" import GithubProvider from "next-auth/providers/github" import PostgresAdapter from "@auth/pg-adapter" import { Adapter } from "next-auth/adapters" -import { AuthjsSession } from "@/common/session" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" import { AccessTokenRefreshingGitHubClient, + AuthjsSession, GitHubClient, + ISession, KeyValueUserDataRepository, PostgreSQLDB, SessionMutexFactory @@ -33,7 +34,6 @@ import { ErrorIgnoringLogOutHandler, GitHubOrganizationSessionValidator, OAuthAccountCredentialPersistingLogInHandler, - HostOnlySessionValidator, LockingAccessTokenRefresher, OAuthTokenRepository, TransferringAccessTokenReader, @@ -109,7 +109,7 @@ const gitHubAppCredentials = { .toString("utf-8") } -export const session = new AuthjsSession({ authOptions }) +export const session: ISession = new AuthjsSession({ authOptions }) const accessTokenReader = new TransferringAccessTokenReader({ userIdReader: session, @@ -155,12 +155,10 @@ export const userGitHubClient = new AccessTokenRefreshingGitHubClient({ export const blockingSessionValidator = new AccessTokenSessionValidator({ accessTokenReader }) -export const delayedSessionValidator = new HostOnlySessionValidator({ - isGuestReader: session, - sessionValidator: new GitHubOrganizationSessionValidator({ - acceptedOrganization: GITHUB_ORGANIZATION_NAME, - organizationMembershipStatusReader: userGitHubClient - }) +export const delayedSessionValidator = new GitHubOrganizationSessionValidator({ + acceptedOrganization: GITHUB_ORGANIZATION_NAME, + organizationMembershipStatusReader: userGitHubClient, + accountProviderTypeReader: session }) const projectUserDataRepository = new KeyValueUserDataRepository( diff --git a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts index 9d1f6f62..36759971 100644 --- a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts @@ -1,9 +1,14 @@ +import { AccountProviderType } from "@/common" import SessionValidity from "./SessionValidity" type OrganizationMembershipStatus = { readonly state: "active" | "pending" } +interface IAccountProviderTypeReader { + getAccountProviderType(): Promise +} + interface IOrganizationMembershipStatusReader { getOrganizationMembershipStatus( request: { organizationName: string } @@ -13,18 +18,26 @@ interface IOrganizationMembershipStatusReader { export default class GitHubOrganizationSessionValidator { private readonly acceptedOrganization: string private readonly organizationMembershipStatusReader: IOrganizationMembershipStatusReader + private readonly accountProviderTypeReader: IAccountProviderTypeReader constructor( config: { acceptedOrganization: string - organizationMembershipStatusReader: IOrganizationMembershipStatusReader + organizationMembershipStatusReader: IOrganizationMembershipStatusReader, + accountProviderTypeReader: IAccountProviderTypeReader } ) { this.acceptedOrganization = config.acceptedOrganization this.organizationMembershipStatusReader = config.organizationMembershipStatusReader + this.accountProviderTypeReader = config.accountProviderTypeReader } async validateSession(): Promise { + const accountProviderType = await this.accountProviderTypeReader.getAccountProviderType() + if (accountProviderType !== "github") { + // Only validate GitHub sessions and consider any other valid. + return SessionValidity.VALID + } try { const response = await this.organizationMembershipStatusReader.getOrganizationMembershipStatus({ organizationName: this.acceptedOrganization diff --git a/src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts b/src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts deleted file mode 100644 index 2fee68a5..00000000 --- a/src/features/auth/domain/sessionValidity/HostOnlySessionValidator.ts +++ /dev/null @@ -1,33 +0,0 @@ -import SessionValidity from "./SessionValidity" - -interface IIsGuestReader { - getIsGuest(): Promise -} - -interface ISessionValidator { - validateSession(): Promise -} - -export default class HostOnlySessionValidator { - private readonly isGuestReader: IIsGuestReader - private readonly sessionValidator: ISessionValidator - - constructor( - config: { - isGuestReader: IIsGuestReader - sessionValidator: ISessionValidator - } - ) { - this.isGuestReader = config.isGuestReader - this.sessionValidator = config.sessionValidator - } - - async validateSession(): Promise { - const isGuest = await this.isGuestReader.getIsGuest() - if (!isGuest) { - return await this.sessionValidator.validateSession() - } else { - return SessionValidity.VALID - } - } -} diff --git a/src/features/auth/domain/sessionValidity/index.ts b/src/features/auth/domain/sessionValidity/index.ts index de4d2dde..f67c06b5 100644 --- a/src/features/auth/domain/sessionValidity/index.ts +++ b/src/features/auth/domain/sessionValidity/index.ts @@ -1,6 +1,5 @@ export { default as AccessTokenSessionValidator } from "./AccessTokenSessionValidator" export { default as GitHubOrganizationSessionValidator } from "./GitHubOrganizationSessionValidator" -export { default as HostOnlySessionValidator } from "./HostOnlySessionValidator" export { default as SessionValidity } from "./SessionValidity" export * from "./SessionValidity" export { default as useRepositoryAccess } from "./useRepositoryAccess" From a2abf213abacceed3afca966f565f01adf88a91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 18:20:28 +0100 Subject: [PATCH 019/191] Removes final "guest" terminology --- src/app/api/user/repository-access/route.ts | 4 ++-- src/composition.ts | 10 +++++----- src/features/auth/view/SessionBarrier.tsx | 12 ++---------- ...> NonGitHubAccountAccessTokenInvalidPage.tsx} | 2 +- src/features/auth/view/client/SessionBarrier.tsx | 16 +++++++++------- src/features/projects/view/ProjectsPage.tsx | 4 ++-- 6 files changed, 21 insertions(+), 27 deletions(-) rename src/features/auth/view/client/{GuestAccessTokenInvalidPage.tsx => NonGitHubAccountAccessTokenInvalidPage.tsx} (96%) diff --git a/src/app/api/user/repository-access/route.ts b/src/app/api/user/repository-access/route.ts index 0e0b8983..39b61ff4 100644 --- a/src/app/api/user/repository-access/route.ts +++ b/src/app/api/user/repository-access/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server" import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" -import { session, guestRepositoryAccessReader } from "@/composition" +import { session, repositoryAccessReader } from "@/composition" export async function GET() { const isAuthenticated = await session.getIsAuthenticated() @@ -14,7 +14,7 @@ export async function GET() { return makeAPIErrorResponse(401, "Unauthorized") } try { - const repositoryNames = await guestRepositoryAccessReader.getRepositoryNames(userId) + const repositoryNames = await repositoryAccessReader.getRepositoryNames(userId) return NextResponse.json({repositories: repositoryNames}) /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ } catch (error: any) { diff --git a/src/composition.ts b/src/composition.ts index a1a4b434..6b607241 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -117,13 +117,13 @@ const accessTokenReader = new TransferringAccessTokenReader({ destinationOAuthTokenRepository: new OAuthTokenRepository({ provider: "github", db }) }) -const guestRepositoryAccessRepository = new KeyValueUserDataRepository( +const repositoryAccessRepository = new KeyValueUserDataRepository( new RedisKeyValueStore(REDIS_URL), - "guestRepositoryAccess" + "repositoryAccess" ) -export const guestRepositoryAccessReader = new CachingRepositoryAccessReader({ - repository: guestRepositoryAccessRepository, +export const repositoryAccessReader = new CachingRepositoryAccessReader({ + repository: repositoryAccessRepository, repositoryAccessReader: new AuthjsRepositoryAccessReader() }) @@ -182,6 +182,6 @@ export const projectDataSource = new CachingProjectDataSource({ export const logOutHandler = new ErrorIgnoringLogOutHandler( new CompositeLogOutHandler([ new UserDataCleanUpLogOutHandler(session, projectUserDataRepository), - new UserDataCleanUpLogOutHandler(session, guestRepositoryAccessRepository) + new UserDataCleanUpLogOutHandler(session, repositoryAccessRepository) ]) ) diff --git a/src/features/auth/view/SessionBarrier.tsx b/src/features/auth/view/SessionBarrier.tsx index d7c62839..82e94102 100644 --- a/src/features/auth/view/SessionBarrier.tsx +++ b/src/features/auth/view/SessionBarrier.tsx @@ -17,19 +17,11 @@ export default async function SessionBarrier({ if (!isAuthenticated) { return redirect("/api/auth/signin") } - const getIsGuest = async () => { - try { - return await session.getIsGuest() - } catch { - // We assume it's a guest. - return true - } - } - const isGuest = await getIsGuest() + const accountProviderType = await session.getAccountProviderType() const sessionValidity = await blockingSessionValidator.validateSession() return ( {children} case SessionValidity.INVALID_ACCESS_TOKEN: - if (isGuest) { - return - } else { + switch (accountProviderType) { + case "email": + return + case "github": return ( It was not possible to obtain access to the projects on the {organizationName} organization on GitHub. diff --git a/src/features/projects/view/ProjectsPage.tsx b/src/features/projects/view/ProjectsPage.tsx index be16f64c..e7625c69 100644 --- a/src/features/projects/view/ProjectsPage.tsx +++ b/src/features/projects/view/ProjectsPage.tsx @@ -9,11 +9,11 @@ export default async function ProjectsPage({ projectRepository: ProjectRepository path: string }) { - const isGuest = await session.getIsGuest() + const enableGitHubLinks = await session.getAccountProviderType() == "github" const projects = await projectRepository.get() return ( From f7b35d4ceceb3282c40d68d39a43b37989a3bdbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 18:23:37 +0100 Subject: [PATCH 020/191] Removes CachingRepositoryAccessReader --- ...achingRepositoryAccessReaderConfig.test.ts | 94 ------------------- src/composition.ts | 14 +-- ...ositoryRestrictingAccessTokenDataSource.ts | 0 src/features/auth/domain/accessToken/index.ts | 1 + .../CachingRepositoryAccessReader.ts | 57 ----------- .../auth/domain/repositoryAccess/index.ts | 2 - 6 files changed, 3 insertions(+), 165 deletions(-) delete mode 100644 __test__/auth/CachingRepositoryAccessReaderConfig.test.ts rename src/features/auth/domain/{repositoryAccess => accessToken}/RepositoryRestrictingAccessTokenDataSource.ts (100%) delete mode 100644 src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts delete mode 100644 src/features/auth/domain/repositoryAccess/index.ts diff --git a/__test__/auth/CachingRepositoryAccessReaderConfig.test.ts b/__test__/auth/CachingRepositoryAccessReaderConfig.test.ts deleted file mode 100644 index f4b4f2a1..00000000 --- a/__test__/auth/CachingRepositoryAccessReaderConfig.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { CachingRepositoryAccessReader } from "../../src/features/auth/domain" - -test("It fetches repository names for user if they are not cached", async () => { - let didFetchRepositoryNames = false - let requestedUserId: string | undefined - const sut = new CachingRepositoryAccessReader({ - repository: { - async get() { - return null - }, - async set() {}, - async setExpiring() {}, - async delete() {} - }, - repositoryAccessReader: { - async getRepositoryNames(userId: string) { - didFetchRepositoryNames = true - requestedUserId = userId - return [] - } - } - }) - await sut.getRepositoryNames("1234") - expect(didFetchRepositoryNames).toBeTruthy() - expect(requestedUserId).toEqual("1234") -}) - -test("It does not fetch repository names if they are cached", async () => { - let didFetchRepositoryNames = false - const sut = new CachingRepositoryAccessReader({ - repository: { - async get() { - return "[\"foo\"]" - }, - async set() {}, - async setExpiring() {}, - async delete() {} - }, - repositoryAccessReader: { - async getRepositoryNames() { - didFetchRepositoryNames = true - return [] - } - } - }) - await sut.getRepositoryNames("1234") - expect(didFetchRepositoryNames).toBeFalsy() -}) - -test("It caches fetched repository names for user", async () => { - let cachedUserId: string | undefined - let cachedRepositoryNames: string | undefined - const sut = new CachingRepositoryAccessReader({ - repository: { - async get() { - return null - }, - async set() {}, - async setExpiring(userId: string, value: string) { - cachedUserId = userId - cachedRepositoryNames = value - }, - async delete() {} - }, - repositoryAccessReader: { - async getRepositoryNames() { - return ["foo"] - } - } - }) - await sut.getRepositoryNames("1234") - expect(cachedUserId).toEqual("1234") - expect(cachedRepositoryNames).toEqual("[\"foo\"]") -}) - -test("It decodes cached repository names", async () => { - const sut = new CachingRepositoryAccessReader({ - repository: { - async get() { - return "[\"foo\",\"bar\"]" - }, - async set() {}, - async setExpiring() {}, - async delete() {} - }, - repositoryAccessReader: { - async getRepositoryNames() { - return [] - } - } - }) - const repositoryNames = await sut.getRepositoryNames("1234") - expect(repositoryNames).toEqual(["foo", "bar"]) -}) diff --git a/src/composition.ts b/src/composition.ts index 6b607241..2386cbbf 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -29,7 +29,6 @@ import { import { AccessTokenRefresher, AccessTokenSessionValidator, - CachingRepositoryAccessReader, CompositeLogOutHandler, ErrorIgnoringLogOutHandler, GitHubOrganizationSessionValidator, @@ -117,15 +116,7 @@ const accessTokenReader = new TransferringAccessTokenReader({ destinationOAuthTokenRepository: new OAuthTokenRepository({ provider: "github", db }) }) -const repositoryAccessRepository = new KeyValueUserDataRepository( - new RedisKeyValueStore(REDIS_URL), - "repositoryAccess" -) - -export const repositoryAccessReader = new CachingRepositoryAccessReader({ - repository: repositoryAccessRepository, - repositoryAccessReader: new AuthjsRepositoryAccessReader() -}) +export const repositoryAccessReader = new AuthjsRepositoryAccessReader() export const gitHubClient = new GitHubClient({ ...gitHubAppCredentials, @@ -181,7 +172,6 @@ export const projectDataSource = new CachingProjectDataSource({ export const logOutHandler = new ErrorIgnoringLogOutHandler( new CompositeLogOutHandler([ - new UserDataCleanUpLogOutHandler(session, projectUserDataRepository), - new UserDataCleanUpLogOutHandler(session, repositoryAccessRepository) + new UserDataCleanUpLogOutHandler(session, projectUserDataRepository) ]) ) diff --git a/src/features/auth/domain/repositoryAccess/RepositoryRestrictingAccessTokenDataSource.ts b/src/features/auth/domain/accessToken/RepositoryRestrictingAccessTokenDataSource.ts similarity index 100% rename from src/features/auth/domain/repositoryAccess/RepositoryRestrictingAccessTokenDataSource.ts rename to src/features/auth/domain/accessToken/RepositoryRestrictingAccessTokenDataSource.ts diff --git a/src/features/auth/domain/accessToken/index.ts b/src/features/auth/domain/accessToken/index.ts index c497aba8..e56c9c44 100644 --- a/src/features/auth/domain/accessToken/index.ts +++ b/src/features/auth/domain/accessToken/index.ts @@ -1,4 +1,5 @@ export { default as AccessTokenRefresher } from "./AccessTokenRefresher" export type { default as IAccessTokenRefresher } from "./IAccessTokenRefresher" export { default as LockingAccessTokenRefresher } from "./LockingAccessTokenRefresher" +export { default as RepositoryRestrictingAccessTokenDataSource } from "./RepositoryRestrictingAccessTokenDataSource" export { default as TransferringAccessTokenReader } from "./TransferringAccessTokenReader" diff --git a/src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts b/src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts deleted file mode 100644 index 9bff92c6..00000000 --- a/src/features/auth/domain/repositoryAccess/CachingRepositoryAccessReader.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { z } from "zod" -import { ZodJSONCoder, IUserDataRepository } from "../../../../common" - -export const RepositoryNamesContainerSchema = z.string().array() - -interface IRepositoryAccessReader { - getRepositoryNames(userId: string): Promise -} - -type IRepositoryNameRepository = IUserDataRepository - -export default class CachingRepositoryAccessReader { - private readonly repository: IRepositoryNameRepository - private readonly repositoryAccessReader: IRepositoryAccessReader - - constructor( - config: { - repository: IRepositoryNameRepository - repositoryAccessReader: IRepositoryAccessReader - } - ) { - this.repository = config.repository - this.repositoryAccessReader = config.repositoryAccessReader - } - - async getRepositoryNames(userId: string): Promise { - const cachedValue = await this.getCachedRepositoryNames(userId) - if (cachedValue) { - return cachedValue - } - return await this.refreshRepositoryNames(userId) - } - - private async getCachedRepositoryNames(userId: string): Promise { - const str = await this.repository.get(userId) - if (!str) { - return null - } - try { - return ZodJSONCoder.decode(RepositoryNamesContainerSchema, str) - } catch (error: unknown) { - console.error(error) - return null - } - } - - private async refreshRepositoryNames(userId: string): Promise { - const repositoryNames = await this.repositoryAccessReader.getRepositoryNames(userId) - try { - const str = ZodJSONCoder.encode(RepositoryNamesContainerSchema, repositoryNames) - await this.repository.setExpiring(userId, str, 7 * 24 * 3600) - } catch (error: unknown) { - console.error(error) - } - return repositoryNames - } -} diff --git a/src/features/auth/domain/repositoryAccess/index.ts b/src/features/auth/domain/repositoryAccess/index.ts deleted file mode 100644 index 1224c7b8..00000000 --- a/src/features/auth/domain/repositoryAccess/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as CachingRepositoryAccessReader } from "./CachingRepositoryAccessReader" -export { default as RepositoryRestrictingAccessTokenDataSource } from "./RepositoryRestrictingAccessTokenDataSource" From 484bb77efafe3776a800a4372053255152d433ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 8 Feb 2024 18:38:32 +0100 Subject: [PATCH 021/191] Removes unused import --- src/features/auth/domain/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/features/auth/domain/index.ts b/src/features/auth/domain/index.ts index 33f00b09..742ac319 100644 --- a/src/features/auth/domain/index.ts +++ b/src/features/auth/domain/index.ts @@ -2,5 +2,4 @@ export * from "./accessToken" export * from "./logIn" export * from "./logOut" export * from "./oAuthToken" -export * from "./repositoryAccess" export * from "./sessionValidity" From 365b550d5147379513553f8af6428dc3e8717e31 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 6 Feb 2024 11:59:40 +0100 Subject: [PATCH 022/191] Add magic link based on postgres adapter --- package-lock.json | 2 ++ package.json | 2 ++ 2 files changed, 4 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7edd4043..7fd180be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "shape-docs", "version": "0.1.0", "dependencies": { + "@auth/pg-adapter": "^0.4.1", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -29,6 +30,7 @@ "next-auth": "^4.24.5", "npm": "^10.2.4", "octokit": "^3.1.2", + "pg": "^8.11.3", "react": "^18.2.0", "react-dom": "^18.2.0", "redis-semaphore": "^5.5.0", diff --git a/package.json b/package.json index c9f2eb1b..262ba2b1 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "test": "jest" }, "dependencies": { + "@auth/pg-adapter": "^0.4.1", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -35,6 +36,7 @@ "next-auth": "^4.24.5", "npm": "^10.2.4", "octokit": "^3.1.2", + "pg": "^8.11.3", "react": "^18.2.0", "react-dom": "^18.2.0", "redis-semaphore": "^5.5.0", From c2764f629331050a35681ecf1d6701d37cc94107 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Wed, 7 Feb 2024 15:34:56 +0100 Subject: [PATCH 023/191] Basic setup for guest user admin --- src/composition.ts | 36 +++++++++++++ src/features/admin/domain/Guest.ts | 6 +++ src/features/admin/domain/IGuestInviter.ts | 3 ++ src/features/admin/domain/IGuestRepository.ts | 7 +++ src/features/admin/view/AdminPage.tsx | 51 +++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 src/features/admin/domain/Guest.ts create mode 100644 src/features/admin/domain/IGuestInviter.ts create mode 100644 src/features/admin/domain/IGuestRepository.ts create mode 100644 src/features/admin/view/AdminPage.tsx diff --git a/src/composition.ts b/src/composition.ts index 2386cbbf..5d2f79ee 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -38,6 +38,8 @@ import { TransferringAccessTokenReader, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" +import { IGuestInviter } from "./features/admin/domain/IGuestInviter" +import { randomUUID } from "crypto" const { GITHUB_APP_ID, @@ -175,3 +177,37 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( new UserDataCleanUpLogOutHandler(session, projectUserDataRepository) ]) ) + +export const guestRepository: IGuestRepository = { + getAll: function (): Promise { + return Promise.resolve([ + { + id: randomUUID(), + email: "ulrik@shape.dk", + status: "active", + projects: ["deas", "moonboon"] + }, + { + id: randomUUID(), + email: "lars@company.com", + status: "invited", + projects: ["deas"] + } + ]) + }, + findById: function (id: string): Promise { + throw new Error("Function not implemented.") + }, + create: function (guest: Guest): Promise { + throw new Error("Function not implemented.") + }, + removeById: function (id: string): Promise { + throw new Error("Function not implemented.") + } +} + +export const guestInviter: IGuestInviter = { + inviteGuest: async (invitee: string) => { + console.log(`Inviting ${invitee}`) + } +} \ No newline at end of file diff --git a/src/features/admin/domain/Guest.ts b/src/features/admin/domain/Guest.ts new file mode 100644 index 00000000..8b81fe3e --- /dev/null +++ b/src/features/admin/domain/Guest.ts @@ -0,0 +1,6 @@ +type Guest = { + id: string; + email: string; + status: "active" | "invited"; + projects: string[]; +}; diff --git a/src/features/admin/domain/IGuestInviter.ts b/src/features/admin/domain/IGuestInviter.ts new file mode 100644 index 00000000..21672ee6 --- /dev/null +++ b/src/features/admin/domain/IGuestInviter.ts @@ -0,0 +1,3 @@ +export interface IGuestInviter { + inviteGuest: (invitee: string) => Promise +} diff --git a/src/features/admin/domain/IGuestRepository.ts b/src/features/admin/domain/IGuestRepository.ts new file mode 100644 index 00000000..d1b2c486 --- /dev/null +++ b/src/features/admin/domain/IGuestRepository.ts @@ -0,0 +1,7 @@ + +interface IGuestRepository { + getAll(): Promise + findById(id: string): Promise + create(guest: Guest): Promise + removeById(id: string): Promise +} diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx new file mode 100644 index 00000000..b0b2011f --- /dev/null +++ b/src/features/admin/view/AdminPage.tsx @@ -0,0 +1,51 @@ +import { guestRepository } from "@/composition" +import { Button, ButtonGroup, Chip, FormGroup, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField } from "@mui/material" +import Table from "@mui/material/Table" + +const AdminPage = async () => { + const guests = await guestRepository.getAll() + + return ( + <> +

Guest Admin

+ +

Invite Guest

+ + + + + + +

Guests

+ + + + + Email + Status + Projects + Actions + + + + {guests.map((row, index) => ( + + {row.email} + + {row.projects.join(", ")} + + + + + + + + ))} + +
+
+ + ) +} + +export default AdminPage From ca56c2377d3b66f1d77e491bf0f80274560ab622 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Thu, 8 Feb 2024 12:55:01 +0100 Subject: [PATCH 024/191] Init db in composition.ts --- src/composition.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/composition.ts b/src/composition.ts index 5d2f79ee..c23e8c91 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -40,6 +40,7 @@ import { } from "@/features/auth/domain" import { IGuestInviter } from "./features/admin/domain/IGuestInviter" import { randomUUID } from "crypto" +import { Pool } from "pg" const { GITHUB_APP_ID, @@ -203,6 +204,15 @@ export const guestRepository: IGuestRepository = { }, removeById: function (id: string): Promise { throw new Error("Function not implemented.") + +export const pool = new Pool({ + host: 'localhost', // TODO: Move to env + user: 'ua', // TODO: Move to env + database: 'shape-docs', // TODO: Move to env + max: 20, + idleTimeoutMillis: 30000, + connectionTimeoutMillis: 2000, +}) } } From d3853c6460762388fe4b3a82fb171a30c0b9a80a Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Thu, 8 Feb 2024 12:58:38 +0100 Subject: [PATCH 025/191] Add db backed guest repo --- package-lock.json | 33 ++++++-- package.json | 2 + src/app/admin/guests/page.tsx | 7 ++ src/composition.ts | 68 +++++++++------- src/features/admin/data/DbGuestRepository.ts | 77 +++++++++++++++++++ src/features/admin/domain/Guest.ts | 1 - src/features/admin/domain/IGuestInviter.ts | 2 +- src/features/admin/domain/IGuestRepository.ts | 7 +- src/features/admin/view/AdminPage.tsx | 38 +++++++-- 9 files changed, 186 insertions(+), 49 deletions(-) create mode 100644 src/app/admin/guests/page.tsx create mode 100644 src/features/admin/data/DbGuestRepository.ts diff --git a/package-lock.json b/package-lock.json index 7fd180be..27067345 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "mobx": "^6.12.0", "next": "14.0.2", "next-auth": "^4.24.5", + "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", "pg": "^8.11.3", @@ -46,6 +47,7 @@ "@auth/pg-adapter": "^0.5.2", "@types/jest": "^29.5.8", "@types/node": "^20.9.2", + "@types/nodemailer": "^6.4.14", "@types/pg": "^8.11.0", "@types/react": "^18", "@types/react-dom": "^18", @@ -4145,9 +4147,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", - "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -4199,6 +4201,15 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.14", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.14.tgz", + "integrity": "sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -10713,6 +10724,14 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "devOptional": true }, + "node_modules/nodemailer": { + "version": "6.9.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", + "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -16686,9 +16705,9 @@ "dev": true }, "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", + "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -16704,7 +16723,7 @@ "ts-jest": "cli.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", diff --git a/package.json b/package.json index 262ba2b1..6f5ec056 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "mobx": "^6.12.0", "next": "14.0.2", "next-auth": "^4.24.5", + "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", "pg": "^8.11.3", @@ -52,6 +53,7 @@ "@auth/pg-adapter": "^0.5.2", "@types/jest": "^29.5.8", "@types/node": "^20.9.2", + "@types/nodemailer": "^6.4.14", "@types/pg": "^8.11.0", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/src/app/admin/guests/page.tsx b/src/app/admin/guests/page.tsx new file mode 100644 index 00000000..0c559a54 --- /dev/null +++ b/src/app/admin/guests/page.tsx @@ -0,0 +1,7 @@ +import AdminPage from "@/features/admin/view/AdminPage"; + +export default async function Page() { + return ( + + ) +} diff --git a/src/composition.ts b/src/composition.ts index c23e8c91..b2bf3f14 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -40,7 +40,11 @@ import { } from "@/features/auth/domain" import { IGuestInviter } from "./features/admin/domain/IGuestInviter" import { randomUUID } from "crypto" +import { createTransport } from "nodemailer" +import StreamTransport from "nodemailer/lib/stream-transport" +import SMTPTransport from "nodemailer/lib/smtp-transport" import { Pool } from "pg" +import DbGuestRepository from "./features/admin/data/DbGuestRepository" const { GITHUB_APP_ID, @@ -179,31 +183,6 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( ]) ) -export const guestRepository: IGuestRepository = { - getAll: function (): Promise { - return Promise.resolve([ - { - id: randomUUID(), - email: "ulrik@shape.dk", - status: "active", - projects: ["deas", "moonboon"] - }, - { - id: randomUUID(), - email: "lars@company.com", - status: "invited", - projects: ["deas"] - } - ]) - }, - findById: function (id: string): Promise { - throw new Error("Function not implemented.") - }, - create: function (guest: Guest): Promise { - throw new Error("Function not implemented.") - }, - removeById: function (id: string): Promise { - throw new Error("Function not implemented.") export const pool = new Pool({ host: 'localhost', // TODO: Move to env @@ -213,11 +192,42 @@ export const pool = new Pool({ idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }) + +export const guestRepository: IGuestRepository = new DbGuestRepository(pool) + +const transport = createTransport({ + host: "sandbox.smtp.mailtrap.io", + port: 2525, + auth: { + user: "0682027d57d0db", + pass: "28c3dbfbfc0af8" } -} +}); export const guestInviter: IGuestInviter = { - inviteGuest: async (invitee: string) => { - console.log(`Inviting ${invitee}`) + inviteGuestByEmail: function (email: string): Promise { + transport.sendMail({ + to: email, + from: "no-reply@docs.shapetools.io", + subject: "You have been invited to join Shape Docs", + html: ` + + + + + +

You have been invited to join Shape Docs!

+

Shape Docs uses magic links for authentication. This means that you don't need to remember a password.

+

Click the link below to request your first magic link to log in:

+ Log in + + + `, + }) + return Promise.resolve() } -} \ No newline at end of file +} diff --git a/src/features/admin/data/DbGuestRepository.ts b/src/features/admin/data/DbGuestRepository.ts new file mode 100644 index 00000000..af228532 --- /dev/null +++ b/src/features/admin/data/DbGuestRepository.ts @@ -0,0 +1,77 @@ +import { Pool } from "pg" + +export default class DbGuestRepository implements IGuestRepository { + readonly pool: Pool + + constructor(pool: Pool) { + this.pool = pool + } + + /** + * Uses users table owned by Authjs to determine if a guest is active = there's a user with the same email + * + * @returns all guests including their status + */ + async getAll(): Promise { + const sql = ` + SELECT + g.*, + CASE + WHEN u.email IS NULL THEN 'invited' + ELSE 'active' + END as status + FROM guests g + LEFT JOIN users u ON g.email = u.email + ORDER BY g.email ASC + ` + const result = await this.pool.query(sql) + return result.rows + } + + /** + * Find a guest by email + * + * @param email Email of the guest + * @returns The guest or undefined if not found + */ + async findByEmail(email: string): Promise { + const sql = "SELECT * FROM guests WHERE email = $1" + const result = await this.pool.query(sql, [email]) + return result.rows[0] + } + + /** + * Create a guest + * + * @param email Email of the guest + * @param projects List of projects the guest should be associated with + * @returns Newly created guest + */ + async create(email: string, projects: string[]): Promise { + const sql = "INSERT INTO guests (email, projects) VALUES ($1, $2)" + await this.pool.query(sql, [email, JSON.stringify(projects)]) + return this.findByEmail(email) + } + + /** + * Remove a guest by email + * + * @param email Email of the guest + */ + async removeByEmail(email: string): Promise { + const sql = "DELETE FROM guests WHERE email = $1 LIMIT 1" + await this.pool.query(sql, [email]) + } + + /** + * Get projects for a guest + * + * @param email Email of the guest + * @returns The projects the guest is associated with. If the guest is not found, an empty array is returned. + */ + async getProjectsForEmail(email: string): Promise { + const sql = "SELECT projects FROM guests WHERE email = $1" + const result = await this.pool.query(sql, [email]) + return result.rows[0] ? result.rows[0].projects : [] + } +} diff --git a/src/features/admin/domain/Guest.ts b/src/features/admin/domain/Guest.ts index 8b81fe3e..00fd1d14 100644 --- a/src/features/admin/domain/Guest.ts +++ b/src/features/admin/domain/Guest.ts @@ -1,5 +1,4 @@ type Guest = { - id: string; email: string; status: "active" | "invited"; projects: string[]; diff --git a/src/features/admin/domain/IGuestInviter.ts b/src/features/admin/domain/IGuestInviter.ts index 21672ee6..c358b1aa 100644 --- a/src/features/admin/domain/IGuestInviter.ts +++ b/src/features/admin/domain/IGuestInviter.ts @@ -1,3 +1,3 @@ export interface IGuestInviter { - inviteGuest: (invitee: string) => Promise + inviteGuestByEmail: (email: string) => Promise } diff --git a/src/features/admin/domain/IGuestRepository.ts b/src/features/admin/domain/IGuestRepository.ts index d1b2c486..fd95b396 100644 --- a/src/features/admin/domain/IGuestRepository.ts +++ b/src/features/admin/domain/IGuestRepository.ts @@ -1,7 +1,8 @@ interface IGuestRepository { getAll(): Promise - findById(id: string): Promise - create(guest: Guest): Promise - removeById(id: string): Promise + findByEmail(email: string): Promise + create(email: string, projects: string[]): Promise + removeByEmail(email: string): Promise + getProjectsForEmail(email: string): Promise } diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx index b0b2011f..78b3162d 100644 --- a/src/features/admin/view/AdminPage.tsx +++ b/src/features/admin/view/AdminPage.tsx @@ -1,20 +1,40 @@ -import { guestRepository } from "@/composition" +import { guestInviter, guestRepository } from "@/composition" import { Button, ButtonGroup, Chip, FormGroup, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField } from "@mui/material" import Table from "@mui/material/Table" +import { revalidatePath } from "next/cache" const AdminPage = async () => { const guests = await guestRepository.getAll() + async function sendInvite(formData: FormData): Promise { + 'use server' + const email = formData.get('email') as string + const projects = formData.get('projects') as string + guestInviter.inviteGuestByEmail(email as string) + guestRepository.create(email as string, projects.split(",").map(p => p.trim())) + revalidatePath('/admin/guests') + } + + async function removeGuest(formData: FormData): Promise { + 'use server' + console.log(formData) + const email = formData.get('email') as string + guestRepository.removeByEmail(email) + revalidatePath('/admin/guests') + } + return ( <>

Guest Admin

Invite Guest

- - - - - +
+ + + + + +

Guests

@@ -35,8 +55,10 @@ const AdminPage = async () => { {row.projects.join(", ")} - - +
+ + +
From cb849c7998c591dfaf32fa7275406348f449cad6 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Thu, 8 Feb 2024 13:01:12 +0100 Subject: [PATCH 026/191] Document db schemas --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/README.md b/README.md index 178baf2d..b403e3b1 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,69 @@ npm run dev Finally, open the application on https://dev.local:3000. +## Database Schemas + +### Authjs +```sql +CREATE TABLE verification_token +( + identifier TEXT NOT NULL, + expires TIMESTAMPTZ NOT NULL, + token TEXT NOT NULL, + + PRIMARY KEY (identifier, token) +); + +CREATE TABLE accounts +( + id SERIAL, + "userId" INTEGER NOT NULL, + type VARCHAR(255) NOT NULL, + provider VARCHAR(255) NOT NULL, + "providerAccountId" VARCHAR(255) NOT NULL, + refresh_token TEXT, + access_token TEXT, + expires_at BIGINT, + id_token TEXT, + scope TEXT, + session_state TEXT, + token_type TEXT, + + PRIMARY KEY (id) +); + +CREATE TABLE sessions +( + id SERIAL, + "userId" INTEGER NOT NULL, + expires TIMESTAMPTZ NOT NULL, + "sessionToken" VARCHAR(255) NOT NULL, + + PRIMARY KEY (id) +); + +CREATE TABLE users +( + id SERIAL, + name VARCHAR(255), + email VARCHAR(255), + "emailVerified" TIMESTAMPTZ, + image TEXT, + + PRIMARY KEY (id) +); + +``` + +### Guests and Project Access +```sql +CREATE TABLE guests ( + id SERIAL PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + projects jsonb +); +``` + ## 🚀 Deploying the App The app is hosted on Heroku in two different environments. From ce23130bca244c6396779bd4d5c686a983367b43 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Thu, 8 Feb 2024 13:09:47 +0100 Subject: [PATCH 027/191] Fix minor error --- src/features/admin/data/DbGuestRepository.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features/admin/data/DbGuestRepository.ts b/src/features/admin/data/DbGuestRepository.ts index af228532..1b225fe2 100644 --- a/src/features/admin/data/DbGuestRepository.ts +++ b/src/features/admin/data/DbGuestRepository.ts @@ -50,7 +50,13 @@ export default class DbGuestRepository implements IGuestRepository { async create(email: string, projects: string[]): Promise { const sql = "INSERT INTO guests (email, projects) VALUES ($1, $2)" await this.pool.query(sql, [email, JSON.stringify(projects)]) - return this.findByEmail(email) + + const insertedGuest = await this.findByEmail(email) + if (!insertedGuest) { + throw new Error("Guest not found after insert") + } + + return insertedGuest } /** From 6150da1e3619f93eec6c68043521115b732d6f84 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Thu, 8 Feb 2024 13:13:40 +0100 Subject: [PATCH 028/191] Refactor admin page --- src/features/admin/data/DbGuestRepository.ts | 2 +- src/features/admin/view/AdminPage.tsx | 45 ++++++++++++-------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/features/admin/data/DbGuestRepository.ts b/src/features/admin/data/DbGuestRepository.ts index 1b225fe2..089dbe32 100644 --- a/src/features/admin/data/DbGuestRepository.ts +++ b/src/features/admin/data/DbGuestRepository.ts @@ -65,7 +65,7 @@ export default class DbGuestRepository implements IGuestRepository { * @param email Email of the guest */ async removeByEmail(email: string): Promise { - const sql = "DELETE FROM guests WHERE email = $1 LIMIT 1" + const sql = "DELETE FROM guests WHERE email = $1" await this.pool.query(sql, [email]) } diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx index 78b3162d..11b39194 100644 --- a/src/features/admin/view/AdminPage.tsx +++ b/src/features/admin/view/AdminPage.tsx @@ -3,25 +3,36 @@ import { Button, ButtonGroup, Chip, FormGroup, TableBody, TableCell, TableContai import Table from "@mui/material/Table" import { revalidatePath } from "next/cache" -const AdminPage = async () => { - const guests = await guestRepository.getAll() +/** + * Server action to send an invite + */ +const sendInvite = async (formData: FormData): Promise => { + 'use server' + + const email = formData.get('email') as string + const projects = formData.get('projects') as string + + guestInviter.inviteGuestByEmail(email as string) + guestRepository.create(email as string, projects.split(",").map(p => p.trim())) + + revalidatePath('/admin/guests') +} + +/** + * Server action to remove a guest + */ +const removeGuest = async (formData: FormData): Promise => { + 'use server' + + const email = formData.get('email') as string - async function sendInvite(formData: FormData): Promise { - 'use server' - const email = formData.get('email') as string - const projects = formData.get('projects') as string - guestInviter.inviteGuestByEmail(email as string) - guestRepository.create(email as string, projects.split(",").map(p => p.trim())) - revalidatePath('/admin/guests') - } + guestRepository.removeByEmail(email) - async function removeGuest(formData: FormData): Promise { - 'use server' - console.log(formData) - const email = formData.get('email') as string - guestRepository.removeByEmail(email) - revalidatePath('/admin/guests') - } + revalidatePath('/admin/guests') +} + +const AdminPage = async () => { + const guests = await guestRepository.getAll() return ( <> From d0e1c40d8645e03442852411a8f6e2dda91a7a5b Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 16 Feb 2024 07:57:10 +0100 Subject: [PATCH 029/191] Remove unused and duplicate imports --- src/composition.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index b2bf3f14..aa62ed93 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -39,11 +39,7 @@ import { UserDataCleanUpLogOutHandler } from "@/features/auth/domain" import { IGuestInviter } from "./features/admin/domain/IGuestInviter" -import { randomUUID } from "crypto" import { createTransport } from "nodemailer" -import StreamTransport from "nodemailer/lib/stream-transport" -import SMTPTransport from "nodemailer/lib/smtp-transport" -import { Pool } from "pg" import DbGuestRepository from "./features/admin/data/DbGuestRepository" const { @@ -183,16 +179,6 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( ]) ) - -export const pool = new Pool({ - host: 'localhost', // TODO: Move to env - user: 'ua', // TODO: Move to env - database: 'shape-docs', // TODO: Move to env - max: 20, - idleTimeoutMillis: 30000, - connectionTimeoutMillis: 2000, -}) - export const guestRepository: IGuestRepository = new DbGuestRepository(pool) const transport = createTransport({ From 8f722fdbe3635f7f40063908a1bf6253df6b3ff4 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 16 Feb 2024 15:35:24 +0100 Subject: [PATCH 030/191] Declare gitHubAppCredentials earlier --- src/composition.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index aa62ed93..dece9375 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -54,6 +54,15 @@ const { POSTGRESQL_DB } = process.env +const gitHubAppCredentials = { + appId: GITHUB_APP_ID, + clientId: GITHUB_CLIENT_ID, + clientSecret: GITHUB_CLIENT_SECRET, + privateKey: Buffer + .from(GITHUB_PRIVATE_KEY_BASE_64, "base64") + .toString("utf-8") +} + const pool = new Pool({ host: POSTGRESQL_HOST, user: POSTGRESQL_USER, @@ -102,15 +111,6 @@ export const authOptions: NextAuthOptions = { } } -const gitHubAppCredentials = { - appId: GITHUB_APP_ID, - clientId: GITHUB_CLIENT_ID, - clientSecret: GITHUB_CLIENT_SECRET, - privateKey: Buffer - .from(GITHUB_PRIVATE_KEY_BASE_64, "base64") - .toString("utf-8") -} - export const session: ISession = new AuthjsSession({ authOptions }) const accessTokenReader = new TransferringAccessTokenReader({ From 7918fda0febc6ab0a04ed44c5d91b59070a7cb75 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 16 Feb 2024 16:03:28 +0100 Subject: [PATCH 031/191] Sign in using email and obtain access token via app auth --- src/composition.ts | 49 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index dece9375..b27f46f4 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -1,6 +1,7 @@ import { Pool } from "pg" import { NextAuthOptions } from "next-auth" import GithubProvider from "next-auth/providers/github" +import EmailProvider from "next-auth/providers/email" import PostgresAdapter from "@auth/pg-adapter" import { Adapter } from "next-auth/adapters" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" @@ -24,7 +25,8 @@ import { import { GitHubOAuthTokenRefresher, AuthjsOAuthTokenRepository, - AuthjsRepositoryAccessReader + AuthjsRepositoryAccessReader, + GitHubInstallationAccessTokenDataSource } from "@/features/auth/data" import { AccessTokenRefresher, @@ -60,7 +62,8 @@ const gitHubAppCredentials = { clientSecret: GITHUB_CLIENT_SECRET, privateKey: Buffer .from(GITHUB_PRIVATE_KEY_BASE_64, "base64") - .toString("utf-8") + .toString("utf-8"), + organization: GITHUB_ORGANIZATION_NAME, } const pool = new Pool({ @@ -79,6 +82,8 @@ const logInHandler = new OAuthAccountCredentialPersistingLogInHandler({ provider: "github" }) +const gitHubInstallationAccessTokenDataSource = new GitHubInstallationAccessTokenDataSource(gitHubAppCredentials) + export const authOptions: NextAuthOptions = { adapter: PostgresAdapter(pool) as Adapter, secret: process.env.NEXTAUTH_SECRET, @@ -91,13 +96,49 @@ export const authOptions: NextAuthOptions = { scope: "repo" } } - }) + }), + EmailProvider({ + sendVerificationRequest: ({ url }) => { + console.log("Magic link", url) // print to console for now + }, + }), ], session: { strategy: "database" }, callbacks: { - async signIn({ user, account }) { + async signIn({ user, account, email }) { + if (email && email.verificationRequest && user.email) { // in verification request flow + const guest = await guestRepository.findByEmail(user.email) + if (guest == undefined) { + return false // email not invited + } + } + else if (account?.provider == "email" && user.email) { // in sign in flow, click on magic link + // obtain access token from GitHub using app auth + const guest = await guestRepository.findByEmail(user.email) + if (guest == undefined) { + return false // email not invited + } + const accessToken = await gitHubInstallationAccessTokenDataSource.getAccessToken(guest.projects || []) + const query = ` + INSERT INTO access_tokens ( + provider, + provider_account_id, + access_token + ) + VALUES ($1, $2, $3) + ON CONFLICT (provider, provider_account_id) + DO UPDATE SET access_token = $3, last_updated_at = NOW(); + ` + await pool.query(query, [ + account.provider, + account.providerAccountId, + accessToken, + ]) + return true + } + if (account) { return await logInHandler.handleLogIn(user.id, account) } else { From 87ccbe971337494f9915ca8edb41426563447d30 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 16 Feb 2024 16:03:52 +0100 Subject: [PATCH 032/191] Move guest table schema to create-tables.sql --- README.md | 61 +---------------------------------------------- create-tables.sql | 6 +++++ 2 files changed, 7 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index b403e3b1..0f7751aa 100644 --- a/README.md +++ b/README.md @@ -58,66 +58,7 @@ Finally, open the application on https://dev.local:3000. ## Database Schemas -### Authjs -```sql -CREATE TABLE verification_token -( - identifier TEXT NOT NULL, - expires TIMESTAMPTZ NOT NULL, - token TEXT NOT NULL, - - PRIMARY KEY (identifier, token) -); - -CREATE TABLE accounts -( - id SERIAL, - "userId" INTEGER NOT NULL, - type VARCHAR(255) NOT NULL, - provider VARCHAR(255) NOT NULL, - "providerAccountId" VARCHAR(255) NOT NULL, - refresh_token TEXT, - access_token TEXT, - expires_at BIGINT, - id_token TEXT, - scope TEXT, - session_state TEXT, - token_type TEXT, - - PRIMARY KEY (id) -); - -CREATE TABLE sessions -( - id SERIAL, - "userId" INTEGER NOT NULL, - expires TIMESTAMPTZ NOT NULL, - "sessionToken" VARCHAR(255) NOT NULL, - - PRIMARY KEY (id) -); - -CREATE TABLE users -( - id SERIAL, - name VARCHAR(255), - email VARCHAR(255), - "emailVerified" TIMESTAMPTZ, - image TEXT, - - PRIMARY KEY (id) -); - -``` - -### Guests and Project Access -```sql -CREATE TABLE guests ( - id SERIAL PRIMARY KEY, - email VARCHAR(255) UNIQUE NOT NULL, - projects jsonb -); -``` +See `create-tables.sql` ## 🚀 Deploying the App diff --git a/create-tables.sql b/create-tables.sql index 6bed5983..cc2ae091 100644 --- a/create-tables.sql +++ b/create-tables.sql @@ -56,3 +56,9 @@ CREATE TABLE access_tokens PRIMARY KEY (provider, provider_account_id) ); + +CREATE TABLE guests ( + id SERIAL PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + projects jsonb +); From 534baa7a5305d17c26b61c0e3f308ca96a808316 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 16 Feb 2024 16:04:46 +0100 Subject: [PATCH 033/191] Make refresh_token nullable We do not get refresh tokens for app auth --- create-tables.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create-tables.sql b/create-tables.sql index cc2ae091..11733c16 100644 --- a/create-tables.sql +++ b/create-tables.sql @@ -51,7 +51,7 @@ CREATE TABLE access_tokens provider VARCHAR(255) NOT NULL, provider_account_id VARCHAR(255) NOT NULL, access_token VARCHAR(255) NOT NULL, - refresh_token VARCHAR(255) NOT NULL, + refresh_token VARCHAR(255) NULL, last_updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (provider, provider_account_id) From 7c3a98d37230cd34dfd8d280ef1cfce5d724d781 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 20 Feb 2024 14:53:06 +0100 Subject: [PATCH 034/191] Add guests to drop table sql --- drop-tables.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/drop-tables.sql b/drop-tables.sql index 35bf3d95..82ea5042 100644 --- a/drop-tables.sql +++ b/drop-tables.sql @@ -3,3 +3,4 @@ DROP TABLE verification_token; DROP TABLE accounts; DROP TABLE sessions; DROP TABLE users; +DROP TABLE guests; From 79f9fdcc4c22870e2b9c64c8a577e2ac7d06ce51 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 20 Feb 2024 15:13:12 +0100 Subject: [PATCH 035/191] Add "Manage guests" menu item --- src/features/user/view/SettingsList.tsx | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/features/user/view/SettingsList.tsx b/src/features/user/view/SettingsList.tsx index a62d6883..19ec0a56 100644 --- a/src/features/user/view/SettingsList.tsx +++ b/src/features/user/view/SettingsList.tsx @@ -3,6 +3,20 @@ import ThickDivider from "@/common/ui/ThickDivider" import DocumentationVisualizationPicker from "./DocumentationVisualizationPicker" import { signOut } from "next-auth/react" +const SettingsItem = ({ onClick, href, children }: { + onClick?: () => void; + href?: string; + children?: string; +}) => + + const SettingsList = () => { return ( { }}> - + ) } From dfa12afa0436646fbfa5a8ad0bf664b83c07f713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 25 Apr 2024 14:13:17 +0200 Subject: [PATCH 036/191] Removes OAuthAccountCredentialPersistingLogInHandler --- src/composition.ts | 7 +--- .../domain/logIn/NullObjectLogInHandler.ts | 9 ++++ ...AccountCredentialPersistingLogInHandler.ts | 42 ------------------- src/features/auth/domain/logIn/index.ts | 2 +- 4 files changed, 12 insertions(+), 48 deletions(-) create mode 100644 src/features/auth/domain/logIn/NullObjectLogInHandler.ts delete mode 100644 src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts diff --git a/src/composition.ts b/src/composition.ts index b27f46f4..f0a08791 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -34,8 +34,8 @@ import { CompositeLogOutHandler, ErrorIgnoringLogOutHandler, GitHubOrganizationSessionValidator, - OAuthAccountCredentialPersistingLogInHandler, LockingAccessTokenRefresher, + NullObjectLogInHandler, OAuthTokenRepository, TransferringAccessTokenReader, UserDataCleanUpLogOutHandler @@ -77,10 +77,7 @@ const pool = new Pool({ const db = new PostgreSQLDB({ pool }) -const logInHandler = new OAuthAccountCredentialPersistingLogInHandler({ - db, - provider: "github" -}) +const logInHandler = new NullObjectLogInHandler() const gitHubInstallationAccessTokenDataSource = new GitHubInstallationAccessTokenDataSource(gitHubAppCredentials) diff --git a/src/features/auth/domain/logIn/NullObjectLogInHandler.ts b/src/features/auth/domain/logIn/NullObjectLogInHandler.ts new file mode 100644 index 00000000..1051df99 --- /dev/null +++ b/src/features/auth/domain/logIn/NullObjectLogInHandler.ts @@ -0,0 +1,9 @@ +import { ILogInHandler, IAccount } from "." + +export default class NullObjectLogInHandler implements ILogInHandler { + constructor() {} + + async handleLogIn(_userId: string, _account?: IAccount): Promise { + return true + } +} diff --git a/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts b/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts deleted file mode 100644 index 4938932b..00000000 --- a/src/features/auth/domain/logIn/OAuthAccountCredentialPersistingLogInHandler.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IDB } from "@/common" -import { ILogInHandler, IAccount } from "." - -export default class OAuthAccountCredentialPersistingLogInHandler implements ILogInHandler { - private readonly db: IDB - private readonly provider: string - - constructor(config: { db: IDB, provider: string }) { - this.db = config.db - this.provider = config.provider - } - - async handleLogIn(_userId: string, account?: IAccount): Promise { - if (!account) { - return true - } - if (account.provider !== this.provider) { - return true - } - if (!account.access_token || !account.refresh_token) { - return false - } - const query = ` - INSERT INTO access_tokens ( - provider, - provider_account_id, - access_token, - refresh_token - ) - VALUES ($1, $2, $3, $4) - ON CONFLICT (provider, provider_account_id) - DO UPDATE SET access_token = $3, refresh_token = $4, last_updated_at = NOW(); - ` - await this.db.query(query, [ - account.provider, - account.providerAccountId, - account.access_token, - account.refresh_token - ]) - return true - } -} diff --git a/src/features/auth/domain/logIn/index.ts b/src/features/auth/domain/logIn/index.ts index cef36bee..de977d4e 100644 --- a/src/features/auth/domain/logIn/index.ts +++ b/src/features/auth/domain/logIn/index.ts @@ -1,2 +1,2 @@ export type { default as ILogInHandler, IAccount } from "./ILogInHandler" -export { default as OAuthAccountCredentialPersistingLogInHandler } from "./OAuthAccountCredentialPersistingLogInHandler" +export { default as NullObjectLogInHandler } from "./NullObjectLogInHandler" From 25c80a0c1f9ccaf1e0df0d73bedd94fcb4180516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 25 Apr 2024 15:22:43 +0200 Subject: [PATCH 037/191] Renames TransferringAccessTokenReader to AccessTokenReader --- __test__/auth/TransferringAccessTokenReader.test.ts | 10 +++++----- src/composition.ts | 4 ++-- ...erringAccessTokenReader.ts => AccessTokenReader.ts} | 2 +- src/features/auth/domain/accessToken/index.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/features/auth/domain/accessToken/{TransferringAccessTokenReader.ts => AccessTokenReader.ts} (95%) diff --git a/__test__/auth/TransferringAccessTokenReader.test.ts b/__test__/auth/TransferringAccessTokenReader.test.ts index 03636e6c..a3a68902 100644 --- a/__test__/auth/TransferringAccessTokenReader.test.ts +++ b/__test__/auth/TransferringAccessTokenReader.test.ts @@ -1,8 +1,8 @@ -import { TransferringAccessTokenReader } from "../../src/features/auth/domain" +import { AccessTokenReader } from "../../src/features/auth/domain" test("It reads user ID", async () => { let didReadUserId = false - const sut = new TransferringAccessTokenReader({ + const sut = new AccessTokenReader({ userIdReader: { async getUserId() { didReadUserId = true @@ -37,7 +37,7 @@ test("It reads user ID", async () => { test("It skips reading from source repository if a token was found in the destination repository", async () => { let didReadOAuthTokenFromSource = false let didReadOAuthTokenFromDestination = false - const sut = new TransferringAccessTokenReader({ + const sut = new AccessTokenReader({ userIdReader: { async getUserId() { return "1234" @@ -74,7 +74,7 @@ test("It skips reading from source repository if a token was found in the destin test("It reads from source repository if no token was found in destination repository", async () => { let didReadOAuthTokenFromSource = false let didReadOAuthTokenFromDestination = false - const sut = new TransferringAccessTokenReader({ + const sut = new AccessTokenReader({ userIdReader: { async getUserId() { return "1234" @@ -108,7 +108,7 @@ test("It reads from source repository if no token was found in destination repos test("It stores token read from source repository in destination repository", async () => { let storedAccessToken: string | undefined let storedRefreshToken: string | undefined - const sut = new TransferringAccessTokenReader({ + const sut = new AccessTokenReader({ userIdReader: { async getUserId() { return "1234" diff --git a/src/composition.ts b/src/composition.ts index f0a08791..f14cbddd 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -37,7 +37,7 @@ import { LockingAccessTokenRefresher, NullObjectLogInHandler, OAuthTokenRepository, - TransferringAccessTokenReader, + AccessTokenReader, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" import { IGuestInviter } from "./features/admin/domain/IGuestInviter" @@ -151,7 +151,7 @@ export const authOptions: NextAuthOptions = { export const session: ISession = new AuthjsSession({ authOptions }) -const accessTokenReader = new TransferringAccessTokenReader({ +const accessTokenReader = new AccessTokenReader({ userIdReader: session, sourceOAuthTokenRepository: new AuthjsOAuthTokenRepository({ provider: "github", db }), destinationOAuthTokenRepository: new OAuthTokenRepository({ provider: "github", db }) diff --git a/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts b/src/features/auth/domain/accessToken/AccessTokenReader.ts similarity index 95% rename from src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts rename to src/features/auth/domain/accessToken/AccessTokenReader.ts index ef98c790..b5c15a35 100644 --- a/src/features/auth/domain/accessToken/TransferringAccessTokenReader.ts +++ b/src/features/auth/domain/accessToken/AccessTokenReader.ts @@ -4,7 +4,7 @@ interface IUserIDReader { getUserId(): Promise } -export default class TransferringAccessTokenReader { +export default class AccessTokenReader { private readonly userIdReader: IUserIDReader private readonly sourceOAuthTokenRepository: IOAuthTokenRepository private readonly destinationOAuthTokenRepository: IOAuthTokenRepository diff --git a/src/features/auth/domain/accessToken/index.ts b/src/features/auth/domain/accessToken/index.ts index e56c9c44..14640dd7 100644 --- a/src/features/auth/domain/accessToken/index.ts +++ b/src/features/auth/domain/accessToken/index.ts @@ -2,4 +2,4 @@ export { default as AccessTokenRefresher } from "./AccessTokenRefresher" export type { default as IAccessTokenRefresher } from "./IAccessTokenRefresher" export { default as LockingAccessTokenRefresher } from "./LockingAccessTokenRefresher" export { default as RepositoryRestrictingAccessTokenDataSource } from "./RepositoryRestrictingAccessTokenDataSource" -export { default as TransferringAccessTokenReader } from "./TransferringAccessTokenReader" +export { default as AccessTokenReader } from "./AccessTokenReader" From de9194a48307275b35c3641e4dd697ddb2017c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 25 Apr 2024 15:23:03 +0200 Subject: [PATCH 038/191] Removes OAuthAccountCredentialPersistingLogInHandler tests --- ...ntCredentialPersistingLogInHandler.test.ts | 173 ------------------ 1 file changed, 173 deletions(-) delete mode 100644 __test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts diff --git a/__test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts b/__test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts deleted file mode 100644 index 25a00652..00000000 --- a/__test__/auth/OAuthAccountCredentialPersistingLogInHandler.test.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { OAuthAccountCredentialPersistingLogInHandler } from "../../src/features/auth/domain" - -test("It skips handling log in if account is unavailable", async () => { - let didQuery = false - const sut = new OAuthAccountCredentialPersistingLogInHandler({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query() { - didQuery = true - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } - } - } - }) - const allowLogin = await sut.handleLogIn("42") - expect(allowLogin).toBeTruthy() - expect(didQuery).toBeFalsy() -}) - -test("It skips handling log in if account does not come from the expected provider", async () => { - let didQuery = false - const sut = new OAuthAccountCredentialPersistingLogInHandler({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query() { - didQuery = true - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } - } - } - }) - const allowLogin = await sut.handleLogIn("42", { - provider: "twitter", - providerAccountId: "1234", - access_token: "foo", - refresh_token: "bar" - }) - expect(allowLogin).toBeTruthy() - expect(didQuery).toBeFalsy() -}) - -test("It disallows login if account has no access token", async () => { - let didQuery = false - const sut = new OAuthAccountCredentialPersistingLogInHandler({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query() { - didQuery = true - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } - } - } - }) - const allowLogin = await sut.handleLogIn("42", { - provider: "twitter", - providerAccountId: "1234", - refresh_token: "bar" - }) - expect(allowLogin).toBeTruthy() - expect(didQuery).toBeFalsy() -}) - -test("It disallows login if account has no refresh token", async () => { - let didQuery = false - const sut = new OAuthAccountCredentialPersistingLogInHandler({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query() { - didQuery = true - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } - } - } - }) - const allowLogin = await sut.handleLogIn("42", { - provider: "twitter", - providerAccountId: "1234", - access_token: "foo" - }) - expect(allowLogin).toBeTruthy() - expect(didQuery).toBeFalsy() -}) - -test("It stores OAuth account information", async () => { - let storedProvider: string | undefined - let storedProviderAccountID: string | undefined - let storedAccessToken: string | undefined - let storedRefreshToken: string | undefined - const sut = new OAuthAccountCredentialPersistingLogInHandler({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query(_query: string, values: any[] = []) { - storedProvider = values[0] - storedProviderAccountID = values[1] - storedAccessToken = values[2] - storedRefreshToken = values[3] - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } - } - } - }) - const allowLogin = await sut.handleLogIn("42", { - provider: "github", - providerAccountId: "1234", - access_token: "foo", - refresh_token: "bar" - }) - expect(allowLogin).toBeTruthy() - expect(storedProvider).toBe("github") - expect(storedProviderAccountID).toBe("1234") - expect(storedAccessToken).toBe("foo") - expect(storedRefreshToken).toBe("bar") -}) From 05192628eaba4778e2f7a06a9bfe797cff123946 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Mon, 29 Apr 2024 12:23:58 +0200 Subject: [PATCH 039/191] Send magic link emails via SMTP --- README.md | 23 +++++++++++++++++++++++ src/composition.ts | 17 ++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0f7751aa..4469dcfc 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' GITHUB_ORGANIZATION_NAME='shapehq' REDIS_URL='' +SMTP_HOST='' +SMTP_USER='' +SMTP_PASS='' ``` Each environment variable is described in the table below. @@ -47,6 +50,10 @@ Each environment variable is described in the table below. |GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST|Comma-separated list of repositories from which webhook calls should be ignored. The list of disallowed repositories takes precedence over the list of allowed repositories.| |GITHUB_ORGANIZATION_NAME|Name of the organization to show repositories for.| |REDIS_URL|The URL to the Redis store.| +|SMTP_HOST|Hostname for SMTP server used for sending magic links and guest invitation.| +|SMTP_USER|Username for SMTP server used for sending magic links and guest invitation.| +|SMTP_PASS|Password for SMTP server used for sending magic links and guest invitation.| +|FROM_EMAIL|Sender email for magic links and guest invitations.| Run the app using the following command: @@ -60,6 +67,22 @@ Finally, open the application on https://dev.local:3000. See `create-tables.sql` +## SMTP Setup + +Magic links are sent via email. + +Configure email sending via SMTP using the `SMTP_*` environment variables. + +Follow this guide for AWS SES: [Using the Amazon SES SMTP interface to send email](https://docs.aws.amazon.com/ses/latest/dg/send-email-smtp.html). + +Example (for AWS SES): + +``` +SMTP_HOST="email-smtp.eu-central-1.amazonaws.com" +SMTP_USER="3AH7TB7N4I4JDIK2A66E" +SMTP_PASS="SX/vT1W7q9d44Oe2fmEURYIWqttgBNbhrtuMDb6CBBBg" +``` + ## 🚀 Deploying the App The app is hosted on Heroku in two different environments. diff --git a/src/composition.ts b/src/composition.ts index b27f46f4..f59b1cc8 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -53,7 +53,11 @@ const { REDIS_URL, POSTGRESQL_HOST, POSTGRESQL_USER, - POSTGRESQL_DB + POSTGRESQL_DB, + SMTP_HOST, + SMTP_USER, + SMTP_PASS, + FROM_EMAIL, } = process.env const gitHubAppCredentials = { @@ -84,6 +88,8 @@ const logInHandler = new OAuthAccountCredentialPersistingLogInHandler({ const gitHubInstallationAccessTokenDataSource = new GitHubInstallationAccessTokenDataSource(gitHubAppCredentials) +const fromEmail = FROM_EMAIL || "Shape Docs " // must be a verified email in AWS SES + export const authOptions: NextAuthOptions = { adapter: PostgresAdapter(pool) as Adapter, secret: process.env.NEXTAUTH_SECRET, @@ -98,9 +104,14 @@ export const authOptions: NextAuthOptions = { } }), EmailProvider({ - sendVerificationRequest: ({ url }) => { - console.log("Magic link", url) // print to console for now + server: { + host: SMTP_HOST, + auth: { + user: SMTP_USER, + pass: SMTP_PASS, + } }, + from: fromEmail, }), ], session: { From 75687234432653a2bef2b55b7d73ecc5383fb6a6 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Mon, 29 Apr 2024 12:43:31 +0200 Subject: [PATCH 040/191] Send guest invitation via email --- src/composition.ts | 45 ++++---------------- src/features/admin/data/EmailGuestInviter.ts | 42 ++++++++++++++++++ types/env.d.ts | 6 ++- 3 files changed, 56 insertions(+), 37 deletions(-) create mode 100644 src/features/admin/data/EmailGuestInviter.ts diff --git a/src/composition.ts b/src/composition.ts index f59b1cc8..cb564d8d 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -43,6 +43,7 @@ import { import { IGuestInviter } from "./features/admin/domain/IGuestInviter" import { createTransport } from "nodemailer" import DbGuestRepository from "./features/admin/data/DbGuestRepository" +import { EmailGuestInviter } from "./features/admin/data/EmailGuestInviter" const { GITHUB_APP_ID, @@ -233,39 +234,11 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( export const guestRepository: IGuestRepository = new DbGuestRepository(pool) -const transport = createTransport({ - host: "sandbox.smtp.mailtrap.io", - port: 2525, - auth: { - user: "0682027d57d0db", - pass: "28c3dbfbfc0af8" - } -}); - -export const guestInviter: IGuestInviter = { - inviteGuestByEmail: function (email: string): Promise { - transport.sendMail({ - to: email, - from: "no-reply@docs.shapetools.io", - subject: "You have been invited to join Shape Docs", - html: ` - - - - - -

You have been invited to join Shape Docs!

-

Shape Docs uses magic links for authentication. This means that you don't need to remember a password.

-

Click the link below to request your first magic link to log in:

- Log in - - - `, - }) - return Promise.resolve() - } -} +export const guestInviter: IGuestInviter = new EmailGuestInviter({ + server: { + host: SMTP_HOST, + user: SMTP_USER, + pass: SMTP_PASS, + }, + from: fromEmail, +}) diff --git a/src/features/admin/data/EmailGuestInviter.ts b/src/features/admin/data/EmailGuestInviter.ts new file mode 100644 index 00000000..a61f88c5 --- /dev/null +++ b/src/features/admin/data/EmailGuestInviter.ts @@ -0,0 +1,42 @@ +import { Transporter, createTransport } from "nodemailer"; +import { IGuestInviter } from "../domain/IGuestInviter"; + +export class EmailGuestInviter implements IGuestInviter { + readonly transport: Transporter; + readonly from: string; + + constructor(options: { server: {host: string, user: string, pass: string}, from: string }) { + this.transport = createTransport({ + host: options.server.host, + auth: { + user: options.server.user, + pass: options.server.pass, + }, + }); + this.from = options.from; + } + + async inviteGuestByEmail(email: string): Promise { + await this.transport.sendMail({ + to: email, + from: this.from, + subject: "You have been invited to join Shape Docs", + html: ` + + + + + +

You have been invited to join Shape Docs!

+

Shape Docs uses magic links for authentication. This means that you don't need to remember a password.

+

To get started click here: Sign in

+ + + `, + }); + } +} diff --git a/types/env.d.ts b/types/env.d.ts index dbb1daa8..21e886bc 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -15,6 +15,10 @@ namespace NodeJS { REDIS_URL: string POSTGRESQL_HOST: string POSTGRESQL_USER: string - POSTGRESQL_DB: string + POSTGRESQL_DB: string, + SMTP_HOST: string, + SMTP_USER: string, + SMTP_PASS: string, + FROM_EMAIL: string | undefined, } } From d65080e03b00b61371ef1f33e71c16201a06f7b4 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Mon, 29 Apr 2024 12:55:47 +0200 Subject: [PATCH 041/191] Basic styling of admin page --- src/features/admin/view/AdminPage.tsx | 96 +++++++++++++++------------ 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx index 11b39194..41f331a1 100644 --- a/src/features/admin/view/AdminPage.tsx +++ b/src/features/admin/view/AdminPage.tsx @@ -1,5 +1,5 @@ import { guestInviter, guestRepository } from "@/composition" -import { Button, ButtonGroup, Chip, FormGroup, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField } from "@mui/material" +import { Box, Button, ButtonGroup, Chip, FormGroup, Paper, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography } from "@mui/material" import Table from "@mui/material/Table" import { revalidatePath } from "next/cache" @@ -36,47 +36,59 @@ const AdminPage = async () => { return ( <> -

Guest Admin

- -

Invite Guest

-
- - - - - -
- -

Guests

- - - - - Email - Status - Projects - Actions - - - - {guests.map((row, index) => ( - - {row.email} - - {row.projects.join(", ")} - - -
- - - -
-
-
- ))} -
-
-
+ + Admin + + + Invite Guest + + Invite a guest to access the platform. An invitation email will be sent to the provided email address. + +
+ + + + + +
+
+ Guests + + + Manage guests who have access to the platform. + + + + + + + Email + Status + Projects + Actions + + + + {guests.map((row, index) => ( + + {row.email} + + {row.projects.join(", ")} + + +
+ + + +
+
+
+ ))} +
+
+
+
+
) } From 8ab9d26f5c92f6261608a2c29995004f792611b1 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 30 Apr 2024 09:30:15 +0200 Subject: [PATCH 042/191] Improve invite guest + remove guest --- __test__/admin/EmailGuestInviter.test.ts | 55 +++++++++++++++++++ src/features/admin/view/Actions.tsx | 61 +++++++++++++++++++++ src/features/admin/view/AdminPage.tsx | 54 +++--------------- src/features/admin/view/InviteGuestForm.tsx | 28 ++++++++++ src/features/admin/view/RemoveGuestForm.tsx | 10 ++++ 5 files changed, 162 insertions(+), 46 deletions(-) create mode 100644 __test__/admin/EmailGuestInviter.test.ts create mode 100644 src/features/admin/view/Actions.tsx create mode 100644 src/features/admin/view/InviteGuestForm.tsx create mode 100644 src/features/admin/view/RemoveGuestForm.tsx diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts new file mode 100644 index 00000000..b0b5f166 --- /dev/null +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -0,0 +1,55 @@ +import nodemailer from 'nodemailer'; +import { EmailGuestInviter } from "@/features/admin/data/EmailGuestInviter" + +jest.mock('nodemailer', () => { + return { + createTransport: jest.fn().mockReturnValue({ + sendMail: jest.fn(), + }), + } +}) + +describe('EmailGuestInviter', () => { + describe('constructor', () => { + it('should create a transporter', () => { + const sut = new EmailGuestInviter({ + server: { + host: 'smtp.example.com', + user: 'user', + pass: 'pass', + }, + from: 'some@email.dk', + }) + + expect(nodemailer.createTransport).toHaveBeenCalledWith({ + host: 'smtp.example.com', + auth: { + user: 'user', + pass: 'pass', + }, + }) + }) + }) + + describe('inviteGuestByEmail', () => { + it('should send an invite', async () => { + const sut = new EmailGuestInviter({ + server: { + host: 'smtp.example.com', + user: 'user', + pass: 'pass', + }, + from: 'some@email.dk', + }) + + sut.inviteGuestByEmail('guest@email.dk') + + expect(nodemailer.createTransport().sendMail).toHaveBeenCalledWith({ + to: 'guest@email.dk', + from: 'some@email.dk', + subject: 'You have been invited to join Shape Docs', + html: expect.any(String), // difficult to test the exact content + }); + }) + }) +}) diff --git a/src/features/admin/view/Actions.tsx b/src/features/admin/view/Actions.tsx new file mode 100644 index 00000000..12abbabe --- /dev/null +++ b/src/features/admin/view/Actions.tsx @@ -0,0 +1,61 @@ +'use server' + +import { guestInviter, guestRepository } from "@/composition" +import { revalidatePath } from "next/cache" +import { z } from 'zod' + +const sendInviteSchema = z.object({ + email: z.string().email(), + projects: z.string().transform(v => v.trim().split(",")) +}) + +export interface SendInviteResult { + error?: string, + success: boolean, +} + +/** +* Server action to send an invite +*/ +export const sendInvite = async (prevState: any, formData: FormData): Promise => { + const validatedFields = sendInviteSchema.safeParse({ + email: formData.get('email'), + projects: formData.get('projects'), + }) + + if (!validatedFields.success) { + return { + error: 'Validation failed', + success: false, + } + } + + try { + await guestRepository.create(validatedFields.data.email, validatedFields.data.projects) + await guestInviter.inviteGuestByEmail(validatedFields.data.email) + + revalidatePath('/admin/guests') + + return { + success: true, + } + } catch (error: unknown) { + return { + success: false, + error: error instanceof Error ? error.message : 'An error occurred', + } + } +} + +/** + * Server action to remove a guest + */ +export const removeGuest = async (formData: FormData): Promise => { + 'use server' + + const email = formData.get('email') as string + + await guestRepository.removeByEmail(email) + + revalidatePath('/admin/guests') +} diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx index 41f331a1..c8a4c425 100644 --- a/src/features/admin/view/AdminPage.tsx +++ b/src/features/admin/view/AdminPage.tsx @@ -1,41 +1,13 @@ -import { guestInviter, guestRepository } from "@/composition" -import { Box, Button, ButtonGroup, Chip, FormGroup, Paper, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography } from "@mui/material" +import { guestRepository } from "@/composition" +import { Box, ButtonGroup, Chip, Paper, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material" import Table from "@mui/material/Table" -import { revalidatePath } from "next/cache" - -/** - * Server action to send an invite - */ -const sendInvite = async (formData: FormData): Promise => { - 'use server' - - const email = formData.get('email') as string - const projects = formData.get('projects') as string - - guestInviter.inviteGuestByEmail(email as string) - guestRepository.create(email as string, projects.split(",").map(p => p.trim())) - - revalidatePath('/admin/guests') -} - -/** - * Server action to remove a guest - */ -const removeGuest = async (formData: FormData): Promise => { - 'use server' - - const email = formData.get('email') as string - - guestRepository.removeByEmail(email) - - revalidatePath('/admin/guests') -} +import { InviteGuestForm } from "./InviteGuestForm" +import { RemoveGuestForm } from "./RemoveGuestForm" const AdminPage = async () => { - const guests = await guestRepository.getAll() + const guests = await guestRepository.getAll() return ( - <> Admin @@ -44,13 +16,7 @@ const AdminPage = async () => { Invite a guest to access the platform. An invitation email will be sent to the provided email address. -
- - - - - -
+ Guests @@ -72,14 +38,11 @@ const AdminPage = async () => { {guests.map((row, index) => ( {row.email} - + {row.projects.join(", ")} -
- - -
+ { /* TODO: Ask the user for confirmation */}
@@ -89,7 +52,6 @@ const AdminPage = async () => {
- ) } diff --git a/src/features/admin/view/InviteGuestForm.tsx b/src/features/admin/view/InviteGuestForm.tsx new file mode 100644 index 00000000..ab67a972 --- /dev/null +++ b/src/features/admin/view/InviteGuestForm.tsx @@ -0,0 +1,28 @@ +'use client' + +import { useFormState } from 'react-dom' +import { SendInviteResult, sendInvite } from './Actions' +import { Alert, Button, FormGroup, TextField } from '@mui/material' + +const initialState: SendInviteResult = { + success: false, +} + +export const InviteGuestForm = () => { + const [state, formAction] = useFormState(sendInvite, initialState) + + return ( +
+ {state.error && Failed to invite guest: {state.error}} + {state.success && + + Guest was invited successfully 🥳 + } + + + + + +
+ ) +} diff --git a/src/features/admin/view/RemoveGuestForm.tsx b/src/features/admin/view/RemoveGuestForm.tsx new file mode 100644 index 00000000..6aa298c2 --- /dev/null +++ b/src/features/admin/view/RemoveGuestForm.tsx @@ -0,0 +1,10 @@ +'use client' + +import { Button } from "@mui/material"; +import { removeGuest } from "./Actions"; + +export const RemoveGuestForm = ({ email }: { email: string }) => +
+ + +
From cc79270bbd1b2cfaa0d140d905c602152f940ce1 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 30 Apr 2024 09:54:14 +0200 Subject: [PATCH 043/191] Ask for confirmation before removing guest --- src/features/admin/view/AdminPage.tsx | 2 +- src/features/admin/view/RemoveGuestForm.tsx | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx index c8a4c425..a6606acf 100644 --- a/src/features/admin/view/AdminPage.tsx +++ b/src/features/admin/view/AdminPage.tsx @@ -42,7 +42,7 @@ const AdminPage = async () => { {row.projects.join(", ")} - { /* TODO: Ask the user for confirmation */} + diff --git a/src/features/admin/view/RemoveGuestForm.tsx b/src/features/admin/view/RemoveGuestForm.tsx index 6aa298c2..e27499a3 100644 --- a/src/features/admin/view/RemoveGuestForm.tsx +++ b/src/features/admin/view/RemoveGuestForm.tsx @@ -3,8 +3,17 @@ import { Button } from "@mui/material"; import { removeGuest } from "./Actions"; -export const RemoveGuestForm = ({ email }: { email: string }) => -
- - -
+export const RemoveGuestForm = ({ email }: { email: string }) => { + const onClick = (e: React.MouseEvent) => { + e.preventDefault() + if (confirm(`Please confirm that you want to remove ${email}`) === false) return + e.currentTarget.form?.submit() + } + + return ( +
+ + +
+ ) +} \ No newline at end of file From 33953cedc47960188b4e4ae55548e718418d26e1 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 30 Apr 2024 15:16:56 +0200 Subject: [PATCH 044/191] Add testwatch task --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f5ec056..3702a147 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "build": "next build", "start": "next start", "lint": "next lint --max-warnings=0", - "test": "jest" + "test": "jest", + "testwatch": "jest --watch" }, "dependencies": { "@auth/pg-adapter": "^0.4.1", From 654a86632cb1ac0318fd6e16841605e6d2b80443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 6 May 2024 08:02:49 +0200 Subject: [PATCH 045/191] Fixes imports --- src/features/admin/data/DbGuestRepository.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features/admin/data/DbGuestRepository.ts b/src/features/admin/data/DbGuestRepository.ts index 089dbe32..2cbbaf7f 100644 --- a/src/features/admin/data/DbGuestRepository.ts +++ b/src/features/admin/data/DbGuestRepository.ts @@ -1,4 +1,5 @@ import { Pool } from "pg" +import { Guest, IGuestRepository } from "../domain" export default class DbGuestRepository implements IGuestRepository { readonly pool: Pool From 4b2a52fdd8daba1df4d918360a6d3e795d8d00e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 6 May 2024 08:02:55 +0200 Subject: [PATCH 046/191] Adds index to admin/data --- src/features/admin/data/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/features/admin/data/index.ts diff --git a/src/features/admin/data/index.ts b/src/features/admin/data/index.ts new file mode 100644 index 00000000..c8919c55 --- /dev/null +++ b/src/features/admin/data/index.ts @@ -0,0 +1 @@ +export { default as DbGuestRepository } from "./DbGuestRepository" From d6e738eb24b55fe0a4bec11e5f9de0728e132874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 6 May 2024 08:03:09 +0200 Subject: [PATCH 047/191] Adds default exports in admin/domain --- src/features/admin/domain/Guest.ts | 2 ++ src/features/admin/domain/IGuestInviter.ts | 2 +- src/features/admin/domain/IGuestRepository.ts | 3 ++- src/features/admin/domain/index.ts | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/features/admin/domain/index.ts diff --git a/src/features/admin/domain/Guest.ts b/src/features/admin/domain/Guest.ts index 00fd1d14..d2a75c32 100644 --- a/src/features/admin/domain/Guest.ts +++ b/src/features/admin/domain/Guest.ts @@ -3,3 +3,5 @@ type Guest = { status: "active" | "invited"; projects: string[]; }; + +export default Guest diff --git a/src/features/admin/domain/IGuestInviter.ts b/src/features/admin/domain/IGuestInviter.ts index c358b1aa..8b8217d6 100644 --- a/src/features/admin/domain/IGuestInviter.ts +++ b/src/features/admin/domain/IGuestInviter.ts @@ -1,3 +1,3 @@ -export interface IGuestInviter { +export default interface IGuestInviter { inviteGuestByEmail: (email: string) => Promise } diff --git a/src/features/admin/domain/IGuestRepository.ts b/src/features/admin/domain/IGuestRepository.ts index fd95b396..7550e5c7 100644 --- a/src/features/admin/domain/IGuestRepository.ts +++ b/src/features/admin/domain/IGuestRepository.ts @@ -1,5 +1,6 @@ +import Guest from "./Guest" -interface IGuestRepository { +export default interface IGuestRepository { getAll(): Promise findByEmail(email: string): Promise create(email: string, projects: string[]): Promise diff --git a/src/features/admin/domain/index.ts b/src/features/admin/domain/index.ts new file mode 100644 index 00000000..5ab705f3 --- /dev/null +++ b/src/features/admin/domain/index.ts @@ -0,0 +1,3 @@ +export type { default as Guest } from "./Guest" +export type { default as IGuestRepository } from "./IGuestRepository" +export type { default as IGuestInviter } from "./IGuestInviter" From 0b46f31a8f18c03bc9d7bfc4c7b60b460dc67731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 6 May 2024 11:41:45 +0200 Subject: [PATCH 048/191] Transfers OAuth tokens --- __test__/auth/AccessTokenDataSource.test.ts | 268 ++++++++++++++++++ .../auth/AccessTokenSessionValidator.test.ts | 39 +++ __test__/auth/AccessTokenTransferrer.test.ts | 71 +++++ .../auth/AuthjsOAuthTokenDataSource.test.ts | 32 +++ .../auth/AuthjsOAuthTokenRepository.test.ts | 75 ----- .../GitHubUserAccessTokenTransferrer.test.ts | 212 ++++++++++++++ .../auth/GuestAccessTokenTransferrer.test.ts | 224 +++++++++++++++ .../GuestRepositoryAccessDataSource.test.ts | 143 ++++++++++ __test__/auth/OAuthTokenRepository.test.ts | 2 +- .../TransferringAccessTokenReader.test.ts | 141 --------- .../AccessTokenRefreshingGitHubClient.test.ts | 14 +- create-tables.sql | 12 +- drop-tables.sql | 2 +- .../AccessTokenRefreshingGitHubClient.ts | 10 +- src/common/github/GitHubClient.ts | 14 +- src/common/mutex/SessionMutexFactory.ts | 12 +- src/common/session/AuthjsSession.ts | 35 ++- src/common/session/ISession.ts | 1 + src/composition.ts | 100 +++---- src/features/admin/domain/Guest.ts | 6 +- src/features/admin/domain/IGuestRepository.ts | 10 +- ...itory.ts => AuthjsOAuthTokenDataSource.ts} | 12 +- .../auth/data/AuthjsRepositoryAccessReader.ts | 7 - src/features/auth/data/index.ts | 3 +- .../accessToken/AccessTokenDataSource.ts | 56 ++++ .../domain/accessToken/AccessTokenReader.ts | 37 --- .../accessToken/AccessTokenRefresher.ts | 9 +- .../accessToken/AccessTokenTransferrer.ts | 32 +++ .../GitHubAccessTokenTransferrer.ts | 27 ++ .../GuestAccessTokenTransferrer.ts | 48 ++++ .../GuestRepositoryAccessDataSource.ts | 28 ++ .../accessToken/IAccessTokenRefresher.ts | 3 - .../accessToken/IAccessTokenTransferrer.ts | 3 + .../LockingAccessTokenRefresher.ts | 7 +- src/features/auth/domain/accessToken/index.ts | 7 +- .../oAuthToken/IOAuthTokenDataSource.ts | 5 + .../auth/domain/oAuthToken/OAuthToken.ts | 2 +- .../domain/oAuthToken/OAuthTokenRepository.ts | 38 +-- src/features/auth/domain/oAuthToken/index.ts | 1 + .../AccessTokenSessionValidator.ts | 11 +- types/@next-auth.d.ts | 1 + 41 files changed, 1354 insertions(+), 406 deletions(-) create mode 100644 __test__/auth/AccessTokenDataSource.test.ts create mode 100644 __test__/auth/AccessTokenSessionValidator.test.ts create mode 100644 __test__/auth/AccessTokenTransferrer.test.ts create mode 100644 __test__/auth/AuthjsOAuthTokenDataSource.test.ts delete mode 100644 __test__/auth/AuthjsOAuthTokenRepository.test.ts create mode 100644 __test__/auth/GitHubUserAccessTokenTransferrer.test.ts create mode 100644 __test__/auth/GuestAccessTokenTransferrer.test.ts create mode 100644 __test__/auth/GuestRepositoryAccessDataSource.test.ts delete mode 100644 __test__/auth/TransferringAccessTokenReader.test.ts rename src/features/auth/data/{AuthjsOAuthTokenRepository.ts => AuthjsOAuthTokenDataSource.ts} (62%) delete mode 100644 src/features/auth/data/AuthjsRepositoryAccessReader.ts create mode 100644 src/features/auth/domain/accessToken/AccessTokenDataSource.ts delete mode 100644 src/features/auth/domain/accessToken/AccessTokenReader.ts create mode 100644 src/features/auth/domain/accessToken/AccessTokenTransferrer.ts create mode 100644 src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts create mode 100644 src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts create mode 100644 src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts delete mode 100644 src/features/auth/domain/accessToken/IAccessTokenRefresher.ts create mode 100644 src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts create mode 100644 src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts diff --git a/__test__/auth/AccessTokenDataSource.test.ts b/__test__/auth/AccessTokenDataSource.test.ts new file mode 100644 index 00000000..6bb7b329 --- /dev/null +++ b/__test__/auth/AccessTokenDataSource.test.ts @@ -0,0 +1,268 @@ +import { AccessTokenDataSource } from "../../src/features/auth/domain" + +test("It skips transferring if an OAuth token already exists", async () => { + let didTransferAccessToken = false + const sut = new AccessTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + oauthTokenDataSource: { + async get(_user) { + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + accessTokenTransferrer: { + async transferAccessToken() { + didTransferAccessToken = true + return "new-github-access-token" + } + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() {} + } + } + } + }) + await sut.getAccessToken() + expect(didTransferAccessToken).toBeFalsy() +}) + +test("It skips transferring if an OAuth token has been stored while waiting to acquire the lock", async () => { + let didAcquireLock = false + let didTransferAccessToken = false + const sut = new AccessTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + oauthTokenDataSource: { + async get(_user) { + if (didAcquireLock) { + return { + accessToken: "foo", + refreshToken: "bar" + } + } else { + return null + } + } + }, + accessTokenTransferrer: { + async transferAccessToken() { + didTransferAccessToken = true + return "new-github-access-token" + } + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() { + didAcquireLock = true + }, + async release() {} + } + } + } + }) + await sut.getAccessToken() + expect(didAcquireLock).toBeTruthy() + expect(didTransferAccessToken).toBeFalsy() +}) + +test("It does not acquire lock if an OAuth token already exists", async () => { + let didAcquireLock = false + const sut = new AccessTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + oauthTokenDataSource: { + async get(_user) { + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + accessTokenTransferrer: { + async transferAccessToken() { + return "new-github-access-token" + } + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() { + didAcquireLock = true + }, + async release() {} + } + } + } + }) + await sut.getAccessToken() + expect(didAcquireLock).toBeFalsy() +}) + +test("It transfers access token if no OAuth token exists", async () => { + let didTransferAccessToken = false + const sut = new AccessTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + oauthTokenDataSource: { + async get(_user) { + return null + } + }, + accessTokenTransferrer: { + async transferAccessToken() { + didTransferAccessToken = true + return "new-github-access-token" + } + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() {} + } + } + } + }) + const accessToken = await sut.getAccessToken() + expect(didTransferAccessToken).toBeTruthy() + expect(accessToken).toBe("new-github-access-token") +}) + +test("It acquires lock", async () => { + let didAcquireLock = false + const sut = new AccessTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + oauthTokenDataSource: { + async get(_user) { + return null + } + }, + accessTokenTransferrer: { + async transferAccessToken() { + return "new-github-access-token" + } + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() { + didAcquireLock = true + }, + async release() {} + } + } + } + }) + await sut.getAccessToken() + expect(didAcquireLock).toBeTruthy() +}) + +test("It releases lock", async () => { + let didAcquireLock = false + const sut = new AccessTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + oauthTokenDataSource: { + async get(_user) { + return null + } + }, + accessTokenTransferrer: { + async transferAccessToken() { + return "new-github-access-token" + } + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() { + didAcquireLock = true + } + } + } + } + }) + await sut.getAccessToken() + expect(didAcquireLock).toBeTruthy() +}) diff --git a/__test__/auth/AccessTokenSessionValidator.test.ts b/__test__/auth/AccessTokenSessionValidator.test.ts new file mode 100644 index 00000000..8c76d62c --- /dev/null +++ b/__test__/auth/AccessTokenSessionValidator.test.ts @@ -0,0 +1,39 @@ +import { AccessTokenSessionValidator, SessionValidity } from "../../src/features/auth/domain" + +test("It reads the access token", async () => { + let didReadAccessToken = false + const sut = new AccessTokenSessionValidator({ + accessTokenDataSource: { + async getAccessToken() { + didReadAccessToken = true + return "foo" + } + } + }) + await sut.validateSession() + expect(didReadAccessToken).toBeTruthy() +}) + +test("It considers session valid when it can read an access token", async () => { + const sut = new AccessTokenSessionValidator({ + accessTokenDataSource: { + async getAccessToken() { + return "foo" + } + } + }) + const validity = await sut.validateSession() + expect(validity).toBe(SessionValidity.VALID) +}) + +test("It considers access token to be invalid when failing to get access token", async () => { + const sut = new AccessTokenSessionValidator({ + accessTokenDataSource: { + async getAccessToken() { + throw new Error("Mock error") + } + } + }) + const validity = await sut.validateSession() + expect(validity).toBe(SessionValidity.INVALID_ACCESS_TOKEN) +}) diff --git a/__test__/auth/AccessTokenTransferrer.test.ts b/__test__/auth/AccessTokenTransferrer.test.ts new file mode 100644 index 00000000..f306c662 --- /dev/null +++ b/__test__/auth/AccessTokenTransferrer.test.ts @@ -0,0 +1,71 @@ +import { AccessTokenTransferrer } from "../../src/features/auth/domain" + +test("It transfers access token for GitHub account provider type", async () => { + let didTransferAccessToken = false + const sut = new AccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + gitHubAccessTokenTransferrer: { + async transferAccessToken() { + didTransferAccessToken = true + return "new-github-access-token" + } + }, + guestAccessTokenTransferrer: { + async transferAccessToken() { + didTransferAccessToken = false + return "new-guest-access-token" + } + } + }) + const accessToken = await sut.transferAccessToken() + expect(didTransferAccessToken).toBeTruthy() + expect(accessToken).toBe("new-github-access-token") +}) + +test("It transfers access token for email account provider type", async () => { + let didTransferAccessToken = false + const sut = new AccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "email" + } + }, + gitHubAccessTokenTransferrer: { + async transferAccessToken() { + didTransferAccessToken = false + return "new-github-access-token" + } + }, + guestAccessTokenTransferrer: { + async transferAccessToken() { + didTransferAccessToken = true + return "new-guest-access-token" + } + } + }) + const accessToken = await sut.transferAccessToken() + expect(didTransferAccessToken).toBeTruthy() + expect(accessToken).toBe("new-guest-access-token") +}) diff --git a/__test__/auth/AuthjsOAuthTokenDataSource.test.ts b/__test__/auth/AuthjsOAuthTokenDataSource.test.ts new file mode 100644 index 00000000..968e7691 --- /dev/null +++ b/__test__/auth/AuthjsOAuthTokenDataSource.test.ts @@ -0,0 +1,32 @@ +import { AuthjsOAuthTokenDataSource } from "../../src/features/auth/data" + +test("It queries token for user ID and provider", async () => { + let queriedUserId: string | undefined + let queriedProvider: string | undefined + const sut = new AuthjsOAuthTokenDataSource({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query, values: any[] = []) { + queriedUserId = values[0] + queriedProvider = values[1] + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + await sut.get("1234") + expect(queriedUserId).toEqual("1234") + expect(queriedProvider).toEqual("github") +}) diff --git a/__test__/auth/AuthjsOAuthTokenRepository.test.ts b/__test__/auth/AuthjsOAuthTokenRepository.test.ts deleted file mode 100644 index 5579aee4..00000000 --- a/__test__/auth/AuthjsOAuthTokenRepository.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AuthjsOAuthTokenRepository } from "../../src/features/auth/data" - -test("It queries token for user ID and provider", async () => { - let queriedUserId: string | undefined - let queriedProvider: string | undefined - const sut = new AuthjsOAuthTokenRepository({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query(_query, values: any[] = []) { - queriedUserId = values[0] - queriedProvider = values[1] - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } - } - } - }) - await sut.get("1234") - expect(queriedUserId).toEqual("1234") - expect(queriedProvider).toEqual("github") -}) - -test("It cannot update tokens", async () => { - const sut = new AuthjsOAuthTokenRepository({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query() { - return { rows: [] } - } - } - }) - expect(sut.set("1234", { - accessToken: "foo", - refreshToken: "bar" - })).rejects.toThrow() -}) - -test("It cannot delete tokens", async () => { - const sut = new AuthjsOAuthTokenRepository({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query() { - return { rows: [] } - } - } - }) - expect(sut.delete("1234")).rejects.toThrow() -}) diff --git a/__test__/auth/GitHubUserAccessTokenTransferrer.test.ts b/__test__/auth/GitHubUserAccessTokenTransferrer.test.ts new file mode 100644 index 00000000..179a7070 --- /dev/null +++ b/__test__/auth/GitHubUserAccessTokenTransferrer.test.ts @@ -0,0 +1,212 @@ +import { GitHubAccessTokenTransferrer } from "../../src/features/auth/domain" + +test("It reads the user ID", async () => { + let didReadUserId = false + const sut = new GitHubAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + didReadUserId = true + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + sourceOAuthTokenDataSource: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.transferAccessToken() + expect(didReadUserId).toBeTruthy() +}) + +test("It reads the OAuth token from the data source", async () => { + let didReadOAuthToken = false + const sut = new GitHubAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + sourceOAuthTokenDataSource: { + async get(_userId) { + didReadOAuthToken = true + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.transferAccessToken() + expect(didReadOAuthToken).toBeTruthy() +}) + +test("It stores the OAuth token in the destination repository", async () => { + let didStoreOAuthToken = false + const sut = new GitHubAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + sourceOAuthTokenDataSource: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) { + didStoreOAuthToken = true + }, + async delete(_userId) {} + } + }) + await sut.transferAccessToken() + expect(didStoreOAuthToken).toBeTruthy() +}) + +test("It stores the OAuth token that was read from the data source for the user ID", async () => { + let storedUserId: string | undefined + let storedAccessToken: string | undefined + let storedRefreshToken: string | undefined + const sut = new GitHubAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + sourceOAuthTokenDataSource: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(userId, token) { + storedUserId = userId + storedAccessToken = token.accessToken + storedRefreshToken = token.refreshToken + }, + async delete(_userId) {} + } + }) + await sut.transferAccessToken() + expect(storedUserId).toBe("1234") + expect(storedAccessToken).toBe("foo") + expect(storedRefreshToken).toBe("bar") +}) + +test("It returns newly created access token", async () => { + const sut = new GitHubAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + sourceOAuthTokenDataSource: { + async get(_userId) { + return { + accessToken: "foo-bar", + refreshToken: "bar" + } + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + const accessToken = await sut.transferAccessToken() + expect(accessToken).toBe("foo-bar") +}) diff --git a/__test__/auth/GuestAccessTokenTransferrer.test.ts b/__test__/auth/GuestAccessTokenTransferrer.test.ts new file mode 100644 index 00000000..5d6afc00 --- /dev/null +++ b/__test__/auth/GuestAccessTokenTransferrer.test.ts @@ -0,0 +1,224 @@ +import { GuestAccessTokenTransferrer } from "../../src/features/auth/domain" + +test("It reads the user ID and email", async () => { + let didReadUserId = false + let didReadEmail = false + const sut = new GuestAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + didReadUserId = true + return "1234" + }, + async getEmail() { + didReadEmail = true + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + guestRepository: { + async findByEmail(_email) { + return { projects: [] } + } + }, + installationAccessTokenDataSource: { + async getAccessToken(_projects) { + return "foo" + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.transferAccessToken() + expect(didReadUserId).toBeTruthy() + expect(didReadEmail).toBeTruthy() +}) + +test("It throws an error if the guest was not found", async () => { + const sut = new GuestAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + guestRepository: { + async findByEmail(_email) { + return undefined + } + }, + installationAccessTokenDataSource: { + async getAccessToken(_projects) { + return "foo" + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await expect(sut.transferAccessToken()).rejects.toThrow() +}) + +test("It creates installation access token for guest's projects", async () => { + let didCreateAccessToken = false + let allowedProjects: string[] | undefined + const sut = new GuestAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + guestRepository: { + async findByEmail(_email) { + return { projects: ["foo", "bar" ]} + } + }, + installationAccessTokenDataSource: { + async getAccessToken(projects) { + didCreateAccessToken = true + allowedProjects = projects + return "foo" + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.transferAccessToken() + expect(didCreateAccessToken).toBeTruthy() + expect(allowedProjects).toEqual(["foo", "bar"]) +}) + +test("It stores OAuth token with an access token and no refresh token for user ID", async () => { + let storedUserId: string | undefined + let storedAccessToken: string | undefined + let storedRefreshToken: string | undefined = "to_be_overriden_with_undefined" + const sut = new GuestAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + guestRepository: { + async findByEmail(_email) { + return { projects: ["foo", "bar" ]} + } + }, + installationAccessTokenDataSource: { + async getAccessToken(_projects) { + return "foo" + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(userId, token) { + storedUserId = userId + storedAccessToken = token.accessToken + storedRefreshToken = token.refreshToken + }, + async delete(_userId) {} + } + }) + await sut.transferAccessToken() + expect(storedUserId).toBe("1234") + expect(storedAccessToken).toBe("foo") + expect(storedRefreshToken).toBeUndefined() +}) + +test("It returns newly created access token", async () => { + const sut = new GuestAccessTokenTransferrer({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProviderType() { + return "github" + } + }, + guestRepository: { + async findByEmail(_email) { + return { projects: ["foo", "bar" ]} + } + }, + installationAccessTokenDataSource: { + async getAccessToken(_projects) { + return "foo-bar" + } + }, + destinationOAuthTokenRepository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + const accessToken = await sut.transferAccessToken() + expect(accessToken).toBe("foo-bar") +}) \ No newline at end of file diff --git a/__test__/auth/GuestRepositoryAccessDataSource.test.ts b/__test__/auth/GuestRepositoryAccessDataSource.test.ts new file mode 100644 index 00000000..4cc52c0e --- /dev/null +++ b/__test__/auth/GuestRepositoryAccessDataSource.test.ts @@ -0,0 +1,143 @@ +import { GuestRepositoryAccessDataSource } from "../../src/features/auth/domain" + +test("It throws error if no user was found for the user ID", async () => { + const sut = new GuestRepositoryAccessDataSource({ + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + // Return no rows as the user was not found. + return { rows: [] } + }, + async disconnect() {} + } + }, + async query(_query, _values = []) { + // Return no rows as the user was not found. + return { rows: [] } + } + }, + guestRepository: { + async getProjectsForEmail(_email) { + return [] + } + } + }) + await expect(sut.getRepositoryNames("1234")).rejects.toThrow() +}) + +test("It throws an error if the user does not have an e-mail", async () => { + const sut = new GuestRepositoryAccessDataSource({ + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + return { + rows: [{ email: null }] + } + }, + async disconnect() {} + } + }, + async query(_query, _values = []) { + return { + rows: [{ email: null }] + } + } + }, + guestRepository: { + async getProjectsForEmail(_email) { + return [] + } + } + }) + await expect(sut.getRepositoryNames("1234")).rejects.toThrow() +}) + +test("It throws an error if the user has an empty e-mail", async () => { + const sut = new GuestRepositoryAccessDataSource({ + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + return { + rows: [{ email: "" }] + } + }, + async disconnect() {} + } + }, + async query(_query, _values = []) { + return { + rows: [{ email: "" }] + } + } + }, + guestRepository: { + async getProjectsForEmail(_email) { + return [] + } + } + }) + await expect(sut.getRepositoryNames("1234")).rejects.toThrow() +}) + +test("It queries the guest repository using the user's e-mail", async () => { + let queriedEmail: string | undefined + const sut = new GuestRepositoryAccessDataSource({ + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + return { + rows: [{ email: "john@example.com" }] + } + }, + async disconnect() {} + } + }, + async query(_query, _values = []) { + return { + rows: [{ email: "john@example.com" }] + } + } + }, + guestRepository: { + async getProjectsForEmail(email) { + queriedEmail = email + return [] + } + } + }) + await sut.getRepositoryNames("1234") + expect(queriedEmail).toBe("john@example.com") +}) + +test("It returns repository names fetched from guest repository", async () => { + const sut = new GuestRepositoryAccessDataSource({ + db: { + async connect() { + return { + async query(_query: string, _values: any[] = []) { + return { + rows: [{ email: "john@example.com" }] + } + }, + async disconnect() {} + } + }, + async query(_query, _values = []) { + return { + rows: [{ email: "john@example.com" }] + } + } + }, + guestRepository: { + async getProjectsForEmail(_email) { + return ["foo", "bar"] + } + } + }) + const repositoryNames = await sut.getRepositoryNames("1234") + expect(repositoryNames).toEqual(["foo", "bar"]) +}) diff --git a/__test__/auth/OAuthTokenRepository.test.ts b/__test__/auth/OAuthTokenRepository.test.ts index 06e2b664..e7f1a124 100644 --- a/__test__/auth/OAuthTokenRepository.test.ts +++ b/__test__/auth/OAuthTokenRepository.test.ts @@ -83,7 +83,7 @@ test("It deletes the auth token for the specified user", async () => { } }, async query(_query: string, values: any[] = []) { - deletedUserId = values[0] + deletedUserId = values[1] return { rows: [] } } } diff --git a/__test__/auth/TransferringAccessTokenReader.test.ts b/__test__/auth/TransferringAccessTokenReader.test.ts deleted file mode 100644 index a3a68902..00000000 --- a/__test__/auth/TransferringAccessTokenReader.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { AccessTokenReader } from "../../src/features/auth/domain" - -test("It reads user ID", async () => { - let didReadUserId = false - const sut = new AccessTokenReader({ - userIdReader: { - async getUserId() { - didReadUserId = true - return "1234" - }, - }, - sourceOAuthTokenRepository: { - async get() { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - }, - destinationOAuthTokenRepository: { - async get() { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - } - }) - await sut.getAccessToken() - expect(didReadUserId).toBeTruthy() -}) - -test("It skips reading from source repository if a token was found in the destination repository", async () => { - let didReadOAuthTokenFromSource = false - let didReadOAuthTokenFromDestination = false - const sut = new AccessTokenReader({ - userIdReader: { - async getUserId() { - return "1234" - }, - }, - sourceOAuthTokenRepository: { - async get() { - didReadOAuthTokenFromSource = true - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - }, - destinationOAuthTokenRepository: { - async get() { - didReadOAuthTokenFromDestination = true - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - } - }) - await sut.getAccessToken() - expect(didReadOAuthTokenFromSource).toBeFalsy() - expect(didReadOAuthTokenFromDestination).toBeTruthy() -}) - -test("It reads from source repository if no token was found in destination repository", async () => { - let didReadOAuthTokenFromSource = false - let didReadOAuthTokenFromDestination = false - const sut = new AccessTokenReader({ - userIdReader: { - async getUserId() { - return "1234" - }, - }, - sourceOAuthTokenRepository: { - async get() { - didReadOAuthTokenFromSource = true - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - }, - destinationOAuthTokenRepository: { - async get() { - didReadOAuthTokenFromDestination = true - throw new Error("No token found") - }, - async set() {}, - async delete() {} - } - }) - await sut.getAccessToken() - expect(didReadOAuthTokenFromSource).toBeTruthy() - expect(didReadOAuthTokenFromDestination).toBeTruthy() -}) - -test("It stores token read from source repository in destination repository", async () => { - let storedAccessToken: string | undefined - let storedRefreshToken: string | undefined - const sut = new AccessTokenReader({ - userIdReader: { - async getUserId() { - return "1234" - }, - }, - sourceOAuthTokenRepository: { - async get() { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - }, - destinationOAuthTokenRepository: { - async get() { - throw new Error("No token found") - }, - async set(_userId, token) { - storedAccessToken = token.accessToken - storedRefreshToken = token.refreshToken - }, - async delete() {} - } - }) - await sut.getAccessToken() - expect(storedAccessToken).toEqual("foo") - expect(storedRefreshToken).toEqual("bar") -}) diff --git a/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts b/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts index 42c405d1..f33b318a 100644 --- a/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts +++ b/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts @@ -9,7 +9,7 @@ import { test("It forwards a GraphQL request", async () => { let forwardedRequest: GraphQLQueryRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenReader: { + accessTokenDataSource: { async getAccessToken() { return "foo" } @@ -47,7 +47,7 @@ test("It forwards a GraphQL request", async () => { test("It forwards a request to get the repository content", async () => { let forwardedRequest: GetRepositoryContentRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenReader: { + accessTokenDataSource: { async getAccessToken() { return "foo" } @@ -87,7 +87,7 @@ test("It forwards a request to get the repository content", async () => { test("It forwards a request to get comments to a pull request", async () => { let forwardedRequest: GetPullRequestCommentsRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenReader: { + accessTokenDataSource: { async getAccessToken() { return "foo" } @@ -127,7 +127,7 @@ test("It forwards a request to get comments to a pull request", async () => { test("It forwards a request to add a comment to a pull request", async () => { let forwardedRequest: AddCommentToPullRequestRequest | undefined const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenReader: { + accessTokenDataSource: { async getAccessToken() { return "foo" } @@ -170,7 +170,7 @@ test("It retries with a refreshed access token when receiving HTTP 401", async ( let didRefreshAccessToken = false let didRespondWithAuthorizationError = false const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenReader: { + accessTokenDataSource: { async getAccessToken() { return "foo" } @@ -212,7 +212,7 @@ test("It retries with a refreshed access token when receiving HTTP 401", async ( test("It only retries a request once when receiving HTTP 401", async () => { let requestCount = 0 const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenReader: { + accessTokenDataSource: { async getAccessToken() { return "foo" } @@ -254,7 +254,7 @@ test("It only retries a request once when receiving HTTP 401", async () => { test("It does not refresh an access token when the initial request was successful", async () => { let didRefreshAccesstoken = false const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenReader: { + accessTokenDataSource: { async getAccessToken() { return "foo" } diff --git a/create-tables.sql b/create-tables.sql index 11733c16..73fbaf14 100644 --- a/create-tables.sql +++ b/create-tables.sql @@ -46,19 +46,19 @@ CREATE TABLE users PRIMARY KEY (id) ); -CREATE TABLE access_tokens +CREATE TABLE oauth_tokens ( + user_id VARCHAR(255) NOT NULL, provider VARCHAR(255) NOT NULL, - provider_account_id VARCHAR(255) NOT NULL, access_token VARCHAR(255) NOT NULL, refresh_token VARCHAR(255) NULL, last_updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (provider, provider_account_id) + PRIMARY KEY (user_id, provider) ); CREATE TABLE guests ( - id SERIAL PRIMARY KEY, - email VARCHAR(255) UNIQUE NOT NULL, - projects jsonb + id SERIAL PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + projects jsonb ); diff --git a/drop-tables.sql b/drop-tables.sql index 82ea5042..b2b03219 100644 --- a/drop-tables.sql +++ b/drop-tables.sql @@ -1,4 +1,4 @@ -DROP TABLE access_tokens; +DROP TABLE oauth_tokens; DROP TABLE verification_token; DROP TABLE accounts; DROP TABLE sessions; diff --git a/src/common/github/AccessTokenRefreshingGitHubClient.ts b/src/common/github/AccessTokenRefreshingGitHubClient.ts index dfe0b22c..cb73f4a7 100644 --- a/src/common/github/AccessTokenRefreshingGitHubClient.ts +++ b/src/common/github/AccessTokenRefreshingGitHubClient.ts @@ -15,7 +15,7 @@ const HttpErrorSchema = z.object({ status: z.number() }) -interface IGitHubAccessTokenReader { +interface IGitHubAccessTokenDataSource { getAccessToken(): Promise } @@ -24,18 +24,18 @@ interface IGitHubAccessTokenRefresher { } export default class AccessTokenRefreshingGitHubClient implements IGitHubClient { - private readonly accessTokenReader: IGitHubAccessTokenReader + private readonly accessTokenDataSource: IGitHubAccessTokenDataSource private readonly accessTokenRefresher: IGitHubAccessTokenRefresher private readonly gitHubClient: IGitHubClient constructor( config: { - accessTokenReader: IGitHubAccessTokenReader + accessTokenDataSource: IGitHubAccessTokenDataSource accessTokenRefresher: IGitHubAccessTokenRefresher gitHubClient: IGitHubClient } ) { - this.accessTokenReader = config.accessTokenReader + this.accessTokenDataSource = config.accessTokenDataSource this.accessTokenRefresher = config.accessTokenRefresher this.gitHubClient = config.gitHubClient } @@ -73,7 +73,7 @@ export default class AccessTokenRefreshingGitHubClient implements IGitHubClient } private async send(fn: () => Promise): Promise { - const accessToken = await this.accessTokenReader.getAccessToken() + const accessToken = await this.accessTokenDataSource.getAccessToken() try { return await fn() } catch (e) { diff --git a/src/common/github/GitHubClient.ts b/src/common/github/GitHubClient.ts index 3f41c0c2..e0ced8f2 100644 --- a/src/common/github/GitHubClient.ts +++ b/src/common/github/GitHubClient.ts @@ -12,7 +12,7 @@ import IGitHubClient, { PullRequestComment } from "./IGitHubClient" -interface IGitHubAccessTokenReader { +interface IGitHubAccessTokenDataSource { getAccessToken(): Promise } @@ -21,7 +21,7 @@ type GitHubContentItem = {download_url: string} type InstallationAuthenticator = (installationId: number) => Promise<{token: string}> export default class GitHubClient implements IGitHubClient { - private readonly accessTokenReader: IGitHubAccessTokenReader + private readonly accessTokenDataSource: IGitHubAccessTokenDataSource private readonly installationAuthenticator: InstallationAuthenticator constructor( @@ -30,10 +30,10 @@ export default class GitHubClient implements IGitHubClient { clientId: string clientSecret: string privateKey: string - accessTokenReader: IGitHubAccessTokenReader + accessTokenDataSource: IGitHubAccessTokenDataSource } ) { - this.accessTokenReader = config.accessTokenReader + this.accessTokenDataSource = config.accessTokenDataSource const appAuth = createAppAuth({ appId: config.appId, clientId: config.clientId, @@ -46,13 +46,13 @@ export default class GitHubClient implements IGitHubClient { } async graphql(request: GraphQLQueryRequest): Promise { - const accessToken = await this.accessTokenReader.getAccessToken() + const accessToken = await this.accessTokenDataSource.getAccessToken() const octokit = new Octokit({ auth: accessToken }) return await octokit.graphql(request.query, request.variables) } async getRepositoryContent(request: GetRepositoryContentRequest): Promise { - const accessToken = await this.accessTokenReader.getAccessToken() + const accessToken = await this.accessTokenDataSource.getAccessToken() const octokit = new Octokit({ auth: accessToken }) const response = await octokit.rest.repos.getContent({ owner: request.repositoryOwner, @@ -96,7 +96,7 @@ export default class GitHubClient implements IGitHubClient { async getOrganizationMembershipStatus( request: GetOrganizationMembershipStatusRequest ): Promise { - const accessToken = await this.accessTokenReader.getAccessToken() + const accessToken = await this.accessTokenDataSource.getAccessToken() const octokit = new Octokit({ auth: accessToken }) const response = await octokit.rest.orgs.getMembershipForAuthenticatedUser({ org: request.organizationName diff --git a/src/common/mutex/SessionMutexFactory.ts b/src/common/mutex/SessionMutexFactory.ts index 3ae5f13b..67010c4a 100644 --- a/src/common/mutex/SessionMutexFactory.ts +++ b/src/common/mutex/SessionMutexFactory.ts @@ -11,14 +11,14 @@ export default class SessionMutexFactory implements IMutexFactory { private readonly userIdReader: IUserIDReader private readonly baseKey: string - constructor( - mutexFactory: IKeyedMutexFactory, + constructor(config: { userIdReader: IUserIDReader, + mutexFactory: IKeyedMutexFactory, baseKey: string - ) { - this.userIdReader = userIdReader - this.baseKey = baseKey - this.mutexFactory = mutexFactory + }) { + this.userIdReader = config.userIdReader + this.baseKey = config.baseKey + this.mutexFactory = config.mutexFactory } async makeMutex(): Promise { diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index dffc47a7..95682b65 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -1,12 +1,15 @@ import { NextAuthOptions } from "next-auth" import { getServerSession } from "next-auth/next" +import { IDB, UnauthorizedError } from "../../common" import ISession, { AccountProviderType } from "./ISession" -import { UnauthorizedError } from "../../common" export default class AuthjsSession implements ISession { + private readonly db: IDB private readonly authOptions: NextAuthOptions + private accountProviderType: AccountProviderType | undefined - constructor(config: { authOptions: NextAuthOptions }) { + constructor(config: { db: IDB, authOptions: NextAuthOptions }) { + this.db = config.db this.authOptions = config.authOptions } @@ -23,7 +26,33 @@ export default class AuthjsSession implements ISession { return session.user.id } + async getEmail(): Promise { + const session = await getServerSession(this.authOptions) + if (!session || !session.user || !session.user.email) { + throw new UnauthorizedError("User's email is unavailable because the user is not authenticated.") + } + return session.user.email + } + async getAccountProviderType(): Promise { - return "github" + if (this.accountProviderType) { + return this.accountProviderType + } + const accountProviderType = await this.readAccountProviderTypeFromDB() + this.accountProviderType = accountProviderType + return accountProviderType + } + + private async readAccountProviderTypeFromDB(): Promise { + const userId = await this.getUserId() + const result = await this.db.query( + "SELECT true FROM accounts WHERE \"userId\" = $1 AND provider = $2 LIMIT 1", + [userId, "github"] + ) + if (result.rows.length > 0) { + return "github" + } else { + return "email" + } } } diff --git a/src/common/session/ISession.ts b/src/common/session/ISession.ts index c4c85ed2..af223c97 100644 --- a/src/common/session/ISession.ts +++ b/src/common/session/ISession.ts @@ -3,5 +3,6 @@ export type AccountProviderType = "github" | "email" export default interface ISession { getIsAuthenticated(): Promise getUserId(): Promise + getEmail(): Promise getAccountProviderType(): Promise } diff --git a/src/composition.ts b/src/composition.ts index f14cbddd..21f5be15 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -24,25 +24,28 @@ import { } from "@/features/projects/domain" import { GitHubOAuthTokenRefresher, - AuthjsOAuthTokenRepository, - AuthjsRepositoryAccessReader, + AuthjsOAuthTokenDataSource, GitHubInstallationAccessTokenDataSource } from "@/features/auth/data" import { + AccessTokenDataSource, AccessTokenRefresher, + AccessTokenTransferrer, AccessTokenSessionValidator, CompositeLogOutHandler, ErrorIgnoringLogOutHandler, GitHubOrganizationSessionValidator, + GitHubAccessTokenTransferrer, + GuestRepositoryAccessDataSource, + GuestAccessTokenTransferrer, LockingAccessTokenRefresher, NullObjectLogInHandler, OAuthTokenRepository, - AccessTokenReader, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" -import { IGuestInviter } from "./features/admin/domain/IGuestInviter" +import { DbGuestRepository } from "./features/admin/data" +import { IGuestInviter, IGuestRepository } from "./features/admin/domain" import { createTransport } from "nodemailer" -import DbGuestRepository from "./features/admin/data/DbGuestRepository" const { GITHUB_APP_ID, @@ -79,8 +82,6 @@ const db = new PostgreSQLDB({ pool }) const logInHandler = new NullObjectLogInHandler() -const gitHubInstallationAccessTokenDataSource = new GitHubInstallationAccessTokenDataSource(gitHubAppCredentials) - export const authOptions: NextAuthOptions = { adapter: PostgresAdapter(pool) as Adapter, secret: process.env.NEXTAUTH_SECRET, @@ -111,31 +112,6 @@ export const authOptions: NextAuthOptions = { return false // email not invited } } - else if (account?.provider == "email" && user.email) { // in sign in flow, click on magic link - // obtain access token from GitHub using app auth - const guest = await guestRepository.findByEmail(user.email) - if (guest == undefined) { - return false // email not invited - } - const accessToken = await gitHubInstallationAccessTokenDataSource.getAccessToken(guest.projects || []) - const query = ` - INSERT INTO access_tokens ( - provider, - provider_account_id, - access_token - ) - VALUES ($1, $2, $3) - ON CONFLICT (provider, provider_account_id) - DO UPDATE SET access_token = $3, last_updated_at = NOW(); - ` - await pool.query(query, [ - account.provider, - account.providerAccountId, - accessToken, - ]) - return true - } - if (account) { return await logInHandler.handleLogIn(user.id, account) } else { @@ -149,33 +125,60 @@ export const authOptions: NextAuthOptions = { } } -export const session: ISession = new AuthjsSession({ authOptions }) +export const session: ISession = new AuthjsSession({ db, authOptions }) + +export const guestRepository: IGuestRepository = new DbGuestRepository(pool) + +const oauthTokenRepository = new OAuthTokenRepository({ db, provider: "github" }) -const accessTokenReader = new AccessTokenReader({ - userIdReader: session, - sourceOAuthTokenRepository: new AuthjsOAuthTokenRepository({ provider: "github", db }), - destinationOAuthTokenRepository: new OAuthTokenRepository({ provider: "github", db }) +const githubAccessTokenDataSource = new AccessTokenDataSource({ + session, + oauthTokenDataSource: oauthTokenRepository, + accessTokenTransferrer: new AccessTokenTransferrer({ + session, + gitHubAccessTokenTransferrer: new GitHubAccessTokenTransferrer({ + session, + sourceOAuthTokenDataSource: new AuthjsOAuthTokenDataSource({ db, provider: "github" }), + destinationOAuthTokenRepository: oauthTokenRepository + }), + guestAccessTokenTransferrer: new GuestAccessTokenTransferrer({ + session, + guestRepository, + installationAccessTokenDataSource: new GitHubInstallationAccessTokenDataSource( + gitHubAppCredentials + ), + destinationOAuthTokenRepository: oauthTokenRepository + }) + }), + mutexFactory: new SessionMutexFactory({ + baseKey: "mutexTransferAccessToken", + mutexFactory: new RedisKeyedMutexFactory(REDIS_URL), + userIdReader: session + }) }) -export const repositoryAccessReader = new AuthjsRepositoryAccessReader() +export const repositoryAccessReader = new GuestRepositoryAccessDataSource({ + db, + guestRepository +}) export const gitHubClient = new GitHubClient({ ...gitHubAppCredentials, - accessTokenReader + accessTokenDataSource: githubAccessTokenDataSource }) export const userGitHubClient = new AccessTokenRefreshingGitHubClient({ gitHubClient, - accessTokenReader, + accessTokenDataSource: githubAccessTokenDataSource, accessTokenRefresher: new LockingAccessTokenRefresher({ - mutexFactory: new SessionMutexFactory( - new RedisKeyedMutexFactory(REDIS_URL), - session, - "mutexAccessToken" - ), + mutexFactory: new SessionMutexFactory({ + baseKey: "mutexAccessToken", + mutexFactory: new RedisKeyedMutexFactory(REDIS_URL), + userIdReader: session + }), accessTokenRefresher: new AccessTokenRefresher({ userIdReader: session, - oAuthTokenRepository: new OAuthTokenRepository({ db, provider: "github" }), + oAuthTokenRepository: oauthTokenRepository, oAuthTokenRefresher: new GitHubOAuthTokenRefresher({ clientId: gitHubAppCredentials.clientId, clientSecret: gitHubAppCredentials.clientSecret @@ -185,8 +188,9 @@ export const userGitHubClient = new AccessTokenRefreshingGitHubClient({ }) export const blockingSessionValidator = new AccessTokenSessionValidator({ - accessTokenReader + accessTokenDataSource: githubAccessTokenDataSource }) + export const delayedSessionValidator = new GitHubOrganizationSessionValidator({ acceptedOrganization: GITHUB_ORGANIZATION_NAME, organizationMembershipStatusReader: userGitHubClient, @@ -217,8 +221,6 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( ]) ) -export const guestRepository: IGuestRepository = new DbGuestRepository(pool) - const transport = createTransport({ host: "sandbox.smtp.mailtrap.io", port: 2525, diff --git a/src/features/admin/domain/Guest.ts b/src/features/admin/domain/Guest.ts index d2a75c32..bb953c03 100644 --- a/src/features/admin/domain/Guest.ts +++ b/src/features/admin/domain/Guest.ts @@ -1,7 +1,7 @@ type Guest = { - email: string; - status: "active" | "invited"; - projects: string[]; + email: string; + status: "active" | "invited"; + projects: string[]; }; export default Guest diff --git a/src/features/admin/domain/IGuestRepository.ts b/src/features/admin/domain/IGuestRepository.ts index 7550e5c7..2326779f 100644 --- a/src/features/admin/domain/IGuestRepository.ts +++ b/src/features/admin/domain/IGuestRepository.ts @@ -1,9 +1,9 @@ import Guest from "./Guest" export default interface IGuestRepository { - getAll(): Promise - findByEmail(email: string): Promise - create(email: string, projects: string[]): Promise - removeByEmail(email: string): Promise - getProjectsForEmail(email: string): Promise + getAll(): Promise + findByEmail(email: string): Promise + create(email: string, projects: string[]): Promise + removeByEmail(email: string): Promise + getProjectsForEmail(email: string): Promise } diff --git a/src/features/auth/data/AuthjsOAuthTokenRepository.ts b/src/features/auth/data/AuthjsOAuthTokenDataSource.ts similarity index 62% rename from src/features/auth/data/AuthjsOAuthTokenRepository.ts rename to src/features/auth/data/AuthjsOAuthTokenDataSource.ts index a80112bd..4b29e824 100644 --- a/src/features/auth/data/AuthjsOAuthTokenRepository.ts +++ b/src/features/auth/data/AuthjsOAuthTokenDataSource.ts @@ -1,7 +1,7 @@ import { IDB, UnauthorizedError } from "../../../common" -import { IOAuthTokenRepository, OAuthToken } from "../domain" +import { OAuthToken, IOAuthTokenDataSource } from "../domain" -export default class AuthjsOAuthTokenRepository implements IOAuthTokenRepository { +export default class AuthjsOAuthTokenDataSource implements IOAuthTokenDataSource { private readonly db: IDB private readonly provider: string @@ -25,12 +25,4 @@ export default class AuthjsOAuthTokenRepository implements IOAuthTokenRepository } return { accessToken, refreshToken } } - - async set(_userId: string, _token: OAuthToken): Promise { - throw new Error("Not implemented. We do not support modifying data owned by Auth.js. Use our access_tokens table instead.") - } - - async delete(_userId: string): Promise { - throw new Error("Not implemented. We do not support modifying data owned by Auth.js. Use our access_tokens table instead.") - } } diff --git a/src/features/auth/data/AuthjsRepositoryAccessReader.ts b/src/features/auth/data/AuthjsRepositoryAccessReader.ts deleted file mode 100644 index 4d524d8f..00000000 --- a/src/features/auth/data/AuthjsRepositoryAccessReader.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default class AuthjsRepositoryAccessReader { - constructor() {} - - async getRepositoryNames(userId: string): Promise { - throw new Error("Not implemented") - } -} diff --git a/src/features/auth/data/index.ts b/src/features/auth/data/index.ts index b17c6f92..46ed8515 100644 --- a/src/features/auth/data/index.ts +++ b/src/features/auth/data/index.ts @@ -1,4 +1,3 @@ -export { default as AuthjsOAuthTokenRepository } from "./AuthjsOAuthTokenRepository" -export { default as AuthjsRepositoryAccessReader } from "./AuthjsRepositoryAccessReader" +export { default as AuthjsOAuthTokenDataSource } from "./AuthjsOAuthTokenDataSource" export { default as GitHubInstallationAccessTokenDataSource } from "./GitHubInstallationAccessTokenDataSource" export { default as GitHubOAuthTokenRefresher } from "./GitHubOAuthTokenRefresher" diff --git a/src/features/auth/domain/accessToken/AccessTokenDataSource.ts b/src/features/auth/domain/accessToken/AccessTokenDataSource.ts new file mode 100644 index 00000000..1bd8b908 --- /dev/null +++ b/src/features/auth/domain/accessToken/AccessTokenDataSource.ts @@ -0,0 +1,56 @@ +import { ISession } from "@/common" +import { OAuthToken } from ".." +import IAccessTokenTransferrer from "./IAccessTokenTransferrer" +import { IMutexFactory } from "@/common" +import withMutex from "../../../../common/mutex/withMutex" + +interface IOAuthTokenDataSource { + get(userId: string): Promise +} + +export default class AccessTokenDataSource { + private readonly session: ISession + private readonly oauthTokenDataSource: IOAuthTokenDataSource + private readonly accessTokenTransferrer: IAccessTokenTransferrer + private readonly mutexFactory: IMutexFactory + + constructor( + config: { + session: ISession, + oauthTokenDataSource: IOAuthTokenDataSource, + accessTokenTransferrer: IAccessTokenTransferrer, + mutexFactory: IMutexFactory + } + ) { + this.session = config.session + this.oauthTokenDataSource = config.oauthTokenDataSource + this.accessTokenTransferrer = config.accessTokenTransferrer + this.mutexFactory = config.mutexFactory + } + + async getAccessToken(): Promise { + // Immediately try to get the OAuth token to avoid a mutex. + const existingOAuthToken = await this.getExistingOAuthToken() + if (existingOAuthToken) { + return existingOAuthToken.accessToken + } + const mutex = await this.mutexFactory.makeMutex() + return await withMutex(mutex, async () => { + // Make a second attempt to get the OAuth token as it might have been transferred in the mean time. + const existingOAuthToken = await this.getExistingOAuthToken() + if (existingOAuthToken) { + return existingOAuthToken.accessToken + } else { + return await this.accessTokenTransferrer.transferAccessToken() + } + }) + } + + private async getExistingOAuthToken(): Promise { + const userId = await this.session.getUserId() + try { + return await this.oauthTokenDataSource.get(userId) + } catch {} + return null + } +} diff --git a/src/features/auth/domain/accessToken/AccessTokenReader.ts b/src/features/auth/domain/accessToken/AccessTokenReader.ts deleted file mode 100644 index b5c15a35..00000000 --- a/src/features/auth/domain/accessToken/AccessTokenReader.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { IOAuthTokenRepository, OAuthToken } from ".." - -interface IUserIDReader { - getUserId(): Promise -} - -export default class AccessTokenReader { - private readonly userIdReader: IUserIDReader - private readonly sourceOAuthTokenRepository: IOAuthTokenRepository - private readonly destinationOAuthTokenRepository: IOAuthTokenRepository - - constructor( - config: { - userIdReader: IUserIDReader - sourceOAuthTokenRepository: IOAuthTokenRepository - destinationOAuthTokenRepository: IOAuthTokenRepository - } - ) { - this.userIdReader = config.userIdReader - this.sourceOAuthTokenRepository = config.sourceOAuthTokenRepository - this.destinationOAuthTokenRepository = config.destinationOAuthTokenRepository - } - - async getAccessToken(): Promise { - const userId = await this.userIdReader.getUserId() - let destinationOAuthToken: OAuthToken | undefined - try { - destinationOAuthToken = await this.destinationOAuthTokenRepository.get(userId) - } catch {} - if (destinationOAuthToken) { - return destinationOAuthToken.accessToken - } - const sourceOAuthToken = await this.sourceOAuthTokenRepository.get(userId) - await this.destinationOAuthTokenRepository.set(userId, sourceOAuthToken) - return sourceOAuthToken.accessToken - } -} diff --git a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts index add96af1..5773f034 100644 --- a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts +++ b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts @@ -1,4 +1,3 @@ -import IAccessTokenRefresher from "./IAccessTokenRefresher" import IOAuthTokenRepository from "../oAuthToken/IOAuthTokenRepository" import IOAuthTokenRefresher from "../oAuthToken/IOAuthTokenRefresher" @@ -6,7 +5,7 @@ interface IUserIDReader { getUserId(): Promise } -export default class AccessTokenRefresher implements IAccessTokenRefresher { +export default class AccessTokenRefresher { private readonly userIdReader: IUserIDReader private readonly oAuthTokenRepository: IOAuthTokenRepository private readonly oAuthTokenRefresher: IOAuthTokenRefresher @@ -31,7 +30,11 @@ export default class AccessTokenRefresher implements IAccessTokenRefresher { return oAuthToken.accessToken } // Given access token is stale so we refresh it. - const newOAuthToken = await this.oAuthTokenRefresher.refreshOAuthToken(oAuthToken.refreshToken) + const refreshToken = oAuthToken.refreshToken + if (!refreshToken) { + throw new Error("OAuth token does not have a refresh token") + } + const newOAuthToken = await this.oAuthTokenRefresher.refreshOAuthToken(refreshToken) await this.oAuthTokenRepository.set(userId, newOAuthToken) return newOAuthToken.accessToken } diff --git a/src/features/auth/domain/accessToken/AccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/AccessTokenTransferrer.ts new file mode 100644 index 00000000..e05cf9f3 --- /dev/null +++ b/src/features/auth/domain/accessToken/AccessTokenTransferrer.ts @@ -0,0 +1,32 @@ +import { ISession } from "@/common" +import IAccessTokenTransferrer from "./IAccessTokenTransferrer" + +export default class AccessTokenTransferrer implements IAccessTokenTransferrer { + private readonly session: ISession + private readonly gitHubAccessTokenTransferrer: IAccessTokenTransferrer + private readonly guestAccessTokenTransferrer: IAccessTokenTransferrer + + constructor( + config: { + session: ISession, + gitHubAccessTokenTransferrer: IAccessTokenTransferrer, + guestAccessTokenTransferrer: IAccessTokenTransferrer + } + ) { + this.session = config.session + this.gitHubAccessTokenTransferrer = config.gitHubAccessTokenTransferrer + this.guestAccessTokenTransferrer = config.guestAccessTokenTransferrer + } + + async transferAccessToken(): Promise { + const accountProviderType = await this.session.getAccountProviderType() + switch (accountProviderType) { + case "github": + return await this.gitHubAccessTokenTransferrer.transferAccessToken() + case "email": + return await this.guestAccessTokenTransferrer.transferAccessToken() + default: + throw new Error(`Unsupported account provider type "${accountProviderType}"`) + } + } +} diff --git a/src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts new file mode 100644 index 00000000..06d90601 --- /dev/null +++ b/src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts @@ -0,0 +1,27 @@ +import { ISession } from "@/common" +import { IOAuthTokenRepository, IOAuthTokenDataSource } from ".." + +export default class GitHubAccessTokenTransferrer { + private readonly session: ISession + private readonly sourceOAuthTokenDataSource: IOAuthTokenDataSource + private readonly destinationOAuthTokenRepository: IOAuthTokenRepository + + constructor( + config: { + session: ISession, + sourceOAuthTokenDataSource: IOAuthTokenDataSource, + destinationOAuthTokenRepository: IOAuthTokenRepository + } + ) { + this.session = config.session + this.sourceOAuthTokenDataSource = config.sourceOAuthTokenDataSource + this.destinationOAuthTokenRepository = config.destinationOAuthTokenRepository + } + + async transferAccessToken(): Promise { + const userId = await this.session.getUserId() + const oAuthToken = await this.sourceOAuthTokenDataSource.get(userId) + await this.destinationOAuthTokenRepository.set(userId, oAuthToken) + return oAuthToken.accessToken + } +} diff --git a/src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts new file mode 100644 index 00000000..baea032b --- /dev/null +++ b/src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts @@ -0,0 +1,48 @@ +import { ISession } from "@/common" +import { OAuthToken, IOAuthTokenRepository } from ".." + +interface IGuest { + readonly projects: string[] +} + +interface IGitHubInstallationAccessTokenDataSource { + getAccessToken(projects: string[]): Promise +} + +interface IGuestRepository { + findByEmail(email: string): Promise +} + +export default class GuestAccessTokenTransferrer { + private readonly session: ISession + private readonly guestRepository: IGuestRepository + private readonly installationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource + private readonly destinationOAuthTokenRepository: IOAuthTokenRepository + + constructor( + config: { + session: ISession, + guestRepository: IGuestRepository, + installationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource, + destinationOAuthTokenRepository: IOAuthTokenRepository + } + ) { + this.session = config.session + this.guestRepository = config.guestRepository + this.installationAccessTokenDataSource = config.installationAccessTokenDataSource + this.destinationOAuthTokenRepository = config.destinationOAuthTokenRepository + } + + async transferAccessToken(): Promise { + const userId = await this.session.getUserId() + const email = await this.session.getEmail() + const guest = await this.guestRepository.findByEmail(email) + if (!guest) { + throw new Error("The user was not found") + } + const accessToken = await this.installationAccessTokenDataSource.getAccessToken(guest.projects) + const oauthToken: OAuthToken = { accessToken } + await this.destinationOAuthTokenRepository.set(userId, oauthToken) + return accessToken + } +} diff --git a/src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts b/src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts new file mode 100644 index 00000000..4fac737d --- /dev/null +++ b/src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts @@ -0,0 +1,28 @@ +import { IDB } from "@/common" + +interface IGuestRepository { + getProjectsForEmail(email: string): Promise +} + +export default class GuestRepositoryAccessDataSource { + private readonly db: IDB + private readonly guestRepository: IGuestRepository + + constructor(config: { db: IDB, guestRepository: IGuestRepository }) { + this.db = config.db + this.guestRepository = config.guestRepository + } + + async getRepositoryNames(userId: string): Promise { + const query = `SELECT email FROM users where id = $1` + const results = await this.db.query(query, [userId]) + if (results.rows.length == 0) { + throw new Error("User not found") + } + const row = results.rows[0] + if (!row.email || row.email.length == 0) { + throw new Error("User does not have an e-mail") + } + return await this.guestRepository.getProjectsForEmail(row.email) + } +} diff --git a/src/features/auth/domain/accessToken/IAccessTokenRefresher.ts b/src/features/auth/domain/accessToken/IAccessTokenRefresher.ts deleted file mode 100644 index 3e1da196..00000000 --- a/src/features/auth/domain/accessToken/IAccessTokenRefresher.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IAccessTokenRefresher { - refreshAccessToken(accessToken: string): Promise -} diff --git a/src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts new file mode 100644 index 00000000..9521409b --- /dev/null +++ b/src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts @@ -0,0 +1,3 @@ +export default interface IAccessTokenTransferrer { + transferAccessToken(): Promise +} diff --git a/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts b/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts index c8b61f2d..6d92267e 100644 --- a/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts +++ b/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts @@ -1,8 +1,11 @@ import { IMutexFactory } from "@/common" import withMutex from "../../../../common/mutex/withMutex" -import IAccessTokenRefresher from "./IAccessTokenRefresher" -export default class LockingAccessTokenRefresher implements IAccessTokenRefresher { +interface IAccessTokenRefresher { + refreshAccessToken(accessToken: string): Promise +} + +export default class LockingAccessTokenRefresher { private readonly mutexFactory: IMutexFactory private readonly accessTokenRefresher: IAccessTokenRefresher diff --git a/src/features/auth/domain/accessToken/index.ts b/src/features/auth/domain/accessToken/index.ts index 14640dd7..9748d093 100644 --- a/src/features/auth/domain/accessToken/index.ts +++ b/src/features/auth/domain/accessToken/index.ts @@ -1,5 +1,8 @@ export { default as AccessTokenRefresher } from "./AccessTokenRefresher" -export type { default as IAccessTokenRefresher } from "./IAccessTokenRefresher" +export { default as AccessTokenTransferrer } from "./AccessTokenTransferrer" +export { default as GitHubAccessTokenTransferrer } from "./GitHubAccessTokenTransferrer" +export { default as GuestAccessTokenTransferrer } from "./GuestAccessTokenTransferrer" export { default as LockingAccessTokenRefresher } from "./LockingAccessTokenRefresher" export { default as RepositoryRestrictingAccessTokenDataSource } from "./RepositoryRestrictingAccessTokenDataSource" -export { default as AccessTokenReader } from "./AccessTokenReader" +export { default as AccessTokenDataSource } from "./AccessTokenDataSource" +export { default as GuestRepositoryAccessDataSource } from "./GuestRepositoryAccessDataSource" diff --git a/src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts b/src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts new file mode 100644 index 00000000..0a57b7e2 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts @@ -0,0 +1,5 @@ +import OAuthToken from "./OAuthToken" + +export default interface IOAuthTokenDataSource { + get(userId: string): Promise +} diff --git a/src/features/auth/domain/oAuthToken/OAuthToken.ts b/src/features/auth/domain/oAuthToken/OAuthToken.ts index a7af5025..ae776734 100644 --- a/src/features/auth/domain/oAuthToken/OAuthToken.ts +++ b/src/features/auth/domain/oAuthToken/OAuthToken.ts @@ -2,7 +2,7 @@ import { z } from "zod" export const OAuthTokenSchema = z.object({ accessToken: z.string(), - refreshToken: z.string() + refreshToken: z.string().optional() }) type OAuthToken = z.infer diff --git a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts index 73d278a8..99539234 100644 --- a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts +++ b/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts @@ -2,26 +2,23 @@ import { IDB, UnauthorizedError } from "../../../../common" import { IOAuthTokenRepository, OAuthToken } from "." export default class OAuthTokenRepository implements IOAuthTokenRepository { - private readonly provider: string private readonly db: IDB + private readonly provider: string - constructor(config: { provider: string, db: IDB }) { - this.provider = config.provider + constructor(config: { db: IDB, provider: string }) { this.db = config.db + this.provider = config.provider } async get(userId: string): Promise { const query = ` SELECT - access_tokens.access_token, - access_tokens.refresh_token + access_token, + refresh_token FROM - accounts - INNER JOIN - access_tokens ON access_tokens.provider_account_id = accounts."providerAccountId" + oauth_tokens WHERE - access_tokens.provider = $1 - AND accounts."userId" = $2; + provider = $1 AND user_id = $2; ` const result = await this.db.query(query, [this.provider, userId]) if (result.rows.length == 0) { @@ -35,23 +32,15 @@ export default class OAuthTokenRepository implements IOAuthTokenRepository { async set(userId: string, token: OAuthToken): Promise { const query = ` - INSERT INTO access_tokens ( + INSERT INTO oauth_tokens ( provider, - provider_account_id, + user_id, access_token, refresh_token ) - SELECT - $1, - "providerAccountId", - $3, - $4 - FROM - accounts - WHERE - accounts."userId" = $2 - ON CONFLICT (provider, provider_account_id) - DO UPDATE SET access_token = excluded.access_token, refresh_token = excluded.refresh_token, last_updated_at = NOW(); + VALUES ($1, $2, $3, $4) + ON CONFLICT (user_id, provider) + DO UPDATE SET access_token = $3, refresh_token = $4, last_updated_at = NOW(); ` try { await this.db.query(query, [this.provider, userId, token.accessToken, token.refreshToken]) @@ -62,6 +51,7 @@ export default class OAuthTokenRepository implements IOAuthTokenRepository { } async delete(userId: string): Promise { - await this.db.query("DELETE FROM access_tokens WHERE user_id = $1", [userId]) + const query = `DELETE FROM oauth_tokens WHERE provider = $1 AND user_id = $2` + await this.db.query(query, [this.provider, userId]) } } diff --git a/src/features/auth/domain/oAuthToken/index.ts b/src/features/auth/domain/oAuthToken/index.ts index 2eae1ad1..79997eef 100644 --- a/src/features/auth/domain/oAuthToken/index.ts +++ b/src/features/auth/domain/oAuthToken/index.ts @@ -2,3 +2,4 @@ export type { default as IOAuthTokenRefresher } from "./IOAuthTokenRefresher" export type { default as IOAuthTokenRepository } from "./IOAuthTokenRepository" export type { default as OAuthToken } from "./OAuthToken" export { default as OAuthTokenRepository } from "./OAuthTokenRepository" +export type { default as IOAuthTokenDataSource } from "./IOAuthTokenDataSource" diff --git a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts index f3fc7453..1b059326 100644 --- a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts @@ -1,22 +1,21 @@ import SessionValidity from "./SessionValidity" -interface IAccessTokenReader { +interface IAccessTokenDataSource { getAccessToken(): Promise } export default class AccessTokenSessionValidator { - private readonly accessTokenReader: IAccessTokenReader + private readonly accessTokenDataSource: IAccessTokenDataSource - constructor(config: { accessTokenReader: IAccessTokenReader }) { - this.accessTokenReader = config.accessTokenReader + constructor(config: { accessTokenDataSource: IAccessTokenDataSource }) { + this.accessTokenDataSource = config.accessTokenDataSource } async validateSession(): Promise { try { - await this.accessTokenReader.getAccessToken() + await this.accessTokenDataSource.getAccessToken() return SessionValidity.VALID } catch (error) { - console.error(error) return SessionValidity.INVALID_ACCESS_TOKEN } } diff --git a/types/@next-auth.d.ts b/types/@next-auth.d.ts index 184b26d8..b94d3866 100644 --- a/types/@next-auth.d.ts +++ b/types/@next-auth.d.ts @@ -4,6 +4,7 @@ declare module "next-auth" { interface Session { user: { id: string + email: string } } } From 8ad4e52224f00c4a425613024833cb6a84e220df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 6 May 2024 12:37:52 +0200 Subject: [PATCH 049/191] Removes unused import --- src/composition.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/composition.ts b/src/composition.ts index 2b1bc517..81161a91 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -4,7 +4,6 @@ import GithubProvider from "next-auth/providers/github" import EmailProvider from "next-auth/providers/email" import PostgresAdapter from "@auth/pg-adapter" import { Adapter } from "next-auth/adapters" -import { createTransport } from "nodemailer" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" import { From 5d49d499c82fa2c4eb86b79b120f32c381cee8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 6 May 2024 14:17:56 +0200 Subject: [PATCH 050/191] Uses Auth.js 5.0-beta --- README.md | 4 +- package-lock.json | 1684 +++++++++-------------- package.json | 8 +- src/app/api/auth/[...nextauth]/route.ts | 7 +- src/app/signin/page.tsx | 10 + src/common/session/AuthjsSession.ts | 18 +- src/composition.ts | 17 +- types/env.d.ts | 2 +- 8 files changed, 701 insertions(+), 1049 deletions(-) create mode 100644 src/app/signin/page.tsx diff --git a/README.md b/README.md index 4469dcfc..eb6cc6ef 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Create a file named `.env.local` in the root of the project with the following c ``` NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' SHAPE_DOCS_BASE_URL='https://docs.shapetools.io' -NEXTAUTH_URL='https://docs.shapetools.io' -NEXTAUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' +AUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' +NEXTAUTH_URL_INTERNAL='https://docs.shapetools.io' GITHUB_CLIENT_ID='GitHub App client ID' GITHUB_CLIENT_SECRET='GitHub App client secret' GITHUB_APP_ID='the GitHub App id' diff --git a/package-lock.json b/package-lock.json index 27067345..8c487e45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "name": "shape-docs", "version": "0.1.0", "dependencies": { - "@auth/pg-adapter": "^0.4.1", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -27,11 +26,10 @@ "ioredis": "^5.3.2", "mobx": "^6.12.0", "next": "14.0.2", - "next-auth": "^4.24.5", + "next-auth": "^5.0.0-beta.17", "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", - "pg": "^8.11.3", "react": "^18.2.0", "react-dom": "^18.2.0", "redis-semaphore": "^5.5.0", @@ -44,7 +42,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@auth/pg-adapter": "^0.5.2", + "@auth/pg-adapter": "^0.5.3", "@types/jest": "^29.5.8", "@types/node": "^20.9.2", "@types/nodemailer": "^6.4.14", @@ -57,7 +55,7 @@ "autoprefixer": "^10", "eslint": "^8.56.0", "eslint-config-next": "14.0.3", - "pg": "^8.11.3", + "pg": "^8.11.5", "postcss": "^8", "tailwindcss": "^3", "ts-jest": "^29.1.1", @@ -104,9 +102,9 @@ } }, "node_modules/@auth/core": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.26.2.tgz", - "integrity": "sha512-BK+NaUDLcX8AMW/O32wxzk+SE9lr/xvjO4PsTgla9ToHHanoPMl+6pYu2/WoNKnZomdHBWOl/00LFfajuulIVA==", + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.26.3.tgz", + "integrity": "sha512-Ka6rMjWMdiQCvLW/CnYZxj4Rq2bhQ/ZtU32NLxmtyAaixGb0mRXQ9MxJUBZA7GHovbghdzu55p2Cb54qNlVFzw==", "dev": true, "dependencies": { "@panva/hkdf": "^1.1.1", @@ -143,50 +141,13 @@ "node": ">= 0.6" } }, - "node_modules/@auth/core/node_modules/jose": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.1.tgz", - "integrity": "sha512-qiaQhtQRw6YrOaOj0v59h3R6hUY9NvxBmmnMfKemkqYmBB0tEc97NbLP7ix44VP5p9/0YHG8Vyhzuo5YBNwviA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/@auth/core/node_modules/preact": { - "version": "10.11.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", - "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/@auth/core/node_modules/preact-render-to-string": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", - "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", - "dev": true, - "dependencies": { - "pretty-format": "^3.8.0" - }, - "peerDependencies": { - "preact": ">=10" - } - }, - "node_modules/@auth/core/node_modules/pretty-format": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", - "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", - "dev": true - }, "node_modules/@auth/pg-adapter": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@auth/pg-adapter/-/pg-adapter-0.5.2.tgz", - "integrity": "sha512-QEC9ZbBAl1jeP0Uf9kdJa717ToH+yIKfWQj1/q7CavnQVFkKX+vGx9mIOHmU5rlFRzG+PwXXT/PKNyXxDuFTRA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@auth/pg-adapter/-/pg-adapter-0.5.3.tgz", + "integrity": "sha512-Q3/yAl4ifZvsklADDfZBt31wRMaqtBLnbk5wXKV+Hp/Y4ytQclyXMV9hoUwlWUT3YKe3mdjfIdlY5vo3gF2XPw==", "dev": true, "dependencies": { - "@auth/core": "0.26.2" + "@auth/core": "0.26.3" }, "peerDependencies": { "pg": "^8" @@ -819,9 +780,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2600,20 +2561,20 @@ } }, "node_modules/@react-types/checkbox": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.6.0.tgz", - "integrity": "sha512-vgbuJzQpVCNT5AZWV0OozXCnihqrXxoZKfJFIw0xro47pT2sn3t5UC4RA9wfjDGMoK4frw1K/4HQLsQIOsPBkw==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.8.0.tgz", + "integrity": "sha512-IBJ2bAsb3xoXaL+f0pwfRLDvRkhxfcX/q4NRJ2oT9jeHLU+j6svgK1Dqk8IGmY+vw1ltKbbMlIVeVonKQ3fgHw==", "dependencies": { - "@react-types/shared": "^3.22.0" + "@react-types/shared": "^3.23.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, "node_modules/@react-types/shared": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.22.0.tgz", - "integrity": "sha512-yVOekZWbtSmmiThGEIARbBpnmUIuePFlLyctjvCbgJgGhz8JnEJOipLQ/a4anaWfzAgzSceQP8j/K+VOOePleA==", + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.0.tgz", + "integrity": "sha512-GQm/iPiii3ikcaMNR4WdVkJ4w0mKtV3mLqeSfSqzdqbPr6vONkqXbh3RhPlPmAJs1b4QHnexd/wZQP3U9DHOwQ==", "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } @@ -2847,11 +2808,11 @@ } }, "node_modules/@stoplight/elements": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@stoplight/elements/-/elements-7.15.3.tgz", - "integrity": "sha512-aMmjjUvktuSsy+taYj0UhmW4OYLqhBkRpkGfPuGTi71F8843on1bTGiTpeyABDmb1q7TmrBZRCZJfc+OB+G13g==", + "version": "7.16.6", + "resolved": "https://registry.npmjs.org/@stoplight/elements/-/elements-7.16.6.tgz", + "integrity": "sha512-E9sM60guNcNOLOjLM3MCHkjLu3vNa8jut2Dz/Ita8/gzLu5g5OjgMkfH34EtlKe7BX+ihEmkRJveII+OmK5Gpg==", "dependencies": { - "@stoplight/elements-core": "~7.15.1", + "@stoplight/elements-core": "~7.16.6", "@stoplight/http-spec": "^6.0.0", "@stoplight/json": "^3.18.1", "@stoplight/mosaic": "^1.46.1", @@ -2872,22 +2833,22 @@ } }, "node_modules/@stoplight/elements-core": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@stoplight/elements-core/-/elements-core-7.15.1.tgz", - "integrity": "sha512-ZwwnhgMuoKheIQyku6hZfa6sHZPo1t0zGuk8kr7uNeU7OcCfuH0tf6DIIrzJU/dn2KTiQolUbuKMokPtzXL0YA==", + "version": "7.16.6", + "resolved": "https://registry.npmjs.org/@stoplight/elements-core/-/elements-core-7.16.6.tgz", + "integrity": "sha512-Mip2FRflP49iDFdE6dWiOAHx3ne70uZ7Z9rDZ72yDOV/2IAF31GGEKvytkzmS0xr4L5OHgLiY5UAjzAmEYuDxQ==", "dependencies": { "@stoplight/http-spec": "^6.0.0", "@stoplight/json": "^3.18.1", "@stoplight/json-schema-ref-parser": "^9.0.5", "@stoplight/json-schema-sampler": "0.2.3", - "@stoplight/json-schema-tree": "^2.3.0", - "@stoplight/json-schema-viewer": "^4.14.0", + "@stoplight/json-schema-tree": "^3.0.0", + "@stoplight/json-schema-viewer": "^4.14.2", "@stoplight/markdown-viewer": "^5.6.0", "@stoplight/mosaic": "^1.46.1", "@stoplight/mosaic-code-editor": "^1.46.1", "@stoplight/mosaic-code-viewer": "^1.46.1", "@stoplight/path": "^1.3.2", - "@stoplight/react-error-boundary": "^2.0.0", + "@stoplight/react-error-boundary": "^3.0.0", "@stoplight/types": "^14.0.0", "@stoplight/yaml": "^4.2.3", "classnames": "^2.2.6", @@ -2914,9 +2875,9 @@ } }, "node_modules/@stoplight/http-spec": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@stoplight/http-spec/-/http-spec-6.0.2.tgz", - "integrity": "sha512-6uQxW0ycJF3cUSthsv7u3EClGn6AgqkLW9Q0dlrxOYJ1iH5Rx6SJEGYtmnkbTc/iXX/LygyoQ40lk8ebpqSegQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@stoplight/http-spec/-/http-spec-6.0.3.tgz", + "integrity": "sha512-ED5kELP/5QH/SdKsFVx4EYUcE8FRjnDeiAx7ipHOk97t1pKgPhxH1wsvrQG3wL9TlNm0YQLLniE9Y3I6ZARJzA==", "dependencies": { "@stoplight/json": "^3.18.1", "@stoplight/json-schema-generator": "1.0.2", @@ -2937,6 +2898,18 @@ "node": ">=14.13" } }, + "node_modules/@stoplight/http-spec/node_modules/@stoplight/types": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.0.0.tgz", + "integrity": "sha512-w7Ejau6TaB7RqR0vWzGJSdmgLEYD2frjgbHPZoxgGQwAq/R8Qh/D9p9Bl9JFdii+YTL5xoDjyX0c1WDRlbMV8g==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "utility-types": "^3.10.0" + }, + "engines": { + "node": "^12.20 || >=14.13" + } + }, "node_modules/@stoplight/http-spec/node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2979,9 +2952,9 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/@stoplight/json-schema-merge-allof": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.7.8.tgz", - "integrity": "sha512-JTDt6GYpCWQSb7+UW1P91IAp/pcLWis0mmEzWVFcLsrNgtUYK7JLtYYz0ZPSR4QVL0fJ0YQejM+MPq5iNDFO4g==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.8.0.tgz", + "integrity": "sha512-g8e0s43v96Xbzvd8d6KKUuJTO16CS2oJglJrviUi8ASIUxzFvAJqTHWLtGmpTryisQopqg1evXGJfi0+164+Qw==", "dependencies": { "compute-lcm": "^1.1.0", "json-schema-compare": "^0.2.2", @@ -3012,12 +2985,12 @@ } }, "node_modules/@stoplight/json-schema-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@stoplight/json-schema-tree/-/json-schema-tree-2.3.1.tgz", - "integrity": "sha512-99jviuRT+ZFIoRBX7nw3JAyojifKc4pf0WP8c3HC/SXSO0qUO1tzbsY0MSVOSONdHBWJVVkeQ8vplULe/eb0lQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@stoplight/json-schema-tree/-/json-schema-tree-3.0.0.tgz", + "integrity": "sha512-lxALWJtl7ev+iTNbW+QHDr66+nmspwrDyVEhNSIjWjp8Vd6+qYxqk3r9zr8Ktfrx4pREfG7iTq6rwNSxYdP+Nw==", "dependencies": { "@stoplight/json": "^3.12.0", - "@stoplight/json-schema-merge-allof": "^0.7.8", + "@stoplight/json-schema-merge-allof": "^0.8.0", "@stoplight/lifecycle": "^2.3.2", "@types/json-schema": "^7.0.7", "magic-error": "0.0.1" @@ -3027,12 +3000,12 @@ } }, "node_modules/@stoplight/json-schema-viewer": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@stoplight/json-schema-viewer/-/json-schema-viewer-4.14.0.tgz", - "integrity": "sha512-cI7HmlsCFAXcKKYDsLJsA7g5PyF1xgM0pe4qOxH6p63UKxnFuhS1mDANURAzWiNTECVz+DfFmWqcLdfpCJoEQw==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/@stoplight/json-schema-viewer/-/json-schema-viewer-4.16.0.tgz", + "integrity": "sha512-5IYPI7sEbyQq2QyDhU9MqDGi9/KhNvaKsG+VOVelTcSqs56Vg9ASbSr/U6dISi5FK0FEfTh6FUggToUAzpr4NQ==", "dependencies": { "@stoplight/json": "^3.20.1", - "@stoplight/json-schema-tree": "^2.3.0", + "@stoplight/json-schema-tree": "^4.0.0", "@stoplight/react-error-boundary": "^2.0.0", "@types/json-schema": "^7.0.7", "classnames": "^2.2.6", @@ -3051,6 +3024,36 @@ "react-dom": ">=16.8" } }, + "node_modules/@stoplight/json-schema-viewer/node_modules/@stoplight/json-schema-tree": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@stoplight/json-schema-tree/-/json-schema-tree-4.0.0.tgz", + "integrity": "sha512-SAGtof+ihIdPqETR+7XXOaqZJcrbSih/xEahaw5t1nXk5sVW6ss2l5A1WCIuvtvnQiUKnBfanmZU4eoM1ZvItg==", + "dependencies": { + "@stoplight/json": "^3.12.0", + "@stoplight/json-schema-merge-allof": "^0.8.0", + "@stoplight/lifecycle": "^2.3.2", + "@types/json-schema": "^7.0.7", + "magic-error": "0.0.1" + }, + "engines": { + "node": ">=10.18" + } + }, + "node_modules/@stoplight/json-schema-viewer/node_modules/@stoplight/react-error-boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@stoplight/react-error-boundary/-/react-error-boundary-2.0.0.tgz", + "integrity": "sha512-r9cyaaH2h0kFe5c0aP+yJuY9CyXgfbBaMO6660M/wRQXqM49K5Ul7kexE4ei2cqYgo+Cd6ALl6RXSZFYwf2kCA==", + "dependencies": { + "@sentry/react": "^6.13.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@stoplight/json-schema-viewer/node_modules/jotai": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/jotai/-/jotai-1.13.1.tgz", @@ -3157,9 +3160,9 @@ } }, "node_modules/@stoplight/markdown-viewer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@stoplight/markdown-viewer/-/markdown-viewer-5.6.0.tgz", - "integrity": "sha512-vKHn1Bv9nafBYQWtNLlrRZ1aKqFRTOdfWUAhCSV1ZH4iqxGa+O1OWWTBbeOF5du8vcrWu1tTrXoCXkZjmq1NlA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@stoplight/markdown-viewer/-/markdown-viewer-5.7.0.tgz", + "integrity": "sha512-SOFmEapj/RAJ6fUqgZO41diB9ocEFxhrSL7C8aJKNoYoHme0xxk5DK53DvFNBayEEXnf9e/YmKPfwJjFdKUilA==", "dependencies": { "@rehooks/component-size": "^1.0.3", "@stoplight/markdown": "^3.1.3", @@ -3186,6 +3189,21 @@ "react-dom": ">=16.14" } }, + "node_modules/@stoplight/markdown-viewer/node_modules/@stoplight/react-error-boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@stoplight/react-error-boundary/-/react-error-boundary-2.0.0.tgz", + "integrity": "sha512-r9cyaaH2h0kFe5c0aP+yJuY9CyXgfbBaMO6660M/wRQXqM49K5Ul7kexE4ei2cqYgo+Cd6ALl6RXSZFYwf2kCA==", + "dependencies": { + "@sentry/react": "^6.13.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@stoplight/markdown-viewer/node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -3224,9 +3242,9 @@ } }, "node_modules/@stoplight/markdown-viewer/node_modules/property-information": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz", - "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -3254,9 +3272,9 @@ } }, "node_modules/@stoplight/mosaic": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/@stoplight/mosaic/-/mosaic-1.46.1.tgz", - "integrity": "sha512-ziMwdgbB77jvOoQEHr/IyqheaCzVGSkfTeBv0t6RJGADDkp+Oj8ItwDECXP7CA12VfxCgbKrGb33iyHCE/tVYA==", + "version": "1.53.2", + "resolved": "https://registry.npmjs.org/@stoplight/mosaic/-/mosaic-1.53.2.tgz", + "integrity": "sha512-fhSU1jqXLP3+9DghzrAHBgL+TfzaRDCwwg9+6Xe9Iz2LikOguAMPfBriantrtKOoM0pfUBOjHEuTO8QPw6uLKw==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/react-fontawesome": "^0.2.0", @@ -3273,7 +3291,6 @@ "clsx": "^1.1.1", "copy-to-clipboard": "^3.3.1", "dom-helpers": "^3.3.1", - "focus-trap-react": "^10.2.3", "lodash.get": "^4.4.2", "nano-memoize": "^1.2.1", "polished": "^4.1.3", @@ -3289,9 +3306,9 @@ } }, "node_modules/@stoplight/mosaic-code-editor": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/@stoplight/mosaic-code-editor/-/mosaic-code-editor-1.46.1.tgz", - "integrity": "sha512-rG2Zhwe0P9KJ8Lv/s7u4zsXQwX6znYz0DSs+Cq1cVJCgztOpAs1x6ISmOWdVLFJLiPioElrY+sCAwjxJcWnUuQ==", + "version": "1.53.2", + "resolved": "https://registry.npmjs.org/@stoplight/mosaic-code-editor/-/mosaic-code-editor-1.53.2.tgz", + "integrity": "sha512-1+dwU6BuDVCZJmiyJt6zj/xo/LeDsisEWQNq1rdvCFpVagAZDMXatbRNm8zIKkSDD+oww2+mCnp2fuNqTLCC1A==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/react-fontawesome": "^0.2.0", @@ -3300,13 +3317,12 @@ "@react-types/radio": "3.1.2", "@react-types/shared": "3.9.0", "@react-types/switch": "3.1.2", - "@stoplight/mosaic": "1.46.1", - "@stoplight/mosaic-code-viewer": "1.46.1", + "@stoplight/mosaic": "1.53.2", + "@stoplight/mosaic-code-viewer": "1.53.2", "@stoplight/types": "^13.7.0", "clsx": "^1.1.1", "copy-to-clipboard": "^3.3.1", "dom-helpers": "^3.3.1", - "focus-trap-react": "^10.2.3", "lodash.get": "^4.4.2", "nano-memoize": "^1.2.1", "polished": "^4.1.3", @@ -3382,10 +3398,22 @@ "@babel/runtime": "^7.1.2" } }, + "node_modules/@stoplight/mosaic-code-editor/node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, "node_modules/@stoplight/mosaic-code-viewer": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/@stoplight/mosaic-code-viewer/-/mosaic-code-viewer-1.46.1.tgz", - "integrity": "sha512-upz74Tq4p6/heg9fWYvFzlcmaajzgXybw9I4/Y8oFTxAkkOlVkHLiUKdUlZUWxWUZQNJXFSK0EMMIf+qcWZv2A==", + "version": "1.53.2", + "resolved": "https://registry.npmjs.org/@stoplight/mosaic-code-viewer/-/mosaic-code-viewer-1.53.2.tgz", + "integrity": "sha512-BtiAfotKmy4TtwrXTD2ktruFEwTLPwW3wt/KUQAWpGnwmTcJ9cXph2hsskFKAt1G866UzsKE3OKOAIv9GKHOdQ==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/react-fontawesome": "^0.2.0", @@ -3394,12 +3422,11 @@ "@react-types/radio": "3.1.2", "@react-types/shared": "3.9.0", "@react-types/switch": "3.1.2", - "@stoplight/mosaic": "1.46.1", + "@stoplight/mosaic": "1.53.2", "@stoplight/types": "^13.7.0", "clsx": "^1.1.1", "copy-to-clipboard": "^3.3.1", "dom-helpers": "^3.3.1", - "focus-trap-react": "^10.2.3", "lodash.get": "^4.4.2", "nano-memoize": "^1.2.1", "polished": "^4.1.3", @@ -3475,6 +3502,18 @@ "@babel/runtime": "^7.1.2" } }, + "node_modules/@stoplight/mosaic-code-viewer/node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, "node_modules/@stoplight/mosaic/node_modules/@react-types/button": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.4.1.tgz", @@ -3541,12 +3580,12 @@ } }, "node_modules/@stoplight/mosaic/node_modules/@types/react": { - "version": "17.0.71", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.71.tgz", - "integrity": "sha512-lfqOu9mp16nmaGRrS8deS2Taqhd5Ih0o92Te5Ws6I1py4ytHBcXLqh0YIqVsViqwVI5f+haiFM6hju814BzcmA==", + "version": "17.0.80", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz", + "integrity": "sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", + "@types/scheduler": "^0.16", "csstype": "^3.0.2" } }, @@ -3574,6 +3613,18 @@ "@babel/runtime": "^7.1.2" } }, + "node_modules/@stoplight/mosaic/node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, "node_modules/@stoplight/ordered-object-literal": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", @@ -3591,24 +3642,20 @@ } }, "node_modules/@stoplight/react-error-boundary": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@stoplight/react-error-boundary/-/react-error-boundary-2.0.0.tgz", - "integrity": "sha512-r9cyaaH2h0kFe5c0aP+yJuY9CyXgfbBaMO6660M/wRQXqM49K5Ul7kexE4ei2cqYgo+Cd6ALl6RXSZFYwf2kCA==", - "dependencies": { - "@sentry/react": "^6.13.2" - }, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@stoplight/react-error-boundary/-/react-error-boundary-3.0.0.tgz", + "integrity": "sha512-lFuTpGy2fu4hffmRTnJot1URa9/ifVLyPPQg62WW3RYo9LsxxHF0PrnFzAeXEQb40g1kc55S/oX6zQc8YJrKXg==", "engines": { "node": ">=10" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "react": ">=16.8" } }, "node_modules/@stoplight/types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.0.0.tgz", - "integrity": "sha512-w7Ejau6TaB7RqR0vWzGJSdmgLEYD2frjgbHPZoxgGQwAq/R8Qh/D9p9Bl9JFdii+YTL5xoDjyX0c1WDRlbMV8g==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", + "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" @@ -3618,13 +3665,13 @@ } }, "node_modules/@stoplight/yaml": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz", - "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.3.0.tgz", + "integrity": "sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==", "dependencies": { - "@stoplight/ordered-object-literal": "^1.0.1", - "@stoplight/types": "^13.0.0", - "@stoplight/yaml-ast-parser": "0.0.48", + "@stoplight/ordered-object-literal": "^1.0.5", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml-ast-parser": "0.0.50", "tslib": "^2.2.0" }, "engines": { @@ -3632,21 +3679,9 @@ } }, "node_modules/@stoplight/yaml-ast-parser": { - "version": "0.0.48", - "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz", - "integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==" - }, - "node_modules/@stoplight/yaml/node_modules/@stoplight/types": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.20.0.tgz", - "integrity": "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==", - "dependencies": { - "@types/json-schema": "^7.0.4", - "utility-types": "^3.10.0" - }, - "engines": { - "node": "^12.20 || >=14.13" - } + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.50.tgz", + "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==" }, "node_modules/@swagger-api/apidom-ast": { "version": "0.77.0", @@ -4087,8 +4122,7 @@ "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, "node_modules/@types/graceful-fs": { "version": "4.1.7", @@ -5405,15 +5439,6 @@ "dev": true, "peer": true }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -7098,32 +7123,10 @@ "resolved": "https://registry.npmjs.org/fnv-plus/-/fnv-plus-1.3.1.tgz", "integrity": "sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw==" }, - "node_modules/focus-trap": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", - "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", - "dependencies": { - "tabbable": "^6.2.0" - } - }, - "node_modules/focus-trap-react": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/focus-trap-react/-/focus-trap-react-10.2.3.tgz", - "integrity": "sha512-YXBpFu/hIeSu6NnmV2xlXzOYxuWkoOtar9jzgp3lOmjWLWY59C/b8DtDHEAV4SPU07Nd/t+nS/SBNGkhUBFmEw==", - "dependencies": { - "focus-trap": "^7.5.4", - "tabbable": "^6.2.0" - }, - "peerDependencies": { - "prop-types": "^15.8.1", - "react": ">=16.3.0", - "react-dom": ">=16.3.0" - } - }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -7584,9 +7587,9 @@ } }, "node_modules/hast-to-hyperscript/node_modules/property-information": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz", - "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -7657,9 +7660,9 @@ } }, "node_modules/hast-util-from-parse5/node_modules/property-information": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz", - "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -7785,9 +7788,9 @@ } }, "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz", - "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -9289,9 +9292,9 @@ } }, "node_modules/jose": { - "version": "4.15.2", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.2.tgz", - "integrity": "sha512-IY73F228OXRl9ar3jJagh7Vnuhj/GzBunPiZP13K0lOl7Am9SoWW3kEzq3MCllJMTtZqHTiDXQvoRd4U95aU6A==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.4.tgz", + "integrity": "sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -9863,12 +9866,12 @@ } }, "node_modules/match-sorter": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", - "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", "dependencies": { - "@babel/runtime": "^7.12.5", - "remove-accents": "0.4.2" + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" } }, "node_modules/mdast-util-definitions": { @@ -10476,30 +10479,6 @@ "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", "optional": true }, - "node_modules/nano-css": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.1.tgz", - "integrity": "sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "css-tree": "^1.1.2", - "csstype": "^3.1.2", - "fastest-stable-stringify": "^2.0.2", - "inline-style-prefixer": "^7.0.0", - "rtl-css-js": "^1.16.1", - "stacktrace-js": "^2.0.2", - "stylis": "^4.3.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/nano-css/node_modules/stylis": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", - "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" - }, "node_modules/nano-memoize": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/nano-memoize/-/nano-memoize-1.3.1.tgz", @@ -10588,38 +10567,67 @@ } }, "node_modules/next-auth": { - "version": "4.24.5", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.5.tgz", - "integrity": "sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==", - "dependencies": { - "@babel/runtime": "^7.20.13", - "@panva/hkdf": "^1.0.2", - "cookie": "^0.5.0", - "jose": "^4.11.4", - "oauth": "^0.9.15", - "openid-client": "^5.4.0", - "preact": "^10.6.3", - "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" + "version": "5.0.0-beta.17", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.17.tgz", + "integrity": "sha512-XA/7JtAjOgDfAeotJPFUsFZGGItZwzZrxLt9Gc9fE7EchLk6zydZfuZ22Vvwixs3IilkN644D5IoD5tEOAFGCQ==", + "dependencies": { + "@auth/core": "0.30.0" }, "peerDependencies": { - "next": "^12.2.5 || ^13 || ^14", + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14", "nodemailer": "^6.6.5", - "react": "^17.0.2 || ^18", - "react-dom": "^17.0.2 || ^18" + "react": "^18.2.0" }, "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, "nodemailer": { "optional": true } } }, - "node_modules/next-auth/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/next-auth/node_modules/@auth/core": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.30.0.tgz", + "integrity": "sha512-8AE4m/nk+4EIiVCJwxZAsJeAQuzpEC8M8768mmKVn60CGDdupKQkVhxbRlm5Qh7eNRCoFFME+0DvtaX2aXrYaA==", + "dependencies": { + "@panva/hkdf": "^1.1.1", + "@types/cookie": "0.6.0", + "cookie": "0.6.0", + "jose": "^5.1.3", + "oauth4webapi": "^2.4.0", + "preact": "10.11.3", + "preact-render-to-string": "5.2.3" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^6.8.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-auth/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" } }, "node_modules/node-abi": { @@ -10751,9 +10759,9 @@ } }, "node_modules/npm": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.2.4.tgz", - "integrity": "sha512-umEuYneVEYO9KoEEI8n2sSGmNQeqco/3BSeacRlqIkCzw4E7XGtYSWMeJobxzr6hZ2n9cM+u5TsMTcC5bAgoWA==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.7.0.tgz", + "integrity": "sha512-FXylyYSXNjgXx3l82BT8RSQvCoGIQ3h8YdRFGKNvo3Pv/bKscK4pdWkx/onwTpHDqGw+oeLf4Rxln9WVyxAxlQ==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -10762,6 +10770,7 @@ "@npmcli/map-workspaces", "@npmcli/package-json", "@npmcli/promise-spawn", + "@npmcli/redact", "@npmcli/run-script", "@sigstore/tuf", "abbrev", @@ -10770,8 +10779,6 @@ "chalk", "ci-info", "cli-columns", - "cli-table3", - "columnify", "fastest-levenshtein", "fs-minipass", "glob", @@ -10807,7 +10814,6 @@ "npm-profile", "npm-registry-fetch", "npm-user-validate", - "npmlog", "p-map", "pacote", "parse-conflict-json", @@ -10817,7 +10823,6 @@ "semver", "spdx-expression-parse", "ssri", - "strip-ansi", "supports-color", "tar", "text-table", @@ -10832,67 +10837,64 @@ "@npmcli/arborist": "^7.2.1", "@npmcli/config": "^8.0.2", "@npmcli/fs": "^3.1.0", - "@npmcli/map-workspaces": "^3.0.4", - "@npmcli/package-json": "^5.0.0", - "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^7.0.2", - "@sigstore/tuf": "^2.2.0", + "@npmcli/map-workspaces": "^3.0.6", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.1", + "@npmcli/redact": "^2.0.0", + "@npmcli/run-script": "^8.1.0", + "@sigstore/tuf": "^2.3.2", "abbrev": "^2.0.0", "archy": "~1.0.0", - "cacache": "^18.0.0", + "cacache": "^18.0.2", "chalk": "^5.3.0", "ci-info": "^4.0.0", "cli-columns": "^4.0.0", - "cli-table3": "^0.6.3", - "columnify": "^1.6.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", - "glob": "^10.3.10", + "glob": "^10.3.12", "graceful-fs": "^4.2.11", "hosted-git-info": "^7.0.1", - "ini": "^4.1.1", - "init-package-json": "^6.0.0", - "is-cidr": "^5.0.3", - "json-parse-even-better-errors": "^3.0.0", + "ini": "^4.1.2", + "init-package-json": "^6.0.2", + "is-cidr": "^5.0.5", + "json-parse-even-better-errors": "^3.0.1", "libnpmaccess": "^8.0.1", "libnpmdiff": "^6.0.3", - "libnpmexec": "^7.0.4", + "libnpmexec": "^8.0.0", "libnpmfund": "^5.0.1", "libnpmhook": "^10.0.0", "libnpmorg": "^6.0.1", - "libnpmpack": "^6.0.3", + "libnpmpack": "^7.0.0", "libnpmpublish": "^9.0.2", "libnpmsearch": "^7.0.0", "libnpmteam": "^6.0.0", - "libnpmversion": "^5.0.1", - "make-fetch-happen": "^13.0.0", - "minimatch": "^9.0.3", + "libnpmversion": "^6.0.0", + "make-fetch-happen": "^13.0.1", + "minimatch": "^9.0.4", "minipass": "^7.0.4", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", - "node-gyp": "^10.0.1", + "node-gyp": "^10.1.0", "nopt": "^7.2.0", "normalize-package-data": "^6.0.0", "npm-audit-report": "^5.0.0", "npm-install-checks": "^6.3.0", - "npm-package-arg": "^11.0.1", + "npm-package-arg": "^11.0.2", "npm-pick-manifest": "^9.0.0", - "npm-profile": "^9.0.0", - "npm-registry-fetch": "^16.1.0", + "npm-profile": "^9.0.2", + "npm-registry-fetch": "^17.0.0", "npm-user-validate": "^2.0.0", - "npmlog": "^7.0.1", "p-map": "^4.0.0", - "pacote": "^17.0.4", + "pacote": "^18.0.3", "parse-conflict-json": "^3.0.1", - "proc-log": "^3.0.0", + "proc-log": "^4.2.0", "qrcode-terminal": "^0.12.0", - "read": "^2.1.0", - "semver": "^7.5.4", - "spdx-expression-parse": "^3.0.1", + "read": "^3.0.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0", "ssri": "^10.0.5", - "strip-ansi": "^7.1.0", "supports-color": "^9.4.0", - "tar": "^6.2.0", + "tar": "^6.2.1", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", "treeverse": "^3.0.0", @@ -10921,15 +10923,6 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", "inBundle": true, @@ -10946,8 +10939,19 @@ "node": ">=12" } }, - "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", "inBundle": true, "license": "MIT" }, @@ -10967,13 +10971,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/agent": { - "version": "2.2.0", + "version": "2.2.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -10981,45 +10999,46 @@ "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.3" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "7.2.1", + "version": "7.5.1", "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^3.1.0", - "@npmcli/installed-package-contents": "^2.0.2", + "@npmcli/installed-package-contents": "^2.1.0", "@npmcli/map-workspaces": "^3.0.2", - "@npmcli/metavuln-calculator": "^7.0.0", + "@npmcli/metavuln-calculator": "^7.1.0", "@npmcli/name-from-folder": "^2.0.0", "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.0.0", - "@npmcli/query": "^3.0.1", - "@npmcli/run-script": "^7.0.2", + "@npmcli/package-json": "^5.1.0", + "@npmcli/query": "^3.1.0", + "@npmcli/redact": "^2.0.0", + "@npmcli/run-script": "^8.1.0", "bin-links": "^4.0.1", "cacache": "^18.0.0", "common-ancestor-path": "^1.0.1", "hosted-git-info": "^7.0.1", "json-parse-even-better-errors": "^3.0.0", "json-stringify-nice": "^1.1.4", - "minimatch": "^9.0.0", + "minimatch": "^9.0.4", "nopt": "^7.0.0", "npm-install-checks": "^6.2.0", - "npm-package-arg": "^11.0.1", + "npm-package-arg": "^11.0.2", "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^16.0.0", - "npmlog": "^7.0.1", - "pacote": "^17.0.4", + "npm-registry-fetch": "^17.0.0", + "pacote": "^18.0.1", "parse-conflict-json": "^3.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.2.0", + "proggy": "^2.0.0", "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.2", + "promise-call-limit": "^3.0.1", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", "ssri": "^10.0.5", @@ -11034,15 +11053,15 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "8.0.2", + "version": "8.3.1", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/map-workspaces": "^3.0.2", "ci-info": "^4.0.0", - "ini": "^4.1.0", + "ini": "^4.1.2", "nopt": "^7.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.2.0", "read-package-json-fast": "^3.0.2", "semver": "^7.3.5", "walk-up-path": "^3.0.1" @@ -11051,31 +11070,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "ansi-styles": "^4.3.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/disparity-colors/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/npm/node_modules/@npmcli/fs": { "version": "3.1.0", "inBundle": true, @@ -11088,14 +11082,14 @@ } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "5.0.3", + "version": "5.0.6", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^7.0.0", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", @@ -11106,7 +11100,7 @@ } }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "2.0.2", + "version": "2.1.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -11114,14 +11108,14 @@ "npm-normalize-package-bin": "^3.0.0" }, "bin": { - "installed-package-contents": "lib/index.js" + "installed-package-contents": "bin/index.js" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "3.0.4", + "version": "3.0.6", "inBundle": true, "license": "ISC", "dependencies": { @@ -11135,13 +11129,14 @@ } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "7.0.0", + "version": "7.1.0", "inBundle": true, "license": "ISC", "dependencies": { "cacache": "^18.0.0", "json-parse-even-better-errors": "^3.0.0", - "pacote": "^17.0.0", + "pacote": "^18.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5" }, "engines": { @@ -11165,7 +11160,7 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "5.0.0", + "version": "5.1.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -11174,7 +11169,7 @@ "hosted-git-info": "^7.0.0", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.0.0", "semver": "^7.5.3" }, "engines": { @@ -11182,7 +11177,7 @@ } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "7.0.0", + "version": "7.0.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -11193,7 +11188,7 @@ } }, "node_modules/npm/node_modules/@npmcli/query": { - "version": "3.0.1", + "version": "3.1.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -11203,15 +11198,24 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "7.0.2", + "version": "8.1.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", - "read-package-json-fast": "^3.0.0", + "proc-log": "^4.0.0", "which": "^4.0.0" }, "engines": { @@ -11228,31 +11232,40 @@ } }, "node_modules/npm/node_modules/@sigstore/bundle": { - "version": "2.1.0", + "version": "2.3.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.2.1" + "@sigstore/protobuf-specs": "^0.3.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.1.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.2.1", + "version": "0.3.1", "inBundle": true, "license": "Apache-2.0", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/sign": { - "version": "2.2.0", + "version": "2.3.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.1.0", - "@sigstore/protobuf-specs": "^0.2.1", + "@sigstore/bundle": "^2.3.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.1", "make-fetch-happen": "^13.0.0" }, "engines": { @@ -11260,12 +11273,25 @@ } }, "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "2.2.0", + "version": "2.3.2", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.2.1", - "tuf-js": "^2.1.0" + "@sigstore/protobuf-specs": "^0.3.0", + "tuf-js": "^2.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.1", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -11299,19 +11325,8 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/abort-controller": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/npm/node_modules/agent-base": { - "version": "7.1.0", + "version": "7.1.1", "inBundle": true, "license": "MIT", "dependencies": { @@ -11334,14 +11349,11 @@ } }, "node_modules/npm/node_modules/ansi-regex": { - "version": "6.0.1", + "version": "5.0.1", "inBundle": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/npm/node_modules/ansi-styles": { @@ -11365,42 +11377,11 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "4.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^4.1.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/base64-js": { - "version": "1.5.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, "node_modules/npm/node_modules/bin-links": { "version": "4.0.3", "inBundle": true, @@ -11416,11 +11397,14 @@ } }, "node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", + "version": "2.3.0", "inBundle": true, "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/brace-expansion": { @@ -11431,31 +11415,8 @@ "balanced-match": "^1.0.0" } }, - "node_modules/npm/node_modules/buffer": { - "version": "6.0.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/npm/node_modules/builtins": { - "version": "5.0.1", + "version": "5.1.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -11463,7 +11424,7 @@ } }, "node_modules/npm/node_modules/cacache": { - "version": "18.0.0", + "version": "18.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -11472,7 +11433,7 @@ "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", - "minipass-collect": "^1.0.2", + "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", @@ -11518,7 +11479,7 @@ } }, "node_modules/npm/node_modules/cidr-regex": { - "version": "4.0.3", + "version": "4.0.5", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -11548,47 +11509,6 @@ "node": ">= 10" } }, - "node_modules/npm/node_modules/cli-columns/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cli-columns/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/npm/node_modules/cmd-shim": { "version": "6.0.2", "inBundle": true, @@ -11613,55 +11533,11 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/npm/node_modules/columnify": { - "version": "1.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/npm/node_modules/columnify/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/columnify/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/cross-spawn": { "version": "7.0.3", "inBundle": true, @@ -11721,24 +11597,8 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, "node_modules/npm/node_modules/diff": { - "version": "5.1.0", + "version": "5.2.0", "inBundle": true, "license": "BSD-3-Clause", "engines": { @@ -11777,22 +11637,6 @@ "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/event-target-shim": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/events": { - "version": "3.3.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/npm/node_modules/exponential-backoff": { "version": "3.1.1", "inBundle": true, @@ -11840,53 +11684,16 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm/node_modules/gauge": { - "version": "5.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^4.0.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/gauge/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/gauge/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/npm/node_modules/glob": { - "version": "10.3.10", + "version": "10.3.12", "inBundle": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", + "jackspeak": "^2.3.6", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" }, "bin": { "glob": "dist/esm/bin.mjs" @@ -11903,13 +11710,8 @@ "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/hasown": { - "version": "2.0.0", + "version": "2.0.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -11936,7 +11738,7 @@ "license": "BSD-2-Clause" }, "node_modules/npm/node_modules/http-proxy-agent": { - "version": "7.0.0", + "version": "7.0.2", "inBundle": true, "license": "MIT", "dependencies": { @@ -11948,7 +11750,7 @@ } }, "node_modules/npm/node_modules/https-proxy-agent": { - "version": "7.0.2", + "version": "7.0.4", "inBundle": true, "license": "MIT", "dependencies": { @@ -11971,27 +11773,8 @@ "node": ">=0.10.0" } }, - "node_modules/npm/node_modules/ieee754": { - "version": "1.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "BSD-3-Clause" - }, "node_modules/npm/node_modules/ignore-walk": { - "version": "6.0.3", + "version": "6.0.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -12018,7 +11801,7 @@ } }, "node_modules/npm/node_modules/ini": { - "version": "4.1.1", + "version": "4.1.2", "inBundle": true, "license": "ISC", "engines": { @@ -12026,14 +11809,14 @@ } }, "node_modules/npm/node_modules/init-package-json": { - "version": "6.0.0", + "version": "6.0.2", "inBundle": true, "license": "ISC", "dependencies": { + "@npmcli/package-json": "^5.0.0", "npm-package-arg": "^11.0.0", "promzard": "^1.0.0", - "read": "^2.0.0", - "read-package-json": "^7.0.0", + "read": "^3.0.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^5.0.0" @@ -12042,10 +11825,17 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/ip": { - "version": "2.0.0", + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", "inBundle": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } }, "node_modules/npm/node_modules/ip-regex": { "version": "5.0.0", @@ -12059,11 +11849,11 @@ } }, "node_modules/npm/node_modules/is-cidr": { - "version": "5.0.3", + "version": "5.0.5", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "4.0.3" + "cidr-regex": "^4.0.4" }, "engines": { "node": ">=14" @@ -12115,8 +11905,13 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", + "version": "3.0.1", "inBundle": true, "license": "MIT", "engines": { @@ -12150,49 +11945,47 @@ "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "8.0.1", + "version": "8.0.5", "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^11.0.1", - "npm-registry-fetch": "^16.0.0" + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "6.0.3", + "version": "6.1.1", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", - "@npmcli/disparity-colors": "^3.0.0", - "@npmcli/installed-package-contents": "^2.0.2", - "binary-extensions": "^2.2.0", + "@npmcli/installed-package-contents": "^2.1.0", + "binary-extensions": "^2.3.0", "diff": "^5.1.0", - "minimatch": "^9.0.0", - "npm-package-arg": "^11.0.1", - "pacote": "^17.0.4", - "tar": "^6.2.0" + "minimatch": "^9.0.4", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.1", + "tar": "^6.2.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "7.0.4", + "version": "8.1.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", - "@npmcli/run-script": "^7.0.2", + "@npmcli/run-script": "^8.1.0", "ci-info": "^4.0.0", - "npm-package-arg": "^11.0.1", - "npmlog": "^7.0.1", - "pacote": "^17.0.4", - "proc-log": "^3.0.0", - "read": "^2.0.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.1", + "proc-log": "^4.2.0", + "read": "^3.0.1", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", "walk-up-path": "^3.0.1" @@ -12202,7 +11995,7 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "5.0.1", + "version": "5.0.9", "inBundle": true, "license": "ISC", "dependencies": { @@ -12213,55 +12006,55 @@ } }, "node_modules/npm/node_modules/libnpmhook": { - "version": "10.0.0", + "version": "10.0.4", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^16.0.0" + "npm-registry-fetch": "^17.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "6.0.1", + "version": "6.0.5", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^16.0.0" + "npm-registry-fetch": "^17.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "6.0.3", + "version": "7.0.1", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", - "@npmcli/run-script": "^7.0.2", - "npm-package-arg": "^11.0.1", - "pacote": "^17.0.4" + "@npmcli/run-script": "^8.1.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "9.0.2", + "version": "9.0.7", "inBundle": true, "license": "ISC", "dependencies": { "ci-info": "^4.0.0", "normalize-package-data": "^6.0.0", - "npm-package-arg": "^11.0.1", - "npm-registry-fetch": "^16.0.0", - "proc-log": "^3.0.0", + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.2.0", "semver": "^7.3.7", - "sigstore": "^2.1.0", + "sigstore": "^2.2.0", "ssri": "^10.0.5" }, "engines": { @@ -12269,37 +12062,37 @@ } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "7.0.0", + "version": "7.0.4", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^16.0.0" + "npm-registry-fetch": "^17.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "6.0.0", + "version": "6.0.4", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^16.0.0" + "npm-registry-fetch": "^17.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "5.0.1", + "version": "6.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^5.0.3", - "@npmcli/run-script": "^7.0.2", + "@npmcli/git": "^5.0.6", + "@npmcli/run-script": "^8.1.0", "json-parse-even-better-errors": "^3.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.2.0", "semver": "^7.3.7" }, "engines": { @@ -12307,18 +12100,15 @@ } }, "node_modules/npm/node_modules/lru-cache": { - "version": "10.0.2", + "version": "10.2.2", "inBundle": true, "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, "engines": { "node": "14 || >=16.14" } }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "13.0.0", + "version": "13.0.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -12331,6 +12121,7 @@ "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^4.2.0", "promise-retry": "^2.0.1", "ssri": "^10.0.0" }, @@ -12339,7 +12130,7 @@ } }, "node_modules/npm/node_modules/minimatch": { - "version": "9.0.3", + "version": "9.0.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -12361,25 +12152,14 @@ } }, "node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", + "version": "2.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-fetch": { @@ -12540,7 +12320,7 @@ } }, "node_modules/npm/node_modules/node-gyp": { - "version": "10.0.1", + "version": "10.1.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -12562,6 +12342,14 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/nopt": { "version": "7.2.0", "inBundle": true, @@ -12629,12 +12417,12 @@ } }, "node_modules/npm/node_modules/npm-package-arg": { - "version": "11.0.1", + "version": "11.0.2", "inBundle": true, "license": "ISC", "dependencies": { "hosted-git-info": "^7.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" }, @@ -12643,11 +12431,11 @@ } }, "node_modules/npm/node_modules/npm-packlist": { - "version": "8.0.0", + "version": "8.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "ignore-walk": "^6.0.0" + "ignore-walk": "^6.0.4" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -12668,29 +12456,30 @@ } }, "node_modules/npm/node_modules/npm-profile": { - "version": "9.0.0", + "version": "9.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^16.0.0", - "proc-log": "^3.0.0" + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "16.1.0", + "version": "17.0.0", "inBundle": true, "license": "ISC", "dependencies": { + "@npmcli/redact": "^2.0.0", "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", "npm-package-arg": "^11.0.0", - "proc-log": "^3.0.0" + "proc-log": "^4.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -12704,20 +12493,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npmlog": { - "version": "7.0.1", - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^4.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^5.0.0", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/npm/node_modules/p-map": { "version": "4.0.0", "inBundle": true, @@ -12733,26 +12508,25 @@ } }, "node_modules/npm/node_modules/pacote": { - "version": "17.0.4", + "version": "18.0.3", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^7.0.0", + "@npmcli/run-script": "^8.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^16.0.0", - "proc-log": "^3.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^7.0.0", - "read-package-json-fast": "^3.0.0", - "sigstore": "^2.0.0", + "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, @@ -12785,11 +12559,11 @@ } }, "node_modules/npm/node_modules/path-scurry": { - "version": "1.10.1", + "version": "1.10.2", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { @@ -12800,7 +12574,7 @@ } }, "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.13", + "version": "6.0.16", "inBundle": true, "license": "MIT", "dependencies": { @@ -12812,19 +12586,19 @@ } }, "node_modules/npm/node_modules/proc-log": { - "version": "3.0.0", + "version": "4.2.0", "inBundle": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/process": { - "version": "0.11.10", + "node_modules/npm/node_modules/proggy": { + "version": "2.0.0", "inBundle": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">= 0.6.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm/node_modules/promise-all-reject-late": { @@ -12836,7 +12610,7 @@ } }, "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.2", + "version": "3.0.1", "inBundle": true, "license": "ISC", "funding": { @@ -12861,11 +12635,11 @@ } }, "node_modules/npm/node_modules/promzard": { - "version": "1.0.0", + "version": "1.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "read": "^2.0.0" + "read": "^3.0.1" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -12879,11 +12653,11 @@ } }, "node_modules/npm/node_modules/read": { - "version": "2.1.0", + "version": "3.0.1", "inBundle": true, "license": "ISC", "dependencies": { - "mute-stream": "~1.0.0" + "mute-stream": "^1.0.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -12897,20 +12671,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/read-package-json": { - "version": "7.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, "node_modules/npm/node_modules/read-package-json-fast": { "version": "3.0.2", "inBundle": true, @@ -12923,47 +12683,13 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/readable-stream": { - "version": "4.4.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", "inBundle": true, "license": "MIT", "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" + "node": ">= 4" + } }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", @@ -12972,7 +12698,7 @@ "optional": true }, "node_modules/npm/node_modules/semver": { - "version": "7.5.4", + "version": "7.6.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -12996,11 +12722,6 @@ "node": ">=10" } }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/shebang-command": { "version": "2.0.0", "inBundle": true, @@ -13032,14 +12753,16 @@ } }, "node_modules/npm/node_modules/sigstore": { - "version": "2.1.0", + "version": "2.3.0", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.1.0", - "@sigstore/protobuf-specs": "^0.2.1", - "@sigstore/sign": "^2.1.0", - "@sigstore/tuf": "^2.1.0" + "@sigstore/bundle": "^2.3.1", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.1", + "@sigstore/sign": "^2.3.0", + "@sigstore/tuf": "^2.3.1", + "@sigstore/verify": "^1.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -13055,24 +12778,24 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.7.1", + "version": "2.8.3", "inBundle": true, "license": "MIT", "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "8.0.2", + "version": "8.0.3", "inBundle": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.1", "debug": "^4.3.4", "socks": "^2.7.1" }, @@ -13089,13 +12812,22 @@ "spdx-license-ids": "^3.0.0" } }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", + "version": "2.5.0", "inBundle": true, "license": "CC-BY-3.0" }, "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", + "version": "4.0.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -13104,10 +12836,15 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.16", + "version": "3.0.17", "inBundle": true, "license": "CC0-1.0" }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, "node_modules/npm/node_modules/ssri": { "version": "10.0.5", "inBundle": true, @@ -13119,14 +12856,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/npm/node_modules/string-width": { "version": "4.2.3", "inBundle": true, @@ -13154,34 +12883,7 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/string-width/node_modules/strip-ansi": { + "node_modules/npm/node_modules/strip-ansi": { "version": "6.0.1", "inBundle": true, "license": "MIT", @@ -13192,20 +12894,6 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "7.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/npm/node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", @@ -13218,14 +12906,6 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/npm/node_modules/supports-color": { "version": "9.4.0", "inBundle": true, @@ -13238,7 +12918,7 @@ } }, "node_modules/npm/node_modules/tar": { - "version": "6.2.0", + "version": "6.2.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -13302,7 +12982,7 @@ } }, "node_modules/npm/node_modules/tuf-js": { - "version": "2.1.0", + "version": "2.2.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -13350,6 +13030,15 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/npm/node_modules/validate-npm-package-name": { "version": "5.0.0", "inBundle": true, @@ -13366,14 +13055,6 @@ "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/npm/node_modules/which": { "version": "4.0.0", "inBundle": true, @@ -13396,14 +13077,6 @@ "node": ">=16" } }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", "inBundle": true, @@ -13437,14 +13110,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", "inBundle": true, @@ -13459,15 +13124,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.0.1", "inBundle": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { @@ -13491,6 +13156,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/npm/node_modules/write-file-atomic": { "version": "5.0.1", "inBundle": true, @@ -13608,16 +13287,10 @@ "node": ">= 6" } }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, "node_modules/oauth4webapi": { "version": "2.10.2", "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.10.2.tgz", "integrity": "sha512-ib0x1f4tCaZkTEEnRpkt96D8F2e38AFWzTOwpha1Wmme5kD+RFFgDVkrXyBSxBefFeQUoODVaieS/w9QmkZbnQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -13778,14 +13451,6 @@ "node": ">= 18" } }, - "node_modules/oidc-token-hash": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", - "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", - "engines": { - "node": "^10.13.0 || >=12.0.0" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -13850,28 +13515,6 @@ "node": ">= 6" } }, - "node_modules/openid-client": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.0.tgz", - "integrity": "sha512-uFTkN/iqgKvSnmpVAS/T6SNThukRMBcmymTQ71Ngus1F60tdtKVap7zCrleocY+fogPtpmoxi5Q1YdrgYuTlkA==", - "dependencies": { - "jose": "^4.15.1", - "lru-cache": "^6.0.0", - "object-hash": "^2.2.0", - "oidc-token-hash": "^5.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/openid-client/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "engines": { - "node": ">= 6" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -13937,12 +13580,6 @@ "node": ">=6" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", - "dev": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -14103,16 +13740,14 @@ "integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==" }, "node_modules/pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", "dev": true, "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -14139,9 +13774,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", "dev": true }, "node_modules/pg-int8": { @@ -14163,18 +13798,18 @@ } }, "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", "dev": true, "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==", "dev": true }, "node_modules/pg-types": { @@ -14499,9 +14134,9 @@ "dev": true }, "node_modules/postman-collection": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.3.0.tgz", - "integrity": "sha512-QpmNOw1JhAVQTFWRz443/qpKs4/3T1MFrKqDZ84RS1akxOzhXXr15kD8+/+jeA877qyy9rfMsrFgLe2W7aCPjw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.4.0.tgz", + "integrity": "sha512-2BGDFcUwlK08CqZFUlIC8kwRJueVzPjZnnokWPtJCd9f2J06HBQpGL7t2P1Ud1NEsK9NHq9wdipUhWLOPj5s/Q==", "dependencies": { "@faker-js/faker": "5.5.3", "file-type": "3.9.0", @@ -14519,14 +14154,6 @@ "node": ">=10" } }, - "node_modules/postman-collection/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/postman-url-encoder": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz", @@ -14539,18 +14166,18 @@ } }, "node_modules/preact": { - "version": "10.19.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", - "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==", + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", + "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" } }, "node_modules/preact-render-to-string": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", - "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz", + "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==", "dependencies": { "pretty-format": "^3.8.0" }, @@ -14945,6 +14572,55 @@ "react": ">=16" } }, + "node_modules/react-overflow-list/node_modules/react-use": { + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.5.0.tgz", + "integrity": "sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==", + "dependencies": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.6.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/react-overflow-list/node_modules/react-use/node_modules/nano-css": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.1.tgz", + "integrity": "sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "css-tree": "^1.1.2", + "csstype": "^3.1.2", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^7.0.0", + "rtl-css-js": "^1.16.1", + "stacktrace-js": "^2.0.2", + "stylis": "^4.3.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/react-overflow-list/node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "node_modules/react-query": { "version": "3.39.3", "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", @@ -15120,31 +14796,6 @@ "tslib": "*" } }, - "node_modules/react-use": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.4.1.tgz", - "integrity": "sha512-f3EdGM5ea+2EEIkfgggE+Jhza7uEek8aEMTpd1TQnyqGAD4wew3CMVshiXEP6kstjBE4XUGoKVxttqio76ijNw==", - "dependencies": { - "@types/js-cookie": "^2.2.6", - "@xobotyi/scrollbar-width": "^1.9.5", - "copy-to-clipboard": "^3.3.1", - "fast-deep-equal": "^3.1.3", - "fast-shallow-equal": "^1.0.0", - "js-cookie": "^2.2.1", - "nano-css": "^5.6.1", - "react-universal-interface": "^0.6.2", - "resize-observer-polyfill": "^1.5.1", - "screenfull": "^5.1.0", - "set-harmonic-interval": "^1.0.1", - "throttle-debounce": "^3.0.1", - "ts-easing": "^0.2.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -15419,9 +15070,9 @@ } }, "node_modules/remove-accents": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", - "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" }, "node_modules/repeat-string": { "version": "1.6.1", @@ -16447,11 +16098,6 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" - }, "node_modules/tailwindcss": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz", @@ -16577,9 +16223,9 @@ } }, "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, "node_modules/tiny-warning": { "version": "1.0.3", @@ -16945,9 +16591,9 @@ } }, "node_modules/undici": { - "version": "5.26.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.26.3.tgz", - "integrity": "sha512-H7n2zmKEWgOllKkIUkLvFmsJQj062lSm3uA4EYApG8gLuiOM0/go9bIoC3HVaSnfg4xunowDE2i9p8drkXuvDw==", + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", "dependencies": { "@fastify/busboy": "^2.0.0" }, @@ -17191,18 +16837,6 @@ "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" }, - "node_modules/use-resize-observer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", - "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -17243,13 +16877,21 @@ "devOptional": true }, "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", "engines": { "node": ">= 4" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", @@ -17434,9 +17076,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/whatwg-fetch": { - "version": "3.6.19", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz", - "integrity": "sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==" + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" }, "node_modules/whatwg-url": { "version": "5.0.0", diff --git a/package.json b/package.json index 3702a147..84d25606 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "testwatch": "jest --watch" }, "dependencies": { - "@auth/pg-adapter": "^0.4.1", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -34,11 +33,10 @@ "ioredis": "^5.3.2", "mobx": "^6.12.0", "next": "14.0.2", - "next-auth": "^4.24.5", + "next-auth": "^5.0.0-beta.17", "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", - "pg": "^8.11.3", "react": "^18.2.0", "react-dom": "^18.2.0", "redis-semaphore": "^5.5.0", @@ -51,7 +49,7 @@ "zod": "^3.22.4" }, "devDependencies": { - "@auth/pg-adapter": "^0.5.2", + "@auth/pg-adapter": "^0.5.3", "@types/jest": "^29.5.8", "@types/node": "^20.9.2", "@types/nodemailer": "^6.4.14", @@ -64,7 +62,7 @@ "autoprefixer": "^10", "eslint": "^8.56.0", "eslint-config-next": "14.0.3", - "pg": "^8.11.3", + "pg": "^8.11.5", "postcss": "^8", "tailwindcss": "^3", "ts-jest": "^29.1.1", diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index 2d251c9c..1d38a1d9 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,6 +1,3 @@ -import NextAuth from "next-auth" -import { authOptions } from "@/composition" +import { auth } from "@/composition" -const handler = NextAuth(authOptions) - -export { handler as GET, handler as POST } +export const { GET, POST } = auth.handlers diff --git a/src/app/signin/page.tsx b/src/app/signin/page.tsx new file mode 100644 index 00000000..174cc94a --- /dev/null +++ b/src/app/signin/page.tsx @@ -0,0 +1,10 @@ +import { Button } from "@mui/material" +import { signIn } from "next-auth/react" + +export default async function Page() { + return ( + + ) +} diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index 95682b65..58f333b6 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -1,25 +1,26 @@ -import { NextAuthOptions } from "next-auth" -import { getServerSession } from "next-auth/next" +import { NextAuthResult } from "next-auth" import { IDB, UnauthorizedError } from "../../common" import ISession, { AccountProviderType } from "./ISession" export default class AuthjsSession implements ISession { private readonly db: IDB - private readonly authOptions: NextAuthOptions + private readonly auth: NextAuthResult private accountProviderType: AccountProviderType | undefined - constructor(config: { db: IDB, authOptions: NextAuthOptions }) { + constructor(config: { db: IDB, auth: NextAuthResult }) { this.db = config.db - this.authOptions = config.authOptions + this.auth = config.auth } async getIsAuthenticated(): Promise { - const session = await getServerSession(this.authOptions) + const { auth } = this.auth + const session = await auth() return session != null } async getUserId(): Promise { - const session = await getServerSession(this.authOptions) + const { auth } = this.auth + const session = await auth() if (!session || !session.user || !session.user.id) { throw new UnauthorizedError("User ID is unavailable because the user is not authenticated.") } @@ -27,7 +28,8 @@ export default class AuthjsSession implements ISession { } async getEmail(): Promise { - const session = await getServerSession(this.authOptions) + const { auth } = this.auth + const session = await auth() if (!session || !session.user || !session.user.email) { throw new UnauthorizedError("User's email is unavailable because the user is not authenticated.") } diff --git a/src/composition.ts b/src/composition.ts index 81161a91..ae4d4c76 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -1,9 +1,8 @@ import { Pool } from "pg" -import { NextAuthOptions } from "next-auth" +import NextAuth from "next-auth" import GithubProvider from "next-auth/providers/github" -import EmailProvider from "next-auth/providers/email" +import EmailProvider from "next-auth/providers/nodemailer" import PostgresAdapter from "@auth/pg-adapter" -import { Adapter } from "next-auth/adapters" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" import { @@ -93,8 +92,8 @@ const logInHandler = new NullObjectLogInHandler() const fromEmail = FROM_EMAIL || "Shape Docs " // must be a verified email in AWS SES -export const authOptions: NextAuthOptions = { - adapter: PostgresAdapter(pool) as Adapter, +export const auth = NextAuth({ + adapter: PostgresAdapter(pool), secret: process.env.NEXTAUTH_SECRET, providers: [ GithubProvider({ @@ -115,6 +114,7 @@ export const authOptions: NextAuthOptions = { } }, from: fromEmail, + name: "email" }), ], session: { @@ -128,6 +128,9 @@ export const authOptions: NextAuthOptions = { return false // email not invited } } + if (!user.id) { + return false + } if (account) { return await logInHandler.handleLogIn(user.id, account) } else { @@ -139,9 +142,9 @@ export const authOptions: NextAuthOptions = { return session } } -} +}) -export const session: ISession = new AuthjsSession({ db, authOptions }) +export const session: ISession = new AuthjsSession({ db, auth }) export const guestRepository: IGuestRepository = new DbGuestRepository(pool) diff --git a/types/env.d.ts b/types/env.d.ts index 21e886bc..ef71a2f8 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -2,8 +2,8 @@ namespace NodeJS { interface ProcessEnv { NEXT_PUBLIC_SHAPE_DOCS_TITLE: string SHAPE_DOCS_BASE_URL: string + AUTH_SECRET: string NEXTAUTH_URL: string - NEXTAUTH_SECRET: string GITHUB_CLIENT_ID: string GITHUB_CLIENT_SECRET: string GITHUB_APP_ID: string From 0ed749d6a853b97b64f6faa50220aede0681e051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 7 May 2024 10:51:24 +0200 Subject: [PATCH 051/191] Moves useProjectNavigator to client-side --- src/features/projects/domain/useProjectNavigator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features/projects/domain/useProjectNavigator.ts b/src/features/projects/domain/useProjectNavigator.ts index e0f5fac9..ae2b5d43 100644 --- a/src/features/projects/domain/useProjectNavigator.ts +++ b/src/features/projects/domain/useProjectNavigator.ts @@ -1,3 +1,4 @@ +"use client" import { useRouter } from "next/navigation" import ProjectNavigator from "./ProjectNavigator" From 214f45c0fe09f7b0ab8bac90c6ce4e86ac165ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 7 May 2024 11:58:02 +0200 Subject: [PATCH 052/191] Updates Next --- package-lock.json | 465 ++++++++++++++++++++++++++++++++++++---------- package.json | 8 +- 2 files changed, 375 insertions(+), 98 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c487e45..96d96915 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,13 +25,13 @@ "install": "^0.13.0", "ioredis": "^5.3.2", "mobx": "^6.12.0", - "next": "14.0.2", + "next": "^14.2.3", "next-auth": "^5.0.0-beta.17", "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "redis-semaphore": "^5.5.0", "redoc": "^2.1.3", "styled-components": "^6.1.1", @@ -54,7 +54,7 @@ "@typescript-eslint/parser": "^6.9.1", "autoprefixer": "^10", "eslint": "^8.56.0", - "eslint-config-next": "14.0.3", + "eslint-config-next": "^14.2.3", "pg": "^8.11.5", "postcss": "^8", "tailwindcss": "^3", @@ -1211,6 +1211,96 @@ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1930,23 +2020,69 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@next/env": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.2.tgz", - "integrity": "sha512-HAW1sljizEaduEOes/m84oUqeIDAUYBR1CDwu2tobNlNDFP3cSm9d6QsOsGeNlIppU1p/p1+bWbYCbvwjFiceA==" + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", + "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" }, "node_modules/@next/eslint-plugin-next": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.0.3.tgz", - "integrity": "sha512-j4K0n+DcmQYCVnSAM+UByTVfIHnYQy2ODozfQP+4RdwtRDfobrIvKq1K4Exb2koJ79HSSa7s6B2SA8T/1YR3RA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz", + "integrity": "sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==", + "dev": true, + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { - "glob": "7.1.7" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.2.tgz", - "integrity": "sha512-i+jQY0fOb8L5gvGvojWyZMfQoQtDVB2kYe7fufOEiST6sicvzI2W5/EXo4lX5bLUjapHKe+nFxuVv7BA+Pd7LQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", + "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", "cpu": [ "arm64" ], @@ -1959,9 +2095,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.2.tgz", - "integrity": "sha512-zRCAO0d2hW6gBEa4wJaLn+gY8qtIqD3gYd9NjruuN98OCI6YyelmhWVVLlREjS7RYrm9OUQIp/iVJFeB6kP1hg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", + "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", "cpu": [ "x64" ], @@ -1974,9 +2110,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.2.tgz", - "integrity": "sha512-tSJmiaon8YaKsVhi7GgRizZoV0N1Sx5+i+hFTrCKKQN7s3tuqW0Rov+RYdPhAv/pJl4qiG+XfSX4eJXqpNg3dA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", + "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", "cpu": [ "arm64" ], @@ -1989,9 +2125,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.2.tgz", - "integrity": "sha512-dXJLMSEOwqJKcag1BeX1C+ekdPPJ9yXbWIt3nAadhbLx5CjACoB2NQj9Xcqu2tmdr5L6m34fR+fjGPs+ZVPLzA==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", + "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", "cpu": [ "arm64" ], @@ -2004,9 +2140,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.2.tgz", - "integrity": "sha512-WC9KAPSowj6as76P3vf1J3mf2QTm3Wv3FBzQi7UJ+dxWjK3MhHVWsWUo24AnmHx9qDcEtHM58okgZkXVqeLB+Q==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", + "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", "cpu": [ "x64" ], @@ -2019,9 +2155,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.2.tgz", - "integrity": "sha512-KSSAwvUcjtdZY4zJFa2f5VNJIwuEVnOSlqYqbQIawREJA+gUI6egeiRu290pXioQXnQHYYdXmnVNZ4M+VMB7KQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", + "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", "cpu": [ "x64" ], @@ -2034,9 +2170,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.2.tgz", - "integrity": "sha512-2/O0F1SqJ0bD3zqNuYge0ok7OEWCQwk55RPheDYD0va5ij7kYwrFkq5ycCRN0TLjLfxSF6xI5NM6nC5ux7svEQ==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", + "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", "cpu": [ "arm64" ], @@ -2049,9 +2185,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.2.tgz", - "integrity": "sha512-vJI/x70Id0oN4Bq/R6byBqV1/NS5Dl31zC+lowO8SDu1fHmUxoAdILZR5X/sKbiJpuvKcCrwbYgJU8FF/Gh50Q==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", + "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", "cpu": [ "ia32" ], @@ -2064,9 +2200,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.2.tgz", - "integrity": "sha512-Ut4LXIUvC5m8pHTe2j0vq/YDnTEyq6RSR9vHYPqnELrDapPhLNz9Od/L5Ow3J8RNDWpEnfCiQXuVdfjlNEJ7ug==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", + "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", "cpu": [ "x64" ], @@ -2467,6 +2603,16 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -4056,11 +4202,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, "node_modules/@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", "dependencies": { + "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, @@ -5503,9 +5655,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001546", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz", - "integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==", + "version": "1.0.30001616", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", + "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", "funding": [ { "type": "opencollective", @@ -6202,6 +6354,12 @@ "node": ">=4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -6476,14 +6634,14 @@ } }, "node_modules/eslint-config-next": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.0.3.tgz", - "integrity": "sha512-IKPhpLdpSUyKofmsXUfrvBC49JMUTdeaD8ZIH4v9Vk0sC1X6URTuTJCLtA0Vwuj7V/CQh0oISuSTvNn5//Buew==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.3.tgz", + "integrity": "sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "14.0.3", + "@next/eslint-plugin-next": "14.2.3", "@rushstack/eslint-patch": "^1.3.3", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.28.1", @@ -7155,6 +7313,34 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -7410,11 +7596,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -8694,6 +8875,24 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -10386,6 +10585,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz", + "integrity": "sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -10522,17 +10730,17 @@ "dev": true }, "node_modules/next": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/next/-/next-14.0.2.tgz", - "integrity": "sha512-jsAU2CkYS40GaQYOiLl9m93RTv2DA/tTJ0NRlmZIBIL87YwQ/xR8k796z7IqgM3jydI8G25dXvyYMC9VDIevIg==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", + "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", "dependencies": { - "@next/env": "14.0.2", - "@swc/helpers": "0.5.2", + "@next/env": "14.2.3", + "@swc/helpers": "0.5.5", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" @@ -10541,18 +10749,19 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.0.2", - "@next/swc-darwin-x64": "14.0.2", - "@next/swc-linux-arm64-gnu": "14.0.2", - "@next/swc-linux-arm64-musl": "14.0.2", - "@next/swc-linux-x64-gnu": "14.0.2", - "@next/swc-linux-x64-musl": "14.0.2", - "@next/swc-win32-arm64-msvc": "14.0.2", - "@next/swc-win32-ia32-msvc": "14.0.2", - "@next/swc-win32-x64-msvc": "14.0.2" + "@next/swc-darwin-arm64": "14.2.3", + "@next/swc-darwin-x64": "14.2.3", + "@next/swc-linux-arm64-gnu": "14.2.3", + "@next/swc-linux-arm64-musl": "14.2.3", + "@next/swc-linux-x64-gnu": "14.2.3", + "@next/swc-linux-x64-musl": "14.2.3", + "@next/swc-win32-arm64-msvc": "14.2.3", + "@next/swc-win32-ia32-msvc": "14.2.3", + "@next/swc-win32-x64-msvc": "14.2.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", "react": "^18.2.0", "react-dom": "^18.2.0", "sass": "^1.3.0" @@ -10561,6 +10770,9 @@ "@opentelemetry/api": { "optional": true }, + "@playwright/test": { + "optional": true + }, "sass": { "optional": true } @@ -13713,6 +13925,31 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -14473,9 +14710,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -14508,15 +14745,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-fast-compare": { @@ -15309,9 +15546,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } @@ -15729,6 +15966,27 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -15823,6 +16081,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -17035,18 +17306,6 @@ "makeerror": "1.0.12" } }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -17199,6 +17458,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 84d25606..e852b0ca 100644 --- a/package.json +++ b/package.json @@ -32,13 +32,13 @@ "install": "^0.13.0", "ioredis": "^5.3.2", "mobx": "^6.12.0", - "next": "14.0.2", + "next": "^14.2.3", "next-auth": "^5.0.0-beta.17", "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "redis-semaphore": "^5.5.0", "redoc": "^2.1.3", "styled-components": "^6.1.1", @@ -61,7 +61,7 @@ "@typescript-eslint/parser": "^6.9.1", "autoprefixer": "^10", "eslint": "^8.56.0", - "eslint-config-next": "14.0.3", + "eslint-config-next": "^14.2.3", "pg": "^8.11.5", "postcss": "^8", "tailwindcss": "^3", From d4156a597f2a8ce6a212142f8e0ef95296ee2ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 7 May 2024 12:22:41 +0200 Subject: [PATCH 053/191] Removes unused page --- src/app/signin/page.tsx | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/app/signin/page.tsx diff --git a/src/app/signin/page.tsx b/src/app/signin/page.tsx deleted file mode 100644 index 174cc94a..00000000 --- a/src/app/signin/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Button } from "@mui/material" -import { signIn } from "next-auth/react" - -export default async function Page() { - return ( - - ) -} From 6d249d7ed3d30c386207cc0caaf04ab8b8fc9989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 7 May 2024 12:26:26 +0200 Subject: [PATCH 054/191] Improves formatting --- src/features/user/view/SettingsList.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/features/user/view/SettingsList.tsx b/src/features/user/view/SettingsList.tsx index 19ec0a56..f91e85f2 100644 --- a/src/features/user/view/SettingsList.tsx +++ b/src/features/user/view/SettingsList.tsx @@ -31,9 +31,7 @@ const SettingsList = () => { Manage guests - signOut()} - > + signOut()}> Log out From bfeb66e654c5d7afc5ffd7fae1d64e0fbd245b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 7 May 2024 12:58:48 +0200 Subject: [PATCH 055/191] Simplifies page structure --- src/app/[...slug]/page.tsx | 24 -------------- src/app/[[...slug]]/page.tsx | 38 +++++++++++++++++++++++ src/app/layout.tsx | 17 +++------- src/app/page.tsx | 6 ---- src/features/auth/view/SessionBarrier.tsx | 5 --- 5 files changed, 42 insertions(+), 48 deletions(-) delete mode 100644 src/app/[...slug]/page.tsx create mode 100644 src/app/[[...slug]]/page.tsx delete mode 100644 src/app/page.tsx diff --git a/src/app/[...slug]/page.tsx b/src/app/[...slug]/page.tsx deleted file mode 100644 index 76b93de3..00000000 --- a/src/app/[...slug]/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import SessionBarrier from "@/features/auth/view/SessionBarrier" -import ProjectsPage from "@/features/projects/view/ProjectsPage" -import { projectRepository } from "@/composition" - -type PageParams = { slug: string | string[] } - -export default async function Page({ params }: { params: PageParams }) { - return ( - - - - ) -} - -function getPath(slug: string | string[]) { - if (typeof slug === "string") { - return "/" + slug - } else { - return slug.reduce((e, acc) => `${e}/${acc}`, "") - } -} diff --git a/src/app/[[...slug]]/page.tsx b/src/app/[[...slug]]/page.tsx new file mode 100644 index 00000000..2b089449 --- /dev/null +++ b/src/app/[[...slug]]/page.tsx @@ -0,0 +1,38 @@ +import { redirect } from "next/navigation" +import { SessionProvider } from "next-auth/react" +import { session, projectRepository } from "@/composition" +import ErrorHandler from "@/common/errors/client/ErrorHandler" +import SessionBarrier from "@/features/auth/view/SessionBarrier" +import ProjectsPage from "@/features/projects/view/ProjectsPage" + +type PageParams = { slug: string | string[] } + +export default async function Page({ params }: { params: PageParams }) { + const isAuthenticated = await session.getIsAuthenticated() + if (!isAuthenticated) { + return redirect("/api/auth/signin") + } + return ( + + + + + + + + ) +} + +function getPath(slug: string | string[] | undefined) { + console.log("Slug: " + slug) + if (slug === undefined) { + return "/" + } else if (typeof slug === "string") { + return "/" + slug + } else { + return slug.reduce((e, acc) => `${e}/${acc}`, "") + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9bc5e417..3fde8192 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,11 +1,8 @@ import "./globals.css" import type { Metadata } from "next" -import SessionProvider from "@/features/auth/view/client/SessionProvider" import { config as fontAwesomeConfig } from "@fortawesome/fontawesome-svg-core" import { CssBaseline } from "@mui/material" import ThemeRegistry from "../common/theme/ThemeRegistry" -import ErrorHandler from "../common/errors/client/ErrorHandler" -import SessionBarrier from "@/features/auth/view/SessionBarrier" import "@fortawesome/fontawesome-svg-core/styles.css" fontAwesomeConfig.autoAddCss = false @@ -19,16 +16,10 @@ export default function RootLayout({ children }: { children: React.ReactNode }) return ( - - - - - - {children} - - - - + + + {children} + ) diff --git a/src/app/page.tsx b/src/app/page.tsx deleted file mode 100644 index 17050857..00000000 --- a/src/app/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import ProjectsPage from "@/features/projects/view/ProjectsPage" -import { projectRepository } from "@/composition" - -export default async function Page() { - return -} diff --git a/src/features/auth/view/SessionBarrier.tsx b/src/features/auth/view/SessionBarrier.tsx index 82e94102..1725475b 100644 --- a/src/features/auth/view/SessionBarrier.tsx +++ b/src/features/auth/view/SessionBarrier.tsx @@ -1,5 +1,4 @@ import { ReactNode } from "react" -import { redirect } from "next/navigation" import { session, blockingSessionValidator } from "@/composition" import ClientSessionBarrier from "./client/SessionBarrier" @@ -13,10 +12,6 @@ export default async function SessionBarrier({ }: { children: ReactNode }) { - const isAuthenticated = await session.getIsAuthenticated() - if (!isAuthenticated) { - return redirect("/api/auth/signin") - } const accountProviderType = await session.getAccountProviderType() const sessionValidity = await blockingSessionValidator.validateSession() return ( From 225ab97eea3ff96d0bb50562e43370dd415c0bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 7 May 2024 13:16:20 +0200 Subject: [PATCH 056/191] Styles sign in page --- src/composition.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/composition.ts b/src/composition.ts index ae4d4c76..dc659364 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -95,6 +95,11 @@ const fromEmail = FROM_EMAIL || "Shape Docs " // mu export const auth = NextAuth({ adapter: PostgresAdapter(pool), secret: process.env.NEXTAUTH_SECRET, + theme: { + logo: "/images/duck.png", + colorScheme: "light", + brandColor: "black" + }, providers: [ GithubProvider({ clientId: process.env.GITHUB_CLIENT_ID, From 53082b944a5bf2e401b84af8f7ce215ace470585 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Wed, 15 May 2024 15:15:38 +0200 Subject: [PATCH 057/191] Revert "Merge branch 'develop' into feature/authjs" This reverts commit 50097eb5eb9f41fbd2ed0a8a4f3307078e1d52a1, reversing changes made to 225ab97eea3ff96d0bb50562e43370dd415c0bbd. --- .env.example | 19 ----- .../workflows/build-and-deploy-staging.yml | 82 ------------------- .github/workflows/build-docker-image.yml | 16 ++++ Dockerfile | 2 +- next.config.js | 4 +- package.json | 6 +- src/app/api/health/route.ts | 5 -- src/features/docs/view/Swagger.tsx | 2 +- 8 files changed, 22 insertions(+), 114 deletions(-) delete mode 100644 .env.example delete mode 100644 .github/workflows/build-and-deploy-staging.yml create mode 100644 .github/workflows/build-docker-image.yml delete mode 100644 src/app/api/health/route.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index 4ffeb0b1..00000000 --- a/.env.example +++ /dev/null @@ -1,19 +0,0 @@ -AUTH0_BASE_URL='http://dev.local:3000' -AUTH0_CLIENT_ID='Your client ID' -AUTH0_CLIENT_SECRET='Your client secret' -AUTH0_ISSUER_BASE_URL='https://shape-docs-dev.eu.auth0.com' -AUTH0_MANAGEMENT_CLIENT_ID='Your client ID' -AUTH0_MANAGEMENT_CLIENT_SECRET='Your client secret' -AUTH0_MANAGEMENT_DOMAIN='shape-docs-dev.eu.auth0.com' -AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value' -GITHUB_APP_ID=123456 -GITHUB_CLIENT_ID='GitHub App client ID' -GITHUB_CLIENT_SECRET='GitHub App client secret' -GITHUB_ORGANIZATION_NAME='shapehq' -GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key' -GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' -GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' -GITHUB_WEBHOOK_SECRET='preshared secret also put in app conf in GitHub' -NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' -REDIS_URL='' -SHAPE_DOCS_BASE_URL='https://docs.shapetools.io' \ No newline at end of file diff --git a/.github/workflows/build-and-deploy-staging.yml b/.github/workflows/build-and-deploy-staging.yml deleted file mode 100644 index 0eea287c..00000000 --- a/.github/workflows/build-and-deploy-staging.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: "[Staging] Build and Deploy" - -on: - workflow_dispatch: {} - -env: - AWS_REGION: eu-central-1 - ECR_REPOSITORY: shapedocs - ECS_SERVICE: StagingApp-AppService7F8F0CA1-jCcia0OvXEXa - ECS_CLUSTER: StagingApp-EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3-L9xrshUBnmqe - ECS_TASK_DEFINITION_NAME: StagingAppAppServiceTaskDef1613562E - CONTAINER_NAME: web - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_SHAPE_DOCS }} - -jobs: - build: - name: Build and Deploy - runs-on: ubuntu-22.04 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install 1Password CLI - uses: 1password/install-cli-action@v1 - - - name: Install AWS credentials from 1Password - run: | - AWS_ACCESS_KEY_ID=$(op read "op://Shape Docs GitHub Actions/AWS GitHub Actions User/access_key_id") - AWS_SECRET_ACCESS_KEY=$(op read "op://Shape Docs GitHub Actions/AWS GitHub Actions User/secret_access_key") - echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> $GITHUB_ENV - echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> $GITHUB_ENV - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4.0.1 - with: - aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION }} - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2.0.1 - - - name: Fetch task definition - id: fetch-task-definition - run: | - aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION_NAME }} --region ${{ env.AWS_REGION }} | jq .taskDefinition > task-definition.json - jq . task-definition.json - - - name: Create .env.local - run: | - cp .env.example .env.local - - - name: Build, tag, and push image to Amazon ECR - id: build-image - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - IMAGE_TAG: ${{ github.sha }} - run: | - # Build a docker image and push it to ECR so that it can - # be deployed to ECS - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest - echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT - - - name: Fill in the new image ID in the Amazon ECS task definition - id: task-def - uses: aws-actions/amazon-ecs-render-task-definition@v1.2.0 - with: - task-definition: task-definition.json - container-name: ${{ env.CONTAINER_NAME }} - image: ${{ steps.build-image.outputs.image }} - - - name: Deploy Amazon ECS task definition - uses: aws-actions/amazon-ecs-deploy-task-definition@v1.4.11 - with: - task-definition: ${{ steps.task-def.outputs.task-definition }} - service: ${{ env.ECS_SERVICE }} - cluster: ${{ env.ECS_CLUSTER }} - wait-for-service-stability: true diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml new file mode 100644 index 00000000..dca98fda --- /dev/null +++ b/.github/workflows/build-docker-image.yml @@ -0,0 +1,16 @@ +name: "Build Docker image" + +on: + workflow_dispatch: {} + +jobs: + build: + name: Build + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build the app and Docker image + run: | + docker build -t shapedocs . diff --git a/Dockerfile b/Dockerfile index 3b14c7ea..e55824e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.8.1-alpine AS base +FROM node:18-alpine AS base # Install dependencies only when needed FROM base AS deps diff --git a/next.config.js b/next.config.js index 761f61ab..5373e6a7 100644 --- a/next.config.js +++ b/next.config.js @@ -4,9 +4,7 @@ const nextConfig = { // Allows production builds to successfully complete even if it has linting errors. // This is only OK because we do linting as part of our CI setup. ignoreDuringBuilds: true, - }, - // Output standalone to be used for Docker builds. - output: 'standalone', + } } module.exports = nextConfig diff --git a/package.json b/package.json index 723f01d3..e852b0ca 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@fortawesome/fontawesome-svg-core": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/react-fontawesome": "^0.2.0", - "@mui/icons-material": "^5.15.10", + "@mui/icons-material": "^5.14.18", "@mui/material": "^5.14.18", "@octokit/auth-app": "^6.0.1", "@octokit/core": "^5.1.0", @@ -42,7 +42,7 @@ "redis-semaphore": "^5.5.0", "redoc": "^2.1.3", "styled-components": "^6.1.1", - "swagger-ui-react": "^5.11.7", + "swagger-ui-react": "^5.9.1", "swr": "^2.2.4", "usehooks-ts": "^2.9.1", "yaml": "^2.3.4", @@ -57,7 +57,7 @@ "@types/react": "^18", "@types/react-dom": "^18", "@types/swagger-ui-react": "^4.18.2", - "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/eslint-plugin": "^6.9.0", "@typescript-eslint/parser": "^6.9.1", "autoprefixer": "^10", "eslint": "^8.56.0", diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts deleted file mode 100644 index fead38a1..00000000 --- a/src/app/api/health/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NextResponse } from "next/server" - -export const GET = async (): Promise => { - return NextResponse.json({ status: "Healthy" }) -} diff --git a/src/features/docs/view/Swagger.tsx b/src/features/docs/view/Swagger.tsx index ad06d1da..98f9838a 100644 --- a/src/features/docs/view/Swagger.tsx +++ b/src/features/docs/view/Swagger.tsx @@ -7,7 +7,7 @@ const Swagger = ({ url }: { url: string }) => { const [isLoading, setLoading] = useState(true) return ( - setLoading(false)} deepLinking persistAuthorization /> + setLoading(false)} deepLinking /> ) } From f2a73d699a6fb9b0c283f5450d1db1d5ca4365b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 25 Jun 2024 09:34:43 +0200 Subject: [PATCH 058/191] Documents access for multiple accounts --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb6cc6ef..1f5ee0da 100644 --- a/README.md +++ b/README.md @@ -96,4 +96,12 @@ Each environment is deployed by merging changes into their respective branch. He ## 📖 Getting Started with Shape Docs -Details on getting started showing documentation on Shape Docs can be [found on our Conflouence](https://shapedk.atlassian.net/wiki/spaces/DEVELOPERS/pages/3795615745/Shape+Docs). +Details on getting started showing documentation on Shape Docs can be [found on our Confluence](https://shapedk.atlassian.net/wiki/spaces/DEVELOPERS/pages/3795615745/Shape+Docs). + +## ✨ Design + +This section documents key decisions that were made during the design of Shape Docs. + +### Multiple Authentication Providers + +Users are allowed to sign in with multiple authentication providers. They can sign in using either GitHub or a magic link. This is key for employees who are also invited as guests on the email associated with their GitHub account. When they sign in using a magic link, they will have access to the repositories that their GitHub account has access to. For users who are both invited as guests and have access through their GitHub account, the access granted by their GitHub account takes priority. From 754b7b352000854bee73102a963feb1f2ae3d372 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 25 Jun 2024 09:48:40 +0200 Subject: [PATCH 059/191] Validate project string length --- src/features/admin/view/Actions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/admin/view/Actions.tsx b/src/features/admin/view/Actions.tsx index 12abbabe..eac20093 100644 --- a/src/features/admin/view/Actions.tsx +++ b/src/features/admin/view/Actions.tsx @@ -6,7 +6,7 @@ import { z } from 'zod' const sendInviteSchema = z.object({ email: z.string().email(), - projects: z.string().transform(v => v.trim().split(",")) + projects: z.string().min(1).transform(v => v.trim().split(",")) }) export interface SendInviteResult { @@ -25,7 +25,7 @@ export const sendInvite = async (prevState: any, formData: FormData): Promise `${e.path.join('.')}: ${e.message}`).join(', '), success: false, } } From b44ceb7b927e584bf487e6ab82211b589080599f Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 25 Jun 2024 09:55:00 +0200 Subject: [PATCH 060/191] Add placeholder edit button --- src/features/admin/view/AdminPage.tsx | 2 ++ src/features/admin/view/EditGuestForm.tsx | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 src/features/admin/view/EditGuestForm.tsx diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx index a6606acf..f2faa6ef 100644 --- a/src/features/admin/view/AdminPage.tsx +++ b/src/features/admin/view/AdminPage.tsx @@ -3,6 +3,7 @@ import { Box, ButtonGroup, Chip, Paper, TableBody, TableCell, TableContainer, Ta import Table from "@mui/material/Table" import { InviteGuestForm } from "./InviteGuestForm" import { RemoveGuestForm } from "./RemoveGuestForm" +import { EditGuestForm } from "./EditGuestForm" const AdminPage = async () => { const guests = await guestRepository.getAll() @@ -42,6 +43,7 @@ const AdminPage = async () => { {row.projects.join(", ")} + diff --git a/src/features/admin/view/EditGuestForm.tsx b/src/features/admin/view/EditGuestForm.tsx new file mode 100644 index 00000000..33070104 --- /dev/null +++ b/src/features/admin/view/EditGuestForm.tsx @@ -0,0 +1,11 @@ +'use client' + +import { Button } from "@mui/material"; + +export const EditGuestForm = () => { + return ( +
+ +
+ ) +} From 419ab958a3c06caeb7de6babec82834a45e08baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 25 Jun 2024 11:15:34 +0200 Subject: [PATCH 061/191] Fixes incorrect type name --- .../auth/data/GitHubInstallationAccessTokenDataSource.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts b/src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts index b82c2112..49e922f5 100644 --- a/src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts +++ b/src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts @@ -1,7 +1,7 @@ import { Octokit } from "octokit" import { createAppAuth } from "@octokit/auth-app" -type GitHubInstallationAccessTokenRefresherConfig = { +type GitHubInstallationAccessTokenDataSourceConfig = { readonly appId: string readonly clientId: string readonly clientSecret: string @@ -9,10 +9,10 @@ type GitHubInstallationAccessTokenRefresherConfig = { readonly organization: string } -export default class GitHubInstallationAccessTokenRefresher { - private readonly config: GitHubInstallationAccessTokenRefresherConfig +export default class GitHubInstallationAccessTokenDataSource { + private readonly config: GitHubInstallationAccessTokenDataSourceConfig - constructor(config: GitHubInstallationAccessTokenRefresherConfig) { + constructor(config: GitHubInstallationAccessTokenDataSourceConfig) { this.config = config } From 998129ae853d817609b20b61d3b0f660fd4bf13f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 25 Jun 2024 11:15:59 +0200 Subject: [PATCH 062/191] Displays email and placeholder avatar for guests --- src/features/user/view/UserButton.tsx | 6 +++--- types/@next-auth.d.ts | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/features/user/view/UserButton.tsx b/src/features/user/view/UserButton.tsx index 2d8597e0..749d8e5a 100644 --- a/src/features/user/view/UserButton.tsx +++ b/src/features/user/view/UserButton.tsx @@ -50,13 +50,13 @@ const UserButton = ({ session }: { session: Session }) => { > - {user?.image && - + {user && + } {user && - {user.name} + {user.name || user.email} } diff --git a/types/@next-auth.d.ts b/types/@next-auth.d.ts index b94d3866..18355b40 100644 --- a/types/@next-auth.d.ts +++ b/types/@next-auth.d.ts @@ -5,6 +5,8 @@ declare module "next-auth" { user: { id: string email: string + name?: string + image?: string } } } From 080b5c60377a5ff6ff1175a4f6131d43e1d554d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 25 Jun 2024 13:27:39 +0200 Subject: [PATCH 063/191] Handles invalid access token --- .../sessionValidity/GitHubOrganizationSessionValidator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts index 36759971..be6170f4 100644 --- a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts @@ -54,6 +54,8 @@ export default class GitHubOrganizationSessionValidator { return SessionValidity.OUTSIDE_GITHUB_ORGANIZATION } else if (error.status == 403) { return SessionValidity.GITHUB_APP_BLOCKED + } else if (error.status == 401) { + return SessionValidity.INVALID_ACCESS_TOKEN } else { throw error } From 8958793d8ec93ba06d1d047daa65cb5439e3ba93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 25 Jun 2024 18:19:49 +0200 Subject: [PATCH 064/191] Simplifies OAuthToken handling --- __test__/auth/AccessTokenDataSource.test.ts | 268 --------------- __test__/auth/AccessTokenRefresher.test.ts | 166 --------- .../auth/AccessTokenSessionValidator.test.ts | 39 --- __test__/auth/AccessTokenTransferrer.test.ts | 71 ---- ...oviderTypeBasedOAuthTokenRefresher.test.ts | 59 ++++ ...AuthjsAccountsOAuthTokenRepository.test.ts | 100 ++++++ .../auth/AuthjsOAuthTokenDataSource.test.ts | 32 -- .../CompositeOAuthTokenRepository.test.ts | 143 ++++++++ ...GitHubOrganizationSessionValidator.test.ts | 28 +- .../GitHubUserAccessTokenTransferrer.test.ts | 212 ------------ .../auth/GuestAccessTokenTransferrer.test.ts | 224 ------------- .../auth/GuestOAuthTokenDataSource.test.ts | 178 ++++++++++ .../auth/GuestOAuthTokenRefresher.test.ts | 35 ++ .../GuestRepositoryAccessDataSource.test.ts | 143 -------- .../auth/LockingAccessTokenRefresher.test.ts | 32 +- __test__/auth/LogInHandler.test.ts | 140 ++++++++ .../auth/OAuthTokenSessionValidator.test.ts | 39 +++ .../PersistingOAuthTokenDataSource.test.ts | 314 ++++++++++++++++++ .../PersistingOAuthTokenRefresher.test.ts | 175 ++++++++++ ...ryRestrictingAccessTokenDataSource.test.ts | 20 -- ... OAuthTokenRefreshingGitHubClient.test.ts} | 139 ++++---- .../KeyValueUserDataRepository.test.ts | 2 +- __test__/common/utils/ZodJSONCoder.test.ts | 2 +- jest.config.js | 3 + src/app/[[...slug]]/page.tsx | 1 - src/app/api/user/projects/route.ts | 2 +- src/app/layout.tsx | 2 +- src/common/github/GitHubClient.ts | 22 +- ...ts => OAuthTokenRefreshingGitHubClient.ts} | 36 +- src/common/github/index.ts | 2 +- src/common/session/AuthjsSession.ts | 20 +- src/common/session/ISession.ts | 4 +- src/common/session/index.ts | 2 +- src/composition.ts | 119 +++---- .../auth/data/AuthjsOAuthTokenDataSource.ts | 28 -- .../auth/data/GitHubOAuthTokenRefresher.ts | 9 +- src/features/auth/data/index.ts | 1 - .../accessToken/AccessTokenDataSource.ts | 56 ---- .../accessToken/AccessTokenRefresher.ts | 41 --- .../accessToken/AccessTokenTransferrer.ts | 32 -- .../GitHubAccessTokenTransferrer.ts | 27 -- .../GuestAccessTokenTransferrer.ts | 48 --- .../GuestRepositoryAccessDataSource.ts | 28 -- .../accessToken/IAccessTokenTransferrer.ts | 3 - .../LockingAccessTokenRefresher.ts | 28 -- ...ositoryRestrictingAccessTokenDataSource.ts | 27 -- src/features/auth/domain/accessToken/index.ts | 8 - src/features/auth/domain/index.ts | 1 - .../auth/domain/logIn/ILogInHandler.ts | 8 +- .../auth/domain/logIn/LogInHandler.ts | 42 +++ .../domain/logIn/NullObjectLogInHandler.ts | 9 - src/features/auth/domain/logIn/index.ts | 2 +- .../oAuthToken/IOAuthTokenDataSource.ts | 5 - .../domain/oAuthToken/IOAuthTokenRefresher.ts | 5 - .../data-source/GuestOAuthTokenDataSource.ts | 35 ++ .../data-source/IOAuthTokenDataSource.ts | 5 + .../PersistingOAuthTokenDataSource.ts | 55 +++ src/features/auth/domain/oAuthToken/index.ts | 16 +- ...untProviderTypeBasedOAuthTokenRefresher.ts | 27 ++ .../refresher/GuestOAuthTokenRefresher.ts | 17 + .../refresher/IOAuthTokenRefresher.ts | 5 + .../refresher/LockingOAuthTokenRefresher.ts | 24 ++ .../PersistingOAuthTokenRefresher.ts | 41 +++ .../AuthjsAccountsOAuthTokenRepository.ts | 60 ++++ .../CompositeOAuthTokenRepository.ts | 33 ++ .../{ => repository}/IOAuthTokenRepository.ts | 2 +- .../{ => repository}/OAuthTokenRepository.ts | 4 +- .../AccessTokenSessionValidator.ts | 22 -- .../GitHubOrganizationSessionValidator.ts | 18 +- .../OAuthTokenSessionValidator.ts | 19 ++ .../auth/domain/sessionValidity/index.ts | 2 +- .../sessionValidity/useRepositoryAccess.ts | 2 +- .../sessionValidity/useSessionValidity.ts | 2 +- src/features/auth/view/SessionBarrier.tsx | 4 +- .../auth/view/client/SessionBarrier.tsx | 8 +- src/features/projects/data/useProjects.ts | 2 +- .../projects/domain/ProjectRepository.ts | 2 +- src/features/projects/view/ProjectsPage.tsx | 2 +- tsconfig.json | 9 +- 79 files changed, 1818 insertions(+), 1780 deletions(-) delete mode 100644 __test__/auth/AccessTokenDataSource.test.ts delete mode 100644 __test__/auth/AccessTokenRefresher.test.ts delete mode 100644 __test__/auth/AccessTokenSessionValidator.test.ts delete mode 100644 __test__/auth/AccessTokenTransferrer.test.ts create mode 100644 __test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts create mode 100644 __test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts delete mode 100644 __test__/auth/AuthjsOAuthTokenDataSource.test.ts create mode 100644 __test__/auth/CompositeOAuthTokenRepository.test.ts delete mode 100644 __test__/auth/GitHubUserAccessTokenTransferrer.test.ts delete mode 100644 __test__/auth/GuestAccessTokenTransferrer.test.ts create mode 100644 __test__/auth/GuestOAuthTokenDataSource.test.ts create mode 100644 __test__/auth/GuestOAuthTokenRefresher.test.ts delete mode 100644 __test__/auth/GuestRepositoryAccessDataSource.test.ts create mode 100644 __test__/auth/LogInHandler.test.ts create mode 100644 __test__/auth/OAuthTokenSessionValidator.test.ts create mode 100644 __test__/auth/PersistingOAuthTokenDataSource.test.ts create mode 100644 __test__/auth/PersistingOAuthTokenRefresher.test.ts delete mode 100644 __test__/auth/RepositoryRestrictingAccessTokenDataSource.test.ts rename __test__/common/github/{AccessTokenRefreshingGitHubClient.test.ts => OAuthTokenRefreshingGitHubClient.test.ts} (66%) rename src/common/github/{AccessTokenRefreshingGitHubClient.ts => OAuthTokenRefreshingGitHubClient.ts} (70%) delete mode 100644 src/features/auth/data/AuthjsOAuthTokenDataSource.ts delete mode 100644 src/features/auth/domain/accessToken/AccessTokenDataSource.ts delete mode 100644 src/features/auth/domain/accessToken/AccessTokenRefresher.ts delete mode 100644 src/features/auth/domain/accessToken/AccessTokenTransferrer.ts delete mode 100644 src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts delete mode 100644 src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts delete mode 100644 src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts delete mode 100644 src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts delete mode 100644 src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts delete mode 100644 src/features/auth/domain/accessToken/RepositoryRestrictingAccessTokenDataSource.ts delete mode 100644 src/features/auth/domain/accessToken/index.ts create mode 100644 src/features/auth/domain/logIn/LogInHandler.ts delete mode 100644 src/features/auth/domain/logIn/NullObjectLogInHandler.ts delete mode 100644 src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts delete mode 100644 src/features/auth/domain/oAuthToken/IOAuthTokenRefresher.ts create mode 100644 src/features/auth/domain/oAuthToken/data-source/GuestOAuthTokenDataSource.ts create mode 100644 src/features/auth/domain/oAuthToken/data-source/IOAuthTokenDataSource.ts create mode 100644 src/features/auth/domain/oAuthToken/data-source/PersistingOAuthTokenDataSource.ts create mode 100644 src/features/auth/domain/oAuthToken/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts create mode 100644 src/features/auth/domain/oAuthToken/refresher/GuestOAuthTokenRefresher.ts create mode 100644 src/features/auth/domain/oAuthToken/refresher/IOAuthTokenRefresher.ts create mode 100644 src/features/auth/domain/oAuthToken/refresher/LockingOAuthTokenRefresher.ts create mode 100644 src/features/auth/domain/oAuthToken/refresher/PersistingOAuthTokenRefresher.ts create mode 100644 src/features/auth/domain/oAuthToken/repository/AuthjsAccountsOAuthTokenRepository.ts create mode 100644 src/features/auth/domain/oAuthToken/repository/CompositeOAuthTokenRepository.ts rename src/features/auth/domain/oAuthToken/{ => repository}/IOAuthTokenRepository.ts (83%) rename src/features/auth/domain/oAuthToken/{ => repository}/OAuthTokenRepository.ts (93%) delete mode 100644 src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts create mode 100644 src/features/auth/domain/sessionValidity/OAuthTokenSessionValidator.ts diff --git a/__test__/auth/AccessTokenDataSource.test.ts b/__test__/auth/AccessTokenDataSource.test.ts deleted file mode 100644 index 6bb7b329..00000000 --- a/__test__/auth/AccessTokenDataSource.test.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { AccessTokenDataSource } from "../../src/features/auth/domain" - -test("It skips transferring if an OAuth token already exists", async () => { - let didTransferAccessToken = false - const sut = new AccessTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - oauthTokenDataSource: { - async get(_user) { - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - accessTokenTransferrer: { - async transferAccessToken() { - didTransferAccessToken = true - return "new-github-access-token" - } - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() {}, - async release() {} - } - } - } - }) - await sut.getAccessToken() - expect(didTransferAccessToken).toBeFalsy() -}) - -test("It skips transferring if an OAuth token has been stored while waiting to acquire the lock", async () => { - let didAcquireLock = false - let didTransferAccessToken = false - const sut = new AccessTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - oauthTokenDataSource: { - async get(_user) { - if (didAcquireLock) { - return { - accessToken: "foo", - refreshToken: "bar" - } - } else { - return null - } - } - }, - accessTokenTransferrer: { - async transferAccessToken() { - didTransferAccessToken = true - return "new-github-access-token" - } - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() { - didAcquireLock = true - }, - async release() {} - } - } - } - }) - await sut.getAccessToken() - expect(didAcquireLock).toBeTruthy() - expect(didTransferAccessToken).toBeFalsy() -}) - -test("It does not acquire lock if an OAuth token already exists", async () => { - let didAcquireLock = false - const sut = new AccessTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - oauthTokenDataSource: { - async get(_user) { - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - accessTokenTransferrer: { - async transferAccessToken() { - return "new-github-access-token" - } - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() { - didAcquireLock = true - }, - async release() {} - } - } - } - }) - await sut.getAccessToken() - expect(didAcquireLock).toBeFalsy() -}) - -test("It transfers access token if no OAuth token exists", async () => { - let didTransferAccessToken = false - const sut = new AccessTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - oauthTokenDataSource: { - async get(_user) { - return null - } - }, - accessTokenTransferrer: { - async transferAccessToken() { - didTransferAccessToken = true - return "new-github-access-token" - } - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() {}, - async release() {} - } - } - } - }) - const accessToken = await sut.getAccessToken() - expect(didTransferAccessToken).toBeTruthy() - expect(accessToken).toBe("new-github-access-token") -}) - -test("It acquires lock", async () => { - let didAcquireLock = false - const sut = new AccessTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - oauthTokenDataSource: { - async get(_user) { - return null - } - }, - accessTokenTransferrer: { - async transferAccessToken() { - return "new-github-access-token" - } - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() { - didAcquireLock = true - }, - async release() {} - } - } - } - }) - await sut.getAccessToken() - expect(didAcquireLock).toBeTruthy() -}) - -test("It releases lock", async () => { - let didAcquireLock = false - const sut = new AccessTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - oauthTokenDataSource: { - async get(_user) { - return null - } - }, - accessTokenTransferrer: { - async transferAccessToken() { - return "new-github-access-token" - } - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() {}, - async release() { - didAcquireLock = true - } - } - } - } - }) - await sut.getAccessToken() - expect(didAcquireLock).toBeTruthy() -}) diff --git a/__test__/auth/AccessTokenRefresher.test.ts b/__test__/auth/AccessTokenRefresher.test.ts deleted file mode 100644 index b156fc41..00000000 --- a/__test__/auth/AccessTokenRefresher.test.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { AccessTokenRefresher, OAuthToken } from "../../src/features/auth/domain" - -test("It refreshes OAuth token using stored refresh token", async () => { - let usedRefreshToken: string | undefined - const sut = new AccessTokenRefresher({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken(refreshToken) { - usedRefreshToken = refreshToken - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - oAuthTokenRepository: { - async get() { - return { - accessToken: "oldAccessToken", - refreshToken: "oldRefreshToken" - } - }, - async set() {}, - async delete() {} - } - }) - await sut.refreshAccessToken("oldAccessToken") - expect(usedRefreshToken).toBe("oldRefreshToken") -}) - -test("It stores the new OAuth token for the user", async () => { - let storedUserId: string | undefined - let storedOAuthToken: OAuthToken | undefined - const sut = new AccessTokenRefresher({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken() { - return { - accessToken: "newAccessToken", - refreshToken: "newRefreshToken" - } - } - }, - oAuthTokenRepository: { - async get() { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(userId, oAuthToken) { - storedUserId = userId - storedOAuthToken = oAuthToken - }, - async delete() {} - } - }) - await sut.refreshAccessToken("foo") - expect(storedUserId).toBe("1234") - expect(storedOAuthToken?.accessToken).toBe("newAccessToken") - expect(storedOAuthToken?.refreshToken).toBe("newRefreshToken") -}) - -test("It refreshes the access token when the input access token is equal to the stored access token", async () => { - let didRefreshAccessToken = false - const sut = new AccessTokenRefresher({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken() { - didRefreshAccessToken = true - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - oAuthTokenRepository: { - async get() { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - } - }) - await sut.refreshAccessToken("foo") - expect(didRefreshAccessToken).toBeTruthy() -}) - -test("It skips refreshing the access token when the input access token is not equal to the stored access token", async () => { - let didRefreshAccessToken = false - const sut = new AccessTokenRefresher({ - userIdReader: { - async getUserId() { - return "1234" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken() { - didRefreshAccessToken = true - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - oAuthTokenRepository: { - async get() { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - } - }) - await sut.refreshAccessToken("outdated") - expect(didRefreshAccessToken).toBeFalsy() -}) - -test("It reads user ID", async () => { - let didReadUserId = false - const sut = new AccessTokenRefresher({ - userIdReader: { - async getUserId() { - didReadUserId = true - return "1234" - } - }, - oAuthTokenRefresher: { - async refreshOAuthToken() { - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - oAuthTokenRepository: { - async get() { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set() {}, - async delete() {} - } - }) - await sut.refreshAccessToken("foo") - expect(didReadUserId).toBeTruthy() -}) diff --git a/__test__/auth/AccessTokenSessionValidator.test.ts b/__test__/auth/AccessTokenSessionValidator.test.ts deleted file mode 100644 index 8c76d62c..00000000 --- a/__test__/auth/AccessTokenSessionValidator.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { AccessTokenSessionValidator, SessionValidity } from "../../src/features/auth/domain" - -test("It reads the access token", async () => { - let didReadAccessToken = false - const sut = new AccessTokenSessionValidator({ - accessTokenDataSource: { - async getAccessToken() { - didReadAccessToken = true - return "foo" - } - } - }) - await sut.validateSession() - expect(didReadAccessToken).toBeTruthy() -}) - -test("It considers session valid when it can read an access token", async () => { - const sut = new AccessTokenSessionValidator({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" - } - } - }) - const validity = await sut.validateSession() - expect(validity).toBe(SessionValidity.VALID) -}) - -test("It considers access token to be invalid when failing to get access token", async () => { - const sut = new AccessTokenSessionValidator({ - accessTokenDataSource: { - async getAccessToken() { - throw new Error("Mock error") - } - } - }) - const validity = await sut.validateSession() - expect(validity).toBe(SessionValidity.INVALID_ACCESS_TOKEN) -}) diff --git a/__test__/auth/AccessTokenTransferrer.test.ts b/__test__/auth/AccessTokenTransferrer.test.ts deleted file mode 100644 index f306c662..00000000 --- a/__test__/auth/AccessTokenTransferrer.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { AccessTokenTransferrer } from "../../src/features/auth/domain" - -test("It transfers access token for GitHub account provider type", async () => { - let didTransferAccessToken = false - const sut = new AccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - gitHubAccessTokenTransferrer: { - async transferAccessToken() { - didTransferAccessToken = true - return "new-github-access-token" - } - }, - guestAccessTokenTransferrer: { - async transferAccessToken() { - didTransferAccessToken = false - return "new-guest-access-token" - } - } - }) - const accessToken = await sut.transferAccessToken() - expect(didTransferAccessToken).toBeTruthy() - expect(accessToken).toBe("new-github-access-token") -}) - -test("It transfers access token for email account provider type", async () => { - let didTransferAccessToken = false - const sut = new AccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "email" - } - }, - gitHubAccessTokenTransferrer: { - async transferAccessToken() { - didTransferAccessToken = false - return "new-github-access-token" - } - }, - guestAccessTokenTransferrer: { - async transferAccessToken() { - didTransferAccessToken = true - return "new-guest-access-token" - } - } - }) - const accessToken = await sut.transferAccessToken() - expect(didTransferAccessToken).toBeTruthy() - expect(accessToken).toBe("new-guest-access-token") -}) diff --git a/__test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts b/__test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts new file mode 100644 index 00000000..55fdd886 --- /dev/null +++ b/__test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts @@ -0,0 +1,59 @@ +import { AccountProviderTypeBasedOAuthTokenRefresher } from "../../src/features/auth/domain" + +test("It refreshes using email refresher when account provider is \"email\"", async () => { + let didRefreshUsingEmailStrategy = false + let didRefreshUsingGitHubStrategy = false + const sut = new AccountProviderTypeBasedOAuthTokenRefresher({ + accountProviderReader: { + async getAccountProvider() { + return "email" + } + }, + strategy: { + email: { + async refreshOAuthToken(_oauthToken) { + didRefreshUsingEmailStrategy = true + return { accessToken: "foo "} + } + }, + github: { + async refreshOAuthToken(_oauthToken) { + didRefreshUsingGitHubStrategy = true + return { accessToken: "foo "} + } + } + } + }) + await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) + expect(didRefreshUsingEmailStrategy).toBeTruthy() + expect(didRefreshUsingGitHubStrategy).toBeFalsy() +}) + +test("It refreshes using GitHub refresher when account provider is \"github\"", async () => { + let didRefreshUsingEmailStrategy = false + let didRefreshUsingGitHubStrategy = false + const sut = new AccountProviderTypeBasedOAuthTokenRefresher({ + accountProviderReader: { + async getAccountProvider() { + return "github" + } + }, + strategy: { + email: { + async refreshOAuthToken(_oauthToken) { + didRefreshUsingEmailStrategy = true + return { accessToken: "foo "} + } + }, + github: { + async refreshOAuthToken(_oauthToken) { + didRefreshUsingGitHubStrategy = true + return { accessToken: "foo "} + } + } + } + }) + await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) + expect(didRefreshUsingEmailStrategy).toBeFalsy() + expect(didRefreshUsingGitHubStrategy).toBeTruthy() +}) diff --git a/__test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts b/__test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts new file mode 100644 index 00000000..b45a12bc --- /dev/null +++ b/__test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts @@ -0,0 +1,100 @@ +import { AuthjsAccountsOAuthTokenRepository } from "../../src/features/auth/domain" + +test("It gets token for user ID and provider", async () => { + let queryUserId: string | undefined + let queryProvider: string | undefined + const sut = new AuthjsAccountsOAuthTokenRepository({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query, values: any[] = []) { + queryProvider = values[0] + queryUserId = values[1] + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + await sut.get("1234") + expect(queryUserId).toEqual("1234") + expect(queryProvider).toEqual("github") +}) + +test("It sets token for user ID and provider", async () => { + let queryUserId: string | undefined + let queryProvider: string | undefined + let queryAccessToken: string | undefined + let queryRefreshToken: string | undefined + const sut = new AuthjsAccountsOAuthTokenRepository({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query, values: any[] = []) { + queryProvider = values[0] + queryUserId = values[1] + queryAccessToken = values[2] + queryRefreshToken = values[3] + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + await sut.set("1234", { accessToken: "foo", refreshToken: "bar" }) + expect(queryUserId).toEqual("1234") + expect(queryProvider).toEqual("github") + expect(queryAccessToken).toEqual("foo") + expect(queryRefreshToken).toEqual("bar") +}) + +test("It deletes token for user ID and provider", async () => { + let queryUserId: string | undefined + let queryProvider: string | undefined + const sut = new AuthjsAccountsOAuthTokenRepository({ + provider: "github", + db: { + async connect() { + return { + async query() { + return { rows: [] } + }, + async disconnect() {}, + } + }, + async query(_query, values: any[] = []) { + queryProvider = values[0] + queryUserId = values[1] + return { + rows: [{ + access_token: "foo", + refresh_token: "bar" + }] + } + } + } + }) + await sut.delete("1234") + expect(queryUserId).toEqual("1234") + expect(queryProvider).toEqual("github") +}) diff --git a/__test__/auth/AuthjsOAuthTokenDataSource.test.ts b/__test__/auth/AuthjsOAuthTokenDataSource.test.ts deleted file mode 100644 index 968e7691..00000000 --- a/__test__/auth/AuthjsOAuthTokenDataSource.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { AuthjsOAuthTokenDataSource } from "../../src/features/auth/data" - -test("It queries token for user ID and provider", async () => { - let queriedUserId: string | undefined - let queriedProvider: string | undefined - const sut = new AuthjsOAuthTokenDataSource({ - provider: "github", - db: { - async connect() { - return { - async query() { - return { rows: [] } - }, - async disconnect() {}, - } - }, - async query(_query, values: any[] = []) { - queriedUserId = values[0] - queriedProvider = values[1] - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } - } - } - }) - await sut.get("1234") - expect(queriedUserId).toEqual("1234") - expect(queriedProvider).toEqual("github") -}) diff --git a/__test__/auth/CompositeOAuthTokenRepository.test.ts b/__test__/auth/CompositeOAuthTokenRepository.test.ts new file mode 100644 index 00000000..963581e4 --- /dev/null +++ b/__test__/auth/CompositeOAuthTokenRepository.test.ts @@ -0,0 +1,143 @@ +import { CompositeOAuthTokenRepository } from "../../src/features/auth/domain" + +test("It traverses all repositories until it gets a value", async () => { + let didGetFromRepository1 = false + let didGetFromRepository2 = false + let didGetFromRepository3 = false + const sut = new CompositeOAuthTokenRepository({ + oAuthTokenRepositories: [{ + async get(_userId) { + didGetFromRepository1 = true + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, { + async get(_userId) { + didGetFromRepository2 = true + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, { + async get(_userId) { + didGetFromRepository3 = true + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }] + }) + await expect(sut.get("1234")).rejects.toThrow() + expect(didGetFromRepository1).toBeTruthy() + expect(didGetFromRepository2).toBeTruthy() + expect(didGetFromRepository3).toBeTruthy() +}) + +test("It skips getting value from following repositories once it finds a value", async () => { + let didGetFromRepository1 = false + let didGetFromRepository2 = false + let didGetFromRepository3 = false + const sut = new CompositeOAuthTokenRepository({ + oAuthTokenRepositories: [{ + async get(_userId) { + didGetFromRepository1 = true + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, { + async get(_userId) { + didGetFromRepository2 = true + return { accessToken: "foo" } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, { + async get(_userId) { + didGetFromRepository3 = true + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }] + }) + await sut.get("1234") + expect(didGetFromRepository1).toBeTruthy() + expect(didGetFromRepository2).toBeTruthy() + expect(didGetFromRepository3).toBeFalsy() +}) + +test("It sets OAuth token in all repositories", async () => { + let didSetInRepository1 = false + let didSetInRepository2 = false + let didSetInRepository3 = false + const sut = new CompositeOAuthTokenRepository({ + oAuthTokenRepositories: [{ + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) { + didSetInRepository1 = true + }, + async delete(_userId) {} + }, { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) { + didSetInRepository2 = true + }, + async delete(_userId) {} + }, { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) { + didSetInRepository3 = true + }, + async delete(_userId) {} + }] + }) + await sut.set("1234", { accessToken: "foo" }) + expect(didSetInRepository1).toBeTruthy() + expect(didSetInRepository2).toBeTruthy() + expect(didSetInRepository3).toBeTruthy() +}) + +test("It deletes OAuth token from all repositories", async () => { + let didDeleteFromRepository1 = false + let didDeleteFromRepository2 = false + let didDeleteFromRepository3 = false + const sut = new CompositeOAuthTokenRepository({ + oAuthTokenRepositories: [{ + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) { + didDeleteFromRepository1 = true + } + }, { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) { + didDeleteFromRepository2 = true + } + }, { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) { + didDeleteFromRepository3 = true + } + }] + }) + await sut.delete("1234") + expect(didDeleteFromRepository1).toBeTruthy() + expect(didDeleteFromRepository2).toBeTruthy() + expect(didDeleteFromRepository3).toBeTruthy() +}) diff --git a/__test__/auth/GitHubOrganizationSessionValidator.test.ts b/__test__/auth/GitHubOrganizationSessionValidator.test.ts index a3aa5489..f6c9df2b 100644 --- a/__test__/auth/GitHubOrganizationSessionValidator.test.ts +++ b/__test__/auth/GitHubOrganizationSessionValidator.test.ts @@ -13,8 +13,8 @@ test("It requests organization membership status for the specified organization" return { state: "active" } } }, - accountProviderTypeReader: { - async getAccountProviderType() { + accountProviderReader: { + async getAccountProvider() { return "github" } } @@ -31,8 +31,8 @@ test("It considers session valid when membership state is \"active\"", async () return { state: "active" } } }, - accountProviderTypeReader: { - async getAccountProviderType() { + accountProviderReader: { + async getAccountProvider() { return "github" } } @@ -49,8 +49,8 @@ test("It considers user not to be part of the organization when membership state return { state: "pending" } } }, - accountProviderTypeReader: { - async getAccountProviderType() { + accountProviderReader: { + async getAccountProvider() { return "github" } } @@ -67,8 +67,8 @@ test("It considers user not to be part of the organization when receiving HTTP 4 throw { status: 404, message: "User is not member of organization"} } }, - accountProviderTypeReader: { - async getAccountProviderType() { + accountProviderReader: { + async getAccountProvider() { return "github" } } @@ -85,8 +85,8 @@ test("It considers organization to have blocked the GitHub app when receiving HT throw { status: 403, message: "Organization has blocked GitHub app"} } }, - accountProviderTypeReader: { - async getAccountProviderType() { + accountProviderReader: { + async getAccountProvider() { return "github" } } @@ -103,8 +103,8 @@ test("It forwards error when getting membership status throws unknown error", as throw { status: 500 } } }, - accountProviderTypeReader: { - async getAccountProviderType() { + accountProviderReader: { + async getAccountProvider() { return "github" } } @@ -120,8 +120,8 @@ test("It considers session valid when the account provider is not \"github\"", a throw { status: "pending" } } }, - accountProviderTypeReader: { - async getAccountProviderType() { + accountProviderReader: { + async getAccountProvider() { return "email" } } diff --git a/__test__/auth/GitHubUserAccessTokenTransferrer.test.ts b/__test__/auth/GitHubUserAccessTokenTransferrer.test.ts deleted file mode 100644 index 179a7070..00000000 --- a/__test__/auth/GitHubUserAccessTokenTransferrer.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { GitHubAccessTokenTransferrer } from "../../src/features/auth/domain" - -test("It reads the user ID", async () => { - let didReadUserId = false - const sut = new GitHubAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - didReadUserId = true - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - sourceOAuthTokenDataSource: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - } - }) - await sut.transferAccessToken() - expect(didReadUserId).toBeTruthy() -}) - -test("It reads the OAuth token from the data source", async () => { - let didReadOAuthToken = false - const sut = new GitHubAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - sourceOAuthTokenDataSource: { - async get(_userId) { - didReadOAuthToken = true - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - } - }) - await sut.transferAccessToken() - expect(didReadOAuthToken).toBeTruthy() -}) - -test("It stores the OAuth token in the destination repository", async () => { - let didStoreOAuthToken = false - const sut = new GitHubAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - sourceOAuthTokenDataSource: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) { - didStoreOAuthToken = true - }, - async delete(_userId) {} - } - }) - await sut.transferAccessToken() - expect(didStoreOAuthToken).toBeTruthy() -}) - -test("It stores the OAuth token that was read from the data source for the user ID", async () => { - let storedUserId: string | undefined - let storedAccessToken: string | undefined - let storedRefreshToken: string | undefined - const sut = new GitHubAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - sourceOAuthTokenDataSource: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(userId, token) { - storedUserId = userId - storedAccessToken = token.accessToken - storedRefreshToken = token.refreshToken - }, - async delete(_userId) {} - } - }) - await sut.transferAccessToken() - expect(storedUserId).toBe("1234") - expect(storedAccessToken).toBe("foo") - expect(storedRefreshToken).toBe("bar") -}) - -test("It returns newly created access token", async () => { - const sut = new GitHubAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - sourceOAuthTokenDataSource: { - async get(_userId) { - return { - accessToken: "foo-bar", - refreshToken: "bar" - } - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - } - }) - const accessToken = await sut.transferAccessToken() - expect(accessToken).toBe("foo-bar") -}) diff --git a/__test__/auth/GuestAccessTokenTransferrer.test.ts b/__test__/auth/GuestAccessTokenTransferrer.test.ts deleted file mode 100644 index 5d6afc00..00000000 --- a/__test__/auth/GuestAccessTokenTransferrer.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { GuestAccessTokenTransferrer } from "../../src/features/auth/domain" - -test("It reads the user ID and email", async () => { - let didReadUserId = false - let didReadEmail = false - const sut = new GuestAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - didReadUserId = true - return "1234" - }, - async getEmail() { - didReadEmail = true - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - guestRepository: { - async findByEmail(_email) { - return { projects: [] } - } - }, - installationAccessTokenDataSource: { - async getAccessToken(_projects) { - return "foo" - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - } - }) - await sut.transferAccessToken() - expect(didReadUserId).toBeTruthy() - expect(didReadEmail).toBeTruthy() -}) - -test("It throws an error if the guest was not found", async () => { - const sut = new GuestAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - guestRepository: { - async findByEmail(_email) { - return undefined - } - }, - installationAccessTokenDataSource: { - async getAccessToken(_projects) { - return "foo" - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - } - }) - await expect(sut.transferAccessToken()).rejects.toThrow() -}) - -test("It creates installation access token for guest's projects", async () => { - let didCreateAccessToken = false - let allowedProjects: string[] | undefined - const sut = new GuestAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - guestRepository: { - async findByEmail(_email) { - return { projects: ["foo", "bar" ]} - } - }, - installationAccessTokenDataSource: { - async getAccessToken(projects) { - didCreateAccessToken = true - allowedProjects = projects - return "foo" - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - } - }) - await sut.transferAccessToken() - expect(didCreateAccessToken).toBeTruthy() - expect(allowedProjects).toEqual(["foo", "bar"]) -}) - -test("It stores OAuth token with an access token and no refresh token for user ID", async () => { - let storedUserId: string | undefined - let storedAccessToken: string | undefined - let storedRefreshToken: string | undefined = "to_be_overriden_with_undefined" - const sut = new GuestAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - guestRepository: { - async findByEmail(_email) { - return { projects: ["foo", "bar" ]} - } - }, - installationAccessTokenDataSource: { - async getAccessToken(_projects) { - return "foo" - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(userId, token) { - storedUserId = userId - storedAccessToken = token.accessToken - storedRefreshToken = token.refreshToken - }, - async delete(_userId) {} - } - }) - await sut.transferAccessToken() - expect(storedUserId).toBe("1234") - expect(storedAccessToken).toBe("foo") - expect(storedRefreshToken).toBeUndefined() -}) - -test("It returns newly created access token", async () => { - const sut = new GuestAccessTokenTransferrer({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProviderType() { - return "github" - } - }, - guestRepository: { - async findByEmail(_email) { - return { projects: ["foo", "bar" ]} - } - }, - installationAccessTokenDataSource: { - async getAccessToken(_projects) { - return "foo-bar" - } - }, - destinationOAuthTokenRepository: { - async get(_userId) { - return { - accessToken: "foo", - refreshToken: "bar" - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - } - }) - const accessToken = await sut.transferAccessToken() - expect(accessToken).toBe("foo-bar") -}) \ No newline at end of file diff --git a/__test__/auth/GuestOAuthTokenDataSource.test.ts b/__test__/auth/GuestOAuthTokenDataSource.test.ts new file mode 100644 index 00000000..eaecc57f --- /dev/null +++ b/__test__/auth/GuestOAuthTokenDataSource.test.ts @@ -0,0 +1,178 @@ +import { GuestOAuthTokenDataSource } from "../../src/features/auth/domain" + +test("It gets the user's account provider type", async () => { + let didReadAccountProvider = false + const sut = new GuestOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getAccountProvider() { + didReadAccountProvider = true + return "email" + }, + async getEmail() { + return "foo@example.com" + }, + }, + gitHubInstallationAccessTokenDataSource: { + async getAccessToken(_repositoryNames) { + return "foo" + }, + }, + guestRepository: { + async getAll() { + throw new Error("Not implemented") + }, + async findByEmail(_email) { + throw new Error("Not implemented") + }, + async create(_email, _projects) { + throw new Error("Not implemented") + }, + async removeByEmail(_email) { + throw new Error("Not implemented") + }, + async getProjectsForEmail(_email) { + return [] + } + } + }) + await sut.getOAuthToken() + expect(didReadAccountProvider).toBeTruthy() +}) + +test("It throws error if account provider type is not \"email\"", async () => { + const sut = new GuestOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getAccountProvider() { + return "github" + }, + async getEmail() { + return "foo@example.com" + }, + }, + gitHubInstallationAccessTokenDataSource: { + async getAccessToken(_repositoryNames) { + return "foo" + }, + }, + guestRepository: { + async getAll() { + throw new Error("Not implemented") + }, + async findByEmail(_email) { + throw new Error("Not implemented") + }, + async create(_email, _projects) { + throw new Error("Not implemented") + }, + async removeByEmail(_email) { + throw new Error("Not implemented") + }, + async getProjectsForEmail(_email) { + throw new Error("Not implemented") + } + } + }) + await expect(sut.getOAuthToken()).rejects.toThrow() +}) + +test("It gets the user's projects", async () => { + let didGetProjects = false + const sut = new GuestOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getAccountProvider() { + return "email" + }, + async getEmail() { + return "foo@example.com" + }, + }, + gitHubInstallationAccessTokenDataSource: { + async getAccessToken(_repositoryNames) { + return "foo" + }, + }, + guestRepository: { + async getAll() { + throw new Error("Not implemented") + }, + async findByEmail(_email) { + throw new Error("Not implemented") + }, + async create(_email, _projects) { + throw new Error("Not implemented") + }, + async removeByEmail(_email) { + throw new Error("Not implemented") + }, + async getProjectsForEmail(_email) { + didGetProjects = true + return [] + } + } + }) + await sut.getOAuthToken() + expect(didGetProjects).toBeTruthy() +}) + +test("It creates a GitHub installation access token with access to the user's projects", async () => { + let repositoriesInAccessToken: string[] = [] + const sut = new GuestOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getAccountProvider() { + return "email" + }, + async getEmail() { + return "foo@example.com" + }, + }, + gitHubInstallationAccessTokenDataSource: { + async getAccessToken(repositoryNames) { + repositoriesInAccessToken = repositoryNames + return "foo" + }, + }, + guestRepository: { + async getAll() { + throw new Error("Not implemented") + }, + async findByEmail(_email) { + throw new Error("Not implemented") + }, + async create(_email, _projects) { + throw new Error("Not implemented") + }, + async removeByEmail(_email) { + throw new Error("Not implemented") + }, + async getProjectsForEmail(_email) { + return ["foo", "bar"] + } + } + }) + await sut.getOAuthToken() + expect(repositoriesInAccessToken).toEqual(["foo", "bar"]) +}) diff --git a/__test__/auth/GuestOAuthTokenRefresher.test.ts b/__test__/auth/GuestOAuthTokenRefresher.test.ts new file mode 100644 index 00000000..9486a5ec --- /dev/null +++ b/__test__/auth/GuestOAuthTokenRefresher.test.ts @@ -0,0 +1,35 @@ +import { GuestOAuthTokenRefresher } from "../../src/features/auth/domain" + +test("It gets OAuth token from data source", async () => { + let didCallDataSource = false + const sut = new GuestOAuthTokenRefresher({ + dataSource: { + async getOAuthToken() { + didCallDataSource = true + return { accessToken: "foo" } + } + } + }) + await sut.refreshOAuthToken({ + accessToken: "foo" + }) + expect(didCallDataSource).toBeTruthy() +}) + +test("It throws an error when the provided OAuth token contains a refresh token", async () => { + const sut = new GuestOAuthTokenRefresher({ + dataSource: { + async getOAuthToken() { + return { accessToken: "foo" } + } + } + }) + await expect( + sut.refreshOAuthToken({ + accessToken: "foo", + refreshToken: "bar" + }) + ) + .rejects + .toThrow() +}) diff --git a/__test__/auth/GuestRepositoryAccessDataSource.test.ts b/__test__/auth/GuestRepositoryAccessDataSource.test.ts deleted file mode 100644 index 4cc52c0e..00000000 --- a/__test__/auth/GuestRepositoryAccessDataSource.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { GuestRepositoryAccessDataSource } from "../../src/features/auth/domain" - -test("It throws error if no user was found for the user ID", async () => { - const sut = new GuestRepositoryAccessDataSource({ - db: { - async connect() { - return { - async query(_query: string, _values: any[] = []) { - // Return no rows as the user was not found. - return { rows: [] } - }, - async disconnect() {} - } - }, - async query(_query, _values = []) { - // Return no rows as the user was not found. - return { rows: [] } - } - }, - guestRepository: { - async getProjectsForEmail(_email) { - return [] - } - } - }) - await expect(sut.getRepositoryNames("1234")).rejects.toThrow() -}) - -test("It throws an error if the user does not have an e-mail", async () => { - const sut = new GuestRepositoryAccessDataSource({ - db: { - async connect() { - return { - async query(_query: string, _values: any[] = []) { - return { - rows: [{ email: null }] - } - }, - async disconnect() {} - } - }, - async query(_query, _values = []) { - return { - rows: [{ email: null }] - } - } - }, - guestRepository: { - async getProjectsForEmail(_email) { - return [] - } - } - }) - await expect(sut.getRepositoryNames("1234")).rejects.toThrow() -}) - -test("It throws an error if the user has an empty e-mail", async () => { - const sut = new GuestRepositoryAccessDataSource({ - db: { - async connect() { - return { - async query(_query: string, _values: any[] = []) { - return { - rows: [{ email: "" }] - } - }, - async disconnect() {} - } - }, - async query(_query, _values = []) { - return { - rows: [{ email: "" }] - } - } - }, - guestRepository: { - async getProjectsForEmail(_email) { - return [] - } - } - }) - await expect(sut.getRepositoryNames("1234")).rejects.toThrow() -}) - -test("It queries the guest repository using the user's e-mail", async () => { - let queriedEmail: string | undefined - const sut = new GuestRepositoryAccessDataSource({ - db: { - async connect() { - return { - async query(_query: string, _values: any[] = []) { - return { - rows: [{ email: "john@example.com" }] - } - }, - async disconnect() {} - } - }, - async query(_query, _values = []) { - return { - rows: [{ email: "john@example.com" }] - } - } - }, - guestRepository: { - async getProjectsForEmail(email) { - queriedEmail = email - return [] - } - } - }) - await sut.getRepositoryNames("1234") - expect(queriedEmail).toBe("john@example.com") -}) - -test("It returns repository names fetched from guest repository", async () => { - const sut = new GuestRepositoryAccessDataSource({ - db: { - async connect() { - return { - async query(_query: string, _values: any[] = []) { - return { - rows: [{ email: "john@example.com" }] - } - }, - async disconnect() {} - } - }, - async query(_query, _values = []) { - return { - rows: [{ email: "john@example.com" }] - } - } - }, - guestRepository: { - async getProjectsForEmail(_email) { - return ["foo", "bar"] - } - } - }) - const repositoryNames = await sut.getRepositoryNames("1234") - expect(repositoryNames).toEqual(["foo", "bar"]) -}) diff --git a/__test__/auth/LockingAccessTokenRefresher.test.ts b/__test__/auth/LockingAccessTokenRefresher.test.ts index f70640b0..119e7b50 100644 --- a/__test__/auth/LockingAccessTokenRefresher.test.ts +++ b/__test__/auth/LockingAccessTokenRefresher.test.ts @@ -1,8 +1,8 @@ -import { LockingAccessTokenRefresher } from "../../src/features/auth/domain" +import { LockingOAuthTokenRefresher } from "../../src/features/auth/domain" test("It acquires a lock", async () => { let didAcquireLock = false - const sut = new LockingAccessTokenRefresher({ + const sut = new LockingOAuthTokenRefresher({ mutexFactory: { async makeMutex() { return { @@ -13,19 +13,19 @@ test("It acquires a lock", async () => { } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { accessToken: "newAccessToken" } } } }) - await sut.refreshAccessToken("bar") + await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) expect(didAcquireLock).toBeTruthy() }) test("It releases the acquired lock", async () => { let didReleaseLock = false - const sut = new LockingAccessTokenRefresher({ + const sut = new LockingOAuthTokenRefresher({ mutexFactory: { async makeMutex() { return { @@ -36,19 +36,19 @@ test("It releases the acquired lock", async () => { } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { accessToken: "newAccessToken" } } } }) - await sut.refreshAccessToken("bar") + await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) expect(didReleaseLock).toBeTruthy() }) test("It refreshes access token", async () => { let didRefreshAccessToken = false - const sut = new LockingAccessTokenRefresher({ + const sut = new LockingOAuthTokenRefresher({ mutexFactory: { async makeMutex() { return { @@ -57,13 +57,13 @@ test("It refreshes access token", async () => { } } }, - accessTokenRefresher: { - async refreshAccessToken() { + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { didRefreshAccessToken = true - return "foo" + return { accessToken: "newAccessToken" } } } }) - await sut.refreshAccessToken("foo") + await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) expect(didRefreshAccessToken).toBeTruthy() }) diff --git a/__test__/auth/LogInHandler.test.ts b/__test__/auth/LogInHandler.test.ts new file mode 100644 index 00000000..4515e1bc --- /dev/null +++ b/__test__/auth/LogInHandler.test.ts @@ -0,0 +1,140 @@ +import { LogInHandler } from "../../src/features/auth/domain" + +test("It disallows logging in when account is undefined", async () => { + const sut = new LogInHandler({ + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn("1234") + expect(didLogin).toBeFalsy() +}) + +test("It allows logging in when account has \"nodemailer\" provider", async () => { + const sut = new LogInHandler({ + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn("1234", { + provider: "nodemailer", + providerAccountId: "foo@example.com" + }) + expect(didLogin).toBeTruthy() +}) + +test("It disallows logging in using a GitHub Account without an access token", async () => { + const sut = new LogInHandler({ + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn("1234", { + provider: "github", + providerAccountId: "foo@example.com", + refresh_token: "bar" + }) + expect(didLogin).toBeFalsy() +}) + +test("It disallows logging in using a GitHub Account without a refresh token", async () => { + const sut = new LogInHandler({ + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn("1234", { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo" + }) + expect(didLogin).toBeFalsy() +}) + +test("It allows logging in when using a GitHub account that has an access token and a refresh token", async () => { + const sut = new LogInHandler({ + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn("1234", { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo", + refresh_token: "bar" + }) + expect(didLogin).toBeTruthy() +}) + +test("It persists access token and refresh token when logging in with GitHub account", async () => { + let persistedUserId: string | undefined + let persistedAccessToken: string | undefined + let persistedRefreshToken: string | undefined + const sut = new LogInHandler({ + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(userId, token) { + persistedUserId = userId + persistedAccessToken = token.accessToken + persistedRefreshToken = token.refreshToken + }, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn("1234", { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo", + refresh_token: "bar" + }) + expect(didLogin).toBeTruthy() + expect(persistedUserId).toBe("1234") + expect(persistedAccessToken).toBe("foo") + expect(persistedRefreshToken).toBe("bar") +}) + +test("It disallows logging in when failing to persist access token and refresh token for GitHub account", async () => { + let didAttemptToPersistTokens = false + const sut = new LogInHandler({ + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) { + didAttemptToPersistTokens = true + throw new Error("Mock error") + }, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn("1234", { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo", + refresh_token: "bar" + }) + expect(didLogin).toBeFalsy() + expect(didAttemptToPersistTokens).toBeTruthy() +}) diff --git a/__test__/auth/OAuthTokenSessionValidator.test.ts b/__test__/auth/OAuthTokenSessionValidator.test.ts new file mode 100644 index 00000000..13156405 --- /dev/null +++ b/__test__/auth/OAuthTokenSessionValidator.test.ts @@ -0,0 +1,39 @@ +import { OAuthTokenSessionValidator, SessionValidity } from "../../src/features/auth/domain" + +test("It reads the access token", async () => { + let didReadOAuthToken = false + const sut = new OAuthTokenSessionValidator({ + oauthTokenDataSource: { + async getOAuthToken() { + didReadOAuthToken = true + return { accessToken: "foo" } + } + } + }) + await sut.validateSession() + expect(didReadOAuthToken).toBeTruthy() +}) + +test("It considers session valid when it can read an access token", async () => { + const sut = new OAuthTokenSessionValidator({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo" } + } + } + }) + const validity = await sut.validateSession() + expect(validity).toBe(SessionValidity.VALID) +}) + +test("It considers access token to be invalid when failing to get access token", async () => { + const sut = new OAuthTokenSessionValidator({ + oauthTokenDataSource: { + async getOAuthToken() { + throw new Error("Mock error") + } + } + }) + const validity = await sut.validateSession() + expect(validity).toBe(SessionValidity.INVALID_ACCESS_TOKEN) +}) diff --git a/__test__/auth/PersistingOAuthTokenDataSource.test.ts b/__test__/auth/PersistingOAuthTokenDataSource.test.ts new file mode 100644 index 00000000..ce32965b --- /dev/null +++ b/__test__/auth/PersistingOAuthTokenDataSource.test.ts @@ -0,0 +1,314 @@ +import { PersistingOAuthTokenDataSource, OAuthToken } from "../../src/features/auth/domain" + +test("It skips obtaining an OAuth token from the data source if one already exists in the repository", async () => { + let didGetOAuthTokenFromDataSource = false + const sut = new PersistingOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProvider() { + return "github" + } + }, + dataSource: { + async getOAuthToken() { + didGetOAuthTokenFromDataSource = true + return { accessToken: "new-github-access-token" } + } + }, + repository: { + async get(_userId) { + return { accessToken: "foo" } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() {} + } + } + } + }) + await sut.getOAuthToken() + expect(didGetOAuthTokenFromDataSource).toBeFalsy() +}) + +test("It skips obtaining an OAuth token from the data source if one has been stored while waiting to acquire the lock", async () => { + let didAcquireLock = false + let didGetOAuthTokenFromDataSource = false + const sut = new PersistingOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProvider() { + return "github" + } + }, + dataSource: { + async getOAuthToken() { + throw new Error("Not found") + } + }, + repository: { + async get(_userId) { + if (didAcquireLock) { + return { accessToken: "new-github-access-token" } + } else { + throw new Error("Not found") + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() { + didAcquireLock = true + }, + async release() {} + } + } + } + }) + await sut.getOAuthToken() + expect(didAcquireLock).toBeTruthy() + expect(didGetOAuthTokenFromDataSource).toBeFalsy() +}) + +test("It does not acquire lock if an OAuth token already exists", async () => { + let didAcquireLock = false + const sut = new PersistingOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProvider() { + return "github" + } + }, + dataSource: { + async getOAuthToken() { + return { accessToken: "new-github-access-token" } + } + }, + repository: { + async get(_userId) { + return { accessToken: "foo" } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() { + didAcquireLock = true + }, + async release() {} + } + } + } + }) + await sut.getOAuthToken() + expect(didAcquireLock).toBeFalsy() +}) + +test("It obtains OAuth token from data source if one does not exist in the repository", async () => { + let didGetOAuthTokenFromDataSource = false + const sut = new PersistingOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProvider() { + return "github" + } + }, + dataSource: { + async getOAuthToken() { + didGetOAuthTokenFromDataSource = true + return { accessToken: "new-github-access-token" } + } + }, + repository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() {} + } + } + } + }) + const oauthToken = await sut.getOAuthToken() + expect(didGetOAuthTokenFromDataSource).toBeTruthy() + expect(oauthToken.accessToken).toBe("new-github-access-token") +}) + +test("It persists the OAuth token obtained from the data source", async () => { + let persistedOAuthToken: OAuthToken | undefined + const sut = new PersistingOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProvider() { + return "github" + } + }, + dataSource: { + async getOAuthToken() { + return { accessToken: "new-github-access-token" } + } + }, + repository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, token) { + persistedOAuthToken = token + }, + async delete(_userId) {} + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() {} + } + } + } + }) + await sut.getOAuthToken() + expect(persistedOAuthToken?.accessToken).toBe("new-github-access-token") +}) + +test("It acquires the lock", async () => { + let didAcquireLock = false + const sut = new PersistingOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProvider() { + return "github" + } + }, + dataSource: { + async getOAuthToken() { + return { accessToken: "new-github-access-token" } + } + }, + repository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() { + didAcquireLock = true + }, + async release() {} + } + } + } + }) + await sut.getOAuthToken() + expect(didAcquireLock).toBeTruthy() +}) + +test("It releases the lock", async () => { + let didAcquireLock = false + const sut = new PersistingOAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + }, + async getEmail() { + return "john@example.com" + }, + async getAccountProvider() { + return "github" + } + }, + dataSource: { + async getOAuthToken() { + return { accessToken: "new-github-access-token" } + } + }, + repository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + mutexFactory: { + async makeMutex() { + return { + async acquire() {}, + async release() { + didAcquireLock = true + } + } + } + } + }) + await sut.getOAuthToken() + expect(didAcquireLock).toBeTruthy() +}) diff --git a/__test__/auth/PersistingOAuthTokenRefresher.test.ts b/__test__/auth/PersistingOAuthTokenRefresher.test.ts new file mode 100644 index 00000000..a2537939 --- /dev/null +++ b/__test__/auth/PersistingOAuthTokenRefresher.test.ts @@ -0,0 +1,175 @@ +import { PersistingOAuthTokenRefresher, OAuthToken } from "../../src/features/auth/domain" + +test("It refreshes OAuth token using provided refresh token", async () => { + let usedRefreshToken: string | undefined + const sut = new PersistingOAuthTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oauthTokenRefresher: { + async refreshOAuthToken(oauthToken) { + usedRefreshToken = oauthToken.refreshToken + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } + } + }, + oauthTokenRepository: { + async get() { + return { + accessToken: "persistedAccessToken", + refreshToken: "persistedRefreshToken" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshOAuthToken({ + accessToken: "persistedAccessToken", + refreshToken: "persistedRefreshToken" + }) + expect(usedRefreshToken).toBe("persistedRefreshToken") +}) + +test("It stores the new OAuth token for the user", async () => { + let storedUserId: string | undefined + let storedOAuthToken: OAuthToken | undefined + const sut = new PersistingOAuthTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oauthTokenRefresher: { + async refreshOAuthToken() { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } + } + }, + oauthTokenRepository: { + async get() { + return { + accessToken: "persistedAccessToken", + refreshToken: "persistedRefreshToken" + } + }, + async set(userId, oAuthToken) { + storedUserId = userId + storedOAuthToken = oAuthToken + }, + async delete() {} + } + }) + await sut.refreshOAuthToken({ + accessToken: "persistedAccessToken", + refreshToken: "persistedRefreshToken" + }) + expect(storedUserId).toBe("1234") + expect(storedOAuthToken?.accessToken).toBe("newAccessToken") + expect(storedOAuthToken?.refreshToken).toBe("newRefreshToken") +}) + +test("It refreshes the OAuth token when the input refresh token is equal to the stored refresh token", async () => { + let didRefreshOAuthToken = false + const sut = new PersistingOAuthTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oauthTokenRefresher: { + async refreshOAuthToken() { + didRefreshOAuthToken = true + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } + } + }, + oauthTokenRepository: { + async get() { + return { + accessToken: "persistedAccessToken", + refreshToken: "persistedRefreshToken" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshOAuthToken({ + accessToken: "persistedAccessToken", + refreshToken: "persistedRefreshToken" + }) + expect(didRefreshOAuthToken).toBeTruthy() +}) + +test("It skips refreshing the OAuth token when the input refresh token is not equal to the stored refresh token", async () => { + let didRefreshAccessToken = false + const sut = new PersistingOAuthTokenRefresher({ + userIdReader: { + async getUserId() { + return "1234" + } + }, + oauthTokenRefresher: { + async refreshOAuthToken() { + didRefreshAccessToken = true + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } + } + }, + oauthTokenRepository: { + async get() { + return { + accessToken: "persistedAccessToken", + refreshToken: "persistedRefreshToken" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) + expect(didRefreshAccessToken).toBeFalsy() +}) + +test("It reads the user ID", async () => { + let didReadUserId = false + const sut = new PersistingOAuthTokenRefresher({ + userIdReader: { + async getUserId() { + didReadUserId = true + return "1234" + } + }, + oauthTokenRefresher: { + async refreshOAuthToken() { + return { + accessToken: "foo", + refreshToken: "bar" + } + } + }, + oauthTokenRepository: { + async get() { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set() {}, + async delete() {} + } + }) + await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) + expect(didReadUserId).toBeTruthy() +}) diff --git a/__test__/auth/RepositoryRestrictingAccessTokenDataSource.test.ts b/__test__/auth/RepositoryRestrictingAccessTokenDataSource.test.ts deleted file mode 100644 index c4a26be7..00000000 --- a/__test__/auth/RepositoryRestrictingAccessTokenDataSource.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { RepositoryRestrictingAccessTokenDataSource } from "../../src/features/auth/domain" - -test("It limits access to the fetched repositories", async () => { - let restrictingRepositoryNames: string[] | undefined - const sut = new RepositoryRestrictingAccessTokenDataSource({ - repositoryAccessReader: { - async getRepositoryNames() { - return ["foo", "bar"] - } - }, - dataSource: { - async getAccessToken(repositoryNames) { - restrictingRepositoryNames = repositoryNames - return "secret" - }, - } - }) - await sut.getAccessToken("1234") - expect(restrictingRepositoryNames).toEqual(["foo", "bar"]) -}) diff --git a/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts b/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts similarity index 66% rename from __test__/common/github/AccessTokenRefreshingGitHubClient.test.ts rename to __test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts index f33b318a..c2fdf710 100644 --- a/__test__/common/github/AccessTokenRefreshingGitHubClient.test.ts +++ b/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts @@ -1,22 +1,25 @@ -import { AccessTokenRefreshingGitHubClient } from "../../../src/common" +import { OAuthTokenRefreshingGitHubClient } from "@/common" import { GraphQLQueryRequest, GetRepositoryContentRequest, GetPullRequestCommentsRequest, AddCommentToPullRequestRequest -} from "../../../src/common/github/IGitHubClient" +} from "@/common/github/IGitHubClient" test("It forwards a GraphQL request", async () => { let forwardedRequest: GraphQLQueryRequest | undefined - const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" + const sut = new OAuthTokenRefreshingGitHubClient({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo", refreshToken: "bar" } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } }, gitHubClient: { @@ -46,15 +49,18 @@ test("It forwards a GraphQL request", async () => { test("It forwards a request to get the repository content", async () => { let forwardedRequest: GetRepositoryContentRequest | undefined - const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" + const sut = new OAuthTokenRefreshingGitHubClient({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo", refreshToken: "bar" } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } }, gitHubClient: { @@ -86,15 +92,18 @@ test("It forwards a request to get the repository content", async () => { test("It forwards a request to get comments to a pull request", async () => { let forwardedRequest: GetPullRequestCommentsRequest | undefined - const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" + const sut = new OAuthTokenRefreshingGitHubClient({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo", refreshToken: "bar" } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } }, gitHubClient: { @@ -126,15 +135,18 @@ test("It forwards a request to get comments to a pull request", async () => { test("It forwards a request to add a comment to a pull request", async () => { let forwardedRequest: AddCommentToPullRequestRequest | undefined - const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" + const sut = new OAuthTokenRefreshingGitHubClient({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo", refreshToken: "bar" } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } }, gitHubClient: { @@ -166,19 +178,22 @@ test("It forwards a request to add a comment to a pull request", async () => { expect(forwardedRequest).toEqual(request) }) -test("It retries with a refreshed access token when receiving HTTP 401", async () => { - let didRefreshAccessToken = false +test("It retries with a refreshed OAuth token when receiving HTTP 401", async () => { + let didRefreshOAuthToken = false let didRespondWithAuthorizationError = false - const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" + const sut = new OAuthTokenRefreshingGitHubClient({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo", refreshToken: "bar" } } }, - accessTokenRefresher: { - async refreshAccessToken() { - didRefreshAccessToken = true - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + didRefreshOAuthToken = true + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } }, gitHubClient: { @@ -206,20 +221,23 @@ test("It retries with a refreshed access token when receiving HTTP 401", async ( variables: { hello: "world" } } await sut.graphql(request) - expect(didRefreshAccessToken).toBeTruthy() + expect(didRefreshOAuthToken).toBeTruthy() }) test("It only retries a request once when receiving HTTP 401", async () => { let requestCount = 0 - const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" + const sut = new OAuthTokenRefreshingGitHubClient({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo", refreshToken: "bar" } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } }, gitHubClient: { @@ -246,22 +264,25 @@ test("It only retries a request once when receiving HTTP 401", async () => { // When receiving the second HTTP 401 the call should fail. await expect(sut.graphql(request)).rejects.toEqual({ status: 401 }) // We expect two requests: - // 1. The initial request that failed after which we refreshed the access token. + // 1. The initial request that failed after which we refreshed the OAuth token. // 2. The second request that failed after which we gave up. expect(requestCount).toEqual(2) }) -test("It does not refresh an access token when the initial request was successful", async () => { - let didRefreshAccesstoken = false - const sut = new AccessTokenRefreshingGitHubClient({ - accessTokenDataSource: { - async getAccessToken() { - return "foo" +test("It does not refresh an OAuth token when the initial request was successful", async () => { + let didRefreshOAuthToken = false + const sut = new OAuthTokenRefreshingGitHubClient({ + oauthTokenDataSource: { + async getOAuthToken() { + return { accessToken: "foo", refreshToken: "bar" } } }, - accessTokenRefresher: { - async refreshAccessToken() { - return "foo" + oauthTokenRefresher: { + async refreshOAuthToken(_refreshToken) { + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } }, gitHubClient: { @@ -285,5 +306,5 @@ test("It does not refresh an access token when the initial request was successfu variables: { hello: "world" } } await sut.graphql(request) - expect(didRefreshAccesstoken).toBeFalsy() + expect(didRefreshOAuthToken).toBeFalsy() }) diff --git a/__test__/common/userData/KeyValueUserDataRepository.test.ts b/__test__/common/userData/KeyValueUserDataRepository.test.ts index 3dc7cb04..09afadf3 100644 --- a/__test__/common/userData/KeyValueUserDataRepository.test.ts +++ b/__test__/common/userData/KeyValueUserDataRepository.test.ts @@ -1,4 +1,4 @@ -import { KeyValueUserDataRepository } from "../../../src/common" +import { KeyValueUserDataRepository } from "@/common" test("It reads the expected key", async () => { let readKey: string | undefined diff --git a/__test__/common/utils/ZodJSONCoder.test.ts b/__test__/common/utils/ZodJSONCoder.test.ts index 6335bbd9..21364cca 100644 --- a/__test__/common/utils/ZodJSONCoder.test.ts +++ b/__test__/common/utils/ZodJSONCoder.test.ts @@ -1,5 +1,5 @@ import { z } from "zod" -import { ZodJSONCoder } from "../../../src/common" +import { ZodJSONCoder } from "@/common" const SampleAuthTokenSchema = z.object({ accessToken: z.string(), diff --git a/jest.config.js b/jest.config.js index 4939f5cb..413c2d93 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,5 +6,8 @@ module.exports = { transform: { "^.+\\.ts$": "ts-jest" }, + moduleNameMapper: { + "^@/(.*)$": "/src/$1" + }, verbose: true } diff --git a/src/app/[[...slug]]/page.tsx b/src/app/[[...slug]]/page.tsx index 2b089449..092f7f40 100644 --- a/src/app/[[...slug]]/page.tsx +++ b/src/app/[[...slug]]/page.tsx @@ -27,7 +27,6 @@ export default async function Page({ params }: { params: PageParams }) { } function getPath(slug: string | string[] | undefined) { - console.log("Slug: " + slug) if (slug === undefined) { return "/" } else if (typeof slug === "string") { diff --git a/src/app/api/user/projects/route.ts b/src/app/api/user/projects/route.ts index 93290bda..fb54ee97 100644 --- a/src/app/api/user/projects/route.ts +++ b/src/app/api/user/projects/route.ts @@ -3,7 +3,7 @@ import { makeAPIErrorResponse, UnauthorizedError, makeUnauthenticatedAPIErrorResponse -} from "../../../../common" +} from "@/common" import { projectDataSource } from "@/composition" import { session } from "@/composition" diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3fde8192..16dd6cc4 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,7 +2,7 @@ import "./globals.css" import type { Metadata } from "next" import { config as fontAwesomeConfig } from "@fortawesome/fontawesome-svg-core" import { CssBaseline } from "@mui/material" -import ThemeRegistry from "../common/theme/ThemeRegistry" +import ThemeRegistry from "@/common/theme/ThemeRegistry" import "@fortawesome/fontawesome-svg-core/styles.css" fontAwesomeConfig.autoAddCss = false diff --git a/src/common/github/GitHubClient.ts b/src/common/github/GitHubClient.ts index e0ced8f2..048fe183 100644 --- a/src/common/github/GitHubClient.ts +++ b/src/common/github/GitHubClient.ts @@ -12,8 +12,8 @@ import IGitHubClient, { PullRequestComment } from "./IGitHubClient" -interface IGitHubAccessTokenDataSource { - getAccessToken(): Promise +interface IGitHubOAuthTokenDataSource { + getOAuthToken(): Promise<{ accessToken: string }> } type GitHubContentItem = {download_url: string} @@ -21,7 +21,7 @@ type GitHubContentItem = {download_url: string} type InstallationAuthenticator = (installationId: number) => Promise<{token: string}> export default class GitHubClient implements IGitHubClient { - private readonly accessTokenDataSource: IGitHubAccessTokenDataSource + private readonly oauthTokenDataSource: IGitHubOAuthTokenDataSource private readonly installationAuthenticator: InstallationAuthenticator constructor( @@ -30,10 +30,10 @@ export default class GitHubClient implements IGitHubClient { clientId: string clientSecret: string privateKey: string - accessTokenDataSource: IGitHubAccessTokenDataSource + oauthTokenDataSource: IGitHubOAuthTokenDataSource } ) { - this.accessTokenDataSource = config.accessTokenDataSource + this.oauthTokenDataSource = config.oauthTokenDataSource const appAuth = createAppAuth({ appId: config.appId, clientId: config.clientId, @@ -46,14 +46,14 @@ export default class GitHubClient implements IGitHubClient { } async graphql(request: GraphQLQueryRequest): Promise { - const accessToken = await this.accessTokenDataSource.getAccessToken() - const octokit = new Octokit({ auth: accessToken }) + const oauthToken = await this.oauthTokenDataSource.getOAuthToken() + const octokit = new Octokit({ auth: oauthToken.accessToken }) return await octokit.graphql(request.query, request.variables) } async getRepositoryContent(request: GetRepositoryContentRequest): Promise { - const accessToken = await this.accessTokenDataSource.getAccessToken() - const octokit = new Octokit({ auth: accessToken }) + const oauthToken = await this.oauthTokenDataSource.getOAuthToken() + const octokit = new Octokit({ auth: oauthToken.accessToken }) const response = await octokit.rest.repos.getContent({ owner: request.repositoryOwner, repo: request.repositoryName, @@ -96,8 +96,8 @@ export default class GitHubClient implements IGitHubClient { async getOrganizationMembershipStatus( request: GetOrganizationMembershipStatusRequest ): Promise { - const accessToken = await this.accessTokenDataSource.getAccessToken() - const octokit = new Octokit({ auth: accessToken }) + const oauthToken = await this.oauthTokenDataSource.getOAuthToken() + const octokit = new Octokit({ auth: oauthToken.accessToken }) const response = await octokit.rest.orgs.getMembershipForAuthenticatedUser({ org: request.organizationName }) diff --git a/src/common/github/AccessTokenRefreshingGitHubClient.ts b/src/common/github/OAuthTokenRefreshingGitHubClient.ts similarity index 70% rename from src/common/github/AccessTokenRefreshingGitHubClient.ts rename to src/common/github/OAuthTokenRefreshingGitHubClient.ts index cb73f4a7..2d4ec2c8 100644 --- a/src/common/github/AccessTokenRefreshingGitHubClient.ts +++ b/src/common/github/OAuthTokenRefreshingGitHubClient.ts @@ -15,28 +15,28 @@ const HttpErrorSchema = z.object({ status: z.number() }) -interface IGitHubAccessTokenDataSource { - getAccessToken(): Promise +type OAuthToken = { accessToken: string, refreshToken?: string } + +interface IGitHubOAuthTokenDataSource { + getOAuthToken(): Promise } -interface IGitHubAccessTokenRefresher { - refreshAccessToken(accessToken: string): Promise +interface IGitHubOAuthTokenRefresher { + refreshOAuthToken(oauthToken: OAuthToken): Promise } -export default class AccessTokenRefreshingGitHubClient implements IGitHubClient { - private readonly accessTokenDataSource: IGitHubAccessTokenDataSource - private readonly accessTokenRefresher: IGitHubAccessTokenRefresher +export default class OAuthTokenRefreshingGitHubClient implements IGitHubClient { + private readonly oauthTokenDataSource: IGitHubOAuthTokenDataSource + private readonly oauthTokenRefresher: IGitHubOAuthTokenRefresher private readonly gitHubClient: IGitHubClient - constructor( - config: { - accessTokenDataSource: IGitHubAccessTokenDataSource - accessTokenRefresher: IGitHubAccessTokenRefresher - gitHubClient: IGitHubClient - } - ) { - this.accessTokenDataSource = config.accessTokenDataSource - this.accessTokenRefresher = config.accessTokenRefresher + constructor(config: { + oauthTokenDataSource: IGitHubOAuthTokenDataSource + oauthTokenRefresher: IGitHubOAuthTokenRefresher + gitHubClient: IGitHubClient + }) { + this.oauthTokenDataSource = config.oauthTokenDataSource + this.oauthTokenRefresher = config.oauthTokenRefresher this.gitHubClient = config.gitHubClient } @@ -73,7 +73,7 @@ export default class AccessTokenRefreshingGitHubClient implements IGitHubClient } private async send(fn: () => Promise): Promise { - const accessToken = await this.accessTokenDataSource.getAccessToken() + const oauthToken = await this.oauthTokenDataSource.getOAuthToken() try { return await fn() } catch (e) { @@ -81,7 +81,7 @@ export default class AccessTokenRefreshingGitHubClient implements IGitHubClient const error = HttpErrorSchema.parse(e) if (error.status == 401) { // Refresh access token and try the request one last time. - await this.accessTokenRefresher.refreshAccessToken(accessToken) + await this.oauthTokenRefresher.refreshOAuthToken(oauthToken) return await fn() } else { // Not an error we can handle so forward it. diff --git a/src/common/github/index.ts b/src/common/github/index.ts index da9c21f3..b5aef3bd 100644 --- a/src/common/github/index.ts +++ b/src/common/github/index.ts @@ -1,3 +1,3 @@ -export { default as AccessTokenRefreshingGitHubClient } from "./AccessTokenRefreshingGitHubClient" +export { default as OAuthTokenRefreshingGitHubClient } from "./OAuthTokenRefreshingGitHubClient" export { default as GitHubClient } from "./GitHubClient" export type { default as IGitHubClient } from "./IGitHubClient" diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index 58f333b6..6136adc6 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -1,11 +1,11 @@ import { NextAuthResult } from "next-auth" -import { IDB, UnauthorizedError } from "../../common" -import ISession, { AccountProviderType } from "./ISession" +import { IDB, UnauthorizedError } from "@/common" +import ISession, { AccountProvider } from "./ISession" export default class AuthjsSession implements ISession { private readonly db: IDB private readonly auth: NextAuthResult - private accountProviderType: AccountProviderType | undefined + private accountProvider: AccountProvider | undefined constructor(config: { db: IDB, auth: NextAuthResult }) { this.db = config.db @@ -36,16 +36,16 @@ export default class AuthjsSession implements ISession { return session.user.email } - async getAccountProviderType(): Promise { - if (this.accountProviderType) { - return this.accountProviderType + async getAccountProvider(): Promise { + if (this.accountProvider) { + return this.accountProvider } - const accountProviderType = await this.readAccountProviderTypeFromDB() - this.accountProviderType = accountProviderType - return accountProviderType + const accountProvider = await this.readAccountProviderFromDB() + this.accountProvider = accountProvider + return accountProvider } - private async readAccountProviderTypeFromDB(): Promise { + private async readAccountProviderFromDB(): Promise { const userId = await this.getUserId() const result = await this.db.query( "SELECT true FROM accounts WHERE \"userId\" = $1 AND provider = $2 LIMIT 1", diff --git a/src/common/session/ISession.ts b/src/common/session/ISession.ts index af223c97..c4578835 100644 --- a/src/common/session/ISession.ts +++ b/src/common/session/ISession.ts @@ -1,8 +1,8 @@ -export type AccountProviderType = "github" | "email" +export type AccountProvider = "github" | "email" export default interface ISession { getIsAuthenticated(): Promise getUserId(): Promise getEmail(): Promise - getAccountProviderType(): Promise + getAccountProvider(): Promise } diff --git a/src/common/session/index.ts b/src/common/session/index.ts index 24ff8f62..ef88e64a 100644 --- a/src/common/session/index.ts +++ b/src/common/session/index.ts @@ -1,2 +1,2 @@ export { default as AuthjsSession } from "./AuthjsSession" -export type { default as ISession, AccountProviderType } from "./ISession" +export type { default as ISession, AccountProvider } from "./ISession" diff --git a/src/composition.ts b/src/composition.ts index dc659364..d285e738 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -6,11 +6,11 @@ import PostgresAdapter from "@auth/pg-adapter" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" import { - AccessTokenRefreshingGitHubClient, AuthjsSession, GitHubClient, ISession, KeyValueUserDataRepository, + OAuthTokenRefreshingGitHubClient, PostgreSQLDB, SessionMutexFactory } from "@/common" @@ -23,23 +23,23 @@ import { } from "@/features/projects/domain" import { GitHubOAuthTokenRefresher, - AuthjsOAuthTokenDataSource, GitHubInstallationAccessTokenDataSource } from "@/features/auth/data" import { - AccessTokenDataSource, - AccessTokenRefresher, - AccessTokenTransferrer, - AccessTokenSessionValidator, + AccountProviderTypeBasedOAuthTokenRefresher, + AuthjsAccountsOAuthTokenRepository, CompositeLogOutHandler, + CompositeOAuthTokenRepository, ErrorIgnoringLogOutHandler, GitHubOrganizationSessionValidator, - GitHubAccessTokenTransferrer, - GuestRepositoryAccessDataSource, - GuestAccessTokenTransferrer, - LockingAccessTokenRefresher, - NullObjectLogInHandler, + GuestOAuthTokenDataSource, + GuestOAuthTokenRefresher, + LockingOAuthTokenRefresher, + LogInHandler, + PersistingOAuthTokenDataSource, + PersistingOAuthTokenRefresher, OAuthTokenRepository, + OAuthTokenSessionValidator, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" import { @@ -88,9 +88,17 @@ const pool = new Pool({ const db = new PostgreSQLDB({ pool }) -const logInHandler = new NullObjectLogInHandler() +const oauthTokenRepository = new CompositeOAuthTokenRepository({ + oAuthTokenRepositories: [ + new OAuthTokenRepository({ db, provider: "github" }), + new AuthjsAccountsOAuthTokenRepository({ db, provider: "github" }) + ] +}) + +const logInHandler = new LogInHandler({ oauthTokenRepository }) -const fromEmail = FROM_EMAIL || "Shape Docs " // must be a verified email in AWS SES +// Must be a verified email in AWS SES. +const fromEmail = FROM_EMAIL || "Shape Docs " export const auth = NextAuth({ adapter: PostgresAdapter(pool), @@ -153,72 +161,65 @@ export const session: ISession = new AuthjsSession({ db, auth }) export const guestRepository: IGuestRepository = new DbGuestRepository(pool) -const oauthTokenRepository = new OAuthTokenRepository({ db, provider: "github" }) +const guestOAuthTokenDataSource = new GuestOAuthTokenDataSource({ + session, + guestRepository, + gitHubInstallationAccessTokenDataSource: new GitHubInstallationAccessTokenDataSource( + gitHubAppCredentials + ) +}) -const githubAccessTokenDataSource = new AccessTokenDataSource({ +const oauthTokenDataSource = new PersistingOAuthTokenDataSource({ session, - oauthTokenDataSource: oauthTokenRepository, - accessTokenTransferrer: new AccessTokenTransferrer({ - session, - gitHubAccessTokenTransferrer: new GitHubAccessTokenTransferrer({ - session, - sourceOAuthTokenDataSource: new AuthjsOAuthTokenDataSource({ db, provider: "github" }), - destinationOAuthTokenRepository: oauthTokenRepository - }), - guestAccessTokenTransferrer: new GuestAccessTokenTransferrer({ - session, - guestRepository, - installationAccessTokenDataSource: new GitHubInstallationAccessTokenDataSource( - gitHubAppCredentials - ), - destinationOAuthTokenRepository: oauthTokenRepository - }) - }), mutexFactory: new SessionMutexFactory({ - baseKey: "mutexTransferAccessToken", + baseKey: "mutexPersistingOAuthTokenDataSource", mutexFactory: new RedisKeyedMutexFactory(REDIS_URL), userIdReader: session - }) + }), + repository: oauthTokenRepository, + dataSource: guestOAuthTokenDataSource }) -export const repositoryAccessReader = new GuestRepositoryAccessDataSource({ - db, - guestRepository +const oauthTokenRefresher = new LockingOAuthTokenRefresher({ + mutexFactory: new SessionMutexFactory({ + baseKey: "mutexLockingOAuthTokenRefresher", + mutexFactory: new RedisKeyedMutexFactory(REDIS_URL), + userIdReader: session + }), + oauthTokenRefresher: new PersistingOAuthTokenRefresher({ + userIdReader: session, + oauthTokenRepository, + oauthTokenRefresher: new AccountProviderTypeBasedOAuthTokenRefresher({ + accountProviderReader: session, + strategy: { + github: new GitHubOAuthTokenRefresher(gitHubAppCredentials), + email: new GuestOAuthTokenRefresher({ + dataSource: guestOAuthTokenDataSource + }) + } + }) + }) }) export const gitHubClient = new GitHubClient({ ...gitHubAppCredentials, - accessTokenDataSource: githubAccessTokenDataSource + oauthTokenDataSource }) -export const userGitHubClient = new AccessTokenRefreshingGitHubClient({ +export const userGitHubClient = new OAuthTokenRefreshingGitHubClient({ gitHubClient, - accessTokenDataSource: githubAccessTokenDataSource, - accessTokenRefresher: new LockingAccessTokenRefresher({ - mutexFactory: new SessionMutexFactory({ - baseKey: "mutexAccessToken", - mutexFactory: new RedisKeyedMutexFactory(REDIS_URL), - userIdReader: session - }), - accessTokenRefresher: new AccessTokenRefresher({ - userIdReader: session, - oAuthTokenRepository: oauthTokenRepository, - oAuthTokenRefresher: new GitHubOAuthTokenRefresher({ - clientId: gitHubAppCredentials.clientId, - clientSecret: gitHubAppCredentials.clientSecret - }) - }) - }) + oauthTokenDataSource, + oauthTokenRefresher, }) -export const blockingSessionValidator = new AccessTokenSessionValidator({ - accessTokenDataSource: githubAccessTokenDataSource +export const blockingSessionValidator = new OAuthTokenSessionValidator({ + oauthTokenDataSource }) export const delayedSessionValidator = new GitHubOrganizationSessionValidator({ acceptedOrganization: GITHUB_ORGANIZATION_NAME, organizationMembershipStatusReader: userGitHubClient, - accountProviderTypeReader: session + accountProviderReader: session }) const projectUserDataRepository = new KeyValueUserDataRepository( @@ -251,5 +252,5 @@ export const guestInviter: IGuestInviter = new EmailGuestInviter({ user: SMTP_USER, pass: SMTP_PASS, }, - from: fromEmail, + from: fromEmail }) diff --git a/src/features/auth/data/AuthjsOAuthTokenDataSource.ts b/src/features/auth/data/AuthjsOAuthTokenDataSource.ts deleted file mode 100644 index 4b29e824..00000000 --- a/src/features/auth/data/AuthjsOAuthTokenDataSource.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { IDB, UnauthorizedError } from "../../../common" -import { OAuthToken, IOAuthTokenDataSource } from "../domain" - -export default class AuthjsOAuthTokenDataSource implements IOAuthTokenDataSource { - private readonly db: IDB - private readonly provider: string - - constructor(config: { db: IDB, provider: string }) { - this.db = config.db - this.provider = config.provider - } - - async get(userId: string): Promise { - const result = await this.db.query( - "SELECT access_token, refresh_token FROM accounts WHERE \"userId\" = $1 AND provider = $2", - [userId, this.provider] - ) - if (result.rows.length == 0) { - throw new UnauthorizedError("The access token was not found. It appears that the user is not authenticated.") - } - const accessToken = result.rows[0].access_token - const refreshToken = result.rows[0].refresh_token - if (!accessToken || !refreshToken) { - throw new UnauthorizedError("") - } - return { accessToken, refreshToken } - } -} diff --git a/src/features/auth/data/GitHubOAuthTokenRefresher.ts b/src/features/auth/data/GitHubOAuthTokenRefresher.ts index 6ef11c5e..32a7276e 100644 --- a/src/features/auth/data/GitHubOAuthTokenRefresher.ts +++ b/src/features/auth/data/GitHubOAuthTokenRefresher.ts @@ -1,4 +1,4 @@ -import { UnauthorizedError } from "../../../common" +import { UnauthorizedError } from "@/common" import { OAuthToken, IOAuthTokenRefresher } from "../domain" export interface GitHubOAuthTokenRefresherConfig { @@ -13,8 +13,11 @@ export default class GitHubOAuthTokenRefresher implements IOAuthTokenRefresher { this.config = config } - async refreshOAuthToken(oldRefreshToken: string): Promise { - const url = this.getAccessTokenURL(oldRefreshToken) + async refreshOAuthToken(oldOAuthToken: OAuthToken): Promise { + if (!oldOAuthToken.refreshToken) { + throw new Error("Cannot refresh OAuth token as it has no refresh token") + } + const url = this.getAccessTokenURL(oldOAuthToken.refreshToken) const response = await fetch(url, { method: "POST" }) if (response.status != 200) { throw new UnauthorizedError( diff --git a/src/features/auth/data/index.ts b/src/features/auth/data/index.ts index 46ed8515..f8808118 100644 --- a/src/features/auth/data/index.ts +++ b/src/features/auth/data/index.ts @@ -1,3 +1,2 @@ -export { default as AuthjsOAuthTokenDataSource } from "./AuthjsOAuthTokenDataSource" export { default as GitHubInstallationAccessTokenDataSource } from "./GitHubInstallationAccessTokenDataSource" export { default as GitHubOAuthTokenRefresher } from "./GitHubOAuthTokenRefresher" diff --git a/src/features/auth/domain/accessToken/AccessTokenDataSource.ts b/src/features/auth/domain/accessToken/AccessTokenDataSource.ts deleted file mode 100644 index 1bd8b908..00000000 --- a/src/features/auth/domain/accessToken/AccessTokenDataSource.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { ISession } from "@/common" -import { OAuthToken } from ".." -import IAccessTokenTransferrer from "./IAccessTokenTransferrer" -import { IMutexFactory } from "@/common" -import withMutex from "../../../../common/mutex/withMutex" - -interface IOAuthTokenDataSource { - get(userId: string): Promise -} - -export default class AccessTokenDataSource { - private readonly session: ISession - private readonly oauthTokenDataSource: IOAuthTokenDataSource - private readonly accessTokenTransferrer: IAccessTokenTransferrer - private readonly mutexFactory: IMutexFactory - - constructor( - config: { - session: ISession, - oauthTokenDataSource: IOAuthTokenDataSource, - accessTokenTransferrer: IAccessTokenTransferrer, - mutexFactory: IMutexFactory - } - ) { - this.session = config.session - this.oauthTokenDataSource = config.oauthTokenDataSource - this.accessTokenTransferrer = config.accessTokenTransferrer - this.mutexFactory = config.mutexFactory - } - - async getAccessToken(): Promise { - // Immediately try to get the OAuth token to avoid a mutex. - const existingOAuthToken = await this.getExistingOAuthToken() - if (existingOAuthToken) { - return existingOAuthToken.accessToken - } - const mutex = await this.mutexFactory.makeMutex() - return await withMutex(mutex, async () => { - // Make a second attempt to get the OAuth token as it might have been transferred in the mean time. - const existingOAuthToken = await this.getExistingOAuthToken() - if (existingOAuthToken) { - return existingOAuthToken.accessToken - } else { - return await this.accessTokenTransferrer.transferAccessToken() - } - }) - } - - private async getExistingOAuthToken(): Promise { - const userId = await this.session.getUserId() - try { - return await this.oauthTokenDataSource.get(userId) - } catch {} - return null - } -} diff --git a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts b/src/features/auth/domain/accessToken/AccessTokenRefresher.ts deleted file mode 100644 index 5773f034..00000000 --- a/src/features/auth/domain/accessToken/AccessTokenRefresher.ts +++ /dev/null @@ -1,41 +0,0 @@ -import IOAuthTokenRepository from "../oAuthToken/IOAuthTokenRepository" -import IOAuthTokenRefresher from "../oAuthToken/IOAuthTokenRefresher" - -interface IUserIDReader { - getUserId(): Promise -} - -export default class AccessTokenRefresher { - private readonly userIdReader: IUserIDReader - private readonly oAuthTokenRepository: IOAuthTokenRepository - private readonly oAuthTokenRefresher: IOAuthTokenRefresher - - constructor( - config: { - userIdReader: IUserIDReader - oAuthTokenRepository: IOAuthTokenRepository - oAuthTokenRefresher: IOAuthTokenRefresher - } - ) { - this.userIdReader = config.userIdReader - this.oAuthTokenRepository = config.oAuthTokenRepository - this.oAuthTokenRefresher = config.oAuthTokenRefresher - } - - async refreshAccessToken(accessToken: string): Promise { - const userId = await this.userIdReader.getUserId() - const oAuthToken = await this.oAuthTokenRepository.get(userId) - if (accessToken != oAuthToken.accessToken) { - // Given access token is outdated so we use our stored access token. - return oAuthToken.accessToken - } - // Given access token is stale so we refresh it. - const refreshToken = oAuthToken.refreshToken - if (!refreshToken) { - throw new Error("OAuth token does not have a refresh token") - } - const newOAuthToken = await this.oAuthTokenRefresher.refreshOAuthToken(refreshToken) - await this.oAuthTokenRepository.set(userId, newOAuthToken) - return newOAuthToken.accessToken - } -} \ No newline at end of file diff --git a/src/features/auth/domain/accessToken/AccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/AccessTokenTransferrer.ts deleted file mode 100644 index e05cf9f3..00000000 --- a/src/features/auth/domain/accessToken/AccessTokenTransferrer.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { ISession } from "@/common" -import IAccessTokenTransferrer from "./IAccessTokenTransferrer" - -export default class AccessTokenTransferrer implements IAccessTokenTransferrer { - private readonly session: ISession - private readonly gitHubAccessTokenTransferrer: IAccessTokenTransferrer - private readonly guestAccessTokenTransferrer: IAccessTokenTransferrer - - constructor( - config: { - session: ISession, - gitHubAccessTokenTransferrer: IAccessTokenTransferrer, - guestAccessTokenTransferrer: IAccessTokenTransferrer - } - ) { - this.session = config.session - this.gitHubAccessTokenTransferrer = config.gitHubAccessTokenTransferrer - this.guestAccessTokenTransferrer = config.guestAccessTokenTransferrer - } - - async transferAccessToken(): Promise { - const accountProviderType = await this.session.getAccountProviderType() - switch (accountProviderType) { - case "github": - return await this.gitHubAccessTokenTransferrer.transferAccessToken() - case "email": - return await this.guestAccessTokenTransferrer.transferAccessToken() - default: - throw new Error(`Unsupported account provider type "${accountProviderType}"`) - } - } -} diff --git a/src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts deleted file mode 100644 index 06d90601..00000000 --- a/src/features/auth/domain/accessToken/GitHubAccessTokenTransferrer.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ISession } from "@/common" -import { IOAuthTokenRepository, IOAuthTokenDataSource } from ".." - -export default class GitHubAccessTokenTransferrer { - private readonly session: ISession - private readonly sourceOAuthTokenDataSource: IOAuthTokenDataSource - private readonly destinationOAuthTokenRepository: IOAuthTokenRepository - - constructor( - config: { - session: ISession, - sourceOAuthTokenDataSource: IOAuthTokenDataSource, - destinationOAuthTokenRepository: IOAuthTokenRepository - } - ) { - this.session = config.session - this.sourceOAuthTokenDataSource = config.sourceOAuthTokenDataSource - this.destinationOAuthTokenRepository = config.destinationOAuthTokenRepository - } - - async transferAccessToken(): Promise { - const userId = await this.session.getUserId() - const oAuthToken = await this.sourceOAuthTokenDataSource.get(userId) - await this.destinationOAuthTokenRepository.set(userId, oAuthToken) - return oAuthToken.accessToken - } -} diff --git a/src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts deleted file mode 100644 index baea032b..00000000 --- a/src/features/auth/domain/accessToken/GuestAccessTokenTransferrer.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ISession } from "@/common" -import { OAuthToken, IOAuthTokenRepository } from ".." - -interface IGuest { - readonly projects: string[] -} - -interface IGitHubInstallationAccessTokenDataSource { - getAccessToken(projects: string[]): Promise -} - -interface IGuestRepository { - findByEmail(email: string): Promise -} - -export default class GuestAccessTokenTransferrer { - private readonly session: ISession - private readonly guestRepository: IGuestRepository - private readonly installationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource - private readonly destinationOAuthTokenRepository: IOAuthTokenRepository - - constructor( - config: { - session: ISession, - guestRepository: IGuestRepository, - installationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource, - destinationOAuthTokenRepository: IOAuthTokenRepository - } - ) { - this.session = config.session - this.guestRepository = config.guestRepository - this.installationAccessTokenDataSource = config.installationAccessTokenDataSource - this.destinationOAuthTokenRepository = config.destinationOAuthTokenRepository - } - - async transferAccessToken(): Promise { - const userId = await this.session.getUserId() - const email = await this.session.getEmail() - const guest = await this.guestRepository.findByEmail(email) - if (!guest) { - throw new Error("The user was not found") - } - const accessToken = await this.installationAccessTokenDataSource.getAccessToken(guest.projects) - const oauthToken: OAuthToken = { accessToken } - await this.destinationOAuthTokenRepository.set(userId, oauthToken) - return accessToken - } -} diff --git a/src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts b/src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts deleted file mode 100644 index 4fac737d..00000000 --- a/src/features/auth/domain/accessToken/GuestRepositoryAccessDataSource.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { IDB } from "@/common" - -interface IGuestRepository { - getProjectsForEmail(email: string): Promise -} - -export default class GuestRepositoryAccessDataSource { - private readonly db: IDB - private readonly guestRepository: IGuestRepository - - constructor(config: { db: IDB, guestRepository: IGuestRepository }) { - this.db = config.db - this.guestRepository = config.guestRepository - } - - async getRepositoryNames(userId: string): Promise { - const query = `SELECT email FROM users where id = $1` - const results = await this.db.query(query, [userId]) - if (results.rows.length == 0) { - throw new Error("User not found") - } - const row = results.rows[0] - if (!row.email || row.email.length == 0) { - throw new Error("User does not have an e-mail") - } - return await this.guestRepository.getProjectsForEmail(row.email) - } -} diff --git a/src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts b/src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts deleted file mode 100644 index 9521409b..00000000 --- a/src/features/auth/domain/accessToken/IAccessTokenTransferrer.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IAccessTokenTransferrer { - transferAccessToken(): Promise -} diff --git a/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts b/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts deleted file mode 100644 index 6d92267e..00000000 --- a/src/features/auth/domain/accessToken/LockingAccessTokenRefresher.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { IMutexFactory } from "@/common" -import withMutex from "../../../../common/mutex/withMutex" - -interface IAccessTokenRefresher { - refreshAccessToken(accessToken: string): Promise -} - -export default class LockingAccessTokenRefresher { - private readonly mutexFactory: IMutexFactory - private readonly accessTokenRefresher: IAccessTokenRefresher - - constructor( - config: { - mutexFactory: IMutexFactory - accessTokenRefresher: IAccessTokenRefresher - } - ) { - this.mutexFactory = config.mutexFactory - this.accessTokenRefresher = config.accessTokenRefresher - } - - async refreshAccessToken(accessToken: string): Promise { - const mutex = await this.mutexFactory.makeMutex() - return await withMutex(mutex, async () => { - return await this.accessTokenRefresher.refreshAccessToken(accessToken) - }) - } -} diff --git a/src/features/auth/domain/accessToken/RepositoryRestrictingAccessTokenDataSource.ts b/src/features/auth/domain/accessToken/RepositoryRestrictingAccessTokenDataSource.ts deleted file mode 100644 index c4f055b9..00000000 --- a/src/features/auth/domain/accessToken/RepositoryRestrictingAccessTokenDataSource.ts +++ /dev/null @@ -1,27 +0,0 @@ -interface IRepositoryAccessReader { - getRepositoryNames(userId: string): Promise -} - -interface IAccessTokenDataSource { - getAccessToken(repositoryNames: string[]): Promise -} - -export default class RepositoryRestrictingAccessTokenDataSource { - private readonly repositoryAccessReader: IRepositoryAccessReader - private readonly dataSource: IAccessTokenDataSource - - constructor( - config: { - repositoryAccessReader: IRepositoryAccessReader - dataSource: IAccessTokenDataSource - } - ) { - this.repositoryAccessReader = config.repositoryAccessReader - this.dataSource = config.dataSource - } - - async getAccessToken(userId: string): Promise { - const repositoryNames = await this.repositoryAccessReader.getRepositoryNames(userId) - return await this.dataSource.getAccessToken(repositoryNames) - } -} diff --git a/src/features/auth/domain/accessToken/index.ts b/src/features/auth/domain/accessToken/index.ts deleted file mode 100644 index 9748d093..00000000 --- a/src/features/auth/domain/accessToken/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { default as AccessTokenRefresher } from "./AccessTokenRefresher" -export { default as AccessTokenTransferrer } from "./AccessTokenTransferrer" -export { default as GitHubAccessTokenTransferrer } from "./GitHubAccessTokenTransferrer" -export { default as GuestAccessTokenTransferrer } from "./GuestAccessTokenTransferrer" -export { default as LockingAccessTokenRefresher } from "./LockingAccessTokenRefresher" -export { default as RepositoryRestrictingAccessTokenDataSource } from "./RepositoryRestrictingAccessTokenDataSource" -export { default as AccessTokenDataSource } from "./AccessTokenDataSource" -export { default as GuestRepositoryAccessDataSource } from "./GuestRepositoryAccessDataSource" diff --git a/src/features/auth/domain/index.ts b/src/features/auth/domain/index.ts index 742ac319..bc75946a 100644 --- a/src/features/auth/domain/index.ts +++ b/src/features/auth/domain/index.ts @@ -1,4 +1,3 @@ -export * from "./accessToken" export * from "./logIn" export * from "./logOut" export * from "./oAuthToken" diff --git a/src/features/auth/domain/logIn/ILogInHandler.ts b/src/features/auth/domain/logIn/ILogInHandler.ts index c39af875..d2451d31 100644 --- a/src/features/auth/domain/logIn/ILogInHandler.ts +++ b/src/features/auth/domain/logIn/ILogInHandler.ts @@ -1,8 +1,8 @@ export interface IAccount { - provider: string - providerAccountId: string - access_token?: string - refresh_token?: string + readonly provider: string + readonly providerAccountId: string + readonly access_token?: string + readonly refresh_token?: string } export default interface ILogInHandler { diff --git a/src/features/auth/domain/logIn/LogInHandler.ts b/src/features/auth/domain/logIn/LogInHandler.ts new file mode 100644 index 00000000..d494166e --- /dev/null +++ b/src/features/auth/domain/logIn/LogInHandler.ts @@ -0,0 +1,42 @@ +import { ILogInHandler, IAccount } from "." +import { IOAuthTokenRepository } from ".." + +export default class LogInHandler implements ILogInHandler { + private readonly oauthTokenRepository: IOAuthTokenRepository + + constructor(config: { oauthTokenRepository: IOAuthTokenRepository }) { + this.oauthTokenRepository = config.oauthTokenRepository + } + + async handleLogIn(userId: string, account?: IAccount): Promise { + if (!account) { + return false + } + if (account.provider === "github") { + return await this.handleLogInForGitHubUser(userId, account) + } else if (account.provider === "nodemailer") { + return true + } else { + console.error("Unhandled account provider: " + account.provider) + return false + } + } + + private async handleLogInForGitHubUser(userId: string, account: IAccount): Promise { + if (!account.access_token) { + return false + } + if (!account.refresh_token) { + return false + } + try { + await this.oauthTokenRepository.set(userId, { + accessToken: account.access_token, + refreshToken: account.refresh_token + }) + return true + } catch { + return false + } + } +} diff --git a/src/features/auth/domain/logIn/NullObjectLogInHandler.ts b/src/features/auth/domain/logIn/NullObjectLogInHandler.ts deleted file mode 100644 index 1051df99..00000000 --- a/src/features/auth/domain/logIn/NullObjectLogInHandler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ILogInHandler, IAccount } from "." - -export default class NullObjectLogInHandler implements ILogInHandler { - constructor() {} - - async handleLogIn(_userId: string, _account?: IAccount): Promise { - return true - } -} diff --git a/src/features/auth/domain/logIn/index.ts b/src/features/auth/domain/logIn/index.ts index de977d4e..732dcdd6 100644 --- a/src/features/auth/domain/logIn/index.ts +++ b/src/features/auth/domain/logIn/index.ts @@ -1,2 +1,2 @@ export type { default as ILogInHandler, IAccount } from "./ILogInHandler" -export { default as NullObjectLogInHandler } from "./NullObjectLogInHandler" +export { default as LogInHandler } from "./LogInHandler" diff --git a/src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts b/src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts deleted file mode 100644 index 0a57b7e2..00000000 --- a/src/features/auth/domain/oAuthToken/IOAuthTokenDataSource.ts +++ /dev/null @@ -1,5 +0,0 @@ -import OAuthToken from "./OAuthToken" - -export default interface IOAuthTokenDataSource { - get(userId: string): Promise -} diff --git a/src/features/auth/domain/oAuthToken/IOAuthTokenRefresher.ts b/src/features/auth/domain/oAuthToken/IOAuthTokenRefresher.ts deleted file mode 100644 index bd1b371a..00000000 --- a/src/features/auth/domain/oAuthToken/IOAuthTokenRefresher.ts +++ /dev/null @@ -1,5 +0,0 @@ -import OAuthToken from "./OAuthToken" - -export default interface IOAuthTokenRefresher { - refreshOAuthToken(refreshToken: string): Promise -} diff --git a/src/features/auth/domain/oAuthToken/data-source/GuestOAuthTokenDataSource.ts b/src/features/auth/domain/oAuthToken/data-source/GuestOAuthTokenDataSource.ts new file mode 100644 index 00000000..b6b77262 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/data-source/GuestOAuthTokenDataSource.ts @@ -0,0 +1,35 @@ +import { ISession } from "@/common" +import { IGuestRepository } from "@/features/admin/domain" +import { OAuthToken } from ".." +import IOAuthTokenDataSource from "./IOAuthTokenDataSource" + +interface IGitHubInstallationAccessTokenDataSource { + getAccessToken(repositoryNames: string[]): Promise +} + +export default class GuestOAuthTokenDataSource implements IOAuthTokenDataSource { + private readonly session: ISession + private readonly guestRepository: IGuestRepository + private readonly gitHubInstallationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource + + constructor(config: { + session: ISession, + guestRepository: IGuestRepository, + gitHubInstallationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource + }) { + this.session = config.session + this.guestRepository = config.guestRepository + this.gitHubInstallationAccessTokenDataSource = config.gitHubInstallationAccessTokenDataSource + } + + async getOAuthToken(): Promise { + const accountProvider = await this.session.getAccountProvider() + if (accountProvider !== "email") { + throw new Error(`Cannot get access token for accounts with the "${accountProvider}" provider.`) + } + const email = await this.session.getEmail() + const repositoryNames = await this.guestRepository.getProjectsForEmail(email) + const accessToken = await this.gitHubInstallationAccessTokenDataSource.getAccessToken(repositoryNames) + return { accessToken } + } +} diff --git a/src/features/auth/domain/oAuthToken/data-source/IOAuthTokenDataSource.ts b/src/features/auth/domain/oAuthToken/data-source/IOAuthTokenDataSource.ts new file mode 100644 index 00000000..0d3cc916 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/data-source/IOAuthTokenDataSource.ts @@ -0,0 +1,5 @@ +import { OAuthToken } from ".." + +export default interface IOAuthTokenDataSource { + getOAuthToken(): Promise +} diff --git a/src/features/auth/domain/oAuthToken/data-source/PersistingOAuthTokenDataSource.ts b/src/features/auth/domain/oAuthToken/data-source/PersistingOAuthTokenDataSource.ts new file mode 100644 index 00000000..36b3d878 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/data-source/PersistingOAuthTokenDataSource.ts @@ -0,0 +1,55 @@ +import { ISession, IMutexFactory, withMutex } from "@/common" +import { OAuthToken, IOAuthTokenRepository } from ".." +import IOAuthTokenDataSource from "./IOAuthTokenDataSource" + +export default class PersistingOAuthTokenDataSource implements IOAuthTokenDataSource { + private readonly session: ISession + private readonly mutexFactory: IMutexFactory + private readonly repository: IOAuthTokenRepository + private readonly dataSource: IOAuthTokenDataSource + + constructor(config: { + session: ISession, + mutexFactory: IMutexFactory, + repository: IOAuthTokenRepository, + dataSource: IOAuthTokenDataSource + }) { + this.session = config.session + this.mutexFactory = config.mutexFactory + this.repository = config.repository + this.dataSource = config.dataSource + } + + async getOAuthToken(): Promise { + // Immediately try to get the OAuth token to avoid a mutex. + const existingOAuthToken = await this.getExistingOAuthToken() + if (existingOAuthToken) { + return existingOAuthToken + } + const mutex = await this.mutexFactory.makeMutex() + return await withMutex(mutex, async () => { + // Make a second attempt to get the OAuth token as it might have been created in the mean time. + const existingOAuthToken = await this.getExistingOAuthToken() + if (existingOAuthToken) { + return existingOAuthToken + } else { + return await this.getNewOAuthToken() + } + }) + } + + private async getExistingOAuthToken(): Promise { + const userId = await this.session.getUserId() + try { + return await this.repository.get(userId) + } catch {} + return null + } + + private async getNewOAuthToken(): Promise { + const userId = await this.session.getUserId() + const oauthToken = await this.dataSource.getOAuthToken() + await this.repository.set(userId, oauthToken) + return oauthToken + } +} diff --git a/src/features/auth/domain/oAuthToken/index.ts b/src/features/auth/domain/oAuthToken/index.ts index 79997eef..affca436 100644 --- a/src/features/auth/domain/oAuthToken/index.ts +++ b/src/features/auth/domain/oAuthToken/index.ts @@ -1,5 +1,13 @@ -export type { default as IOAuthTokenRefresher } from "./IOAuthTokenRefresher" -export type { default as IOAuthTokenRepository } from "./IOAuthTokenRepository" export type { default as OAuthToken } from "./OAuthToken" -export { default as OAuthTokenRepository } from "./OAuthTokenRepository" -export type { default as IOAuthTokenDataSource } from "./IOAuthTokenDataSource" +export { default as GuestOAuthTokenDataSource } from "./data-source/GuestOAuthTokenDataSource" +export type { default as IOAuthTokenDataSource } from "./data-source/IOAuthTokenDataSource" +export { default as PersistingOAuthTokenDataSource } from "./data-source/PersistingOAuthTokenDataSource" +export { default as AccountProviderTypeBasedOAuthTokenRefresher } from "./refresher/AccountProviderTypeBasedOAuthTokenRefresher" +export { default as GuestOAuthTokenRefresher } from "./refresher/GuestOAuthTokenRefresher" +export type { default as IOAuthTokenRefresher } from "./refresher/IOAuthTokenRefresher" +export { default as LockingOAuthTokenRefresher } from "./refresher/LockingOAuthTokenRefresher" +export { default as PersistingOAuthTokenRefresher } from "./refresher/PersistingOAuthTokenRefresher" +export { default as AuthjsAccountsOAuthTokenRepository } from "./repository/AuthjsAccountsOAuthTokenRepository" +export { default as CompositeOAuthTokenRepository } from "./repository/CompositeOAuthTokenRepository" +export type { default as IOAuthTokenRepository } from "./repository/IOAuthTokenRepository" +export { default as OAuthTokenRepository } from "./repository/OAuthTokenRepository" diff --git a/src/features/auth/domain/oAuthToken/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts b/src/features/auth/domain/oAuthToken/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts new file mode 100644 index 00000000..b3ee5b75 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts @@ -0,0 +1,27 @@ +import { AccountProvider } from "@/common" +import { OAuthToken } from ".." +import IOAuthTokenRefresher from "./IOAuthTokenRefresher" + +interface IAccountProviderReader { + getAccountProvider(): Promise +} + +type Strategy = { [key in AccountProvider]: IOAuthTokenRefresher } + +export default class AccountProviderStrategyOAuthTokenRefresher implements IOAuthTokenRefresher { + private readonly accountProviderReader: IAccountProviderReader + private readonly strategy: Strategy + + constructor(config: { + accountProviderReader: IAccountProviderReader, + strategy: Strategy + }) { + this.accountProviderReader = config.accountProviderReader + this.strategy = config.strategy + } + + async refreshOAuthToken(oldOAuthToken: OAuthToken): Promise { + const accountProvider = await this.accountProviderReader.getAccountProvider() + return await this.strategy[accountProvider].refreshOAuthToken(oldOAuthToken) + } +} diff --git a/src/features/auth/domain/oAuthToken/refresher/GuestOAuthTokenRefresher.ts b/src/features/auth/domain/oAuthToken/refresher/GuestOAuthTokenRefresher.ts new file mode 100644 index 00000000..e3bed8bb --- /dev/null +++ b/src/features/auth/domain/oAuthToken/refresher/GuestOAuthTokenRefresher.ts @@ -0,0 +1,17 @@ +import { OAuthToken, IOAuthTokenDataSource } from ".." +import IOAuthTokenRefresher from "./IOAuthTokenRefresher" + +export default class GuestOAuthTokenRefresher implements IOAuthTokenRefresher { + private readonly dataSource: IOAuthTokenDataSource + + constructor(config: { dataSource: IOAuthTokenDataSource }) { + this.dataSource = config.dataSource + } + + async refreshOAuthToken(oldOAuthToken: OAuthToken): Promise { + if (oldOAuthToken.refreshToken) { + throw new Error("The refresher should not be used when a refresh token is available") + } + return await this.dataSource.getOAuthToken() + } +} diff --git a/src/features/auth/domain/oAuthToken/refresher/IOAuthTokenRefresher.ts b/src/features/auth/domain/oAuthToken/refresher/IOAuthTokenRefresher.ts new file mode 100644 index 00000000..b37b79cd --- /dev/null +++ b/src/features/auth/domain/oAuthToken/refresher/IOAuthTokenRefresher.ts @@ -0,0 +1,5 @@ +import { OAuthToken } from ".." + +export default interface IOAuthTokenRefresher { + refreshOAuthToken(oauthToken: OAuthToken): Promise +} diff --git a/src/features/auth/domain/oAuthToken/refresher/LockingOAuthTokenRefresher.ts b/src/features/auth/domain/oAuthToken/refresher/LockingOAuthTokenRefresher.ts new file mode 100644 index 00000000..1e691fab --- /dev/null +++ b/src/features/auth/domain/oAuthToken/refresher/LockingOAuthTokenRefresher.ts @@ -0,0 +1,24 @@ +import { IMutexFactory, withMutex } from "@/common" +import { IOAuthTokenRefresher, OAuthToken } from ".." + +export default class LockingOAuthTokenRefresher implements IOAuthTokenRefresher { + private readonly mutexFactory: IMutexFactory + private readonly oauthTokenRefresher: IOAuthTokenRefresher + + constructor( + config: { + mutexFactory: IMutexFactory + oauthTokenRefresher: IOAuthTokenRefresher + } + ) { + this.mutexFactory = config.mutexFactory + this.oauthTokenRefresher = config.oauthTokenRefresher + } + + async refreshOAuthToken(oauthToken: OAuthToken): Promise { + const mutex = await this.mutexFactory.makeMutex() + return await withMutex(mutex, async () => { + return await this.oauthTokenRefresher.refreshOAuthToken(oauthToken) + }) + } +} diff --git a/src/features/auth/domain/oAuthToken/refresher/PersistingOAuthTokenRefresher.ts b/src/features/auth/domain/oAuthToken/refresher/PersistingOAuthTokenRefresher.ts new file mode 100644 index 00000000..571a52d4 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/refresher/PersistingOAuthTokenRefresher.ts @@ -0,0 +1,41 @@ +import { OAuthToken, IOAuthTokenRepository } from ".." +import IOAuthTokenRefresher from "./IOAuthTokenRefresher" + +interface IUserIDReader { + getUserId(): Promise +} + +export default class PersistingOAuthTokenRefresher implements IOAuthTokenRefresher { + private readonly userIdReader: IUserIDReader + private readonly oauthTokenRepository: IOAuthTokenRepository + private readonly oauthTokenRefresher: IOAuthTokenRefresher + + constructor(config: { + userIdReader: IUserIDReader + oauthTokenRepository: IOAuthTokenRepository + oauthTokenRefresher: IOAuthTokenRefresher + }) { + this.userIdReader = config.userIdReader + this.oauthTokenRepository = config.oauthTokenRepository + this.oauthTokenRefresher = config.oauthTokenRefresher + } + + async refreshOAuthToken(oldOAuthToken: OAuthToken): Promise { + const userId = await this.userIdReader.getUserId() + const currentOAuthToken = await this.oauthTokenRepository.get(userId) + if ( + oldOAuthToken.accessToken != currentOAuthToken.accessToken || + oldOAuthToken.refreshToken != currentOAuthToken.refreshToken + ) { + // Given OAuth token is outdated so we use our current OAuth token. + return { + accessToken: currentOAuthToken.accessToken, + refreshToken: currentOAuthToken.refreshToken + } + } + // Given OAuth token is stale so we refresh it. + const newOAuthToken = await this.oauthTokenRefresher.refreshOAuthToken(oldOAuthToken) + await this.oauthTokenRepository.set(userId, newOAuthToken) + return newOAuthToken + } +} diff --git a/src/features/auth/domain/oAuthToken/repository/AuthjsAccountsOAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/repository/AuthjsAccountsOAuthTokenRepository.ts new file mode 100644 index 00000000..4f250129 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/repository/AuthjsAccountsOAuthTokenRepository.ts @@ -0,0 +1,60 @@ +import { IDB, UnauthorizedError } from "@/common" +import { IOAuthTokenRepository, OAuthToken } from ".." + +export default class AuthjsAccountsOAuthTokenRepository implements IOAuthTokenRepository { + private readonly db: IDB + private readonly provider: string + + constructor(config: { db: IDB, provider: string }) { + this.db = config.db + this.provider = config.provider + } + + async get(userId: string): Promise { + const query = ` + SELECT + access_token, + refresh_token + FROM + accounts + WHERE + provider = $1 AND \"userId\" = $2; + ` + const result = await this.db.query(query, [this.provider, userId]) + if (result.rows.length == 0) { + throw new UnauthorizedError("The access token was not found. It appears that the user is not authenticated.") + } + const row = result.rows[0] + const accessToken = row.access_token + const refreshToken = row.refresh_token + return { accessToken, refreshToken } + } + + async set(userId: string, token: OAuthToken): Promise { + const query = ` + UPDATE accounts + SET access_token = $3, refresh_token = $4 + WHERE provider = $1 AND \"userId\" = $2 + ` + try { + await this.db.query(query, [this.provider, userId, token.accessToken, token.refreshToken]) + } catch (error) { + console.error(error) + throw error + } + } + + async delete(userId: string): Promise { + const query = ` + UPDATE accounts + SET access_token = "", refresh_token = "" + WHERE provider = $1 AND \"userId\" = $2 + ` + try { + await this.db.query(query, [this.provider, userId]) + } catch (error) { + console.error(error) + throw error + } + } +} diff --git a/src/features/auth/domain/oAuthToken/repository/CompositeOAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/repository/CompositeOAuthTokenRepository.ts new file mode 100644 index 00000000..7b234be6 --- /dev/null +++ b/src/features/auth/domain/oAuthToken/repository/CompositeOAuthTokenRepository.ts @@ -0,0 +1,33 @@ +import { UnauthorizedError } from "@/common" +import { IOAuthTokenRepository, OAuthToken } from ".." + +export default class CompositeOAuthTokenRepository implements IOAuthTokenRepository { + private readonly oAuthTokenRepositories: IOAuthTokenRepository[] + + constructor(config: { oAuthTokenRepositories: IOAuthTokenRepository[] }) { + this.oAuthTokenRepositories = config.oAuthTokenRepositories + } + + async set(userId: string, token: OAuthToken): Promise { + await Promise.all(this.oAuthTokenRepositories.map(async repository => { + await repository.set(userId, token) + })) + } + + async delete(userId: string): Promise { + await Promise.all(this.oAuthTokenRepositories.map(async repository => { + await repository.delete(userId) + })) + } + + async get(userId: string): Promise { + for (const repository of this.oAuthTokenRepositories) { + try { + return await repository.get(userId) + } catch { + // Ignore error and handle it out of the for loop. + } + } + throw new UnauthorizedError("The access token was not found. It appears that the user is not authenticated.") + } +} \ No newline at end of file diff --git a/src/features/auth/domain/oAuthToken/IOAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/repository/IOAuthTokenRepository.ts similarity index 83% rename from src/features/auth/domain/oAuthToken/IOAuthTokenRepository.ts rename to src/features/auth/domain/oAuthToken/repository/IOAuthTokenRepository.ts index 745a68c4..7224ca18 100644 --- a/src/features/auth/domain/oAuthToken/IOAuthTokenRepository.ts +++ b/src/features/auth/domain/oAuthToken/repository/IOAuthTokenRepository.ts @@ -1,4 +1,4 @@ -import OAuthToken from "./OAuthToken" +import { OAuthToken } from ".." export default interface IOAuthTokenRepository { get(userId: string): Promise diff --git a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts b/src/features/auth/domain/oAuthToken/repository/OAuthTokenRepository.ts similarity index 93% rename from src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts rename to src/features/auth/domain/oAuthToken/repository/OAuthTokenRepository.ts index 99539234..74f78f99 100644 --- a/src/features/auth/domain/oAuthToken/OAuthTokenRepository.ts +++ b/src/features/auth/domain/oAuthToken/repository/OAuthTokenRepository.ts @@ -1,5 +1,5 @@ -import { IDB, UnauthorizedError } from "../../../../common" -import { IOAuthTokenRepository, OAuthToken } from "." +import { IDB, UnauthorizedError } from "@/common" +import { IOAuthTokenRepository, OAuthToken } from ".." export default class OAuthTokenRepository implements IOAuthTokenRepository { private readonly db: IDB diff --git a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts b/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts deleted file mode 100644 index 1b059326..00000000 --- a/src/features/auth/domain/sessionValidity/AccessTokenSessionValidator.ts +++ /dev/null @@ -1,22 +0,0 @@ -import SessionValidity from "./SessionValidity" - -interface IAccessTokenDataSource { - getAccessToken(): Promise -} - -export default class AccessTokenSessionValidator { - private readonly accessTokenDataSource: IAccessTokenDataSource - - constructor(config: { accessTokenDataSource: IAccessTokenDataSource }) { - this.accessTokenDataSource = config.accessTokenDataSource - } - - async validateSession(): Promise { - try { - await this.accessTokenDataSource.getAccessToken() - return SessionValidity.VALID - } catch (error) { - return SessionValidity.INVALID_ACCESS_TOKEN - } - } -} diff --git a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts index be6170f4..b227c7cd 100644 --- a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts +++ b/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts @@ -1,12 +1,12 @@ -import { AccountProviderType } from "@/common" +import { AccountProvider } from "@/common" import SessionValidity from "./SessionValidity" type OrganizationMembershipStatus = { readonly state: "active" | "pending" } -interface IAccountProviderTypeReader { - getAccountProviderType(): Promise +interface IAccountProviderReader { + getAccountProvider(): Promise } interface IOrganizationMembershipStatusReader { @@ -18,23 +18,25 @@ interface IOrganizationMembershipStatusReader { export default class GitHubOrganizationSessionValidator { private readonly acceptedOrganization: string private readonly organizationMembershipStatusReader: IOrganizationMembershipStatusReader - private readonly accountProviderTypeReader: IAccountProviderTypeReader + private readonly accountProviderReader: IAccountProviderReader constructor( config: { acceptedOrganization: string organizationMembershipStatusReader: IOrganizationMembershipStatusReader, - accountProviderTypeReader: IAccountProviderTypeReader + accountProviderReader: IAccountProviderReader } ) { this.acceptedOrganization = config.acceptedOrganization this.organizationMembershipStatusReader = config.organizationMembershipStatusReader - this.accountProviderTypeReader = config.accountProviderTypeReader + this.accountProviderReader = config.accountProviderReader } async validateSession(): Promise { - const accountProviderType = await this.accountProviderTypeReader.getAccountProviderType() - if (accountProviderType !== "github") { + const accountProvider = await this.accountProviderReader.getAccountProvider() + console.log(accountProvider) + if (accountProvider !== "github") { + console.log("FOOO") // Only validate GitHub sessions and consider any other valid. return SessionValidity.VALID } diff --git a/src/features/auth/domain/sessionValidity/OAuthTokenSessionValidator.ts b/src/features/auth/domain/sessionValidity/OAuthTokenSessionValidator.ts new file mode 100644 index 00000000..77256d55 --- /dev/null +++ b/src/features/auth/domain/sessionValidity/OAuthTokenSessionValidator.ts @@ -0,0 +1,19 @@ +import SessionValidity from "./SessionValidity" +import { IOAuthTokenDataSource } from ".." + +export default class OAuthTokenSessionValidator { + private readonly oauthTokenDataSource: IOAuthTokenDataSource + + constructor(config: { oauthTokenDataSource: IOAuthTokenDataSource }) { + this.oauthTokenDataSource = config.oauthTokenDataSource + } + + async validateSession(): Promise { + try { + await this.oauthTokenDataSource.getOAuthToken() + return SessionValidity.VALID + } catch (error) { + return SessionValidity.INVALID_ACCESS_TOKEN + } + } +} diff --git a/src/features/auth/domain/sessionValidity/index.ts b/src/features/auth/domain/sessionValidity/index.ts index f67c06b5..b5b8f96c 100644 --- a/src/features/auth/domain/sessionValidity/index.ts +++ b/src/features/auth/domain/sessionValidity/index.ts @@ -1,5 +1,5 @@ -export { default as AccessTokenSessionValidator } from "./AccessTokenSessionValidator" export { default as GitHubOrganizationSessionValidator } from "./GitHubOrganizationSessionValidator" +export { default as OAuthTokenSessionValidator } from "./OAuthTokenSessionValidator" export { default as SessionValidity } from "./SessionValidity" export * from "./SessionValidity" export { default as useRepositoryAccess } from "./useRepositoryAccess" diff --git a/src/features/auth/domain/sessionValidity/useRepositoryAccess.ts b/src/features/auth/domain/sessionValidity/useRepositoryAccess.ts index c98d6b03..bb41d33d 100644 --- a/src/features/auth/domain/sessionValidity/useRepositoryAccess.ts +++ b/src/features/auth/domain/sessionValidity/useRepositoryAccess.ts @@ -1,7 +1,7 @@ "use client" import useSWR from "swr" -import { fetcher } from "../../../../common" +import { fetcher } from "@/common" type RepositoriesContainer = { repositories: string[] } diff --git a/src/features/auth/domain/sessionValidity/useSessionValidity.ts b/src/features/auth/domain/sessionValidity/useSessionValidity.ts index ce5d62ec..b334450e 100644 --- a/src/features/auth/domain/sessionValidity/useSessionValidity.ts +++ b/src/features/auth/domain/sessionValidity/useSessionValidity.ts @@ -1,7 +1,7 @@ "use client" import useSWR from "swr" -import { fetcher } from "../../../../common" +import { fetcher } from "@/common" import SessionValidity from "./SessionValidity" type SessionValidityContainer = { sessionValidity: SessionValidity } diff --git a/src/features/auth/view/SessionBarrier.tsx b/src/features/auth/view/SessionBarrier.tsx index 1725475b..648ef1b2 100644 --- a/src/features/auth/view/SessionBarrier.tsx +++ b/src/features/auth/view/SessionBarrier.tsx @@ -12,11 +12,11 @@ export default async function SessionBarrier({ }: { children: ReactNode }) { - const accountProviderType = await session.getAccountProviderType() + const accountProvider = await session.getAccountProvider() const sessionValidity = await blockingSessionValidator.validateSession() return ( {children} case SessionValidity.INVALID_ACCESS_TOKEN: - switch (accountProviderType) { + switch (accountProvider) { case "email": return case "github": diff --git a/src/features/projects/data/useProjects.ts b/src/features/projects/data/useProjects.ts index 8f1d6f11..a0b2cb16 100644 --- a/src/features/projects/data/useProjects.ts +++ b/src/features/projects/data/useProjects.ts @@ -1,7 +1,7 @@ "use client" import useSWR from "swr" -import { fetcher } from "../../../common" +import { fetcher } from "@/common" import { Project } from "../domain" type ProjectContainer = { projects: Project[] } diff --git a/src/features/projects/domain/ProjectRepository.ts b/src/features/projects/domain/ProjectRepository.ts index c55e9341..baf5ea69 100644 --- a/src/features/projects/domain/ProjectRepository.ts +++ b/src/features/projects/domain/ProjectRepository.ts @@ -1,4 +1,4 @@ -import { IUserDataRepository, ZodJSONCoder } from "../../../common" +import { IUserDataRepository, ZodJSONCoder } from "@/common" import IProjectRepository from "./IProjectRepository" import Project, { ProjectSchema } from "./Project" diff --git a/src/features/projects/view/ProjectsPage.tsx b/src/features/projects/view/ProjectsPage.tsx index e7625c69..d800e5d7 100644 --- a/src/features/projects/view/ProjectsPage.tsx +++ b/src/features/projects/view/ProjectsPage.tsx @@ -9,7 +9,7 @@ export default async function ProjectsPage({ projectRepository: ProjectRepository path: string }) { - const enableGitHubLinks = await session.getAccountProviderType() == "github" + const enableGitHubLinks = await session.getAccountProvider() == "github" const projects = await projectRepository.get() return ( Date: Tue, 25 Jun 2024 18:24:16 +0200 Subject: [PATCH 065/191] Uses dashes in folder names --- src/common/index.ts | 4 ++-- .../{keyValueStore => key-value-store}/IKeyValueStore.ts | 0 .../RedisKeyValueStore.ts | 0 src/common/{keyValueStore => key-value-store}/index.ts | 0 src/common/{userData => user-data}/IUserDataRepository.ts | 0 .../{userData => user-data}/KeyValueUserDataRepository.ts | 2 +- src/common/{userData => user-data}/index.ts | 0 src/composition.ts | 2 +- src/features/auth/domain/index.ts | 8 ++++---- .../auth/domain/{logIn => log-in}/ILogInHandler.ts | 0 .../auth/domain/{logIn => log-in}/LogInHandler.ts | 0 src/features/auth/domain/{logIn => log-in}/index.ts | 0 .../domain/{logOut => log-out}/CompositeLogOutHandler.ts | 0 .../{logOut => log-out}/ErrorIgnoringLogOutHandler.ts | 0 .../auth/domain/{logOut => log-out}/ILogOutHandler.ts | 0 .../{logOut => log-out}/UserDataCleanUpLogOutHandler.ts | 0 src/features/auth/domain/{logOut => log-out}/index.ts | 0 .../auth/domain/{oAuthToken => oauth-token}/OAuthToken.ts | 0 .../data-source/GuestOAuthTokenDataSource.ts | 0 .../data-source/IOAuthTokenDataSource.ts | 0 .../data-source/PersistingOAuthTokenDataSource.ts | 0 .../auth/domain/{oAuthToken => oauth-token}/index.ts | 0 .../AccountProviderTypeBasedOAuthTokenRefresher.ts | 0 .../refresher/GuestOAuthTokenRefresher.ts | 0 .../refresher/IOAuthTokenRefresher.ts | 0 .../refresher/LockingOAuthTokenRefresher.ts | 0 .../refresher/PersistingOAuthTokenRefresher.ts | 0 .../repository/AuthjsAccountsOAuthTokenRepository.ts | 0 .../repository/CompositeOAuthTokenRepository.ts | 0 .../repository/IOAuthTokenRepository.ts | 0 .../repository/OAuthTokenRepository.ts | 0 .../GitHubOrganizationSessionValidator.ts | 2 -- .../OAuthTokenSessionValidator.ts | 0 .../SessionValidity.ts | 0 .../domain/{sessionValidity => session-validity}/index.ts | 0 .../useRepositoryAccess.ts | 0 .../useSessionValidity.ts | 0 37 files changed, 8 insertions(+), 10 deletions(-) rename src/common/{keyValueStore => key-value-store}/IKeyValueStore.ts (100%) rename src/common/{keyValueStore => key-value-store}/RedisKeyValueStore.ts (100%) rename src/common/{keyValueStore => key-value-store}/index.ts (100%) rename src/common/{userData => user-data}/IUserDataRepository.ts (100%) rename src/common/{userData => user-data}/KeyValueUserDataRepository.ts (93%) rename src/common/{userData => user-data}/index.ts (100%) rename src/features/auth/domain/{logIn => log-in}/ILogInHandler.ts (100%) rename src/features/auth/domain/{logIn => log-in}/LogInHandler.ts (100%) rename src/features/auth/domain/{logIn => log-in}/index.ts (100%) rename src/features/auth/domain/{logOut => log-out}/CompositeLogOutHandler.ts (100%) rename src/features/auth/domain/{logOut => log-out}/ErrorIgnoringLogOutHandler.ts (100%) rename src/features/auth/domain/{logOut => log-out}/ILogOutHandler.ts (100%) rename src/features/auth/domain/{logOut => log-out}/UserDataCleanUpLogOutHandler.ts (100%) rename src/features/auth/domain/{logOut => log-out}/index.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/OAuthToken.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/data-source/GuestOAuthTokenDataSource.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/data-source/IOAuthTokenDataSource.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/data-source/PersistingOAuthTokenDataSource.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/index.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/refresher/GuestOAuthTokenRefresher.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/refresher/IOAuthTokenRefresher.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/refresher/LockingOAuthTokenRefresher.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/refresher/PersistingOAuthTokenRefresher.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/repository/AuthjsAccountsOAuthTokenRepository.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/repository/CompositeOAuthTokenRepository.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/repository/IOAuthTokenRepository.ts (100%) rename src/features/auth/domain/{oAuthToken => oauth-token}/repository/OAuthTokenRepository.ts (100%) rename src/features/auth/domain/{sessionValidity => session-validity}/GitHubOrganizationSessionValidator.ts (97%) rename src/features/auth/domain/{sessionValidity => session-validity}/OAuthTokenSessionValidator.ts (100%) rename src/features/auth/domain/{sessionValidity => session-validity}/SessionValidity.ts (100%) rename src/features/auth/domain/{sessionValidity => session-validity}/index.ts (100%) rename src/features/auth/domain/{sessionValidity => session-validity}/useRepositoryAccess.ts (100%) rename src/features/auth/domain/{sessionValidity => session-validity}/useSessionValidity.ts (100%) diff --git a/src/common/index.ts b/src/common/index.ts index 4be9c4a1..fdb90059 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,8 +1,8 @@ export * from "./db" export * from "./errors" export * from "./github" -export * from "./keyValueStore" +export * from "./key-value-store" export * from "./mutex" export * from "./session" -export * from "./userData" +export * from "./user-data" export * from "./utils" diff --git a/src/common/keyValueStore/IKeyValueStore.ts b/src/common/key-value-store/IKeyValueStore.ts similarity index 100% rename from src/common/keyValueStore/IKeyValueStore.ts rename to src/common/key-value-store/IKeyValueStore.ts diff --git a/src/common/keyValueStore/RedisKeyValueStore.ts b/src/common/key-value-store/RedisKeyValueStore.ts similarity index 100% rename from src/common/keyValueStore/RedisKeyValueStore.ts rename to src/common/key-value-store/RedisKeyValueStore.ts diff --git a/src/common/keyValueStore/index.ts b/src/common/key-value-store/index.ts similarity index 100% rename from src/common/keyValueStore/index.ts rename to src/common/key-value-store/index.ts diff --git a/src/common/userData/IUserDataRepository.ts b/src/common/user-data/IUserDataRepository.ts similarity index 100% rename from src/common/userData/IUserDataRepository.ts rename to src/common/user-data/IUserDataRepository.ts diff --git a/src/common/userData/KeyValueUserDataRepository.ts b/src/common/user-data/KeyValueUserDataRepository.ts similarity index 93% rename from src/common/userData/KeyValueUserDataRepository.ts rename to src/common/user-data/KeyValueUserDataRepository.ts index b6318099..95b05734 100644 --- a/src/common/userData/KeyValueUserDataRepository.ts +++ b/src/common/user-data/KeyValueUserDataRepository.ts @@ -1,4 +1,4 @@ -import IKeyValueStore from "../keyValueStore/IKeyValueStore" +import IKeyValueStore from "../key-value-store/IKeyValueStore" import IUserDataRepository from "./IUserDataRepository" export default class KeyValueUserDataRepository implements IUserDataRepository { diff --git a/src/common/userData/index.ts b/src/common/user-data/index.ts similarity index 100% rename from src/common/userData/index.ts rename to src/common/user-data/index.ts diff --git a/src/composition.ts b/src/composition.ts index d285e738..4596afe1 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -4,7 +4,7 @@ import GithubProvider from "next-auth/providers/github" import EmailProvider from "next-auth/providers/nodemailer" import PostgresAdapter from "@auth/pg-adapter" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" -import RedisKeyValueStore from "@/common/keyValueStore/RedisKeyValueStore" +import RedisKeyValueStore from "@/common/key-value-store/RedisKeyValueStore" import { AuthjsSession, GitHubClient, diff --git a/src/features/auth/domain/index.ts b/src/features/auth/domain/index.ts index bc75946a..902ad6fe 100644 --- a/src/features/auth/domain/index.ts +++ b/src/features/auth/domain/index.ts @@ -1,4 +1,4 @@ -export * from "./logIn" -export * from "./logOut" -export * from "./oAuthToken" -export * from "./sessionValidity" +export * from "./log-in" +export * from "./log-out" +export * from "./oauth-token" +export * from "./session-validity" diff --git a/src/features/auth/domain/logIn/ILogInHandler.ts b/src/features/auth/domain/log-in/ILogInHandler.ts similarity index 100% rename from src/features/auth/domain/logIn/ILogInHandler.ts rename to src/features/auth/domain/log-in/ILogInHandler.ts diff --git a/src/features/auth/domain/logIn/LogInHandler.ts b/src/features/auth/domain/log-in/LogInHandler.ts similarity index 100% rename from src/features/auth/domain/logIn/LogInHandler.ts rename to src/features/auth/domain/log-in/LogInHandler.ts diff --git a/src/features/auth/domain/logIn/index.ts b/src/features/auth/domain/log-in/index.ts similarity index 100% rename from src/features/auth/domain/logIn/index.ts rename to src/features/auth/domain/log-in/index.ts diff --git a/src/features/auth/domain/logOut/CompositeLogOutHandler.ts b/src/features/auth/domain/log-out/CompositeLogOutHandler.ts similarity index 100% rename from src/features/auth/domain/logOut/CompositeLogOutHandler.ts rename to src/features/auth/domain/log-out/CompositeLogOutHandler.ts diff --git a/src/features/auth/domain/logOut/ErrorIgnoringLogOutHandler.ts b/src/features/auth/domain/log-out/ErrorIgnoringLogOutHandler.ts similarity index 100% rename from src/features/auth/domain/logOut/ErrorIgnoringLogOutHandler.ts rename to src/features/auth/domain/log-out/ErrorIgnoringLogOutHandler.ts diff --git a/src/features/auth/domain/logOut/ILogOutHandler.ts b/src/features/auth/domain/log-out/ILogOutHandler.ts similarity index 100% rename from src/features/auth/domain/logOut/ILogOutHandler.ts rename to src/features/auth/domain/log-out/ILogOutHandler.ts diff --git a/src/features/auth/domain/logOut/UserDataCleanUpLogOutHandler.ts b/src/features/auth/domain/log-out/UserDataCleanUpLogOutHandler.ts similarity index 100% rename from src/features/auth/domain/logOut/UserDataCleanUpLogOutHandler.ts rename to src/features/auth/domain/log-out/UserDataCleanUpLogOutHandler.ts diff --git a/src/features/auth/domain/logOut/index.ts b/src/features/auth/domain/log-out/index.ts similarity index 100% rename from src/features/auth/domain/logOut/index.ts rename to src/features/auth/domain/log-out/index.ts diff --git a/src/features/auth/domain/oAuthToken/OAuthToken.ts b/src/features/auth/domain/oauth-token/OAuthToken.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/OAuthToken.ts rename to src/features/auth/domain/oauth-token/OAuthToken.ts diff --git a/src/features/auth/domain/oAuthToken/data-source/GuestOAuthTokenDataSource.ts b/src/features/auth/domain/oauth-token/data-source/GuestOAuthTokenDataSource.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/data-source/GuestOAuthTokenDataSource.ts rename to src/features/auth/domain/oauth-token/data-source/GuestOAuthTokenDataSource.ts diff --git a/src/features/auth/domain/oAuthToken/data-source/IOAuthTokenDataSource.ts b/src/features/auth/domain/oauth-token/data-source/IOAuthTokenDataSource.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/data-source/IOAuthTokenDataSource.ts rename to src/features/auth/domain/oauth-token/data-source/IOAuthTokenDataSource.ts diff --git a/src/features/auth/domain/oAuthToken/data-source/PersistingOAuthTokenDataSource.ts b/src/features/auth/domain/oauth-token/data-source/PersistingOAuthTokenDataSource.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/data-source/PersistingOAuthTokenDataSource.ts rename to src/features/auth/domain/oauth-token/data-source/PersistingOAuthTokenDataSource.ts diff --git a/src/features/auth/domain/oAuthToken/index.ts b/src/features/auth/domain/oauth-token/index.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/index.ts rename to src/features/auth/domain/oauth-token/index.ts diff --git a/src/features/auth/domain/oAuthToken/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts rename to src/features/auth/domain/oauth-token/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts diff --git a/src/features/auth/domain/oAuthToken/refresher/GuestOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/GuestOAuthTokenRefresher.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/refresher/GuestOAuthTokenRefresher.ts rename to src/features/auth/domain/oauth-token/refresher/GuestOAuthTokenRefresher.ts diff --git a/src/features/auth/domain/oAuthToken/refresher/IOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/IOAuthTokenRefresher.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/refresher/IOAuthTokenRefresher.ts rename to src/features/auth/domain/oauth-token/refresher/IOAuthTokenRefresher.ts diff --git a/src/features/auth/domain/oAuthToken/refresher/LockingOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/LockingOAuthTokenRefresher.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/refresher/LockingOAuthTokenRefresher.ts rename to src/features/auth/domain/oauth-token/refresher/LockingOAuthTokenRefresher.ts diff --git a/src/features/auth/domain/oAuthToken/refresher/PersistingOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/PersistingOAuthTokenRefresher.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/refresher/PersistingOAuthTokenRefresher.ts rename to src/features/auth/domain/oauth-token/refresher/PersistingOAuthTokenRefresher.ts diff --git a/src/features/auth/domain/oAuthToken/repository/AuthjsAccountsOAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/repository/AuthjsAccountsOAuthTokenRepository.ts rename to src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts diff --git a/src/features/auth/domain/oAuthToken/repository/CompositeOAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/CompositeOAuthTokenRepository.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/repository/CompositeOAuthTokenRepository.ts rename to src/features/auth/domain/oauth-token/repository/CompositeOAuthTokenRepository.ts diff --git a/src/features/auth/domain/oAuthToken/repository/IOAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/IOAuthTokenRepository.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/repository/IOAuthTokenRepository.ts rename to src/features/auth/domain/oauth-token/repository/IOAuthTokenRepository.ts diff --git a/src/features/auth/domain/oAuthToken/repository/OAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/OAuthTokenRepository.ts similarity index 100% rename from src/features/auth/domain/oAuthToken/repository/OAuthTokenRepository.ts rename to src/features/auth/domain/oauth-token/repository/OAuthTokenRepository.ts diff --git a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts b/src/features/auth/domain/session-validity/GitHubOrganizationSessionValidator.ts similarity index 97% rename from src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts rename to src/features/auth/domain/session-validity/GitHubOrganizationSessionValidator.ts index b227c7cd..0c69504c 100644 --- a/src/features/auth/domain/sessionValidity/GitHubOrganizationSessionValidator.ts +++ b/src/features/auth/domain/session-validity/GitHubOrganizationSessionValidator.ts @@ -34,9 +34,7 @@ export default class GitHubOrganizationSessionValidator { async validateSession(): Promise { const accountProvider = await this.accountProviderReader.getAccountProvider() - console.log(accountProvider) if (accountProvider !== "github") { - console.log("FOOO") // Only validate GitHub sessions and consider any other valid. return SessionValidity.VALID } diff --git a/src/features/auth/domain/sessionValidity/OAuthTokenSessionValidator.ts b/src/features/auth/domain/session-validity/OAuthTokenSessionValidator.ts similarity index 100% rename from src/features/auth/domain/sessionValidity/OAuthTokenSessionValidator.ts rename to src/features/auth/domain/session-validity/OAuthTokenSessionValidator.ts diff --git a/src/features/auth/domain/sessionValidity/SessionValidity.ts b/src/features/auth/domain/session-validity/SessionValidity.ts similarity index 100% rename from src/features/auth/domain/sessionValidity/SessionValidity.ts rename to src/features/auth/domain/session-validity/SessionValidity.ts diff --git a/src/features/auth/domain/sessionValidity/index.ts b/src/features/auth/domain/session-validity/index.ts similarity index 100% rename from src/features/auth/domain/sessionValidity/index.ts rename to src/features/auth/domain/session-validity/index.ts diff --git a/src/features/auth/domain/sessionValidity/useRepositoryAccess.ts b/src/features/auth/domain/session-validity/useRepositoryAccess.ts similarity index 100% rename from src/features/auth/domain/sessionValidity/useRepositoryAccess.ts rename to src/features/auth/domain/session-validity/useRepositoryAccess.ts diff --git a/src/features/auth/domain/sessionValidity/useSessionValidity.ts b/src/features/auth/domain/session-validity/useSessionValidity.ts similarity index 100% rename from src/features/auth/domain/sessionValidity/useSessionValidity.ts rename to src/features/auth/domain/session-validity/useSessionValidity.ts From 9b4b31922728890294e10289a9da9a8843cd2aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 08:58:15 +0200 Subject: [PATCH 066/191] Updates next-auth --- package-lock.json | 9451 ++++++++++++++++++++++++++++----------------- package.json | 2 +- 2 files changed, 5944 insertions(+), 3509 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96d96915..216af57d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "ioredis": "^5.3.2", "mobx": "^6.12.0", "next": "^14.2.3", - "next-auth": "^5.0.0-beta.17", + "next-auth": "^5.0.0-beta.19", "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", @@ -66,15 +66,6 @@ "npm": "10.1.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -88,14 +79,14 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "devOptional": true, "peer": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -132,15 +123,6 @@ } } }, - "node_modules/@auth/core/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@auth/pg-adapter": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@auth/pg-adapter/-/pg-adapter-0.5.3.tgz", @@ -154,85 +136,21 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", + "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", "devOptional": true, "peer": true, "engines": { @@ -240,22 +158,22 @@ } }, "node_modules/@babel/core": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", - "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", + "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "devOptional": true, "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helpers": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -270,18 +188,12 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "devOptional": true, - "peer": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } + "peer": true }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", @@ -294,15 +206,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "devOptional": true, - "peer": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", + "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -310,15 +220,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", + "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", "devOptional": true, "peer": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -346,73 +256,64 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "devOptional": true, - "peer": true - }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "devOptional": true, - "peer": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dependencies": { + "@babel/types": "^7.24.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "devOptional": true, - "peer": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "devOptional": true, - "peer": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", + "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", "devOptional": true, "peer": true, "dependencies": { - "@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.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -422,9 +323,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", "dev": true, "peer": true, "engines": { @@ -432,51 +333,50 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "devOptional": true, "peer": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "devOptional": true, - "peer": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", + "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", + "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", "devOptional": true, "peer": true, "engines": { @@ -484,103 +384,37 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", - "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", + "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", "devOptional": true, "peer": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "devOptional": true, - "peer": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -654,13 +488,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -764,13 +598,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", + "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", "dev": true, "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -780,9 +614,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -791,9 +625,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz", - "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.7.tgz", + "integrity": "sha512-eytSX6JLBY6PVAeQa2bFlDx/7Mmln/gaEpsit5a3WEvjGfiIytEsgAwuIXCPM0xvw0v0cJn3ilq0/TvXrW0kgA==", "dependencies": { "core-js-pure": "^3.30.2", "regenerator-runtime": "^0.14.0" @@ -803,59 +637,45 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "devOptional": true, - "peer": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "devOptional": true, - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@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.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", + "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "devOptional": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", + "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -870,9 +690,26 @@ "peer": true }, "node_modules/@braintree/sanitize-url": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", - "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.0.2.tgz", + "integrity": "sha512-NVf/1YycDMs6+FxS0Tb/W8MjJRDQdXF+tBfDtZ5UZeiRUkTmwKc4vmYCKZTyymfJk1gnMsauvZSX/HiV9jOABw==" + }, + "node_modules/@cfaester/enzyme-adapter-react-18": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cfaester/enzyme-adapter-react-18/-/enzyme-adapter-react-18-0.8.0.tgz", + "integrity": "sha512-3Z3ThTUouHwz8oIyhTYQljEMNRFtlVyc3VOOHCbxs47U6cnXs8K9ygi/c1tv49s7MBlTXeIcuN+Ttd9aPtILFQ==", + "dependencies": { + "enzyme-shallow-equal": "^1.0.0", + "function.prototype.name": "^1.1.6", + "has": "^1.0.4", + "react-is": "^18.2.0", + "react-shallow-renderer": "^16.15.0" + }, + "peerDependencies": { + "enzyme": "^3.11.0", + "react": ">=18", + "react-dom": ">=18" + } }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", @@ -892,19 +729,6 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/@emotion/babel-plugin/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@emotion/cache": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", @@ -923,9 +747,9 @@ "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", "dependencies": { "@emotion/memoize": "^0.8.1" } @@ -936,14 +760,14 @@ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "node_modules/@emotion/react": { - "version": "11.11.1", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", - "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", + "@emotion/serialize": "^1.1.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", "@emotion/utils": "^1.2.1", "@emotion/weak-memoize": "^0.3.1", @@ -959,9 +783,9 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", "dependencies": { "@emotion/hash": "^0.9.1", "@emotion/memoize": "^0.8.1", @@ -976,14 +800,14 @@ "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" }, "node_modules/@emotion/styled": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", - "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/serialize": "^1.1.2", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", "@emotion/utils": "^1.2.1" }, @@ -1036,9 +860,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", - "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", + "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1067,56 +891,97 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@exodus/schemasafe": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", - "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" - }, - "node_modules/@faker-js/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==" - }, - "node_modules/@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=14" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@floating-ui/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", - "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { - "@floating-ui/utils": "^0.1.3" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/@floating-ui/dom": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", - "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" + }, + "node_modules/@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==" + }, + "node_modules/@floating-ui/core": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.3.tgz", + "integrity": "sha512-1ZpCvYf788/ZXOhRQGFxnYQOVgeU+pi0i+d0Ow34La7qjIXETi6RNswGVKkA6KcDO8/+Ysu2E/CeUmmeEBDvTg==", + "dependencies": { + "@floating-ui/utils": "^0.2.3" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.6.tgz", + "integrity": "sha512-qiTYajAnh3P+38kECeffMSQgbvXty2VB6rS+42iWR4FPIlZjLK84E9qtLnMTLIpPz2znD/TaFqaiavMUrS+Hcw==", "dependencies": { - "@floating-ui/core": "^1.4.2", - "@floating-ui/utils": "^0.1.3" + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.3" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz", - "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", + "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", "dependencies": { - "@floating-ui/dom": "^1.5.1" + "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -1124,47 +989,47 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", - "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.3.tgz", + "integrity": "sha512-XGndio0l5/Gvd6CLIABvsav9HHezgDFFhDfHk1bvLfr9ni8dojqLSvBbotJEjmIwNHL7vK4QzBJTdBRoB+c1ww==" }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", - "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", "hasInstallScript": true, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", - "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz", + "integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" + "@fortawesome/fontawesome-common-types": "6.5.2" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", - "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz", + "integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" + "@fortawesome/fontawesome-common-types": "6.5.2" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/react-fontawesome": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", - "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", "dependencies": { "prop-types": "^15.8.1" }, @@ -1174,19 +1039,42 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1201,9 +1089,10 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, "node_modules/@ioredis/commands": { @@ -1215,7 +1104,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1232,7 +1120,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1240,40 +1127,10 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1284,23 +1141,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1436,6 +1276,82 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/@jest/console/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/core": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", @@ -1484,6 +1400,82 @@ } } }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/@jest/core/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -1604,13 +1596,135 @@ } } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "peer": true, "dependencies": { - "@sinclair/typebox": "^0.27.8" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -1690,6 +1804,89 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/types": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", @@ -1707,34 +1904,101 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "devOptional": true, + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "devOptional": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "devOptional": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } @@ -1745,10 +2009,9 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", - "devOptional": true, + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1765,16 +2028,16 @@ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" }, "node_modules/@mui/base": { - "version": "5.0.0-beta.24", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.24.tgz", - "integrity": "sha512-bKt2pUADHGQtqWDZ8nvL2Lvg2GNJyd/ZUgZAJoYzRgmnxBL9j36MSlS3+exEdYkikcnvVafcBtD904RypFKb0w==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@floating-ui/react-dom": "^2.0.4", - "@mui/types": "^7.2.9", - "@mui/utils": "^5.14.18", + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", "@popperjs/core": "^2.11.8", - "clsx": "^2.0.0", + "clsx": "^2.1.0", "prop-types": "^15.8.1" }, "engines": { @@ -1782,7 +2045,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -1796,27 +2059,27 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.14.18", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.18.tgz", - "integrity": "sha512-yFpF35fEVDV81nVktu0BE9qn2dD/chs7PsQhlyaV3EnTeZi9RZBuvoEfRym1/jmhJ2tcfeWXiRuHG942mQXJJQ==", + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.20.tgz", + "integrity": "sha512-DoL2ppgldL16utL8nNyj/P12f8mCNdx/Hb/AJnX9rLY4b52hCMIx1kH83pbXQ6uMy6n54M3StmEbvSGoj2OFuA==", "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.14.18", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.18.tgz", - "integrity": "sha512-o2z49R1G4SdBaxZjbMmkn+2OdT1bKymLvAYaB6pH59obM1CYv/0vAVm6zO31IqhwtYwXv6A7sLIwCGYTaVkcdg==", + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.20.tgz", + "integrity": "sha512-oGcKmCuHaYbAAoLN67WKSXtHmEgyWcJToT1uRtmPyxMj9N5uqwc/mRtEnst4Wj/eGr+zYH2FiZQ79v9k7kSk1Q==", "dependencies": { - "@babel/runtime": "^7.23.2" + "@babel/runtime": "^7.23.9" }, "engines": { "node": ">=12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@mui/material": "^5.0.0", @@ -1830,19 +2093,19 @@ } }, "node_modules/@mui/material": { - "version": "5.14.18", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.18.tgz", - "integrity": "sha512-y3UiR/JqrkF5xZR0sIKj6y7xwuEiweh9peiN3Zfjy1gXWXhz5wjlaLdoxFfKIEBUFfeQALxr/Y8avlHH+B9lpQ==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/base": "5.0.0-beta.24", - "@mui/core-downloads-tracker": "^5.14.18", - "@mui/system": "^5.14.18", - "@mui/types": "^7.2.9", - "@mui/utils": "^5.14.18", - "@types/react-transition-group": "^4.4.8", - "clsx": "^2.0.0", - "csstype": "^3.1.2", + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.20.tgz", + "integrity": "sha512-tVq3l4qoXx/NxUgIx/x3lZiPn/5xDbdTE8VrLczNpfblLYZzlrbxA7kb9mI8NoBF6+w9WE9IrxWnKK5KlPI2bg==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.20", + "@mui/system": "^5.15.20", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.20", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^18.2.0", "react-transition-group": "^4.4.5" @@ -1852,7 +2115,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -1873,18 +2136,13 @@ } } }, - "node_modules/@mui/material/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/@mui/private-theming": { - "version": "5.14.18", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.18.tgz", - "integrity": "sha512-WSgjqRlzfHU+2Rou3HlR2Gqfr4rZRsvFgataYO3qQ0/m6gShJN+lhVEvwEiJ9QYyVzMDvNpXZAcqp8Y2Vl+PAw==", + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.20.tgz", + "integrity": "sha512-BK8F94AIqSrnaPYXf2KAOjGZJgWfvqAVQ2gVR3EryvQFtuBnG6RwodxrCvd3B48VuMy6Wsk897+lQMUxJyk+6g==", "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/utils": "^5.14.18", + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.20", "prop-types": "^15.8.1" }, "engines": { @@ -1892,7 +2150,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -1905,13 +2163,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.14.18", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.18.tgz", - "integrity": "sha512-pW8bpmF9uCB5FV2IPk6mfbQCjPI5vGI09NOLhtGXPeph/4xIfC3JdIX0TILU0WcTs3aFQqo6s2+1SFgIB9rCXA==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", "dependencies": { - "@babel/runtime": "^7.23.2", + "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -1919,7 +2177,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.4.1", @@ -1936,17 +2194,17 @@ } }, "node_modules/@mui/system": { - "version": "5.14.18", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.18.tgz", - "integrity": "sha512-hSQQdb3KF72X4EN2hMEiv8EYJZSflfdd1TRaGPoR7CIAG347OxCslpBUwWngYobaxgKvq6xTrlIl+diaactVww==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@mui/private-theming": "^5.14.18", - "@mui/styled-engine": "^5.14.18", - "@mui/types": "^7.2.9", - "@mui/utils": "^5.14.18", - "clsx": "^2.0.0", - "csstype": "^3.1.2", + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.20.tgz", + "integrity": "sha512-LoMq4IlAAhxzL2VNUDBTQxAb4chnBe8JvRINVNDiMtHE2PiPOoHlhOPutSxEbaL5mkECPVWSv6p8JEV+uykwIA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.20", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.20", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -1954,7 +2212,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -1975,9 +2233,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.9.tgz", - "integrity": "sha512-k1lN/PolaRZfNsRdAqXtcR71sTnv3z/VCCGPxU8HfdftDkzi335MdJ6scZxvofMAd/K/9EbzCZTFBmlNpQVdCg==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", + "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -1988,12 +2246,12 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.18", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.18.tgz", - "integrity": "sha512-HZDRsJtEZ7WMSnrHV9uwScGze4wM/Y+u6pDVo+grUjt5yXzn+wI8QX/JwTHh9YSw/WpnUL80mJJjgCnWj2VrzQ==", + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.20.tgz", + "integrity": "sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==", "dependencies": { - "@babel/runtime": "^7.23.2", - "@types/prop-types": "^15.7.10", + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -2002,7 +2260,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -2014,75 +2272,24 @@ } } }, - "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/@next/env": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", - "integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==" + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.4.tgz", + "integrity": "sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==" }, "node_modules/@next/eslint-plugin-next": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz", - "integrity": "sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.4.tgz", + "integrity": "sha512-svSFxW9f3xDaZA3idQmlFw7SusOuWTpDTAeBlO3AEPDltrraV+lqs7mAc6A27YdnpQVVIA3sODqUAAHdWhVWsA==", "dev": true, "dependencies": { "glob": "10.3.10" } }, - "node_modules/@next/eslint-plugin-next/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@next/eslint-plugin-next/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@next/eslint-plugin-next/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz", - "integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.4.tgz", + "integrity": "sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==", "cpu": [ "arm64" ], @@ -2095,9 +2302,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz", - "integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz", + "integrity": "sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==", "cpu": [ "x64" ], @@ -2110,9 +2317,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz", - "integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz", + "integrity": "sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==", "cpu": [ "arm64" ], @@ -2125,9 +2332,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz", - "integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz", + "integrity": "sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==", "cpu": [ "arm64" ], @@ -2140,9 +2347,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz", - "integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz", + "integrity": "sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==", "cpu": [ "x64" ], @@ -2155,9 +2362,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz", - "integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz", + "integrity": "sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==", "cpu": [ "x64" ], @@ -2170,9 +2377,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz", - "integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz", + "integrity": "sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==", "cpu": [ "arm64" ], @@ -2185,9 +2392,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz", - "integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz", + "integrity": "sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==", "cpu": [ "ia32" ], @@ -2200,9 +2407,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz", - "integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz", + "integrity": "sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==", "cpu": [ "x64" ], @@ -2250,9 +2457,9 @@ } }, "node_modules/@octokit/app": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.0.2.tgz", - "integrity": "sha512-NCSCktSx+XmjuSUVn2dLfqQ9WIYePGP95SDJs4I9cn/0ZkeXcPkaoCLl64Us3dRKL2ozC7hArwze5Eu+/qt1tg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz", + "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==", "dependencies": { "@octokit/auth-app": "^6.0.0", "@octokit/auth-unauthenticated": "^5.0.0", @@ -2266,42 +2473,61 @@ "node": ">= 18" } }, - "node_modules/@octokit/auth-app": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.0.1.tgz", - "integrity": "sha512-tjCD4nzQNZgmLH62+PSnTF6eGerisFgV4v6euhqJik6yWV96e1ZiiGj+NXIqbgnpjLmtnBqVUrNyGKu3DoGEGA==", + "node_modules/@octokit/app/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + }, + "node_modules/@octokit/app/node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz", + "integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==", "dependencies": { - "@octokit/auth-oauth-app": "^7.0.0", - "@octokit/auth-oauth-user": "^4.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "deprecation": "^2.3.1", - "lru-cache": "^10.0.0", - "universal-github-app-jwt": "^1.1.1", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^12.6.0" }, "engines": { "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/app/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" } }, - "node_modules/@octokit/auth-app/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "node_modules/@octokit/auth-app": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.1.tgz", + "integrity": "sha512-VrTtzRpyuT5nYGUWeGWQqH//hqEZDV+/yb6+w5wmWpmmUA1Tx950XsAc2mBBfvusfcdF2E7w8jZ1r1WwvfZ9pA==", + "dependencies": { + "@octokit/auth-oauth-app": "^7.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", + "deprecation": "^2.3.1", + "lru-cache": "^10.0.0", + "universal-github-app-jwt": "^1.1.2", + "universal-user-agent": "^6.0.0" + }, "engines": { - "node": "14 || >=16.14" + "node": ">= 18" } }, "node_modules/@octokit/auth-oauth-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.0.1.tgz", - "integrity": "sha512-RE0KK0DCjCHXHlQBoubwlLijXEKfhMhKm9gO56xYvFmP1QTMb+vvwRPmQLLx0V+5AvV9N9I3lr1WyTzwL3rMDg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz", + "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==", "dependencies": { - "@octokit/auth-oauth-device": "^6.0.0", - "@octokit/auth-oauth-user": "^4.0.0", - "@octokit/request": "^8.0.2", - "@octokit/types": "^12.0.0", + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", "@types/btoa-lite": "^1.0.0", "btoa-lite": "^1.0.0", "universal-user-agent": "^6.0.0" @@ -2311,13 +2537,13 @@ } }, "node_modules/@octokit/auth-oauth-device": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.0.1.tgz", - "integrity": "sha512-yxU0rkL65QkjbqQedgVx3gmW7YM5fF+r5uaSj9tM/cQGVqloXcqP2xK90eTyYvl29arFVCW8Vz4H/t47mL0ELw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz", + "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==", "dependencies": { - "@octokit/oauth-methods": "^4.0.0", - "@octokit/request": "^8.0.0", - "@octokit/types": "^12.0.0", + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -2325,14 +2551,14 @@ } }, "node_modules/@octokit/auth-oauth-user": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.0.1.tgz", - "integrity": "sha512-N94wWW09d0hleCnrO5wt5MxekatqEJ4zf+1vSe8MKMrhZ7gAXKFOKrDEZW2INltvBWJCyDUELgGRv8gfErH1Iw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz", + "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==", "dependencies": { - "@octokit/auth-oauth-device": "^6.0.0", - "@octokit/oauth-methods": "^4.0.0", - "@octokit/request": "^8.0.2", - "@octokit/types": "^12.0.0", + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", "btoa-lite": "^1.0.0", "universal-user-agent": "^6.0.0" }, @@ -2360,16 +2586,29 @@ "node": ">= 18" } }, + "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + }, + "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, "node_modules/@octokit/core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz", - "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", + "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", "dependencies": { "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, @@ -2378,12 +2617,11 @@ } }, "node_modules/@octokit/endpoint": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz", - "integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz", + "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==", "dependencies": { - "@octokit/types": "^12.0.0", - "is-plain-object": "^5.0.0", + "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -2391,12 +2629,12 @@ } }, "node_modules/@octokit/graphql": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", - "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", + "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", "dependencies": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^12.0.0", + "@octokit/request": "^8.3.0", + "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -2404,9 +2642,9 @@ } }, "node_modules/@octokit/oauth-app": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.0.0.tgz", - "integrity": "sha512-bNMkS+vJ6oz2hCyraT9ZfTpAQ8dZNqJJQVNaKjPLx4ue5RZiFdU1YWXguOPR8AaSHS+lKe+lR3abn2siGd+zow==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.1.0.tgz", + "integrity": "sha512-nIn/8eUJ/BKUVzxUXd5vpzl1rwaVxMyYbQkNZjHrF7Vk/yu98/YDF/N2KeWO7uZ0g3b5EyiFXFkZI8rJ+DH1/g==", "dependencies": { "@octokit/auth-oauth-app": "^7.0.0", "@octokit/auth-oauth-user": "^4.0.0", @@ -2430,42 +2668,29 @@ } }, "node_modules/@octokit/oauth-methods": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.0.0.tgz", - "integrity": "sha512-dqy7BZLfLbi3/8X8xPKUKZclMEK9vN3fK5WF3ortRvtplQTszFvdAGbTo71gGLO+4ZxspNiLjnqdd64Chklf7w==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz", + "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==", "dependencies": { "@octokit/oauth-authorization-url": "^6.0.2", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.0.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", "btoa-lite": "^1.0.0" }, "engines": { "node": ">= 18" } }, - "node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" - }, - "node_modules/@octokit/oauth-methods/node_modules/@octokit/types": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz", - "integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" - } - }, "node_modules/@octokit/openapi-types": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz", - "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==" + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" }, "node_modules/@octokit/plugin-paginate-graphql": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.0.tgz", - "integrity": "sha512-7HcYW5tP7/Z6AETAPU14gp5H5KmCPT3hmJrS/5tO7HIgbwenYmgw4OY9Ma54FDySuxMwD+wsJlxtuGWwuZuItA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.1.tgz", + "integrity": "sha512-R8ZQNmrIKKpHWC6V2gum4x9LG2qF1RxRjo27gjQcG3j+vf2tLsEfE7I/wRWEPzYMaenr1M+qDAtNcwZve1ce1A==", "engines": { "node": ">= 18" }, @@ -2474,31 +2699,31 @@ } }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.4.tgz", - "integrity": "sha512-MvZx4WvfhBnt7PtH5XE7HORsO7bBk4er1FgRIUr1qJ89NR2I6bWjGyKsxk8z42FPQ34hFQm0Baanh4gzdZR4gQ==", + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.1.tgz", + "integrity": "sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==", "dependencies": { - "@octokit/types": "^12.3.0" + "@octokit/types": "^13.5.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=5" + "@octokit/core": "5" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.0.0.tgz", - "integrity": "sha512-16VkwE2v6rXU+/gBsYC62M8lKWOphY5Lg4wpjYnVE9Zbu0J6IwiT5kILoj1YOB53XLmcJR+Nqp8DmifOPY4H3g==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", + "integrity": "sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==", "dependencies": { - "@octokit/types": "^12.0.0" + "@octokit/types": "^13.5.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=5" + "@octokit/core": "^5" } }, "node_modules/@octokit/plugin-retry": { @@ -2517,12 +2742,25 @@ "@octokit/core": ">=5" } }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, "node_modules/@octokit/plugin-throttling": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.0.0.tgz", - "integrity": "sha512-OkMbHYUidj81q92YRkPzWmwXkEtsI3KOcSkNm763aqUOh9IEplyX05XjKAdZFANAvaYH0Q4JBZwu4h2VnPVXZA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", "dependencies": { - "@octokit/types": "^12.0.0", + "@octokit/types": "^12.2.0", "bottleneck": "^2.15.3" }, "engines": { @@ -2532,15 +2770,27 @@ "@octokit/core": "^5.0.0" } }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, "node_modules/@octokit/request": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.3.tgz", - "integrity": "sha512-iUvXP4QmysS8kyE/a4AGwR0A+tHDVxgW6TmPd2ci8/Xc8KjlBtTKSDpZlUT5Y4S4Nu+eM8LvbOYjVAp/sz3Gpg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz", + "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==", "dependencies": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "is-plain-object": "^5.0.0", + "@octokit/endpoint": "^9.0.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -2548,11 +2798,11 @@ } }, "node_modules/@octokit/request-error": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", - "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", + "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", "dependencies": { - "@octokit/types": "^12.0.0", + "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, @@ -2561,21 +2811,21 @@ } }, "node_modules/@octokit/types": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.3.0.tgz", - "integrity": "sha512-nJ8X2HRr234q3w/FcovDlA+ttUU4m1eJAourvfUUtwAWeqL8AsyRqfnLvVnYn3NFbUnsmzQCzLNdFerPwdmcDQ==", + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", "dependencies": { - "@octokit/openapi-types": "^19.0.2" + "@octokit/openapi-types": "^22.2.0" } }, "node_modules/@octokit/webhooks": { - "version": "12.0.7", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.0.7.tgz", - "integrity": "sha512-g9HKed5x+72yBZcTRAYNiR7Ane/iOwNEjd9sdu+Jvrcc/Z0FdkjYBMfBmQdowbOhHM4Nek9zpGC5u3C3Wxfhsg==", + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.2.0.tgz", + "integrity": "sha512-CyuLJ0/P7bKZ+kIYw+fnkeVdhUzNuDKgNSI7pU/m7Nod0T7kP+s4s2f0pNmG9HL8/RZN1S0ZWTDll3VTMrFLAw==", "dependencies": { "@octokit/request-error": "^5.0.0", - "@octokit/webhooks-methods": "^4.0.0", - "@octokit/webhooks-types": "7.1.0", + "@octokit/webhooks-methods": "^4.1.0", + "@octokit/webhooks-types": "7.4.0", "aggregate-error": "^3.1.0" }, "engines": { @@ -2583,22 +2833,22 @@ } }, "node_modules/@octokit/webhooks-methods": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.0.0.tgz", - "integrity": "sha512-M8mwmTXp+VeolOS/kfRvsDdW+IO0qJ8kYodM/sAysk093q6ApgmBXwK1ZlUvAwXVrp/YVHp6aArj4auAxUAOFw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.1.0.tgz", + "integrity": "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ==", "engines": { "node": ">= 18" } }, "node_modules/@octokit/webhooks-types": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.1.0.tgz", - "integrity": "sha512-y92CpG4kFFtBBjni8LHoV12IegJ+KFxLgKRengrVjKmGE5XMeCuGvlfRe75lTRrgXaG6XIWJlFpIDTlkoJsU8w==" + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.4.0.tgz", + "integrity": "sha512-FE2V+QZ2UYlh+9wWd5BPLNXG+J/XUD/PPq0ovS+nCcGX4+3qVbi3jYOmCTW48hg9SBBLtInx9+o7fFt4H5iP0Q==" }, "node_modules/@panva/hkdf": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz", - "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.0.tgz", + "integrity": "sha512-97ZQvZJ4gJhi24Io6zI+W7B67I82q1I8i3BSzQ4OyZj1z4OW87/ruF26lrMES58inTKLy2KgVIDcx8PU4AaANQ==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -2607,7 +2857,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -2707,20 +2956,20 @@ } }, "node_modules/@react-types/checkbox": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.8.0.tgz", - "integrity": "sha512-IBJ2bAsb3xoXaL+f0pwfRLDvRkhxfcX/q4NRJ2oT9jeHLU+j6svgK1Dqk8IGmY+vw1ltKbbMlIVeVonKQ3fgHw==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.8.1.tgz", + "integrity": "sha512-5/oVByPw4MbR/8QSdHCaalmyWC71H/QGgd4aduTJSaNi825o+v/hsN2/CH7Fq9atkLKsC8fvKD00Bj2VGaKriQ==", "dependencies": { - "@react-types/shared": "^3.23.0" + "@react-types/shared": "^3.23.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, "node_modules/@react-types/shared": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.0.tgz", - "integrity": "sha512-GQm/iPiii3ikcaMNR4WdVkJ4w0mKtV3mLqeSfSqzdqbPr6vONkqXbh3RhPlPmAJs1b4QHnexd/wZQP3U9DHOwQ==", + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.1.tgz", + "integrity": "sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==", "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } @@ -2745,14 +2994,20 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/@redocly/config": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.6.1.tgz", + "integrity": "sha512-p4mlj+CD3Byec3wOxDlDln0B0gOcNvEkpl4jn3/e9y8h11ogzXnWxnlJAtE5Kcr1ByujS/7Mbt01df9z1xfMbg==" + }, "node_modules/@redocly/openapi-core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.2.0.tgz", - "integrity": "sha512-Ccft2n/JiF4u2crmj1cdDzPq6C40U7NgLZ+p/BxzAFXbfrddr/5FN0HMJPHT/op329qqv2P2jUrXsV2Bp+rzEQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.16.0.tgz", + "integrity": "sha512-z06h+svyqbUcdAaePq8LPSwTPlm6Ig7j2VlL8skPBYnJvyaQ2IN7x/JkOvRL4ta+wcOCBdAex5JWnZbKaNktJg==", "dependencies": { "@redocly/ajv": "^8.11.0", - "@types/node": "^14.11.8", + "@redocly/config": "^0.6.0", "colorette": "^1.2.0", + "https-proxy-agent": "^7.0.4", "js-levenshtein": "^1.1.6", "js-yaml": "^4.1.0", "lodash.isequal": "^4.5.0", @@ -2762,20 +3017,8 @@ "yaml-ast-parser": "0.0.43" }, "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@redocly/openapi-core/node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" - }, - "node_modules/@redocly/openapi-core/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" + "node": ">=14.19.0", + "npm": ">=7.0.0" } }, "node_modules/@redocly/openapi-core/node_modules/minimatch": { @@ -2798,9 +3041,9 @@ } }, "node_modules/@rushstack/eslint-patch": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz", - "integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz", + "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "dev": true }, "node_modules/@sentry/browser": { @@ -2934,9 +3177,9 @@ "dev": true }, "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "peer": true, "dependencies": { @@ -3056,11 +3299,6 @@ "node": "^12.20 || >=14.13" } }, - "node_modules/@stoplight/http-spec/node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" - }, "node_modules/@stoplight/json": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.21.0.tgz", @@ -3092,11 +3330,6 @@ "json-schema-generator": "bin/cli.js" } }, - "node_modules/@stoplight/json-schema-generator/node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, "node_modules/@stoplight/json-schema-merge-allof": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@stoplight/json-schema-merge-allof/-/json-schema-merge-allof-0.8.0.tgz", @@ -3146,9 +3379,9 @@ } }, "node_modules/@stoplight/json-schema-viewer": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@stoplight/json-schema-viewer/-/json-schema-viewer-4.16.0.tgz", - "integrity": "sha512-5IYPI7sEbyQq2QyDhU9MqDGi9/KhNvaKsG+VOVelTcSqs56Vg9ASbSr/U6dISi5FK0FEfTh6FUggToUAzpr4NQ==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@stoplight/json-schema-viewer/-/json-schema-viewer-4.16.1.tgz", + "integrity": "sha512-gQ1v9/Dj1VP43zERuZoFMOr7RQDBZlgfF7QFh+R0sadP6W30oYFJtD7y2PG2gIQDohKElVuPjhFUbVH/81MnSg==", "dependencies": { "@stoplight/json": "^3.20.1", "@stoplight/json-schema-tree": "^4.0.0", @@ -3350,61 +3583,6 @@ "react-dom": ">=16.8" } }, - "node_modules/@stoplight/markdown-viewer/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/@stoplight/markdown-viewer/node_modules/hast-util-parse-selector": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", - "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", - "dependencies": { - "@types/hast": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@stoplight/markdown-viewer/node_modules/hastscript": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", - "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", - "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^3.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@stoplight/markdown-viewer/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/@stoplight/markdown-viewer/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/@stoplight/markdown/node_modules/@stoplight/types": { "version": "12.5.0", "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-12.5.0.tgz", @@ -3536,26 +3714,6 @@ "node": ">=6" } }, - "node_modules/@stoplight/mosaic-code-editor/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "dependencies": { - "@babel/runtime": "^7.1.2" - } - }, - "node_modules/@stoplight/mosaic-code-editor/node_modules/use-resize-observer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", - "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, "node_modules/@stoplight/mosaic-code-viewer": { "version": "1.53.2", "resolved": "https://registry.npmjs.org/@stoplight/mosaic-code-viewer/-/mosaic-code-viewer-1.53.2.tgz", @@ -3640,26 +3798,6 @@ "node": ">=6" } }, - "node_modules/@stoplight/mosaic-code-viewer/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "dependencies": { - "@babel/runtime": "^7.1.2" - } - }, - "node_modules/@stoplight/mosaic-code-viewer/node_modules/use-resize-observer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", - "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, "node_modules/@stoplight/mosaic/node_modules/@react-types/button": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.4.1.tgz", @@ -3751,32 +3889,12 @@ "node": ">=6" } }, - "node_modules/@stoplight/mosaic/node_modules/dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "dependencies": { - "@babel/runtime": "^7.1.2" - } - }, - "node_modules/@stoplight/mosaic/node_modules/use-resize-observer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", - "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, - "node_modules/@stoplight/ordered-object-literal": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", - "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==", - "engines": { - "node": ">=8" + "node_modules/@stoplight/ordered-object-literal": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", + "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==", + "engines": { + "node": ">=8" } }, "node_modules/@stoplight/path": { @@ -3830,362 +3948,446 @@ "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==" }, "node_modules/@swagger-api/apidom-ast": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-0.77.0.tgz", - "integrity": "sha512-BqYc3oZEJ23x9KlamGjNbIymhKId0qxcqykjet7fO3NWm1c68ix/S+VgheTKig8Gl4IJ2lT+Cz3C178ia90ydQ==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-alpha.5.tgz", + "integrity": "sha512-ZH3xryzmwd8OvUdOJH4ujNAyQMXN6NCrRT0HGR8z9TnA0nFPFoOAswq7317mCn77VJmViu/tpCuvmRS0a9BROg==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-error": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "unraw": "^3.0.0" } }, "node_modules/@swagger-api/apidom-core": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-0.77.0.tgz", - "integrity": "sha512-Yec/Ek6tH8uaHpFsL8/KfOjkunUdQOf42467QfAkG4Df1u9fdrBIuk8y6oFOO5KAE4WXNjoOQW+Z865WCMwmkA==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-alpha.5.tgz", + "integrity": "sha512-iArtPxwcQ/EpQU/VqwBDrD+F0lngyUyLVCa8zR4gT+7mP6fpiU7jcerizw0hDpFmvieXddx5UdfO28Pxuq204g==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.77.0", - "@swagger-api/apidom-error": "^0.77.0", - "@types/ramda": "~0.29.3", + "@swagger-api/apidom-ast": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", "minim": "~0.23.8", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "short-unique-id": "^5.0.2", - "stampit": "^4.3.2" + "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-error": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-0.77.0.tgz", - "integrity": "sha512-7QQPwUdGAxxvAegJntbzuBD0ApPsdMay6nV3UpxQs/F4q4cTaxeTX8HCp2NefXR4B6VHps0oVvIyXf/LDQUtYw==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-alpha.5.tgz", + "integrity": "sha512-5UEgSZuQPdkqKSKDtRXQ0cm7x1o4EPyusLBVsCG4l8QtJvAhG1OOpEzJbTZ48/nRt7VkbK7MTj/up+oEILzVvw==", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@babel/runtime-corejs3": "^7.20.7" } }, "node_modules/@swagger-api/apidom-json-pointer": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-0.77.0.tgz", - "integrity": "sha512-VPslp6GbloFDNYTq3QV4z+ByxiqIDQVqqDebVhg70YWriU2+tVJCNV55770AZa8Yqj7QOIafXQoPFg8uAYu0yw==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-alpha.5.tgz", + "integrity": "sha512-eDAz7/UaGpGCvB0y1GoRjFwxFWseCsF/0ZYIQvvq9PS025inc/I6M+XX8dWMmkpNpbbf+KfD7WlwfqnUZLv/MQ==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-error": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-ns-api-design-systems": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-0.77.0.tgz", - "integrity": "sha512-FaadpkDoClkomlOv4yT7Wc+Q+kb0uN7iBoo7j8+vnI2ID13I3FDaeqUcADsGdIgNT3JxaK/esJVIln+65TTdwA==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.0.0-alpha.5.tgz", + "integrity": "sha512-aq9Ix2Wo2TMfYW3HmheTO3qVd2MYrdinjLFHn9uozzC2x+CSzALhvKkwOc29HiGOn4QQ6QHHPRojNgD86WkwUg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-error": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-1": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-asyncapi-2": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-0.77.0.tgz", - "integrity": "sha512-4IbR49AIihXiegT/NHLCfxp6l+zco/ztUIUoJhnJuRdZ11U1PXaVhYGEdmQX+FoDtEUim17p5FnXSzi0uatbIw==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-alpha.5.tgz", + "integrity": "sha512-JFtQBhCOkYuyNVcYGMFd9+U0UO6lEj9kO5qCgUjPOTgkOpZOZQslVEtg3TDmRlBATwVdmRv39xy3ZLK8O/JdmQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-json-schema-draft-7": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-0.77.0.tgz", - "integrity": "sha512-LLfNNDuoLNgKgN8ddPJxc5QCYgst3G1BnXEU+0lIFyVlFb5xowZiW4utYtfx/eRBy+UxpgTIk04hvvbaYppFZQ==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-alpha.5.tgz", + "integrity": "sha512-aDmcpGikL5JZmDTg7J6EJfLFjtUmX/MfduS4hQeopFCkw91dZsqxO10j7KEiRVVuJBuGStbYoHI5aIsQTlebzA==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.77.0", - "@swagger-api/apidom-core": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "@swagger-api/apidom-ast": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-6": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-0.77.0.tgz", - "integrity": "sha512-1tXzvGdc96mHelU9IXp28pLRf/OHqCTOKtUNywwhmICEQHs9PVrPpFq4fuMjLA+QvusdUA0Z4PsYR6d51Qnv3Q==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-alpha.5.tgz", + "integrity": "sha512-ylh96E59aaV1VDv9sDrNwpTmjVT6vmOSncpmytlc0ynb374dwZkLZ63Hd30rcMFAhKmg5aYOG+i5O1QXKFYz8A==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-error": "^0.77.0", - "@swagger-api/apidom-ns-json-schema-draft-4": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-7": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-0.77.0.tgz", - "integrity": "sha512-UTwogsJ7gnCcXlwIEriezPwi6Q84rgxYrQxbwqEJN6VrYWb0R1MPJ+CnD6XkkciEI8ETfDs/3NKqto98UjRgkw==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-alpha.5.tgz", + "integrity": "sha512-Mks9gabJvz4atkjzLDwjWbo12xirul7a9ifHYZQJc/jfVKfVNy1e3QgFG1+EbSWWG5Yfbr3WKyxUDJLgr75qKg==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + } + }, + "node_modules/@swagger-api/apidom-ns-openapi-2": { + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-alpha.5.tgz", + "integrity": "sha512-uY+1G4oRf9UT/6sGuatvWKstmlRnEiN9XqaVvV8euXESxI4jtwcPbRwoEX31vEYXoTqq2ZScFy8UQJ2CJ2ZADw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-error": "^0.77.0", - "@swagger-api/apidom-ns-json-schema-draft-6": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-openapi-3-0": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-0.77.0.tgz", - "integrity": "sha512-gqd14CVh+ufC8HSVCMmBfpBU7I/2L2fb9TO3b3mI8K38D+k2dbgBsxOch3efo7x+Diwu8QNdwQFuC2n7WAwO5Q==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-alpha.5.tgz", + "integrity": "sha512-UAOGZaGMDVRQ10l8OgXCAfxS9PxGoCW66o/vFmhPfrK8NwU1GEo6sYHYoo1mflNMHCN2eVYyM5LxA+qYm0SJgQ==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-json-schema-draft-4": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-ns-openapi-3-1": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-0.77.0.tgz", - "integrity": "sha512-UcGE5xMKRO+T7oFDIqYjr1kOHKe37MuUsd1CmTwu+QqZALk4L4IwOs6UUxDPyDLNeAP9g8VoXPJAPSFV0IEyYA==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-alpha.5.tgz", + "integrity": "sha512-8VkdZ2MfxXIdmzQZrV0qGk18MG7XNJKIL3GT9lad9NyXyiKSvBVFJDmS4S43qcQTL0rjHXF6ds25yErDSTprjg==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-json-pointer": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-ns-workflows-1": { + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-workflows-1/-/apidom-ns-workflows-1-1.0.0-alpha.5.tgz", + "integrity": "sha512-6cMv37y4kftJySoMAeubz5yhHaRKnSK0YglvCv8v7rE2OBduR/yEITDOej2/KFAnt29LxkhotSbNsmHx0weICQ==", + "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.77.0", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-0": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" } }, "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-0.77.0.tgz", - "integrity": "sha512-dwotraK1i80r4zKhV2a8p0qaPBn3dA4e167KUoY/ugwmf1lAtKL+K/Ow74wiOxQME2VD6HkM/CUV0nFJUWCS2A==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-alpha.5.tgz", + "integrity": "sha512-QVWS2sPKA1sG52UIJut/St6+j7zO8QxzPlL5akR/8QPX2FWKqmw808Ewvjq9WLtqlPhVY2G33tv90d4/FJUNwQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-api-design-systems": "^0.77.0", - "@swagger-api/apidom-parser-adapter-json": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-yaml": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-0.77.0.tgz", - "integrity": "sha512-ftHsFBgNdcpMqVkRXwWyatLjaaOFdgecKPA6/1q0F0NRGEDTdWocyI4KkLuAywbpo6XsbwOHZG2cK26cbLlBEA==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.0.0-alpha.5.tgz", + "integrity": "sha512-T7UD/SWd5u2zlPyswDdtfAStm6Qt5hQWAWvCmQKxy37qJA9QGXcQKNavaSMPGvN660hufNaJEBxgJ/B0Zd5iaw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-api-design-systems": "^0.77.0", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-2": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-0.77.0.tgz", - "integrity": "sha512-nV2aEmZ1YeXSLbnymBNlhBdwWgQAg3DPO1bIEPJifz6zopBjcW+q+MjGAdyj57dmqygL3QoddroKCF7wGgAlLg==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-alpha.5.tgz", + "integrity": "sha512-UfCS9DFIURTUfaHfmEn8omHaevIV2i24Ncp46M/Pnk6JwZHjAEMxmPxsgMl4TTGbzqvySUQsJka8Qz1ziYZ1og==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-asyncapi-2": "^0.77.0", - "@swagger-api/apidom-parser-adapter-json": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-0.77.0.tgz", - "integrity": "sha512-fiYfoOttR3zbquaMzZji/+KcbGK092HQjE0HQpGvu/HfJWGfg51A0iFoWE+ebx2tklN3AhV6CD2NJuRa9DlphA==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.0.0-alpha.5.tgz", + "integrity": "sha512-X5avFyLnlu6Zjyul35f8Ff0DRE70aNc+Bk7il+eV8g+FR/qgrmuNziQEBOhCrIUnYB1kFbTty6BZRsNLdjW9XQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-asyncapi-2": "^0.77.0", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-json": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-0.77.0.tgz", - "integrity": "sha512-nx8zqwHIhI0E+vpgQZ2rONdrmmdnSVum3Qct2h6JetYr72UWnFDqVgxOpGbOScMH1kvG7u2n5LpfjJw02uNDKg==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-alpha.5.tgz", + "integrity": "sha512-NdVjlRrtr1EvrBsk6DHSkjI8zdnSve/bjeGgo0NR2IRmA/8BRcY6rffM1BR76Ku+CjxhCB2mfQxotilD71dL+g==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.77.0", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-error": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2", + "@swagger-api/apidom-ast": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "tree-sitter": "=0.20.4", - "tree-sitter-json": "=0.20.0", + "tree-sitter-json": "=0.20.2", "web-tree-sitter": "=0.20.3" } }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-2": { + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-alpha.5.tgz", + "integrity": "sha512-qOwQl2WezfdDVmtf9ZlOiqT1hcDS52j7ZbBdH9MqMGJ+/mo6sv0qEY2ZXS104lWeRamgi4o/4o4jGqjZS1YrMg==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-0": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-0.77.0.tgz", - "integrity": "sha512-J9hiyVJg19SVgbemK/Ug1l4FjXZ4zCsxTKAlYxVSwjONJI4YdE2SqKG26JagBaTMHeJRZYK4BAC2pXAvAUKISg==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.0.0-alpha.5.tgz", + "integrity": "sha512-t5oj7XteTu2Yh8uNkzXAcKU81CQky+q6Qt/ImQ/S6MGxpXJnWwgVfm/j/dH2wnHFKghNS3vgm6IewpojSbUw4w==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-0": "^0.77.0", - "@swagger-api/apidom-parser-adapter-json": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-1": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-0.77.0.tgz", - "integrity": "sha512-iLputU+USOtJNcUpr5mEMtWFIgdzYGAor4gujfhBbhctGDzdtFAumBU5j/hLRBQoHbfZ00o5379ekxHMDi2/7w==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.0.0-alpha.5.tgz", + "integrity": "sha512-w0G53HXYdzcespfa3atN90jVLDRoH9FU7XEWG4DvFWM90WGwuNscojcaB28r8pZMhSQAKMPxggh6PnmvK3gdEQ==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-2": { + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.0.0-alpha.5.tgz", + "integrity": "sha512-nfeYRL0o6QwtKsyF30d2JmtW7fzoI/EYKSFgzaDm7IFlrQWMpB6BidpZKdk5MtYN4zvmfAM+lOhrqR7a5BvHMg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-1": "^0.77.0", - "@swagger-api/apidom-parser-adapter-json": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-0.77.0.tgz", - "integrity": "sha512-SDZkiCF5863Q6yfCtL4pZkh0s7J6Q8kZodW8CN9zHQ025BbjfbbddTXbSefx7h/Dc3g4QyGi2XT+Qu4lvrlbkg==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.0.0-alpha.5.tgz", + "integrity": "sha512-HRziGD/YUcO21hmDIYNzwYivp/faeZRxcq8Gex7RLLhJZ60fGTJJ1k1yhWFPNSe9DEJUNBN949SDxMdZnGT9PQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-0": "^0.77.0", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-0.77.0.tgz", - "integrity": "sha512-JxfVLS4xB7UctZPaPUZyr0WbOh7DGchfCGJvMYCgTQ+oxJaKxUvAaJveA5Ch6DkMdwLJDIRBYFJGUXQOfKN1eQ==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.0.0-alpha.5.tgz", + "integrity": "sha512-aul2wSOvkdp9jQjSv1pvEGllVaDUnTKmRbCy7M/dFQyIhJQBvwW+/Cu//PprzAODtFNraOBjIXiJ5tVdv6NuIQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-1": "^0.77.0", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.0.0" + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-workflows-json-1": { + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-json-1/-/apidom-parser-adapter-workflows-json-1-1.0.0-alpha.5.tgz", + "integrity": "sha512-R1LVe/gx7fRSCuDmmN3qScWonz6Xlaw11J+NAfiJzrNXBy1Qa1mCxgGs47w0slQN+FjYkVj5Y/q29jJgpUbLHA==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-workflows-1": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-workflows-yaml-1": { + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-yaml-1/-/apidom-parser-adapter-workflows-yaml-1-1.0.0-alpha.5.tgz", + "integrity": "sha512-W5wD+TdGNdW4aP9uqkxFbVmjWvLOXyV02VvyStyTlzxdUaPzKY3FGaxjxk8TGVRqwe2yEQVUc2zfGalrScA/Sg==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ns-workflows-1": "^1.0.0-alpha.5", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-0.77.0.tgz", - "integrity": "sha512-ID3WXpa+4+/ip+4IlDHOvGevS/4M/OzZvtHhNReY4fSz+kTVIdp0C4tqDHcll+2+U360O4Y+bAChvI5BlrYgcw==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-alpha.5.tgz", + "integrity": "sha512-21TIQPkB+Z4ekNj5dh1uN0dhOBBCPeK572YpooA/pBTFLeH6Wtildx7ZZYfpJEejHaQKaqoRx3hp0G42GDOb7g==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^0.77.0", - "@swagger-api/apidom-core": "^0.77.0", - "@swagger-api/apidom-error": "^0.77.0", - "@types/ramda": "~0.29.3", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2", + "@swagger-api/apidom-ast": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", "tree-sitter": "=0.20.4", "tree-sitter-yaml": "=0.5.0", "web-tree-sitter": "=0.20.3" } }, "node_modules/@swagger-api/apidom-reference": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-0.77.0.tgz", - "integrity": "sha512-hwViVP7CORnuMYpxav1LH1YPslJyUAx3YsyMwrg5yeo7d8Fn1PCV7VYyFwvjgfOOdFinDkjZxKA9GXDVk2mR0g==", + "version": "1.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-alpha.5.tgz", + "integrity": "sha512-zPMTScWI8oVUAT//RdAhl9GJuwtQLibP8iCrqFQDGjBzKQS5Uxz4hSXr/jqKPdkCJXbEoP94yYjvQjtI5yrv1A==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^0.77.0", - "@types/ramda": "~0.29.3", + "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@types/ramda": "~0.30.0", "axios": "^1.4.0", "minimatch": "^7.4.3", "process": "^0.11.10", - "ramda": "~0.29.0", - "ramda-adjunct": "^4.1.1", - "stampit": "^4.3.2" + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" }, "optionalDependencies": { - "@swagger-api/apidom-error": "^0.77.0", - "@swagger-api/apidom-json-pointer": "^0.77.0", - "@swagger-api/apidom-ns-asyncapi-2": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-0": "^0.77.0", - "@swagger-api/apidom-ns-openapi-3-1": "^0.77.0", - "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^0.77.0", - "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^0.77.0", - "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.77.0", - "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.77.0", - "@swagger-api/apidom-parser-adapter-json": "^0.77.0", - "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^0.77.0", - "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.77.0", - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^0.77.0", - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^0.77.0", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.77.0" - } - }, - "node_modules/@swagger-api/apidom-reference/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" + "@swagger-api/apidom-error": "^1.0.0-alpha.1", + "@swagger-api/apidom-json-pointer": "^1.0.0-alpha.1", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-alpha.1", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-alpha.1", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.1", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.1", + "@swagger-api/apidom-ns-workflows-1": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-openapi-json-2": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-workflows-json-1": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-workflows-yaml-1": "^1.0.0-alpha.1", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.1" } }, "node_modules/@swagger-api/apidom-reference/node_modules/minimatch": { @@ -4217,14 +4419,14 @@ } }, "node_modules/@types/aws-lambda": { - "version": "8.10.126", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.126.tgz", - "integrity": "sha512-5eh4ffLdGYgGYI1Xr6W5L4IVse4RR7L2ns5OVUXA52nW5GFapIcGMcCzHAIMMOdpcQs3aGVxbvFlJNZH6IpgEQ==" + "version": "8.10.140", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.140.tgz", + "integrity": "sha512-4Dh3dk2TUcbdfHrX0Al90mNGJDvA9NBiTQPzbrjGi/dLxzKCGOYgT8YQ47jUKNFALkAJAadifq0pzyjIUlhVhg==" }, "node_modules/@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "peer": true, "dependencies": { @@ -4236,9 +4438,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", - "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "peer": true, "dependencies": { @@ -4246,9 +4448,9 @@ } }, "node_modules/@types/babel__template": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", - "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "peer": true, "dependencies": { @@ -4257,9 +4459,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", - "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "peer": true, "dependencies": { @@ -4267,9 +4469,9 @@ } }, "node_modules/@types/btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz", + "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==" }, "node_modules/@types/cookie": { "version": "0.6.0", @@ -4277,9 +4479,9 @@ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, "node_modules/@types/graceful-fs": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", - "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "peer": true, "dependencies": { @@ -4292,41 +4494,32 @@ "integrity": "sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==" }, "node_modules/@types/hast": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.6.tgz", - "integrity": "sha512-47rJE80oqPmFdVDCD7IheXBrVdwuBgsYwoczFvKmwfo2Mzsnt+V9OONsYauFmICb6lQPpCuXYJWejBNs4pDJRg==", + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", "dependencies": { "@types/unist": "^2" } }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.3.tgz", - "integrity": "sha512-Wny3a2UXn5FEA1l7gc6BbpoV5mD1XijZqgkp4TRgDCDL5r3B5ieOFGUX5h3n78Tr1MEG7BfvoM8qeztdvNU0fw==", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" @@ -4348,9 +4541,9 @@ "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==" + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -4359,9 +4552,9 @@ "dev": true }, "node_modules/@types/jsonwebtoken": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", - "integrity": "sha512-b0jGiOgHtZ2jqdPgPnP6WLCXZk1T8p06A/vPGzUvxpFGgKMbjXJDjC5m52ErqBnIuWZFgGoIJyRdeG5AyreJjA==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", + "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", "dependencies": { "@types/node": "*" } @@ -4380,26 +4573,26 @@ "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==" }, "node_modules/@types/node": { - "version": "20.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz", - "integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==", + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/nodemailer": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.14.tgz", - "integrity": "sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==", + "version": "6.4.15", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.15.tgz", + "integrity": "sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/parse5": { "version": "6.0.3", @@ -4407,9 +4600,9 @@ "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==" }, "node_modules/@types/pg": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.0.tgz", - "integrity": "sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==", + "version": "8.11.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz", + "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", "dev": true, "dependencies": { "@types/node": "*", @@ -4417,124 +4610,66 @@ "pg-types": "^4.0.1" } }, - "node_modules/@types/pg/node_modules/pg-types": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", - "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", - "dev": true, - "dependencies": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.1.0", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/pg/node_modules/postgres-array": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", - "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-bytea": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", - "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", - "dev": true, - "dependencies": { - "obuf": "~1.1.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/pg/node_modules/postgres-date": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", - "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-interval": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", - "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/@types/prop-types": { - "version": "15.7.10", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", - "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/ramda": { - "version": "0.29.6", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.29.6.tgz", - "integrity": "sha512-4XQ9hYQhCwOxfkoTsIPvDVXc75fY5+MLQHUpExX6ByvU1q+0vOYRLSjWAt1IydkE1hOuhwMH6KvV/9rhzgrvRw==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.0.tgz", + "integrity": "sha512-DQtfqUbSB18iM9NHbQ++kVUDuBWHMr6T2FpW1XTiksYRGjq4WnNPZLt712OEHEBJs7aMyJ68Mf2kGMOP1srVVw==", "dependencies": { - "types-ramda": "^0.29.5" + "types-ramda": "^0.30.0" } }, "node_modules/@types/react": { - "version": "18.2.53", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.53.tgz", - "integrity": "sha512-52IHsMDT8qATp9B9zoOyobW8W3/0QhaJQTw1HwRj0UY2yBpCAQ7+S/CqHYQ8niAm3p4ji+rWUQ9UCib0GxQ60w==", + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz", - "integrity": "sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==", - "devOptional": true, + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-transition-group": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.9.tgz", - "integrity": "sha512-ZVNmWumUIh5NhH8aMD9CR2hdW0fNuYInlocZHaZ+dgk/1K49j1w/HoAuK1ki+pgscQrOFRTlXeoURtuzEkV3dg==", + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", "dependencies": { "@types/react": "*" } }, "node_modules/@types/scheduler": { - "version": "0.16.4", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", - "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==" + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@types/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/stylis": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.1.tgz", - "integrity": "sha512-OSaMrXUKxVigGlKRrET39V2xdhzlztQ9Aqumn1WbCBKHOi9ry7jKSd7rkyj0GzmWaU960Rd+LpOFpLfx5bMQAg==" + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" }, "node_modules/@types/swagger-schema-official": { "version": "2.0.25", @@ -4542,9 +4677,9 @@ "integrity": "sha512-T92Xav+Gf/Ik1uPW581nA+JftmjWPgskw/WBf4TJzxRG/SJ+DfNnNE+WuZ4mrXuzflQMqMkm1LSYjzYW7MB1Cg==" }, "node_modules/@types/swagger-ui-react": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-react/-/swagger-ui-react-4.18.2.tgz", - "integrity": "sha512-uLiHyNGFdgQINNSq3hgca+TNnNMS7/X59M02EbmDfARgvh7KcB6wU+FVfkVY13qE4Zk9opKvjGqygwjjP7+u6w==", + "version": "4.18.3", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-react/-/swagger-ui-react-4.18.3.tgz", + "integrity": "sha512-Mo/R7IjDVwtiFPs84pWvh5pI9iyNGBjmfielxqbOh2Jv+8WVSDVe8Nu25kb5BOuV2xmGS3o33jr6nwDJMBcX+Q==", "dev": true, "dependencies": { "@types/react": "*" @@ -4559,9 +4694,9 @@ } }, "node_modules/@types/unist": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz", - "integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==" + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", @@ -4569,31 +4704,31 @@ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, "node_modules/@types/yargs": { - "version": "17.0.28", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.28.tgz", - "integrity": "sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", - "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/type-utils": "6.9.0", - "@typescript-eslint/utils": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -4619,15 +4754,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", - "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/typescript-estree": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -4646,88 +4781,14 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", - "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", - "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", - "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", - "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.9.1", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", - "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4738,13 +4799,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", - "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.9.0", - "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -4765,9 +4826,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", - "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4778,16 +4839,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", - "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -4805,17 +4867,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", - "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -4829,13 +4891,19 @@ "eslint": "^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", - "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4857,15 +4925,10 @@ "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" - }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", + "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4883,6 +4946,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -4927,19 +5001,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4949,17 +5010,14 @@ } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, "node_modules/any-promise": { @@ -4981,6 +5039,11 @@ "node": ">= 8" } }, + "node_modules/apg-lite": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/apg-lite/-/apg-lite-1.0.3.tgz", + "integrity": "sha512-lOoNkL7vN7PGdyQMFPey1aok2oVVqvs3n7UMFBRvQ9FoELSbKhgPc3rd7JptaGwCmo4125gLX9Cqb8ElvLCFaQ==" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -4993,37 +5056,40 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -5042,17 +5108,18 @@ "node": ">=8" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", - "dev": true, + "node_modules/array.prototype.filter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.4.tgz", + "integrity": "sha512-r+mCJ7zXgXElgR4IRC+fkvNCeoaavWBs6EdCso5Tbcf+iEMKzBU/His60lt34WEZ9vlb8wDkZvQGcVI5GwkfoQ==", + "peer": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-array-method-boxes-properly": "^1.0.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" }, "engines": { "node": ">= 0.4" @@ -5061,14 +5128,53 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "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" }, @@ -5097,31 +5203,46 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.tosorted": { + "node_modules/array.prototype.toreversed": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", - "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "es-shim-unscopables": "^1.0.0" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "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", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", "is-shared-array-buffer": "^1.0.2" }, "engines": { @@ -5132,33 +5253,16 @@ } }, "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/autolinker": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz", @@ -5168,9 +5272,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.16", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", - "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "dev": true, "funding": [ { @@ -5187,9 +5291,9 @@ } ], "dependencies": { - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001538", - "fraction.js": "^4.3.6", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -5205,9 +5309,12 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -5216,31 +5323,31 @@ } }, "node_modules/axe-core": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.2.tgz", - "integrity": "sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", + "integrity": "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/axios": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", - "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", "dev": true, "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/babel-jest": { @@ -5265,6 +5372,82 @@ "@babel/core": "^7.8.0" } }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -5427,12 +5610,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { @@ -5462,20 +5648,20 @@ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -5497,9 +5683,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "devOptional": true, "funding": [ { @@ -5516,10 +5702,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -5603,12 +5789,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5655,9 +5847,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001616", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", - "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", + "version": "1.0.30001637", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001637.tgz", + "integrity": "sha512-1x0qRI1mD1o9e+7mBI7XtzFAP4XszbHaVWsMiGbSPLYekKTJF7K+FNk6AsXH4sUpc+qrsI3pVgf1Jdl/uGkuSQ==", "funding": [ { "type": "opencollective", @@ -5683,18 +5875,24 @@ } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" } }, "node_modules/char-regex": { @@ -5742,17 +5940,61 @@ "node": ">=4.0.0" } }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "peer": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "peer": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "peer": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -5765,6 +6007,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -5802,16 +6047,16 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true, "peer": true }, "node_modules/classnames": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", - "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" }, "node_modules/clean-stack": { "version": "2.2.0", @@ -5839,34 +6084,98 @@ "node": ">=12" } }, - "node_modules/clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">=7.0.0" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "peer": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/collect-v8-coverage": { + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", @@ -5874,20 +6183,17 @@ "peer": true }, "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "color-name": "1.1.3" } }, "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/colorette": { "version": "1.4.0", @@ -5906,22 +6212,19 @@ } }, "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "peer": true }, "node_modules/compute-gcd": { "version": "1.2.1", @@ -5950,16 +6253,14 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "devOptional": true, - "peer": true + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -5973,9 +6274,9 @@ } }, "node_modules/core-js": { - "version": "3.33.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz", - "integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", + "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5983,9 +6284,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.33.0.tgz", - "integrity": "sha512-FKSIDtJnds/YFIEaZ4HszRX7hkxGpNKM7FC9aJ9WLJbSd3lD4vOltFuVIBLR8asSx9frkTSqL0dw90SKQxgKrg==", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.1.tgz", + "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6037,6 +6338,82 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/create-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/create-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/create-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", @@ -6074,6 +6451,22 @@ "hyphenate-style-name": "^1.0.3" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "peer": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css-selector-parser": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz", @@ -6101,6 +6494,26 @@ "node": ">=8.0.0" } }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "peer": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -6119,9 +6532,9 @@ } }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -6129,10 +6542,58 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -6166,9 +6627,9 @@ } }, "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, "peer": true, "peerDependencies": { @@ -6180,6 +6641,38 @@ } } }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -6203,24 +6696,25 @@ } }, "node_modules/define-data-property": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", - "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -6254,19 +6748,10 @@ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "optional": true, "engines": { "node": ">=8" @@ -6314,6 +6799,12 @@ "node": ">=8" } }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "peer": true + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -6333,18 +6824,72 @@ } }, "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "peer": true + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, "node_modules/dompurify": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", - "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", + "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "peer": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } }, "node_modules/drange": { "version": "1.1.1", @@ -6357,8 +6902,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -6369,9 +6913,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.543", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.543.tgz", - "integrity": "sha512-t2ZP4AcGE0iKCCQCBx/K2426crYdxD3YU6l0uK2EO3FZH0pbC4pFz/sZm2ruZsND6hQBTcDWWlo/MLpiOdif5g==", + "version": "1.4.812", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.812.tgz", + "integrity": "sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg==", "devOptional": true }, "node_modules/emittery": { @@ -6390,8 +6934,7 @@ "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/encoding": { "version": "0.1.13", @@ -6411,9 +6954,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", + "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -6423,6 +6966,63 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/enzyme": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz", + "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==", + "peer": true, + "dependencies": { + "array.prototype.flat": "^1.2.3", + "cheerio": "^1.0.0-rc.3", + "enzyme-shallow-equal": "^1.0.1", + "function.prototype.name": "^1.1.2", + "has": "^1.0.3", + "html-element-map": "^1.2.0", + "is-boolean-object": "^1.0.1", + "is-callable": "^1.1.5", + "is-number-object": "^1.0.4", + "is-regex": "^1.0.5", + "is-string": "^1.0.5", + "is-subset": "^0.1.1", + "lodash.escape": "^4.0.1", + "lodash.isequal": "^4.5.0", + "object-inspect": "^1.7.0", + "object-is": "^1.0.2", + "object.assign": "^4.1.0", + "object.entries": "^1.1.1", + "object.values": "^1.1.1", + "raf": "^3.4.1", + "rst-selector-parser": "^2.2.3", + "string.prototype.trim": "^1.2.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/enzyme-shallow-equal": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.7.tgz", + "integrity": "sha512-/um0GFqUXnpM9SvKtje+9Tjoz3f1fpBC3eXRFrNs8kpYn69JljciYP7KZTqM/YQbUY9KUjvKB4jo/q+L6WGGvg==", + "dependencies": { + "hasown": "^2.0.0", + "object-is": "^1.1.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -6440,50 +7040,56 @@ } }, "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", - "dev": true, - "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", - "es-set-tostringtag": "^2.0.1", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "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.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "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", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -6492,56 +7098,112 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "peer": true + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "dependencies": { - "asynciterator.prototype": "^1.0.0", "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", "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", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", + "internal-slot": "^1.0.7", "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -6560,9 +7222,9 @@ "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "engines": { "node": ">=6" } @@ -6579,16 +7241,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -6634,12 +7296,12 @@ } }, "node_modules/eslint-config-next": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.3.tgz", - "integrity": "sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.4.tgz", + "integrity": "sha512-Qr0wMgG9m6m4uYy2jrYJmyuNlYZzPRQq5Kvb9IDlYwn+7yq6W6sfMNFgb+9guM1KYwuIo6TIaiFhZJ6SnQ/Efw==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "14.2.3", + "@next/eslint-plugin-next": "14.2.4", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", "eslint-import-resolver-node": "^0.3.6", @@ -6705,9 +7367,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -6731,28 +7393,28 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", - "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "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-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.13.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" @@ -6761,6 +7423,16 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -6782,6 +7454,18 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -6792,27 +7476,27 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", - "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz", + "integrity": "sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==", "dev": true, "dependencies": { - "@babel/runtime": "^7.20.7", - "aria-query": "^5.1.3", - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.6.2", - "axobject-query": "^3.1.1", + "aria-query": "~5.1.3", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.9.1", + "axobject-query": "~3.1.1", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.3", - "language-tags": "=1.0.5", + "es-iterator-helpers": "^1.0.19", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "semver": "^6.3.0" + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.0" }, "engines": { "node": ">=4.0" @@ -6821,37 +7505,52 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "version": "7.34.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", + "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", + "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.hasown": "^1.1.4", + "object.values": "^1.2.0", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", + "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" + "string.prototype.matchall": "^4.0.11" }, "engines": { "node": ">=4" @@ -6861,9 +7560,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, "engines": { "node": ">=10" @@ -6872,6 +7571,16 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -6884,13 +7593,25 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "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" }, @@ -6938,35 +7659,154 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "peer": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" } }, "node_modules/esquery": { @@ -7012,9 +7852,9 @@ } }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "node_modules/execa": { "version": "5.1.1", @@ -7040,6 +7880,13 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "peer": true + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -7086,9 +7933,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -7156,9 +8003,9 @@ "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==" }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -7217,9 +8064,10 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -7248,32 +8096,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-yarn-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", - "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", - "dependencies": { - "micromatch": "^4.0.2" - } - }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.2.7", + "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/fnv-plus": { @@ -7314,10 +8154,9 @@ "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -7329,18 +8168,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -7374,18 +8201,10 @@ "node": ">= 12.20" } }, - "node_modules/formdata-node/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "engines": { - "node": ">= 14" - } - }, "node_modules/fraction.js": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz", - "integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, "engines": { "node": "*" @@ -7401,20 +8220,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "optional": true }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7435,15 +8240,17 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -7461,7 +8268,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7485,14 +8291,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7527,13 +8337,13 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -7543,9 +8353,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -7566,19 +8376,21 @@ "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==" }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -7597,27 +8409,20 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -7680,35 +8485,33 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -7728,11 +8531,11 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -7741,6 +8544,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hast-to-hyperscript": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-10.0.3.tgz", @@ -7758,33 +8572,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-to-hyperscript/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-to-hyperscript/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-to-hyperscript/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/hast-util-from-parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", @@ -7803,61 +8590,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-parse5/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", - "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", - "dependencies": { - "@types/hast": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/hastscript": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", - "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", - "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^3.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-from-parse5/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/hast-util-from-parse5/node_modules/unist-util-stringify-position": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", @@ -7899,9 +8631,12 @@ } }, "node_modules/hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "dependencies": { + "@types/hast": "^2.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -7959,33 +8694,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-parse5/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-parse5/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/hast-util-whitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", @@ -7996,15 +8704,15 @@ } }, "node_modules/hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", "dependencies": { "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" }, "funding": { "type": "opencollective", @@ -8040,6 +8748,24 @@ "react-is": "^16.7.0" } }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/html-element-map": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.1.tgz", + "integrity": "sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==", + "peer": true, + "dependencies": { + "array.prototype.filter": "^1.0.0", + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -8056,6 +8782,25 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-reasons": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", @@ -8066,6 +8811,18 @@ "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/httpsnippet-lite": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/httpsnippet-lite/-/httpsnippet-lite-3.0.5.tgz", @@ -8090,9 +8847,9 @@ } }, "node_modules/hyphenate-style-name": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==" }, "node_modules/iconv-lite": { "version": "0.6.3", @@ -8125,9 +8882,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -8180,7 +8937,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "engines": { "node": ">=0.8.19" } @@ -8197,6 +8953,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -8210,8 +8967,7 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inline-style-parser": { "version": "0.1.1", @@ -8236,13 +8992,12 @@ } }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -8258,9 +9013,9 @@ } }, "node_modules/ioredis": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", - "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -8318,14 +9073,15 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8355,7 +9111,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -8379,7 +9134,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -8425,11 +9179,28 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", + "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dependencies": { - "has": "^1.0.3" + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8439,7 +9210,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -8459,20 +9229,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -8548,19 +9304,21 @@ } }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "engines": { "node": ">= 0.4" }, @@ -8572,6 +9330,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -8580,7 +9339,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -8616,19 +9374,10 @@ "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -8649,21 +9398,26 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8686,7 +9440,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -8697,11 +9450,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==", + "peer": true + }, "node_modules/is-symbol": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -8713,11 +9471,11 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dependencies": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -8727,10 +9485,13 @@ } }, "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8739,7 +9500,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -8748,34 +9508,25 @@ } }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/isexe": { "version": "2.0.0", @@ -8792,9 +9543,9 @@ } }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "peer": true, "engines": { @@ -8802,15 +9553,15 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", "dev": true, "peer": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" }, @@ -8833,13 +9584,36 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "peer": true, - "dependencies": { + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" @@ -8848,10 +9622,20 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "peer": true, "dependencies": { @@ -8879,7 +9663,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -8967,6 +9750,82 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-circus/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-cli": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", @@ -9001,6 +9860,82 @@ } } }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", @@ -9047,126 +9982,136 @@ } } }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "peer": true, "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "peer": true, "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "peer": true, "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "peer": true, "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=7.0.0" } }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "peer": true }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "peer": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "*" }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "node_modules/jest-config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "peer": true, "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "*" } }, - "node_modules/jest-matcher-utils": { + "node_modules/jest-config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.7.0", + "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" }, @@ -9174,279 +10119,1239 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "peer": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "peer": true, - "engines": { - "node": ">=6" + "dependencies": { + "color-name": "~1.1.4" }, - "peerDependencies": { - "jest-resolve": "*" + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "peer": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-resolve": { + "node_modules/jest-each": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "peer": true, "dependencies": { + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", + "jest-get-type": "^29.6.3", "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "peer": true, "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "peer": true, "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "node_modules/jest-each/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "peer": true, "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-each/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "peer": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "peer": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=7.0.0" } }, - "node_modules/jest-runtime/node_modules/strip-bom": { + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-validate/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "peer": true, "engines": { "node": ">=8" } }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "peer": true, "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" + "has-flag": "^4.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/jest-util": { + "node_modules/jest-watcher": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, + "peer": true, "dependencies": { + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", + "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "peer": true, "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "peer": true, "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jest-worker": { @@ -9465,6 +11370,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9482,18 +11397,18 @@ } }, "node_modules/jiti": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", - "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, "bin": { "jiti": "bin/jiti.js" } }, "node_modules/jose": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.4.tgz", - "integrity": "sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.4.1.tgz", + "integrity": "sha512-U6QajmpV/nhL9SyfAewo000fkiRQ+Yd2H0lBxJJ9apjpOgkOcBQJWOrMo917lxLptdS/n/o/xPzMkXhF46K8hQ==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -9590,8 +11505,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "devOptional": true, - "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -9645,17 +11558,6 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/json-stable-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", - "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", - "dependencies": { - "jsonify": "^0.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -9663,15 +11565,15 @@ "dev": true }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "devOptional": true, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsonc-parser": { @@ -9679,25 +11581,6 @@ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==" }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -9754,22 +11637,14 @@ } }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" } }, - "node_modules/klaw-sync": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", - "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", - "dependencies": { - "graceful-fs": "^4.1.11" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -9781,18 +11656,21 @@ } }, "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true }, "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "dependencies": { - "language-subtag-registry": "~0.3.2" + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" } }, "node_modules/leven": { @@ -9870,6 +11748,18 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, + "node_modules/lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", + "peer": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "peer": true + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -9981,14 +11871,11 @@ } }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/lunr": { @@ -10490,11 +12377,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -10567,29 +12455,28 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/minipass": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.0.tgz", - "integrity": "sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==", - "dev": true, + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -10612,27 +12499,27 @@ "optional": true }, "node_modules/mobx": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.12.0.tgz", - "integrity": "sha512-Mn6CN6meXEnMa0a5u6a5+RKrqRedHBhZGd15AWLk9O6uFY4KYHzImdt8JI8WODo1bjTSRnwXhJox+FCUZhCKCQ==", + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.12.4.tgz", + "integrity": "sha512-uIymg89x+HmItX1p3MG+d09irn2k63J6biftZ5Ok+UpNojS1I3NJPLfcmJT9ANnUltNlHi+HQqrVyxiAN8ISYg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" } }, "node_modules/mobx-react": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.6.0.tgz", - "integrity": "sha512-+HQUNuh7AoQ9ZnU6c4rvbiVVl+wEkb9WqYsVDzGLng+Dqj1XntHu79PvEWKtSMoMj67vFp/ZPXcElosuJO8ckA==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.1.1.tgz", + "integrity": "sha512-gVV7AdSrAAxqXOJ2bAbGa5TkPqvITSzaPiiEkzpW4rRsMhSec7C2NBCJYILADHKp2tzOAIETGRsIY0UaCV5aEw==", "dependencies": { - "mobx-react-lite": "^3.4.0" + "mobx-react-lite": "^4.0.7" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" }, "peerDependencies": { - "mobx": "^6.1.0", + "mobx": "^6.9.0", "react": "^16.8.0 || ^17 || ^18" }, "peerDependenciesMeta": { @@ -10645,15 +12532,18 @@ } }, "node_modules/mobx-react-lite": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz", - "integrity": "sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.0.7.tgz", + "integrity": "sha512-RjwdseshK9Mg8On5tyJZHtGD+J78ZnCnRaxeQDSiciKVQDUbfZcXhmld0VMxAwvcTnPEHZySGGewm467Fcpreg==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" }, "peerDependencies": { - "mobx": "^6.1.0", + "mobx": "^6.9.0", "react": "^16.8.0 || ^17 || ^18" }, "peerDependenciesMeta": { @@ -10665,6 +12555,12 @@ } } }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", + "peer": true + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -10682,11 +12578,35 @@ } }, "node_modules/nan": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", "optional": true }, + "node_modules/nano-css": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.1.tgz", + "integrity": "sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "css-tree": "^1.1.2", + "csstype": "^3.1.2", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^7.0.0", + "rtl-css-js": "^1.16.1", + "stacktrace-js": "^2.0.2", + "stylis": "^4.3.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/nano-css/node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "node_modules/nano-memoize": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/nano-memoize/-/nano-memoize-1.3.1.tgz", @@ -10701,9 +12621,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -10729,12 +12649,34 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "peer": true, + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, "node_modules/next": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", - "integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==", + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.4.tgz", + "integrity": "sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==", "dependencies": { - "@next/env": "14.2.3", + "@next/env": "14.2.4", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -10749,15 +12691,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.3", - "@next/swc-darwin-x64": "14.2.3", - "@next/swc-linux-arm64-gnu": "14.2.3", - "@next/swc-linux-arm64-musl": "14.2.3", - "@next/swc-linux-x64-gnu": "14.2.3", - "@next/swc-linux-x64-musl": "14.2.3", - "@next/swc-win32-arm64-msvc": "14.2.3", - "@next/swc-win32-ia32-msvc": "14.2.3", - "@next/swc-win32-x64-msvc": "14.2.3" + "@next/swc-darwin-arm64": "14.2.4", + "@next/swc-darwin-x64": "14.2.4", + "@next/swc-linux-arm64-gnu": "14.2.4", + "@next/swc-linux-arm64-musl": "14.2.4", + "@next/swc-linux-x64-gnu": "14.2.4", + "@next/swc-linux-x64-musl": "14.2.4", + "@next/swc-win32-arm64-msvc": "14.2.4", + "@next/swc-win32-ia32-msvc": "14.2.4", + "@next/swc-win32-x64-msvc": "14.2.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -10779,18 +12721,18 @@ } }, "node_modules/next-auth": { - "version": "5.0.0-beta.17", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.17.tgz", - "integrity": "sha512-XA/7JtAjOgDfAeotJPFUsFZGGItZwzZrxLt9Gc9fE7EchLk6zydZfuZ22Vvwixs3IilkN644D5IoD5tEOAFGCQ==", + "version": "5.0.0-beta.19", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.19.tgz", + "integrity": "sha512-YHu1igcAxZPh8ZB7GIM93dqgY6gcAzq66FOhQFheAdOx1raxNcApt05nNyNCSB6NegSiyJ4XOPsaNow4pfDmsg==", "dependencies": { - "@auth/core": "0.30.0" + "@auth/core": "0.32.0" }, "peerDependencies": { "@simplewebauthn/browser": "^9.0.1", "@simplewebauthn/server": "^9.0.2", - "next": "^14", + "next": "^14 || ^15.0.0-0", "nodemailer": "^6.6.5", - "react": "^18.2.0" + "react": "^18.2.0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@simplewebauthn/browser": { @@ -10805,15 +12747,15 @@ } }, "node_modules/next-auth/node_modules/@auth/core": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.30.0.tgz", - "integrity": "sha512-8AE4m/nk+4EIiVCJwxZAsJeAQuzpEC8M8768mmKVn60CGDdupKQkVhxbRlm5Qh7eNRCoFFME+0DvtaX2aXrYaA==", + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.32.0.tgz", + "integrity": "sha512-3+ssTScBd+1fd0/fscAyQN1tSygXzuhysuVVzB942ggU4mdfiTbv36P0ccVnExKWYJKvu3E2r3/zxXCCAmTOrg==", "dependencies": { "@panva/hkdf": "^1.1.1", "@types/cookie": "0.6.0", "cookie": "0.6.0", "jose": "^5.1.3", - "oauth4webapi": "^2.4.0", + "oauth4webapi": "^2.9.0", "preact": "10.11.3", "preact-render-to-string": "5.2.3" }, @@ -10834,18 +12776,37 @@ } } }, - "node_modules/next-auth/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, "engines": { - "node": ">= 0.6" + "node": "^10 || ^12 || >=14" } }, "node_modules/node-abi": { - "version": "3.50.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.50.0.tgz", - "integrity": "sha512-2Gxu7Eq7vnBIRfYSmqPruEllMM14FjOQFJSoqdGWthVn+tmwEXzmdPpya6cvvwf0uZA3F5N1fMFr9mijZBplFA==", + "version": "3.65.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz", + "integrity": "sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==", "optional": true, "dependencies": { "semver": "^7.3.5" @@ -10912,6 +12873,14 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-fetch-commonjs/node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/node-fetch-h2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", @@ -10939,15 +12908,15 @@ } }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "devOptional": true }, "node_modules/nodemailer": { - "version": "6.9.9", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", - "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz", + "integrity": "sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==", "engines": { "node": ">=6.0.0" } @@ -10971,9 +12940,9 @@ } }, "node_modules/npm": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.7.0.tgz", - "integrity": "sha512-FXylyYSXNjgXx3l82BT8RSQvCoGIQ3h8YdRFGKNvo3Pv/bKscK4pdWkx/onwTpHDqGw+oeLf4Rxln9WVyxAxlQ==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.1.tgz", + "integrity": "sha512-Dp1C6SvSMYQI7YHq/y2l94uvI+59Eqbu1EpuKQHQ8p16txXRuRit5gH3Lnaagk2aXDIjg/Iru9pd05bnneKgdw==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -11046,71 +13015,71 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^7.2.1", - "@npmcli/config": "^8.0.2", - "@npmcli/fs": "^3.1.0", + "@npmcli/arborist": "^7.5.3", + "@npmcli/config": "^8.3.3", + "@npmcli/fs": "^3.1.1", "@npmcli/map-workspaces": "^3.0.6", - "@npmcli/package-json": "^5.1.0", - "@npmcli/promise-spawn": "^7.0.1", + "@npmcli/package-json": "^5.1.1", + "@npmcli/promise-spawn": "^7.0.2", "@npmcli/redact": "^2.0.0", "@npmcli/run-script": "^8.1.0", - "@sigstore/tuf": "^2.3.2", + "@sigstore/tuf": "^2.3.4", "abbrev": "^2.0.0", "archy": "~1.0.0", - "cacache": "^18.0.2", + "cacache": "^18.0.3", "chalk": "^5.3.0", "ci-info": "^4.0.0", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", - "glob": "^10.3.12", + "glob": "^10.4.1", "graceful-fs": "^4.2.11", - "hosted-git-info": "^7.0.1", - "ini": "^4.1.2", - "init-package-json": "^6.0.2", - "is-cidr": "^5.0.5", - "json-parse-even-better-errors": "^3.0.1", - "libnpmaccess": "^8.0.1", - "libnpmdiff": "^6.0.3", - "libnpmexec": "^8.0.0", - "libnpmfund": "^5.0.1", - "libnpmhook": "^10.0.0", - "libnpmorg": "^6.0.1", - "libnpmpack": "^7.0.0", - "libnpmpublish": "^9.0.2", - "libnpmsearch": "^7.0.0", - "libnpmteam": "^6.0.0", - "libnpmversion": "^6.0.0", + "hosted-git-info": "^7.0.2", + "ini": "^4.1.3", + "init-package-json": "^6.0.3", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^3.0.2", + "libnpmaccess": "^8.0.6", + "libnpmdiff": "^6.1.3", + "libnpmexec": "^8.1.2", + "libnpmfund": "^5.0.11", + "libnpmhook": "^10.0.5", + "libnpmorg": "^6.0.6", + "libnpmpack": "^7.0.3", + "libnpmpublish": "^9.0.9", + "libnpmsearch": "^7.0.6", + "libnpmteam": "^6.0.5", + "libnpmversion": "^6.0.3", "make-fetch-happen": "^13.0.1", "minimatch": "^9.0.4", - "minipass": "^7.0.4", + "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^10.1.0", - "nopt": "^7.2.0", - "normalize-package-data": "^6.0.0", + "nopt": "^7.2.1", + "normalize-package-data": "^6.0.1", "npm-audit-report": "^5.0.0", "npm-install-checks": "^6.3.0", "npm-package-arg": "^11.0.2", - "npm-pick-manifest": "^9.0.0", - "npm-profile": "^9.0.2", - "npm-registry-fetch": "^17.0.0", - "npm-user-validate": "^2.0.0", + "npm-pick-manifest": "^9.0.1", + "npm-profile": "^10.0.0", + "npm-registry-fetch": "^17.0.1", + "npm-user-validate": "^2.0.1", "p-map": "^4.0.0", - "pacote": "^18.0.3", + "pacote": "^18.0.6", "parse-conflict-json": "^3.0.1", "proc-log": "^4.2.0", "qrcode-terminal": "^0.12.0", "read": "^3.0.1", - "semver": "^7.6.0", + "semver": "^7.6.2", "spdx-expression-parse": "^4.0.0", - "ssri": "^10.0.5", + "ssri": "^10.0.6", "supports-color": "^9.4.0", "tar": "^6.2.1", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", "treeverse": "^3.0.0", - "validate-npm-package-name": "^5.0.0", + "validate-npm-package-name": "^5.0.1", "which": "^4.0.0", "write-file-atomic": "^5.0.1" }, @@ -11218,34 +13187,35 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "7.5.1", + "version": "7.5.3", "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^3.1.0", + "@npmcli/fs": "^3.1.1", "@npmcli/installed-package-contents": "^2.1.0", "@npmcli/map-workspaces": "^3.0.2", - "@npmcli/metavuln-calculator": "^7.1.0", + "@npmcli/metavuln-calculator": "^7.1.1", "@npmcli/name-from-folder": "^2.0.0", "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.1.0", "@npmcli/query": "^3.1.0", "@npmcli/redact": "^2.0.0", "@npmcli/run-script": "^8.1.0", - "bin-links": "^4.0.1", - "cacache": "^18.0.0", + "bin-links": "^4.0.4", + "cacache": "^18.0.3", "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^7.0.1", - "json-parse-even-better-errors": "^3.0.0", + "hosted-git-info": "^7.0.2", + "json-parse-even-better-errors": "^3.0.2", "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", "minimatch": "^9.0.4", - "nopt": "^7.0.0", + "nopt": "^7.2.1", "npm-install-checks": "^6.2.0", "npm-package-arg": "^11.0.2", - "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^17.0.0", - "pacote": "^18.0.1", + "npm-pick-manifest": "^9.0.1", + "npm-registry-fetch": "^17.0.1", + "pacote": "^18.0.6", "parse-conflict-json": "^3.0.0", "proc-log": "^4.2.0", "proggy": "^2.0.0", @@ -11253,7 +13223,7 @@ "promise-call-limit": "^3.0.1", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", - "ssri": "^10.0.5", + "ssri": "^10.0.6", "treeverse": "^3.0.0", "walk-up-path": "^3.0.1" }, @@ -11265,14 +13235,14 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "8.3.1", + "version": "8.3.3", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/map-workspaces": "^3.0.2", "ci-info": "^4.0.0", "ini": "^4.1.2", - "nopt": "^7.0.0", + "nopt": "^7.2.1", "proc-log": "^4.2.0", "read-package-json-fast": "^3.0.2", "semver": "^7.3.5", @@ -11283,7 +13253,7 @@ } }, "node_modules/npm/node_modules/@npmcli/fs": { - "version": "3.1.0", + "version": "3.1.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -11294,7 +13264,7 @@ } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "5.0.6", + "version": "5.0.7", "inBundle": true, "license": "ISC", "dependencies": { @@ -11341,7 +13311,7 @@ } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "7.1.0", + "version": "7.1.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -11372,7 +13342,7 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "5.1.0", + "version": "5.1.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -11389,7 +13359,7 @@ } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "7.0.1", + "version": "7.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -11444,11 +13414,11 @@ } }, "node_modules/npm/node_modules/@sigstore/bundle": { - "version": "2.3.1", + "version": "2.3.2", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.3.1" + "@sigstore/protobuf-specs": "^0.3.2" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -11463,7 +13433,7 @@ } }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.3.1", + "version": "0.3.2", "inBundle": true, "license": "Apache-2.0", "engines": { @@ -11471,39 +13441,41 @@ } }, "node_modules/npm/node_modules/@sigstore/sign": { - "version": "2.3.0", + "version": "2.3.2", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.0", + "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.1", - "make-fetch-happen": "^13.0.0" + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "2.3.2", + "version": "2.3.4", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.3.0", - "tuf-js": "^2.2.0" + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/@sigstore/verify": { - "version": "1.2.0", + "version": "1.2.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.1", + "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.1.0", - "@sigstore/protobuf-specs": "^0.3.1" + "@sigstore/protobuf-specs": "^0.3.2" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -11518,12 +13490,12 @@ } }, "node_modules/npm/node_modules/@tufjs/models": { - "version": "2.0.0", + "version": "2.0.1", "inBundle": true, "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.3" + "minimatch": "^9.0.4" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -11595,7 +13567,7 @@ "license": "MIT" }, "node_modules/npm/node_modules/bin-links": { - "version": "4.0.3", + "version": "4.0.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -11627,16 +13599,8 @@ "balanced-match": "^1.0.0" } }, - "node_modules/npm/node_modules/builtins": { - "version": "5.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "semver": "^7.0.0" - } - }, "node_modules/npm/node_modules/cacache": { - "version": "18.0.2", + "version": "18.0.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -11691,7 +13655,7 @@ } }, "node_modules/npm/node_modules/cidr-regex": { - "version": "4.0.5", + "version": "4.1.1", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -11722,7 +13686,7 @@ } }, "node_modules/npm/node_modules/cmd-shim": { - "version": "6.0.2", + "version": "6.0.3", "inBundle": true, "license": "ISC", "engines": { @@ -11897,21 +13861,21 @@ } }, "node_modules/npm/node_modules/glob": { - "version": "10.3.12", + "version": "10.4.1", "inBundle": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -11934,7 +13898,7 @@ } }, "node_modules/npm/node_modules/hosted-git-info": { - "version": "7.0.1", + "version": "7.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -11986,7 +13950,7 @@ } }, "node_modules/npm/node_modules/ignore-walk": { - "version": "6.0.4", + "version": "6.0.5", "inBundle": true, "license": "ISC", "dependencies": { @@ -12013,7 +13977,7 @@ } }, "node_modules/npm/node_modules/ini": { - "version": "4.1.2", + "version": "4.1.3", "inBundle": true, "license": "ISC", "engines": { @@ -12021,7 +13985,7 @@ } }, "node_modules/npm/node_modules/init-package-json": { - "version": "6.0.2", + "version": "6.0.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -12061,11 +14025,11 @@ } }, "node_modules/npm/node_modules/is-cidr": { - "version": "5.0.5", + "version": "5.1.0", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { - "cidr-regex": "^4.0.4" + "cidr-regex": "^4.1.1" }, "engines": { "node": ">=14" @@ -12101,7 +14065,7 @@ "license": "ISC" }, "node_modules/npm/node_modules/jackspeak": { - "version": "2.3.6", + "version": "3.1.2", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -12123,7 +14087,7 @@ "license": "MIT" }, "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "3.0.1", + "version": "3.0.2", "inBundle": true, "license": "MIT", "engines": { @@ -12157,29 +14121,29 @@ "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "8.0.5", + "version": "8.0.6", "inBundle": true, "license": "ISC", "dependencies": { "npm-package-arg": "^11.0.2", - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "6.1.1", + "version": "6.1.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.2.1", + "@npmcli/arborist": "^7.5.3", "@npmcli/installed-package-contents": "^2.1.0", "binary-extensions": "^2.3.0", "diff": "^5.1.0", "minimatch": "^9.0.4", "npm-package-arg": "^11.0.2", - "pacote": "^18.0.1", + "pacote": "^18.0.6", "tar": "^6.2.1" }, "engines": { @@ -12187,15 +14151,15 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "8.1.0", + "version": "8.1.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.2.1", + "@npmcli/arborist": "^7.5.3", "@npmcli/run-script": "^8.1.0", "ci-info": "^4.0.0", "npm-package-arg": "^11.0.2", - "pacote": "^18.0.1", + "pacote": "^18.0.6", "proc-log": "^4.2.0", "read": "^3.0.1", "read-package-json-fast": "^3.0.2", @@ -12207,103 +14171,103 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "5.0.9", + "version": "5.0.11", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.2.1" + "@npmcli/arborist": "^7.5.3" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmhook": { - "version": "10.0.4", + "version": "10.0.5", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "6.0.5", + "version": "6.0.6", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "7.0.1", + "version": "7.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.2.1", + "@npmcli/arborist": "^7.5.3", "@npmcli/run-script": "^8.1.0", "npm-package-arg": "^11.0.2", - "pacote": "^18.0.1" + "pacote": "^18.0.6" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "9.0.7", + "version": "9.0.9", "inBundle": true, "license": "ISC", "dependencies": { "ci-info": "^4.0.0", - "normalize-package-data": "^6.0.0", + "normalize-package-data": "^6.0.1", "npm-package-arg": "^11.0.2", - "npm-registry-fetch": "^17.0.0", + "npm-registry-fetch": "^17.0.1", "proc-log": "^4.2.0", "semver": "^7.3.7", "sigstore": "^2.2.0", - "ssri": "^10.0.5" + "ssri": "^10.0.6" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "7.0.4", + "version": "7.0.6", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "6.0.4", + "version": "6.0.5", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^17.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "6.0.1", + "version": "6.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^5.0.6", + "@npmcli/git": "^5.0.7", "@npmcli/run-script": "^8.1.0", - "json-parse-even-better-errors": "^3.0.0", + "json-parse-even-better-errors": "^3.0.2", "proc-log": "^4.2.0", "semver": "^7.3.7" }, @@ -12356,7 +14320,7 @@ } }, "node_modules/npm/node_modules/minipass": { - "version": "7.0.4", + "version": "7.1.2", "inBundle": true, "license": "ISC", "engines": { @@ -12375,7 +14339,7 @@ } }, "node_modules/npm/node_modules/minipass-fetch": { - "version": "3.0.4", + "version": "3.0.5", "inBundle": true, "license": "MIT", "dependencies": { @@ -12563,7 +14527,7 @@ } }, "node_modules/npm/node_modules/nopt": { - "version": "7.2.0", + "version": "7.2.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -12577,7 +14541,7 @@ } }, "node_modules/npm/node_modules/normalize-package-data": { - "version": "6.0.0", + "version": "6.0.1", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -12599,7 +14563,7 @@ } }, "node_modules/npm/node_modules/npm-bundled": { - "version": "3.0.0", + "version": "3.0.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -12654,7 +14618,7 @@ } }, "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "9.0.0", + "version": "9.0.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -12668,19 +14632,19 @@ } }, "node_modules/npm/node_modules/npm-profile": { - "version": "9.0.2", + "version": "10.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^17.0.0", + "npm-registry-fetch": "^17.0.1", "proc-log": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.0.0" } }, "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "17.0.0", + "version": "17.0.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -12698,7 +14662,7 @@ } }, "node_modules/npm/node_modules/npm-user-validate": { - "version": "2.0.0", + "version": "2.0.1", "inBundle": true, "license": "BSD-2-Clause", "engines": { @@ -12720,7 +14684,7 @@ } }, "node_modules/npm/node_modules/pacote": { - "version": "18.0.3", + "version": "18.0.6", "inBundle": true, "license": "ISC", "dependencies": { @@ -12743,7 +14707,7 @@ "tar": "^6.1.11" }, "bin": { - "pacote": "lib/bin.js" + "pacote": "bin/index.js" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -12771,7 +14735,7 @@ } }, "node_modules/npm/node_modules/path-scurry": { - "version": "1.10.2", + "version": "1.11.1", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -12779,14 +14743,14 @@ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.16", + "version": "6.1.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -12847,7 +14811,7 @@ } }, "node_modules/npm/node_modules/promzard": { - "version": "1.0.1", + "version": "1.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -12910,12 +14874,9 @@ "optional": true }, "node_modules/npm/node_modules/semver": { - "version": "7.6.0", + "version": "7.6.2", "inBundle": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -12923,17 +14884,6 @@ "node": ">=10" } }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/npm/node_modules/shebang-command": { "version": "2.0.0", "inBundle": true, @@ -12965,16 +14915,16 @@ } }, "node_modules/npm/node_modules/sigstore": { - "version": "2.3.0", + "version": "2.3.1", "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.1", + "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.1", - "@sigstore/sign": "^2.3.0", - "@sigstore/tuf": "^2.3.1", - "@sigstore/verify": "^1.2.0" + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -13048,7 +14998,7 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.17", + "version": "3.0.18", "inBundle": true, "license": "CC0-1.0" }, @@ -13058,7 +15008,7 @@ "license": "BSD-3-Clause" }, "node_modules/npm/node_modules/ssri": { - "version": "10.0.5", + "version": "10.0.6", "inBundle": true, "license": "ISC", "dependencies": { @@ -13194,13 +15144,13 @@ } }, "node_modules/npm/node_modules/tuf-js": { - "version": "2.2.0", + "version": "2.2.1", "inBundle": true, "license": "MIT", "dependencies": { - "@tufjs/models": "2.0.0", + "@tufjs/models": "2.0.1", "debug": "^4.3.4", - "make-fetch-happen": "^13.0.0" + "make-fetch-happen": "^13.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -13252,12 +15202,9 @@ } }, "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "5.0.0", + "version": "5.0.1", "inBundle": true, "license": "ISC", - "dependencies": { - "builtins": "^5.0.0" - }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -13500,9 +15447,9 @@ } }, "node_modules/oauth4webapi": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.10.2.tgz", - "integrity": "sha512-ib0x1f4tCaZkTEEnRpkt96D8F2e38AFWzTOwpha1Wmme5kD+RFFgDVkrXyBSxBefFeQUoODVaieS/w9QmkZbnQ==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.11.1.tgz", + "integrity": "sha512-aNzOnL98bL6izG97zgnZs1PFEyO4WDVRhz2Pd066NPak44w5ESLRCYmJIyey8avSBPOMtBjhF3ZDDm7bIb7UOg==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -13525,9 +15472,27 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13536,19 +15501,17 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "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" }, @@ -13560,28 +15523,28 @@ } }, "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -13591,39 +15554,44 @@ } }, "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "dev": true, "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -13644,20 +15612,20 @@ "dev": true }, "node_modules/octokit": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz", - "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.2.1.tgz", + "integrity": "sha512-u+XuSejhe3NdIvty3Jod00JvTdAE/0/+XbhIDhefHbu+2OcTRHd80aCiH6TX19ZybJmwPQBKFQmHGxp0i9mJrg==", "dependencies": { "@octokit/app": "^14.0.2", "@octokit/core": "^5.0.0", "@octokit/oauth-app": "^6.0.0", "@octokit/plugin-paginate-graphql": "^4.0.0", - "@octokit/plugin-paginate-rest": "^9.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.0.0", + "@octokit/plugin-paginate-rest": "11.3.1", + "@octokit/plugin-rest-endpoint-methods": "13.2.2", "@octokit/plugin-retry": "^6.0.0", "@octokit/plugin-throttling": "^8.0.0", "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0" + "@octokit/types": "^13.0.0" }, "engines": { "node": ">= 18" @@ -13687,30 +15655,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "node_modules/openapi-path-templating": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/openapi-path-templating/-/openapi-path-templating-1.6.0.tgz", + "integrity": "sha512-1atBNwOUrZXthTvlvvX8k8ovFEF3iA8mDidYMkdOtvVdndBhTrspbwGXNOzEUaJhm9iUl4Tf5uQaeTLAJvwPig==", "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "apg-lite": "^1.0.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.20.0" } }, "node_modules/openapi-sampler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.3.1.tgz", - "integrity": "sha512-Ert9mvc2tLPmmInwSyGZS+v4Ogu9/YoZuq9oP3EdUklg2cad6+IGndP9yqJJwbgdXwZibiq5fpv6vYujchdJFg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.5.1.tgz", + "integrity": "sha512-tIWIrZUKNAsbqf3bd9U1oH6JEXo8LNYuDlXw26By67EygpjT+ArFnsxxyTMjFWRfbqo5ozkvgSQDK69Gd8CddA==", "dependencies": { "@types/json-schema": "^7.0.7", "json-pointer": "0.6.2" } }, + "node_modules/openapi-server-url-templating": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/openapi-server-url-templating/-/openapi-server-url-templating-1.1.0.tgz", + "integrity": "sha512-dtyTFKx2xVcO0W8JKaluXIHC9l/MLjHeflBaWjiWNMCHp/TBs9dEjQDbj/VFlHR4omFOKjjmqm1pW1aCAhmPBg==", + "dependencies": { + "apg-lite": "^1.0.3" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/openapi3-ts": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-2.0.2.tgz", @@ -13728,30 +15703,22 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -13842,52 +15809,29 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, - "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "peer": true, "dependencies": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^4.1.2", - "ci-info": "^3.7.0", - "cross-spawn": "^7.0.3", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", - "json-stable-stringify": "^1.0.2", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", - "open": "^7.4.2", - "rimraf": "^2.6.3", - "semver": "^7.5.3", - "slash": "^2.0.0", - "tmp": "^0.0.33", - "yaml": "^2.2.2" - }, - "bin": { - "patch-package": "index.js" + "domhandler": "^5.0.2", + "parse5": "^7.0.0" }, - "engines": { - "node": ">=14", - "npm": ">5" + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/patch-package/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "peer": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/patch-package/node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "engines": { - "node": ">=6" + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/path-browserify": { @@ -13926,30 +15870,20 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", - "dev": true, + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -13976,10 +15910,16 @@ "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz", "integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "peer": true + }, "node_modules/pg": { - "version": "8.11.5", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", - "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", + "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", "dev": true, "dependencies": { "pg-connection-string": "^2.6.4", @@ -14050,6 +15990,24 @@ "dev": true }, "node_modules/pg-types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "dev": true, + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pg/node_modules/pg-types": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", @@ -14065,6 +16023,45 @@ "node": ">=4" } }, + "node_modules/pg/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pgpass": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", @@ -14075,14 +16072,15 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -14186,9 +16184,9 @@ } }, "node_modules/polished": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz", - "integrity": "sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", "dependencies": { "@babel/runtime": "^7.17.8" }, @@ -14196,10 +16194,18 @@ "node": ">=10" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -14215,9 +16221,9 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -14260,21 +16266,27 @@ } }, "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "engines": { "node": ">= 14" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" @@ -14288,6 +16300,18 @@ } } }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/postcss-nested": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", @@ -14308,9 +16332,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -14326,42 +16350,42 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", "dev": true, "engines": { - "node": ">=4" + "node": ">=12" } }, "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", "dev": true, + "dependencies": { + "obuf": "~1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", "dev": true, - "dependencies": { - "xtend": "^4.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, "node_modules/postgres-range": { @@ -14391,6 +16415,36 @@ "node": ">=10" } }, + "node_modules/postman-collection/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postman-collection/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postman-collection/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/postman-url-encoder": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz", @@ -14428,9 +16482,9 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", "optional": true, "dependencies": { "detect-libc": "^2.0.0", @@ -14496,12 +16550,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, "node_modules/prism-react-renderer": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", @@ -14550,13 +16598,15 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "dependencies": { - "xtend": "^4.0.0" - }, + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -14578,17 +16628,17 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } }, "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -14603,11 +16653,11 @@ "peer": true }, "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -14641,19 +16691,34 @@ } ] }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "peer": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "peer": true + }, "node_modules/ramda": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", - "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", + "integrity": "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/ramda" } }, "node_modules/ramda-adjunct": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-4.1.1.tgz", - "integrity": "sha512-BnCGsZybQZMDGram9y7RiryoRHS5uwx8YeGuUeDKuZuvK38XO6JJfmK85BwRWAKFA6pZ5nZBO/HBFtExVaf31w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-5.0.1.tgz", + "integrity": "sha512-UTQCcWnoiuYH+ua+jGg3GTktcmCSD2W7OO2++tmv8p2Ze+N9VgVACERg4g36rRfIXklVMtqazyBLBWXfoPKgRQ==", "engines": { "node": ">=0.10.3" }, @@ -14662,19 +16727,20 @@ "url": "https://opencollective.com/ramda-adjunct" }, "peerDependencies": { - "ramda": ">= 0.29.0" + "ramda": ">= 0.30.0" } }, "node_modules/randexp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", - "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "peer": true, "dependencies": { - "drange": "^1.0.2", - "ret": "^0.2.0" + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" }, "engines": { - "node": ">=4" + "node": ">=0.12" } }, "node_modules/randombytes": { @@ -14786,78 +16852,29 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.2.tgz", "integrity": "sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==", - "peerDependencies": { - "react": "^16.8.4 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-overflow-list": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/react-overflow-list/-/react-overflow-list-0.5.0.tgz", - "integrity": "sha512-+UegukgQ10E4ll3txz4DJyrnCgZ3eDVuv5dvR8ziyG5FfgCDZcUKeKhIgbU90oyqQa21aH4oLOoGKt0TiYJRmg==", - "dependencies": { - "react-use": "^17.3.1" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/react-overflow-list/node_modules/react-use": { - "version": "17.5.0", - "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.5.0.tgz", - "integrity": "sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==", - "dependencies": { - "@types/js-cookie": "^2.2.6", - "@xobotyi/scrollbar-width": "^1.9.5", - "copy-to-clipboard": "^3.3.1", - "fast-deep-equal": "^3.1.3", - "fast-shallow-equal": "^1.0.0", - "js-cookie": "^2.2.1", - "nano-css": "^5.6.1", - "react-universal-interface": "^0.6.2", - "resize-observer-polyfill": "^1.5.1", - "screenfull": "^5.1.0", - "set-harmonic-interval": "^1.0.1", - "throttle-debounce": "^3.0.1", - "ts-easing": "^0.2.0", - "tslib": "^2.1.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-overflow-list/node_modules/react-use/node_modules/nano-css": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.1.tgz", - "integrity": "sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==", + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/react-overflow-list": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/react-overflow-list/-/react-overflow-list-0.5.0.tgz", + "integrity": "sha512-+UegukgQ10E4ll3txz4DJyrnCgZ3eDVuv5dvR8ziyG5FfgCDZcUKeKhIgbU90oyqQa21aH4oLOoGKt0TiYJRmg==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "css-tree": "^1.1.2", - "csstype": "^3.1.2", - "fastest-stable-stringify": "^2.0.2", - "inline-style-prefixer": "^7.0.0", - "rtl-css-js": "^1.16.1", - "stacktrace-js": "^2.0.2", - "stylis": "^4.3.0" + "react-use": "^17.3.1" + }, + "engines": { + "node": ">=10" }, "peerDependencies": { - "react": "*", - "react-dom": "*" + "react": ">=16" } }, - "node_modules/react-overflow-list/node_modules/stylis": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", - "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" - }, "node_modules/react-query": { "version": "3.39.3", "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", @@ -14884,48 +16901,27 @@ } }, "node_modules/react-redux": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", - "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", "use-sync-external-store": "^1.0.0" }, "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" + "@types/react": "^18.2.25", + "react": "^18.0", + "redux": "^5.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, "redux": { "optional": true } } }, - "node_modules/react-redux/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, "node_modules/react-router": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", @@ -14974,6 +16970,23 @@ "react-router-dom": ">=4" } }, + "node_modules/react-router/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-shallow-renderer": { + "version": "16.15.0", + "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", + "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", + "dependencies": { + "object-assign": "^4.1.1", + "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-syntax-highlighter": { "version": "15.5.0", "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", @@ -14990,23 +17003,15 @@ } }, "node_modules/react-tabs": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-4.3.0.tgz", - "integrity": "sha512-2GfoG+f41kiBIIyd3gF+/GRCCYtamC8/2zlAcD8cqQmqI9Q+YVz7fJLHMmU9pXDVYYHpJeCgUSBJju85vu5q8Q==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.0.2.tgz", + "integrity": "sha512-aQXTKolnM28k3KguGDBSAbJvcowOQr23A+CUJdzJtOSDOtTwzEaJA+1U4KwhNL9+Obe+jFS7geuvA7ICQPXOnQ==", "dependencies": { - "clsx": "^1.1.0", + "clsx": "^2.0.0", "prop-types": "^15.5.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-0 || ^18.0.0" - } - }, - "node_modules/react-tabs/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" + "react": "^18.0.0" } }, "node_modules/react-transition-group": { @@ -15024,6 +17029,15 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-transition-group/node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/react-universal-interface": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", @@ -15033,6 +17047,31 @@ "tslib": "*" } }, + "node_modules/react-use": { + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.5.0.tgz", + "integrity": "sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==", + "dependencies": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.6.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -15088,9 +17127,9 @@ } }, "node_modules/redis-semaphore": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/redis-semaphore/-/redis-semaphore-5.5.0.tgz", - "integrity": "sha512-JSoCtkcXRr5hN2Hs7cmP/998bKAkZbJJ6C9T+UYh6BlEhYevWkQ6b5IHPRrZxCepPwwJnt/piacmB3a5UoXIYw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/redis-semaphore/-/redis-semaphore-5.6.0.tgz", + "integrity": "sha512-HK0Bvt9BTyYoXg/SanwXVbgoDFQ2n4n08nExRM04JWIOz6VsU3mxmZ16jSgEc+7kFsYt7w9SMh6EF4MGZWLMeA==", "dependencies": { "debug": "^4.3.4" }, @@ -15099,33 +17138,39 @@ }, "peerDependencies": { "ioredis": "^4.1.0 || ^5" + }, + "peerDependenciesMeta": { + "ioredis": { + "optional": true + } } }, "node_modules/redoc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.1.3.tgz", - "integrity": "sha512-d7F9qLLxaiFW4GC03VkwlX9wuRIpx9aiIIf3o6mzMnqPfhxrn2IRKGndrkJeVdItgCfmg9jXZiFEowm60f1meQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.1.5.tgz", + "integrity": "sha512-POSbVg+7WLf+/5/c6GWLxL7+9t2D+1WlZdLN0a6qaCQc+ih3XYzteRBkXEN5kjrYrRNjdspfxTZkDLN5WV3Tzg==", "dependencies": { - "@redocly/openapi-core": "^1.0.0-rc.2", - "classnames": "^2.3.1", + "@cfaester/enzyme-adapter-react-18": "^0.8.0", + "@redocly/openapi-core": "^1.4.0", + "classnames": "^2.3.2", "decko": "^1.2.0", - "dompurify": "^2.2.8", - "eventemitter3": "^4.0.7", + "dompurify": "^3.0.6", + "eventemitter3": "^5.0.1", "json-pointer": "^0.6.2", "lunr": "^2.3.9", "mark.js": "^8.11.1", - "marked": "^4.0.15", - "mobx-react": "^7.2.0", - "openapi-sampler": "^1.3.1", + "marked": "^4.3.0", + "mobx-react": "^9.1.1", + "openapi-sampler": "^1.5.0", "path-browserify": "^1.0.1", "perfect-scrollbar": "^1.5.5", - "polished": "^4.1.3", - "prismjs": "^1.27.0", - "prop-types": "^15.7.2", - "react-tabs": "^4.3.0", + "polished": "^4.2.2", + "prismjs": "^1.29.0", + "prop-types": "^15.8.1", + "react-tabs": "^6.0.2", "slugify": "~1.4.7", "stickyfill": "^1.1.1", - "swagger2openapi": "^7.0.6", + "swagger2openapi": "^7.0.8", "url-template": "^2.0.8" }, "engines": { @@ -15140,18 +17185,10 @@ "styled-components": "^4.1.1 || ^5.1.1 || ^6.0.5" } }, - "node_modules/redoc/node_modules/dompurify": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz", - "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==" - }, "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "dependencies": { - "@babel/runtime": "^7.9.2" - } + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" }, "node_modules/redux-immutable": { "version": "4.0.0", @@ -15162,15 +17199,16 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", "which-builtin-type": "^1.1.3" }, @@ -15195,6 +17233,40 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/refractor/node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/refractor/node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/refractor/node_modules/prismjs": { "version": "1.27.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", @@ -15203,6 +17275,27 @@ "node": ">=6" } }, + "node_modules/refractor/node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/reftools": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", @@ -15212,19 +17305,19 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -15341,9 +17434,9 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", @@ -15351,9 +17444,9 @@ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" }, "node_modules/resolve": { - "version": "1.22.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", - "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -15422,11 +17515,12 @@ } }, "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "peer": true, "engines": { - "node": ">=4" + "node": ">=0.12" } }, "node_modules/reusify": { @@ -15443,6 +17537,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, @@ -15453,6 +17548,56 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rst-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", + "integrity": "sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==", + "peer": true, + "dependencies": { + "lodash.flattendeep": "^4.4.0", + "nearley": "^2.7.10" + } + }, "node_modules/rtl-css-js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", @@ -15485,13 +17630,12 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", - "dev": true, + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -15522,15 +17666,17 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -15565,12 +17711,9 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -15592,15 +17735,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -15651,9 +17821,9 @@ } }, "node_modules/short-unique-id": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.0.3.tgz", - "integrity": "sha512-yhniEILouC0s4lpH0h7rJsfylZdca10W9mDJRAFh3EpcSUanCHGb0R7kcFOIUCZYSAPo0PUD5ZxWQdW0T4xaug==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.2.0.tgz", + "integrity": "sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==", "bin": { "short-unique-id": "bin/short-unique-id", "suid": "bin/short-unique-id" @@ -15708,24 +17878,32 @@ "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==" }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "peer": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/simple-concat": { "version": "1.0.1", @@ -15797,17 +17975,17 @@ } }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } @@ -15823,10 +18001,20 @@ "source-map": "^0.6.0" } }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -15907,11 +18095,6 @@ "stacktrace-gps": "^3.0.4" } }, - "node_modules/stampit": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/stampit/-/stampit-4.3.2.tgz", - "integrity": "sha512-pE2org1+ZWQBnIxRPrBM2gVupkuDD0TTNIo1H6GdT/vO82NXli2z8lRE8cu/nBIHrcOCXFBAHpb9ZldrB2/qOA==" - }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -15922,6 +18105,18 @@ "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", "integrity": "sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA==" }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -15954,16 +18149,19 @@ } }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { @@ -15971,7 +18169,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -15982,45 +18179,80 @@ } }, "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -16030,28 +18262,29 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16086,7 +18319,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -16095,12 +18327,13 @@ } }, "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/strip-final-newline": { @@ -16134,19 +18367,19 @@ } }, "node_modules/styled-components": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.1.tgz", - "integrity": "sha512-cpZZP5RrKRIClBW5Eby4JM1wElLVP4NQrJbJ0h10TidTyJf4SIIwa3zLXOoPb4gJi8MsJ8mjq5mu2IrEhZIAcQ==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.11.tgz", + "integrity": "sha512-Ui0jXPzbp1phYij90h12ksljKGqF8ncGx+pjrNPsSPhbUUjWT2tD1FwGo2LF6USCnbrsIhNngDfodhxbegfEOA==", "dependencies": { - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/unitless": "^0.8.0", - "@types/stylis": "^4.0.2", - "css-to-react-native": "^3.2.0", - "csstype": "^3.1.2", - "postcss": "^8.4.31", - "shallowequal": "^1.1.0", - "stylis": "^4.3.0", - "tslib": "^2.5.0" + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" }, "engines": { "node": ">= 16" @@ -16161,9 +18394,14 @@ } }, "node_modules/styled-components/node_modules/stylis": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", - "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/styled-jsx": { "version": "5.1.1", @@ -16193,14 +18431,14 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, "node_modules/sucrase": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", - "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "7.1.6", + "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", @@ -16211,38 +18449,27 @@ "sucrase-node": "bin/sucrase-node" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 6" } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -16257,45 +18484,46 @@ } }, "node_modules/swagger-client": { - "version": "3.23.1", - "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.23.1.tgz", - "integrity": "sha512-ecRJsoGozhGvEUmim2kIc/pH9BllnPVuajuEXVm49EDbwbwbp7P+i5EW+8w5FLaqmGrx9eio51G9bvJV/XC+YQ==", + "version": "3.28.2", + "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.28.2.tgz", + "integrity": "sha512-g30KCdSVyZlMulWOJnheNo7Ea+L06OZebl0oRU6zHd5Zf5AZKHTqurKRdNOLsMWA3l3bWJiEh7s3JlzFJHRmoQ==", "dependencies": { "@babel/runtime-corejs3": "^7.22.15", - "@swagger-api/apidom-core": ">=0.77.0 <1.0.0", - "@swagger-api/apidom-json-pointer": ">=0.77.0 <1.0.0", - "@swagger-api/apidom-ns-openapi-3-1": ">=0.77.0 <1.0.0", - "@swagger-api/apidom-reference": ">=0.77.0 <1.0.0", - "cookie": "~0.5.0", + "@swagger-api/apidom-core": ">=1.0.0-alpha.5 <1.0.0-beta.0", + "@swagger-api/apidom-error": ">=1.0.0-alpha.5 <1.0.0-beta.0", + "@swagger-api/apidom-json-pointer": ">=1.0.0-alpha.5 <1.0.0-beta.0", + "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-alpha.5 <1.0.0-beta.0", + "@swagger-api/apidom-reference": ">=1.0.0-alpha.5 <1.0.0-beta.0", + "cookie": "~0.6.0", "deepmerge": "~4.3.0", "fast-json-patch": "^3.0.0-1", - "is-plain-object": "^5.0.0", "js-yaml": "^4.1.0", "node-abort-controller": "^3.1.1", - "node-fetch-commonjs": "^3.3.1", + "node-fetch-commonjs": "^3.3.2", + "openapi-path-templating": "^1.5.1", + "openapi-server-url-templating": "^1.0.0", "qs": "^6.10.2", - "traverse": "~0.6.6", - "undici": "^5.24.0" + "ramda-adjunct": "^5.0.0", + "traverse": "=0.6.8" } }, "node_modules/swagger-ui-react": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-5.9.1.tgz", - "integrity": "sha512-DxX8Xm47Q4S+Mn/SsFH3HvSi6PrM3/ElTtKK3UEK1fYlyQvnO13jLhd2P0XggDOitfJVmIghLx+ANvqBjNNPiw==", + "version": "5.17.14", + "resolved": "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-5.17.14.tgz", + "integrity": "sha512-mCXerZrbcn4ftPYifUF0+iKIRTHoVCv0HcJc/sXl9nCe3oeWdsjmOWVqKabzzAkAa0NwsbKNJFv2UL/Ivnf6VQ==", "dependencies": { - "@babel/runtime-corejs3": "^7.23.2", - "@braintree/sanitize-url": "=6.0.4", + "@babel/runtime-corejs3": "^7.24.5", + "@braintree/sanitize-url": "=7.0.2", "base64-js": "^1.5.1", - "classnames": "^2.3.1", + "classnames": "^2.5.1", "css.escape": "1.5.1", "deep-extend": "0.6.0", - "dompurify": "=3.0.6", + "dompurify": "=3.1.4", "ieee754": "^1.2.1", "immutable": "^3.x.x", "js-file-download": "^0.4.12", "js-yaml": "=4.1.0", "lodash": "^4.17.21", - "patch-package": "^8.0.0", "prop-types": "^15.8.1", "randexp": "^0.5.3", "randombytes": "^2.1.0", @@ -16304,23 +18532,48 @@ "react-immutable-proptypes": "2.2.0", "react-immutable-pure-component": "^2.2.0", "react-inspector": "^6.0.1", - "react-redux": "^8.1.3", + "react-redux": "^9.1.2", "react-syntax-highlighter": "^15.5.0", - "redux": "^4.1.2", + "redux": "^5.0.1", "redux-immutable": "^4.0.0", "remarkable": "^2.0.1", - "reselect": "^4.1.8", + "reselect": "^5.1.0", "serialize-error": "^8.1.0", "sha.js": "^2.4.11", - "swagger-client": "^3.23.1", + "swagger-client": "^3.28.1", "url-parse": "^1.5.10", "xml": "=1.0.1", "xml-but-prettier": "^1.0.1", "zenscroll": "^4.0.2" }, "peerDependencies": { - "react": ">=17.0.0", - "react-dom": ">=17.0.0" + "react": ">=16.8.0 <19", + "react-dom": ">=16.8.0 <19" + } + }, + "node_modules/swagger-ui-react/node_modules/dompurify": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.4.tgz", + "integrity": "sha512-2gnshi6OshmuKil8rMZuQCGiUF3cUxHY3NGDzUAdUx/NPEe5DVnO8BDoAQouvgwnx0R/+a6jUn36Z0FSdq8vww==" + }, + "node_modules/swagger-ui-react/node_modules/randexp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", + "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "dependencies": { + "drange": "^1.0.2", + "ret": "^0.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swagger-ui-react/node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "engines": { + "node": ">=4" } }, "node_modules/swagger2openapi": { @@ -16358,9 +18611,9 @@ } }, "node_modules/swr": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.4.tgz", - "integrity": "sha512-njiZ/4RiIhoOlAaLYDqwz5qH/KZXVilRLvomrx83HjzCWTfa+InyfAjv05PSFxnmLzZkNO9ZfvgoqzAaEI4sGQ==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz", + "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==", "dependencies": { "client-only": "^0.0.1", "use-sync-external-store": "^1.2.0" @@ -16370,9 +18623,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz", - "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz", + "integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==", "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -16383,7 +18636,7 @@ "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.19.1", + "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -16458,11 +18711,56 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "node_modules/thenify": { "version": "3.3.1", @@ -16503,17 +18801,6 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -16533,6 +18820,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -16551,9 +18839,12 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/traverse": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", - "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -16570,13 +18861,13 @@ } }, "node_modules/tree-sitter-json": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/tree-sitter-json/-/tree-sitter-json-0.20.0.tgz", - "integrity": "sha512-PteOLH+Tx6Bz4ZA/d40/DbkiSXXRM/gKahhHI8hQ1lWNfFvdknnz9k3Mz84ol5srRyLboJ8wp8GSkhZ6ht9EGQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/tree-sitter-json/-/tree-sitter-json-0.20.2.tgz", + "integrity": "sha512-eUxrowp4F1QEGk/i7Sa+Xl8Crlfp7J0AXxX1QdJEQKQYMWhgMbCIgyQvpO3Q0P9oyTrNQxRLlRipDS44a8EtRw==", "hasInstallScript": true, "optional": true, "dependencies": { - "nan": "^2.14.1" + "nan": "^2.18.0" } }, "node_modules/tree-sitter-yaml": { @@ -16599,12 +18890,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -16622,9 +18913,9 @@ "dev": true }, "node_modules/ts-jest": { - "version": "29.1.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", - "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", + "version": "29.1.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", + "integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -16640,10 +18931,11 @@ "ts-jest": "cli.js" }, "engines": { - "node": "^16.10.0 || ^18.0.0 || >=20.0.0" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", @@ -16653,6 +18945,9 @@ "@babel/core": { "optional": true }, + "@jest/transform": { + "optional": true + }, "@jest/types": { "optional": true }, @@ -16664,32 +18959,25 @@ } } }, - "node_modules/ts-jest/node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/ts-keycode-enum": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/ts-keycode-enum/-/ts-keycode-enum-1.0.6.tgz", "integrity": "sha512-DF8+Cf/FJJnPRxwz8agCoDelQXKZWQOS/gnnwx01nZ106tPJdB3BgJ9QTtLwXgR82D8O+nTjuZzWgf0Rg4vuRA==" }, + "node_modules/ts-mixer": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" + }, "node_modules/ts-toolbelt": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" }, "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", @@ -16698,10 +18986,31 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -16738,9 +19047,11 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -16761,29 +19072,28 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -16793,16 +19103,16 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -16812,31 +19122,36 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/types-ramda": { - "version": "0.29.5", - "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.5.tgz", - "integrity": "sha512-u+bAYXHDPJR+amB0qMrMU/NXRB2PG8QqpO2v6j7yK/0mPZhlaaZj++ynYjnVpkPEpCkZEGxNpWY3X7qyLCGE3w==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.30.0.tgz", + "integrity": "sha512-oVPw/KHB5M0Du0txTEKKM8xZOG9cZBRdCVXvwHYuNJUVkAiJ9oWyqkA+9Bj2gjMsHgkkhsYevobQBWs8I2/Xvw==", "dependencies": { "ts-toolbelt": "^9.6.0" } }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -16850,7 +19165,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -16861,17 +19175,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -17016,26 +19319,18 @@ } }, "node_modules/universal-github-app-jwt": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz", - "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.2.tgz", + "integrity": "sha512-t1iB2FmLFE+yyJY9+3wMx0ejB+MQpEVkH0gQv7dR6FZyltyq+ZZO0uDpbopxhrZ3SLEO4dCEkIujOMldEQ2iOA==", "dependencies": { "@types/jsonwebtoken": "^9.0.0", - "jsonwebtoken": "^9.0.0" + "jsonwebtoken": "^9.0.2" } }, "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" }, "node_modules/unload": { "version": "2.2.0", @@ -17052,9 +19347,9 @@ "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "devOptional": true, "funding": [ { @@ -17071,8 +19366,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -17108,25 +19403,38 @@ "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" }, + "node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/usehooks-ts": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.9.1.tgz", - "integrity": "sha512-2FAuSIGHlY+apM9FVlj8/oNhd+1y+Uwv5QNkMQz1oSfdHk4PXo1qoCw9I5M7j0vpH8CSWFJwXbVPeYDjLCx9PA==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.16.0.tgz", + "integrity": "sha512-bez95WqYujxp6hFdM/CpRDiVPirZPxlMzOH2QB8yopoKQMXpscyZoxOjpEdaxvV+CAWUDSM62cWnqHE0E/MZ7w==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, "engines": { - "node": ">=16.15.0", - "npm": ">=8" + "node": ">=16.15.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17 || ^18" } }, "node_modules/util": { @@ -17164,9 +19472,9 @@ } }, "node_modules/v8-to-istanbul": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", - "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "peer": true, "dependencies": { @@ -17178,6 +19486,13 @@ "node": ">=10.12.0" } }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, "node_modules/validate.io-array": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", @@ -17316,11 +19631,11 @@ } }, "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", "engines": { - "node": ">= 8" + "node": ">= 14" } }, "node_modules/web-tree-sitter": { @@ -17366,7 +19681,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -17405,30 +19719,33 @@ } }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -17442,17 +19759,26 @@ "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz", "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==" }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -17463,7 +19789,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -17476,6 +19801,90 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -17485,8 +19894,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -17495,6 +19902,11 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", @@ -17544,14 +19956,19 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "devOptional": true, + "peer": true }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -17586,6 +20003,24 @@ "node": ">=12" } }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -17604,9 +20039,9 @@ "integrity": "sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg==" }, "node_modules/zod": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", - "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index e852b0ca..4ca11e47 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "ioredis": "^5.3.2", "mobx": "^6.12.0", "next": "^14.2.3", - "next-auth": "^5.0.0-beta.17", + "next-auth": "^5.0.0-beta.19", "nodemailer": "^6.9.9", "npm": "^10.2.4", "octokit": "^3.1.2", From 6343a40b74984857e4dc60ef66818f6af12454ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 10:51:31 +0200 Subject: [PATCH 067/191] Makes rows returned by IDB generic --- src/common/db/IDB.ts | 16 ++++++++++------ src/common/db/PostgreSQLDB.ts | 12 ++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/common/db/IDB.ts b/src/common/db/IDB.ts index 70d29f20..7602b175 100644 --- a/src/common/db/IDB.ts +++ b/src/common/db/IDB.ts @@ -1,15 +1,19 @@ -export interface IDBQueryResult { - readonly rows: any[] +export interface IDBRow { + readonly [column: string]: any; +} + +export interface IDBQueryResult { + readonly rows: T[] } export interface IDBConnection { - query(query: string, values: any[]): Promise - query(query: string): Promise + query(query: string, values: any[]): Promise> + query(query: string): Promise> disconnect(): Promise } export default interface IDB { connect(): Promise - query(query: string, values: any[]): Promise - query(query: string): Promise + query(query: string, values: any[]): Promise> + query(query: string): Promise> } diff --git a/src/common/db/PostgreSQLDB.ts b/src/common/db/PostgreSQLDB.ts index cc2b1bd8..3b41451f 100644 --- a/src/common/db/PostgreSQLDB.ts +++ b/src/common/db/PostgreSQLDB.ts @@ -1,5 +1,5 @@ -import { Pool, PoolClient } from "pg" -import IDB, { IDBConnection, IDBQueryResult } from "./IDB" +import { Pool, PoolClient, QueryResult } from "pg" +import IDB, { IDBConnection, IDBQueryResult, IDBRow } from "./IDB" export class PostgreSQLDBConnection implements IDBConnection { @@ -13,8 +13,8 @@ export class PostgreSQLDBConnection implements IDBConnection { this.client.release() } - async query(query: string, values: any[] = []): Promise { - const result = await this.client.query(query, values) + async query(query: string, values: any[] = []): Promise> { + const result: QueryResult = await this.client.query(query, values) return { rows: result.rows } } } @@ -35,9 +35,9 @@ export default class PostgreSQLDB implements IDB { return new PostgreSQLDBConnection({ client }) } - async query(query: string, values: any[] = []): Promise { + async query(query: string, values: any[] = []): Promise> { const connection = await this.connect() - const result = await connection.query(query, values) + const result = await connection.query(query, values) connection.disconnect() return result } From fe2e3e7a7979ea1ec2ae972229107029ea3f65ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 10:52:16 +0200 Subject: [PATCH 068/191] DbGuestRepository uses IDB instead of Pool --- src/composition.ts | 4 ++-- src/features/admin/data/DbGuestRepository.ts | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index 4596afe1..b3c2a26b 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -97,6 +97,8 @@ const oauthTokenRepository = new CompositeOAuthTokenRepository({ const logInHandler = new LogInHandler({ oauthTokenRepository }) +export const guestRepository: IGuestRepository = new DbGuestRepository({ db }) + // Must be a verified email in AWS SES. const fromEmail = FROM_EMAIL || "Shape Docs " @@ -159,8 +161,6 @@ export const auth = NextAuth({ export const session: ISession = new AuthjsSession({ db, auth }) -export const guestRepository: IGuestRepository = new DbGuestRepository(pool) - const guestOAuthTokenDataSource = new GuestOAuthTokenDataSource({ session, guestRepository, diff --git a/src/features/admin/data/DbGuestRepository.ts b/src/features/admin/data/DbGuestRepository.ts index 2cbbaf7f..80ede323 100644 --- a/src/features/admin/data/DbGuestRepository.ts +++ b/src/features/admin/data/DbGuestRepository.ts @@ -1,11 +1,11 @@ -import { Pool } from "pg" +import { IDB } from "@/common" import { Guest, IGuestRepository } from "../domain" export default class DbGuestRepository implements IGuestRepository { - readonly pool: Pool + private readonly db: IDB - constructor(pool: Pool) { - this.pool = pool + constructor(config: { db: IDB }) { + this.db = config.db } /** @@ -25,7 +25,7 @@ export default class DbGuestRepository implements IGuestRepository { LEFT JOIN users u ON g.email = u.email ORDER BY g.email ASC ` - const result = await this.pool.query(sql) + const result = await this.db.query(sql) return result.rows } @@ -37,7 +37,7 @@ export default class DbGuestRepository implements IGuestRepository { */ async findByEmail(email: string): Promise { const sql = "SELECT * FROM guests WHERE email = $1" - const result = await this.pool.query(sql, [email]) + const result = await this.db.query(sql, [email]) return result.rows[0] } @@ -50,7 +50,7 @@ export default class DbGuestRepository implements IGuestRepository { */ async create(email: string, projects: string[]): Promise { const sql = "INSERT INTO guests (email, projects) VALUES ($1, $2)" - await this.pool.query(sql, [email, JSON.stringify(projects)]) + await this.db.query(sql, [email, JSON.stringify(projects)]) const insertedGuest = await this.findByEmail(email) if (!insertedGuest) { @@ -67,7 +67,7 @@ export default class DbGuestRepository implements IGuestRepository { */ async removeByEmail(email: string): Promise { const sql = "DELETE FROM guests WHERE email = $1" - await this.pool.query(sql, [email]) + await this.db.query(sql, [email]) } /** @@ -78,7 +78,7 @@ export default class DbGuestRepository implements IGuestRepository { */ async getProjectsForEmail(email: string): Promise { const sql = "SELECT projects FROM guests WHERE email = $1" - const result = await this.pool.query(sql, [email]) + const result = await this.db.query(sql, [email]) return result.rows[0] ? result.rows[0].projects : [] } } From e4f11f92c92a5261505c3d6acc8ab0adf2e78932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 10:52:57 +0200 Subject: [PATCH 069/191] Supports logging in with single provider only --- src/composition.ts | 23 +++----- src/features/admin/data/DbUserRepository.ts | 54 +++++++++++++++++++ src/features/admin/data/index.ts | 1 + src/features/admin/domain/Account.ts | 5 ++ src/features/admin/domain/IUserRepository.ts | 5 ++ src/features/admin/domain/User.ts | 11 ++++ src/features/admin/domain/index.ts | 3 ++ .../auth/domain/log-in/ILogInHandler.ts | 11 +++- .../auth/domain/log-in/LogInHandler.ts | 47 ++++++++++------ src/features/auth/domain/log-in/index.ts | 2 +- 10 files changed, 128 insertions(+), 34 deletions(-) create mode 100644 src/features/admin/data/DbUserRepository.ts create mode 100644 src/features/admin/domain/Account.ts create mode 100644 src/features/admin/domain/IUserRepository.ts create mode 100644 src/features/admin/domain/User.ts diff --git a/src/composition.ts b/src/composition.ts index b3c2a26b..206a8956 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -44,11 +44,13 @@ import { } from "@/features/auth/domain" import { DbGuestRepository, + DbUserRepository, EmailGuestInviter } from "./features/admin/data" import { IGuestInviter, - IGuestRepository + IGuestRepository, + IUserRepository } from "./features/admin/domain" const { @@ -95,10 +97,12 @@ const oauthTokenRepository = new CompositeOAuthTokenRepository({ ] }) -const logInHandler = new LogInHandler({ oauthTokenRepository }) +const userRepository: IUserRepository = new DbUserRepository({ db }) export const guestRepository: IGuestRepository = new DbGuestRepository({ db }) +const logInHandler = new LogInHandler({ userRepository, guestRepository }) + // Must be a verified email in AWS SES. const fromEmail = FROM_EMAIL || "Shape Docs " @@ -137,20 +141,7 @@ export const auth = NextAuth({ }, callbacks: { async signIn({ user, account, email }) { - if (email && email.verificationRequest && user.email) { // in verification request flow - const guest = await guestRepository.findByEmail(user.email) - if (guest == undefined) { - return false // email not invited - } - } - if (!user.id) { - return false - } - if (account) { - return await logInHandler.handleLogIn(user.id, account) - } else { - return await logInHandler.handleLogIn(user.id) - } + return await logInHandler.handleLogIn(user, account, email) }, async session({ session, user }) { session.user.id = user.id diff --git a/src/features/admin/data/DbUserRepository.ts b/src/features/admin/data/DbUserRepository.ts new file mode 100644 index 00000000..9f3e760c --- /dev/null +++ b/src/features/admin/data/DbUserRepository.ts @@ -0,0 +1,54 @@ +import { IDB } from "@/common" +import { User, IUserRepository } from "../domain" + +type UserRow = { + readonly id: number + readonly name: string | null + readonly email: string | null + readonly image: string | null + readonly account_provider: string | null +} + +export default class DbUserRepository implements IUserRepository { + private readonly db: IDB + + constructor(config: { db: IDB }) { + this.db = config.db + } + + async findByEmail(email: string): Promise { + const sql = ` + SELECT u.id, u.name, u.email, u.image, a.provider as account_provider + FROM users u + LEFT JOIN accounts a ON u.id = a."userId" + WHERE u.email = $1 + ` + const result = await this.db.query(sql, [email]) + const users = this.groupUserRows(result.rows) + if (users.length == 0) { + return undefined + } + return users[0] + } + + private groupUserRows(rows: UserRow[]): User[] { + const userMap = new Map() + for (const row of rows) { + let user = userMap.get(row.id) + if (!user) { + user = { + id: row.id, + name: row.name, + email: row.email, + image: row.image, + accounts: [] + } + userMap.set(row.id, user) + } + if (row.account_provider) { + user.accounts.push({ provider: row.account_provider }) + } + } + return Array.from(userMap.values()) + } +} \ No newline at end of file diff --git a/src/features/admin/data/index.ts b/src/features/admin/data/index.ts index 1f4fc761..77453e12 100644 --- a/src/features/admin/data/index.ts +++ b/src/features/admin/data/index.ts @@ -1,2 +1,3 @@ export { default as DbGuestRepository } from "./DbGuestRepository" +export { default as DbUserRepository } from "./DbUserRepository" export { default as EmailGuestInviter } from "./EmailGuestInviter" diff --git a/src/features/admin/domain/Account.ts b/src/features/admin/domain/Account.ts new file mode 100644 index 00000000..16e6f547 --- /dev/null +++ b/src/features/admin/domain/Account.ts @@ -0,0 +1,5 @@ +type Account = { + readonly provider: string +} + +export default Account diff --git a/src/features/admin/domain/IUserRepository.ts b/src/features/admin/domain/IUserRepository.ts new file mode 100644 index 00000000..e59b5db4 --- /dev/null +++ b/src/features/admin/domain/IUserRepository.ts @@ -0,0 +1,5 @@ +import User from "./User" + +export default interface IUserRepository { + findByEmail(email: string): Promise +} diff --git a/src/features/admin/domain/User.ts b/src/features/admin/domain/User.ts new file mode 100644 index 00000000..92145db8 --- /dev/null +++ b/src/features/admin/domain/User.ts @@ -0,0 +1,11 @@ +import Account from "./Account" + +type User = { + readonly id: number + readonly name: string | null + readonly email: string | null + readonly image: string | null + readonly accounts: Account[] +} + +export default User diff --git a/src/features/admin/domain/index.ts b/src/features/admin/domain/index.ts index 5ab705f3..cad74df7 100644 --- a/src/features/admin/domain/index.ts +++ b/src/features/admin/domain/index.ts @@ -1,3 +1,6 @@ +export type { default as Account } from "./Account" export type { default as Guest } from "./Guest" export type { default as IGuestRepository } from "./IGuestRepository" export type { default as IGuestInviter } from "./IGuestInviter" +export type { default as IUserRepository } from "./IUserRepository" +export type { default as User } from "./User" diff --git a/src/features/auth/domain/log-in/ILogInHandler.ts b/src/features/auth/domain/log-in/ILogInHandler.ts index d2451d31..b7103fdf 100644 --- a/src/features/auth/domain/log-in/ILogInHandler.ts +++ b/src/features/auth/domain/log-in/ILogInHandler.ts @@ -1,3 +1,8 @@ +export interface IUser { + readonly id?: string + readonly email?: string | null +} + export interface IAccount { readonly provider: string readonly providerAccountId: string @@ -5,6 +10,10 @@ export interface IAccount { readonly refresh_token?: string } +export interface IEmail { + readonly verificationRequest?: boolean +} + export default interface ILogInHandler { - handleLogIn(userId: string, account?: IAccount): Promise + handleLogIn(user: IUser, account: IAccount | null, email?: IEmail): Promise } diff --git a/src/features/auth/domain/log-in/LogInHandler.ts b/src/features/auth/domain/log-in/LogInHandler.ts index d494166e..24d62659 100644 --- a/src/features/auth/domain/log-in/LogInHandler.ts +++ b/src/features/auth/domain/log-in/LogInHandler.ts @@ -1,42 +1,57 @@ -import { ILogInHandler, IAccount } from "." -import { IOAuthTokenRepository } from ".." +import { ILogInHandler, IUser, IAccount, IEmail } from "." +import { IGuestRepository, IUserRepository } from "@/features/admin/domain" export default class LogInHandler implements ILogInHandler { - private readonly oauthTokenRepository: IOAuthTokenRepository + private readonly userRepository: IUserRepository + private readonly guestRepository: IGuestRepository - constructor(config: { oauthTokenRepository: IOAuthTokenRepository }) { - this.oauthTokenRepository = config.oauthTokenRepository + constructor(config: { + userRepository: IUserRepository, + guestRepository: IGuestRepository + }) { + this.userRepository = config.userRepository + this.guestRepository = config.guestRepository } - async handleLogIn(userId: string, account?: IAccount): Promise { + async handleLogIn(user: IUser, account: IAccount | null, email?: IEmail) { if (!account) { return false } if (account.provider === "github") { - return await this.handleLogInForGitHubUser(userId, account) + return await this.handleLogInForGitHubUser(account) } else if (account.provider === "nodemailer") { - return true + return await this.handleLogInForGuestUser(user) } else { console.error("Unhandled account provider: " + account.provider) return false } } - private async handleLogInForGitHubUser(userId: string, account: IAccount): Promise { + private async handleLogInForGitHubUser(account: IAccount) { if (!account.access_token) { return false } if (!account.refresh_token) { return false } - try { - await this.oauthTokenRepository.set(userId, { - accessToken: account.access_token, - refreshToken: account.refresh_token - }) - return true - } catch { + return true + } + + private async handleLogInForGuestUser(user: IUser) { + if (!user.email) { + return false + } + const existingUser = await this.userRepository.findByEmail(user.email) + if (existingUser && existingUser.accounts.length > 0) { + // The user is already authenticated with an identity provider, + // so we'll ask them to use that instead. + return "/api/auth/signin?error=OAuthAccountNotLinked" + } + const guest = await this.guestRepository.findByEmail(user.email) + if (!guest) { + // The e-mail address has not been invited as a guest. return false } + return true } } diff --git a/src/features/auth/domain/log-in/index.ts b/src/features/auth/domain/log-in/index.ts index 732dcdd6..1092abce 100644 --- a/src/features/auth/domain/log-in/index.ts +++ b/src/features/auth/domain/log-in/index.ts @@ -1,2 +1,2 @@ -export type { default as ILogInHandler, IAccount } from "./ILogInHandler" +export type { default as ILogInHandler, IUser, IAccount, IEmail } from "./ILogInHandler" export { default as LogInHandler } from "./LogInHandler" From 1398d429299c6e5f3ea8a38a67d6463c12cfb9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 12:41:13 +0200 Subject: [PATCH 070/191] Improves email with magic link --- src/composition.ts | 9 +- .../admin/data/MagicLinkEmailSender.ts | 123 ++++++++++++++++++ src/features/admin/data/index.ts | 1 + 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 src/features/admin/data/MagicLinkEmailSender.ts diff --git a/src/composition.ts b/src/composition.ts index 206a8956..8dfbcb3b 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -45,7 +45,8 @@ import { import { DbGuestRepository, DbUserRepository, - EmailGuestInviter + EmailGuestInviter, + MagicLinkEmailSender } from "./features/admin/data" import { IGuestInviter, @@ -133,7 +134,11 @@ export const auth = NextAuth({ } }, from: fromEmail, - name: "email" + name: "email", + sendVerificationRequest: async (params) => { + const sender = new MagicLinkEmailSender() + await sender.sendMagicLink(params) + } }), ], session: { diff --git a/src/features/admin/data/MagicLinkEmailSender.ts b/src/features/admin/data/MagicLinkEmailSender.ts new file mode 100644 index 00000000..279f9d6e --- /dev/null +++ b/src/features/admin/data/MagicLinkEmailSender.ts @@ -0,0 +1,123 @@ +import { createTransport } from "nodemailer" +import type { Transport, TransportOptions } from "nodemailer" +import * as JSONTransport from "nodemailer/lib/json-transport/index.js" +import * as SendmailTransport from "nodemailer/lib/sendmail-transport/index.js" +import * as SESTransport from "nodemailer/lib/ses-transport/index.js" +import * as SMTPTransport from "nodemailer/lib/smtp-transport/index.js" +import * as SMTPPool from "nodemailer/lib/smtp-pool/index.js" +import * as StreamTransport from "nodemailer/lib/stream-transport/index.js" + +type AllTransportOptions = + | string + | SMTPTransport + | SMTPTransport.Options + | SMTPPool + | SMTPPool.Options + | SendmailTransport + | SendmailTransport.Options + | StreamTransport + | StreamTransport.Options + | JSONTransport + | JSONTransport.Options + | SESTransport + | SESTransport.Options + | Transport + | TransportOptions + +export default class MagicLinkEmailSender { + constructor() {} + + async sendMagicLink(params: { + identifier: string + expires: Date + url: string + provider: { + from: string + server?: AllTransportOptions + } + }): Promise { + const { identifier, expires, url, provider } = params + const transport = createTransport(provider.server) + const result = await transport.sendMail({ + to: identifier, + from: provider.from, + subject: "Sign in to Shape Docs", + text: text({ url }), + html: html({ url, expires }), + }) + const failed = result.rejected.concat(result.pending).filter(Boolean) + if (failed.length) { + throw new Error(`Email (${failed.join(", ")}) could not be sent`) + } + } +} + +function text({ url }: { url: string }) { + return `Sign in to Shape Docs.\n${url}\n\n` +} + +function html({ url, expires }: { url: string, expires: Date }) { + const imageHost = "http://docs.shapetools.io" + const color = { + background: "#f9f9f9", + text: "#000", + mainBackground: "#fff", + buttonBackground: "#0D6DDB", + buttonText: "#fff" + } + return ` + + + + + + + + + + + + + + + + + +
+ Shape Docs logo +
+ Sign in to Shape Docs +
+ The link can only be used once
and expires on ${formatDate(expires)}. +
+ + + + +
Sign + in
+
+ If you did not request this email, you can safely ignore it. +
+ +` +} + +function formatDate(date: Date) { + const day = date.getDate() + const monthNames = [ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ] + const month = monthNames[date.getMonth()] + const year = date.getFullYear() + const hours = date.getHours().toString().padStart(2, '0') + const minutes = date.getMinutes().toString().padStart(2, '0') + return `${month} ${day}, ${year}, at ${hours}:${minutes}` +} diff --git a/src/features/admin/data/index.ts b/src/features/admin/data/index.ts index 77453e12..3a918381 100644 --- a/src/features/admin/data/index.ts +++ b/src/features/admin/data/index.ts @@ -1,3 +1,4 @@ export { default as DbGuestRepository } from "./DbGuestRepository" export { default as DbUserRepository } from "./DbUserRepository" export { default as EmailGuestInviter } from "./EmailGuestInviter" +export { default as MagicLinkEmailSender } from "./MagicLinkEmailSender" \ No newline at end of file From 0e15c583dbfa4eb4cc58cc607f06972faf283552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 13:02:17 +0200 Subject: [PATCH 071/191] Updates design of invitation e-mail --- src/composition.ts | 2 + src/features/admin/data/EmailGuestInviter.ts | 130 ++++++++++++++----- 2 files changed, 97 insertions(+), 35 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index 8dfbcb3b..7f9afeda 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -55,6 +55,7 @@ import { } from "./features/admin/domain" const { + SHAPE_DOCS_BASE_URL, GITHUB_APP_ID, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, @@ -243,6 +244,7 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( ) export const guestInviter: IGuestInviter = new EmailGuestInviter({ + url: SHAPE_DOCS_BASE_URL, server: { host: SMTP_HOST, user: SMTP_USER, diff --git a/src/features/admin/data/EmailGuestInviter.ts b/src/features/admin/data/EmailGuestInviter.ts index 26a7004e..a490bdd8 100644 --- a/src/features/admin/data/EmailGuestInviter.ts +++ b/src/features/admin/data/EmailGuestInviter.ts @@ -2,41 +2,101 @@ import { Transporter, createTransport } from "nodemailer"; import { IGuestInviter } from "../domain"; export default class EmailGuestInviter implements IGuestInviter { - readonly transport: Transporter; - readonly from: string; + private readonly url: string + private readonly transport: Transporter + private readonly from: string - constructor(options: { server: {host: string, user: string, pass: string}, from: string }) { - this.transport = createTransport({ - host: options.server.host, - auth: { - user: options.server.user, - pass: options.server.pass, - }, - }); - this.from = options.from; - } + constructor(config: { + url: string, + server: { + host: string, + user: string, + pass: string + }, + from: string + }) { + this.url = config.url + this.transport = createTransport({ + host: config.server.host, + auth: { + user: config.server.user, + pass: config.server.pass + } + }) + this.from = config.from + } - async inviteGuestByEmail(email: string): Promise { - await this.transport.sendMail({ - to: email, - from: this.from, - subject: "You have been invited to join Shape Docs", - html: ` - - - - - -

You have been invited to join Shape Docs!

-

Shape Docs uses magic links for authentication. This means that you don't need to remember a password.

-

To get started click here: Sign in

- - - `, - }); - } + async inviteGuestByEmail(email: string): Promise { + const url = this.url + await this.transport.sendMail({ + to: email, + from: this.from, + subject: "You have been invited Shape Docs", + text: text({ url }), + html: html({ url }) + }) + } +} + +function text({ url }: { url: string }) { + return `You have been invited to Shape Docs\n\nSign in with your e-mail on ${url}\n\n` +} + +function html({ url }: { url: string }) { + const imageHost = "http://docs.shapetools.io" + const displayURL = url.replace(/https?:\/\//gi, "") + const color = { + background: "#f9f9f9", + text: "#000", + mainBackground: "#fff", + buttonBackground: "#0D6DDB", + buttonText: "#fff" + } + return ` + + + + + + + + + + + + + + + + + + + + +
+ Shape Docs logo +
+ You have been invited to Shape Docs +
+ Shape Docs uses magic links for signing in,
so you don't need to remember a password.
+
+ Visit ${displayURL} and enter your email to sign in. +
+ + + + +
Go to Shape Docs
+
+ If you did not request this email, you can safely ignore it. +
+ +` } From ad1a86536f45ffdebe18ccacc7d5eb410002b477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 15:07:19 +0200 Subject: [PATCH 072/191] Updates README --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 1f5ee0da..75d12c17 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,20 @@ Each environment variable is described in the table below. |SMTP_PASS|Password for SMTP server used for sending magic links and guest invitation.| |FROM_EMAIL|Sender email for magic links and guest invitations.| +Be aware that the GitHub private key must be PKCS8. GitHub creates PKCS1 keys, so we must manually convert the key from PKCS1 to PKCS8 before base64 encoding it. This can be done as follows: + +```bash +openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt \ + -in ~/Downloads/private-key-pkcs1.pem \ + -out ~/Downloads/private-key-pkcs8.pem +``` + +The key can then be base 64 encoded and assigned to the GITHUB_PRIVATE_KEY_BASE_64 environment variable as follows: + +```bash +base64 -i ~/Downloads/private-key-pkcs8.pem | pbcopy +``` + Run the app using the following command: ``` From b01dc3a86fca0b5b83668fa84d1447d71359738e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 26 Jun 2024 15:10:20 +0200 Subject: [PATCH 073/191] Simplifies command --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75d12c17..e5b4f7e8 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,8 @@ Be aware that the GitHub private key must be PKCS8. GitHub creates PKCS1 keys, s ```bash openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt \ - -in ~/Downloads/private-key-pkcs1.pem \ - -out ~/Downloads/private-key-pkcs8.pem + -in private-key-pkcs1.pem \ + -out private-key-pkcs8.pem ``` The key can then be base 64 encoded and assigned to the GITHUB_PRIVATE_KEY_BASE_64 environment variable as follows: From 55258e166223659862a57ef10a4e6ac6c1cd5c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 27 Jun 2024 14:16:49 +0200 Subject: [PATCH 074/191] Stores access token and refresh token obtained at login --- .../CompositeOAuthTokenRepository.test.ts | 143 ------------------ src/common/utils/saneParseInt.ts | 7 + src/composition.ts | 12 +- .../auth/domain/log-in/LogInHandler.ts | 24 ++- src/features/auth/domain/oauth-token/index.ts | 2 +- .../AuthjsAccountsOAuthTokenRepository.ts | 28 +--- .../CompositeOAuthTokenRepository.ts | 33 ---- .../FallbackOAuthTokenRepository.ts | 37 +++++ 8 files changed, 71 insertions(+), 215 deletions(-) delete mode 100644 __test__/auth/CompositeOAuthTokenRepository.test.ts create mode 100644 src/common/utils/saneParseInt.ts delete mode 100644 src/features/auth/domain/oauth-token/repository/CompositeOAuthTokenRepository.ts create mode 100644 src/features/auth/domain/oauth-token/repository/FallbackOAuthTokenRepository.ts diff --git a/__test__/auth/CompositeOAuthTokenRepository.test.ts b/__test__/auth/CompositeOAuthTokenRepository.test.ts deleted file mode 100644 index 963581e4..00000000 --- a/__test__/auth/CompositeOAuthTokenRepository.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { CompositeOAuthTokenRepository } from "../../src/features/auth/domain" - -test("It traverses all repositories until it gets a value", async () => { - let didGetFromRepository1 = false - let didGetFromRepository2 = false - let didGetFromRepository3 = false - const sut = new CompositeOAuthTokenRepository({ - oAuthTokenRepositories: [{ - async get(_userId) { - didGetFromRepository1 = true - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, { - async get(_userId) { - didGetFromRepository2 = true - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, { - async get(_userId) { - didGetFromRepository3 = true - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }] - }) - await expect(sut.get("1234")).rejects.toThrow() - expect(didGetFromRepository1).toBeTruthy() - expect(didGetFromRepository2).toBeTruthy() - expect(didGetFromRepository3).toBeTruthy() -}) - -test("It skips getting value from following repositories once it finds a value", async () => { - let didGetFromRepository1 = false - let didGetFromRepository2 = false - let didGetFromRepository3 = false - const sut = new CompositeOAuthTokenRepository({ - oAuthTokenRepositories: [{ - async get(_userId) { - didGetFromRepository1 = true - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, { - async get(_userId) { - didGetFromRepository2 = true - return { accessToken: "foo" } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, { - async get(_userId) { - didGetFromRepository3 = true - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }] - }) - await sut.get("1234") - expect(didGetFromRepository1).toBeTruthy() - expect(didGetFromRepository2).toBeTruthy() - expect(didGetFromRepository3).toBeFalsy() -}) - -test("It sets OAuth token in all repositories", async () => { - let didSetInRepository1 = false - let didSetInRepository2 = false - let didSetInRepository3 = false - const sut = new CompositeOAuthTokenRepository({ - oAuthTokenRepositories: [{ - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) { - didSetInRepository1 = true - }, - async delete(_userId) {} - }, { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) { - didSetInRepository2 = true - }, - async delete(_userId) {} - }, { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) { - didSetInRepository3 = true - }, - async delete(_userId) {} - }] - }) - await sut.set("1234", { accessToken: "foo" }) - expect(didSetInRepository1).toBeTruthy() - expect(didSetInRepository2).toBeTruthy() - expect(didSetInRepository3).toBeTruthy() -}) - -test("It deletes OAuth token from all repositories", async () => { - let didDeleteFromRepository1 = false - let didDeleteFromRepository2 = false - let didDeleteFromRepository3 = false - const sut = new CompositeOAuthTokenRepository({ - oAuthTokenRepositories: [{ - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) { - didDeleteFromRepository1 = true - } - }, { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) { - didDeleteFromRepository2 = true - } - }, { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) { - didDeleteFromRepository3 = true - } - }] - }) - await sut.delete("1234") - expect(didDeleteFromRepository1).toBeTruthy() - expect(didDeleteFromRepository2).toBeTruthy() - expect(didDeleteFromRepository3).toBeTruthy() -}) diff --git a/src/common/utils/saneParseInt.ts b/src/common/utils/saneParseInt.ts new file mode 100644 index 00000000..7c69b8b2 --- /dev/null +++ b/src/common/utils/saneParseInt.ts @@ -0,0 +1,7 @@ +export default (str: string) => { + const num = parseInt(str, 10) + if (isNaN(num) || str.trim() !== num.toString()) { + return undefined + } + return num +} diff --git a/src/composition.ts b/src/composition.ts index 7f9afeda..0610bb65 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -29,8 +29,8 @@ import { AccountProviderTypeBasedOAuthTokenRefresher, AuthjsAccountsOAuthTokenRepository, CompositeLogOutHandler, - CompositeOAuthTokenRepository, ErrorIgnoringLogOutHandler, + FallbackOAuthTokenRepository, GitHubOrganizationSessionValidator, GuestOAuthTokenDataSource, GuestOAuthTokenRefresher, @@ -92,18 +92,16 @@ const pool = new Pool({ const db = new PostgreSQLDB({ pool }) -const oauthTokenRepository = new CompositeOAuthTokenRepository({ - oAuthTokenRepositories: [ - new OAuthTokenRepository({ db, provider: "github" }), - new AuthjsAccountsOAuthTokenRepository({ db, provider: "github" }) - ] +const oauthTokenRepository = new FallbackOAuthTokenRepository({ + primaryRepository: new OAuthTokenRepository({ db, provider: "github" }), + secondaryRepository: new AuthjsAccountsOAuthTokenRepository({ db, provider: "github" }) }) const userRepository: IUserRepository = new DbUserRepository({ db }) export const guestRepository: IGuestRepository = new DbGuestRepository({ db }) -const logInHandler = new LogInHandler({ userRepository, guestRepository }) +const logInHandler = new LogInHandler({ userRepository, guestRepository, oauthTokenRepository }) // Must be a verified email in AWS SES. const fromEmail = FROM_EMAIL || "Shape Docs " diff --git a/src/features/auth/domain/log-in/LogInHandler.ts b/src/features/auth/domain/log-in/LogInHandler.ts index 24d62659..76937d31 100644 --- a/src/features/auth/domain/log-in/LogInHandler.ts +++ b/src/features/auth/domain/log-in/LogInHandler.ts @@ -1,16 +1,21 @@ import { ILogInHandler, IUser, IAccount, IEmail } from "." import { IGuestRepository, IUserRepository } from "@/features/admin/domain" +import { IOAuthTokenRepository } from "../oauth-token" +import saneParseInt from "@/common/utils/saneParseInt" export default class LogInHandler implements ILogInHandler { private readonly userRepository: IUserRepository private readonly guestRepository: IGuestRepository + private readonly oauthTokenRepository: IOAuthTokenRepository constructor(config: { userRepository: IUserRepository, - guestRepository: IGuestRepository + guestRepository: IGuestRepository, + oauthTokenRepository: IOAuthTokenRepository }) { this.userRepository = config.userRepository this.guestRepository = config.guestRepository + this.oauthTokenRepository = config.oauthTokenRepository } async handleLogIn(user: IUser, account: IAccount | null, email?: IEmail) { @@ -18,7 +23,7 @@ export default class LogInHandler implements ILogInHandler { return false } if (account.provider === "github") { - return await this.handleLogInForGitHubUser(account) + return await this.handleLogInForGitHubUser(user, account) } else if (account.provider === "nodemailer") { return await this.handleLogInForGuestUser(user) } else { @@ -27,13 +32,22 @@ export default class LogInHandler implements ILogInHandler { } } - private async handleLogInForGitHubUser(account: IAccount) { - if (!account.access_token) { + private async handleLogInForGitHubUser(user: IUser, account: IAccount) { + if (!user.id) { return false } - if (!account.refresh_token) { + const accessToken = account.access_token + const refreshToken = account.refresh_token + if (!accessToken) { return false } + if (!refreshToken) { + return false + } + let userId = saneParseInt(user.id) + if (userId) { + await this.oauthTokenRepository.set(`${userId}`, { accessToken, refreshToken }) + } return true } diff --git a/src/features/auth/domain/oauth-token/index.ts b/src/features/auth/domain/oauth-token/index.ts index affca436..d1c32042 100644 --- a/src/features/auth/domain/oauth-token/index.ts +++ b/src/features/auth/domain/oauth-token/index.ts @@ -8,6 +8,6 @@ export type { default as IOAuthTokenRefresher } from "./refresher/IOAuthTokenRef export { default as LockingOAuthTokenRefresher } from "./refresher/LockingOAuthTokenRefresher" export { default as PersistingOAuthTokenRefresher } from "./refresher/PersistingOAuthTokenRefresher" export { default as AuthjsAccountsOAuthTokenRepository } from "./repository/AuthjsAccountsOAuthTokenRepository" -export { default as CompositeOAuthTokenRepository } from "./repository/CompositeOAuthTokenRepository" +export { default as FallbackOAuthTokenRepository } from "./repository/FallbackOAuthTokenRepository" export type { default as IOAuthTokenRepository } from "./repository/IOAuthTokenRepository" export { default as OAuthTokenRepository } from "./repository/OAuthTokenRepository" diff --git a/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts index 4f250129..9d851802 100644 --- a/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts +++ b/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts @@ -30,31 +30,7 @@ export default class AuthjsAccountsOAuthTokenRepository implements IOAuthTokenRe return { accessToken, refreshToken } } - async set(userId: string, token: OAuthToken): Promise { - const query = ` - UPDATE accounts - SET access_token = $3, refresh_token = $4 - WHERE provider = $1 AND \"userId\" = $2 - ` - try { - await this.db.query(query, [this.provider, userId, token.accessToken, token.refreshToken]) - } catch (error) { - console.error(error) - throw error - } - } + async set(_userId: string, _token: OAuthToken): Promise {} - async delete(userId: string): Promise { - const query = ` - UPDATE accounts - SET access_token = "", refresh_token = "" - WHERE provider = $1 AND \"userId\" = $2 - ` - try { - await this.db.query(query, [this.provider, userId]) - } catch (error) { - console.error(error) - throw error - } - } + async delete(_userId: string): Promise {} } diff --git a/src/features/auth/domain/oauth-token/repository/CompositeOAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/CompositeOAuthTokenRepository.ts deleted file mode 100644 index 7b234be6..00000000 --- a/src/features/auth/domain/oauth-token/repository/CompositeOAuthTokenRepository.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { UnauthorizedError } from "@/common" -import { IOAuthTokenRepository, OAuthToken } from ".." - -export default class CompositeOAuthTokenRepository implements IOAuthTokenRepository { - private readonly oAuthTokenRepositories: IOAuthTokenRepository[] - - constructor(config: { oAuthTokenRepositories: IOAuthTokenRepository[] }) { - this.oAuthTokenRepositories = config.oAuthTokenRepositories - } - - async set(userId: string, token: OAuthToken): Promise { - await Promise.all(this.oAuthTokenRepositories.map(async repository => { - await repository.set(userId, token) - })) - } - - async delete(userId: string): Promise { - await Promise.all(this.oAuthTokenRepositories.map(async repository => { - await repository.delete(userId) - })) - } - - async get(userId: string): Promise { - for (const repository of this.oAuthTokenRepositories) { - try { - return await repository.get(userId) - } catch { - // Ignore error and handle it out of the for loop. - } - } - throw new UnauthorizedError("The access token was not found. It appears that the user is not authenticated.") - } -} \ No newline at end of file diff --git a/src/features/auth/domain/oauth-token/repository/FallbackOAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/FallbackOAuthTokenRepository.ts new file mode 100644 index 00000000..d01ee9c7 --- /dev/null +++ b/src/features/auth/domain/oauth-token/repository/FallbackOAuthTokenRepository.ts @@ -0,0 +1,37 @@ +import { IOAuthTokenRepository, OAuthToken } from ".." + +export default class FallbackOAuthTokenRepository implements IOAuthTokenRepository { + private readonly primaryRepository: IOAuthTokenRepository + private readonly secondaryRepository: IOAuthTokenRepository + + constructor(config: { + primaryRepository: IOAuthTokenRepository, + secondaryRepository: IOAuthTokenRepository + }) { + this.primaryRepository = config.primaryRepository + this.secondaryRepository = config.secondaryRepository + } + + async get(userId: string): Promise { + try { + return await this.primaryRepository.get(userId) + } catch { + // Reading from the primary repository failed so we'll try the secondary repository. + // However, we don't know if the error is due to the OAuth token not existing in + // the primary repository or some other error occurred. + // We might consider changing get(_:) on IOAuthTokenRepository to return null in the + // case a token doesn't exist rather than throwing an error. + const oauthToken = await this.secondaryRepository.get(userId) + await this.primaryRepository.set(userId, oauthToken) + return oauthToken + } + } + + async set(userId: string, token: OAuthToken): Promise { + await this.primaryRepository.set(userId, token) + } + + async delete(userId: string): Promise { + await this.primaryRepository.delete(userId) + } +} From 70c29a8c40d6c1aafb2a300c2c09209f5c748de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 27 Jun 2024 14:20:53 +0200 Subject: [PATCH 075/191] Ensures input to saneParseInt is a string --- src/common/utils/saneParseInt.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common/utils/saneParseInt.ts b/src/common/utils/saneParseInt.ts index 7c69b8b2..79806e19 100644 --- a/src/common/utils/saneParseInt.ts +++ b/src/common/utils/saneParseInt.ts @@ -1,6 +1,7 @@ export default (str: string) => { - const num = parseInt(str, 10) - if (isNaN(num) || str.trim() !== num.toString()) { + let forcedString = `${str}` + const num = parseInt(forcedString, 10) + if (isNaN(num) || forcedString.trim() !== num.toString()) { return undefined } return num From 92afa5da5f460671ec5dfa3e424b1209f5f631e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 27 Jun 2024 14:59:19 +0200 Subject: [PATCH 076/191] Adds unit tests to saneParseInt --- __test__/common/utils/saneParseInt.test.ts | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 __test__/common/utils/saneParseInt.test.ts diff --git a/__test__/common/utils/saneParseInt.test.ts b/__test__/common/utils/saneParseInt.test.ts new file mode 100644 index 00000000..ebd217d4 --- /dev/null +++ b/__test__/common/utils/saneParseInt.test.ts @@ -0,0 +1,27 @@ +import saneParseInt from "@/common/utils/saneParseInt" + +test("It parses an integer", async () => { + // @ts-ignore + const val = saneParseInt(42 as string) + expect(val).toBe(42) +}) + +test("It parses a string representing an integer", async () => { + const val = saneParseInt("42") + expect(val).toBe(42) +}) + +test("It fails parsing a string representing a float", async () => { + const val = saneParseInt("4.2") + expect(val).toBeUndefined() +}) + +test("It fails parsing a string", async () => { + const val = saneParseInt("foo") + expect(val).toBeUndefined() +}) + +test("It fails parsing a UUID", async () => { + const val = saneParseInt("30729470-25e4-4a50-8a0a-106fc67948f1") + expect(val).toBeUndefined() +}) From 1060d3b422ac118c7b595bc504fd6f71933040f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 27 Jun 2024 15:06:51 +0200 Subject: [PATCH 077/191] Adds unit tests for FallbackOAuthTokenRepository --- .../auth/FallbackOAuthTokenRepository.test.ts | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 __test__/auth/FallbackOAuthTokenRepository.test.ts diff --git a/__test__/auth/FallbackOAuthTokenRepository.test.ts b/__test__/auth/FallbackOAuthTokenRepository.test.ts new file mode 100644 index 00000000..d51e1458 --- /dev/null +++ b/__test__/auth/FallbackOAuthTokenRepository.test.ts @@ -0,0 +1,153 @@ +import { FallbackOAuthTokenRepository } from "@/features/auth/domain" + +test("It reads from secondary repository if primary repository throws error", async () => { + let didReadFromPrimaryRepository = false + let didReadFromSecondaryRepository = false + const sut = new FallbackOAuthTokenRepository({ + primaryRepository: { + async get(_userId) { + didReadFromPrimaryRepository = true + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + secondaryRepository: { + async get(_userId) { + didReadFromSecondaryRepository = true + return { accessToken: "foo", refreshToken: "bar" } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.get("1234") + expect(didReadFromPrimaryRepository).toBeTruthy() + expect(didReadFromSecondaryRepository).toBeTruthy() +}) + +test("It skips reading from secondary repository if OAuth token was found in primary repository", async () => { + let didReadFromPrimaryRepository = false + let didReadFromSecondaryRepository = false + const sut = new FallbackOAuthTokenRepository({ + primaryRepository: { + async get(_userId) { + didReadFromPrimaryRepository = true + return { accessToken: "foo", refreshToken: "bar" } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + secondaryRepository: { + async get(_userId) { + didReadFromSecondaryRepository = true + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.get("1234") + expect(didReadFromPrimaryRepository).toBeTruthy() + expect(didReadFromSecondaryRepository).toBeFalsy() +}) + +test("It throws error if OAuth token was not found in any of the repositories", async () => { + const sut = new FallbackOAuthTokenRepository({ + primaryRepository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + }, + secondaryRepository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await expect(sut.get("1234")).rejects.toThrow() +}) + +test("It sets value from secondary repository in primary repository", async () => { + let didSetInPrimaryRepository = false + const sut = new FallbackOAuthTokenRepository({ + primaryRepository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) { + didSetInPrimaryRepository = true + }, + async delete(_userId) {} + }, + secondaryRepository: { + async get(_userId) { + return { accessToken: "foo", refreshToken: "bar" } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.get("1234") + expect(didSetInPrimaryRepository).toBeTruthy() +}) + +test("It sets value in primary repository only", async () => { + let didSetInPrimaryRepository = false + let didSetInSecondaryRepository = false + const sut = new FallbackOAuthTokenRepository({ + primaryRepository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) { + didSetInPrimaryRepository = true + }, + async delete(_userId) {} + }, + secondaryRepository: { + async get(_userId) { + return { accessToken: "foo", refreshToken: "bar" } + }, + async set(_userId, _token) { + didSetInSecondaryRepository = true + }, + async delete(_userId) {} + } + }) + await sut.set("1234", { accessToken: "foo" }) + expect(didSetInPrimaryRepository).toBeTruthy() + expect(didSetInSecondaryRepository).toBeFalsy() +}) + +test("It deletes value from primary repository only", async () => { + let didDeleteFromPrimaryRepository = false + let didDeleteFromSecondaryRepository = false + const sut = new FallbackOAuthTokenRepository({ + primaryRepository: { + async get(_userId) { + throw new Error("Not found") + }, + async set(_userId, _token) {}, + async delete(_userId) { + didDeleteFromPrimaryRepository = true + } + }, + secondaryRepository: { + async get(_userId) { + return { accessToken: "foo", refreshToken: "bar" } + }, + async set(_userId, _token) {}, + async delete(_userId) { + didDeleteFromSecondaryRepository = true + } + } + }) + await sut.delete("1234") + expect(didDeleteFromPrimaryRepository).toBeTruthy() + expect(didDeleteFromSecondaryRepository).toBeFalsy() +}) From e5929386552c357d9e0b74e267d717224c0977f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 27 Jun 2024 15:11:38 +0200 Subject: [PATCH 078/191] Fixes EmailGuestInviter unit tests --- __test__/admin/EmailGuestInviter.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts index dc4f9e8b..f57c1bf7 100644 --- a/__test__/admin/EmailGuestInviter.test.ts +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -13,6 +13,7 @@ describe('EmailGuestInviter', () => { describe('constructor', () => { it('should create a transporter', () => { const sut = new EmailGuestInviter({ + url: "https://docs.shapetools.io", server: { host: 'smtp.example.com', user: 'user', @@ -34,6 +35,7 @@ describe('EmailGuestInviter', () => { describe('inviteGuestByEmail', () => { it('should send an invite', async () => { const sut = new EmailGuestInviter({ + url: "https://docs.shapetools.io", server: { host: 'smtp.example.com', user: 'user', From c0a6f15a17fcbce961a556a0832dea45d6407f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 27 Jun 2024 15:27:56 +0200 Subject: [PATCH 079/191] Fixes LogInHandler unit tests --- __test__/auth/LogInHandler.test.ts | 419 ++++++++++++++++-- src/composition.ts | 4 +- .../auth/domain/log-in/ILogInHandler.ts | 6 +- .../auth/domain/log-in/LogInHandler.ts | 22 +- src/features/auth/domain/log-in/index.ts | 2 +- 5 files changed, 410 insertions(+), 43 deletions(-) diff --git a/__test__/auth/LogInHandler.test.ts b/__test__/auth/LogInHandler.test.ts index 4515e1bc..759e5308 100644 --- a/__test__/auth/LogInHandler.test.ts +++ b/__test__/auth/LogInHandler.test.ts @@ -2,6 +2,26 @@ import { LogInHandler } from "../../src/features/auth/domain" test("It disallows logging in when account is undefined", async () => { const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -10,12 +30,35 @@ test("It disallows logging in when account is undefined", async () => { async delete(_userId) {}, } }) - const didLogin = await sut.handleLogIn("1234") + const didLogin = await sut.handleLogIn({ + user: { id: "1234" }, + account: null + }) expect(didLogin).toBeFalsy() }) -test("It allows logging in when account has \"nodemailer\" provider", async () => { +test("It disallows logging in when guest with no mail", async () => { const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -24,15 +67,179 @@ test("It allows logging in when account has \"nodemailer\" provider", async () = async delete(_userId) {}, } }) - const didLogin = await sut.handleLogIn("1234", { - provider: "nodemailer", - providerAccountId: "foo@example.com" + const didLogin = await sut.handleLogIn({ + user: { + id: "1234" + }, + account: { + provider: "nodemailer", + providerAccountId: "foo@example.com" + } + }) + expect(didLogin).toBeFalsy() +}) + +test("It disallows logging in guest that has not been invited", async () => { + const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn({ + user: { + id: "1234", + email: "foo@example.com" + }, + account: { + provider: "nodemailer", + providerAccountId: "foo@example.com" + } + }) + expect(didLogin).toBeFalsy() +}) + +test("It allows logging in guest who has been invited", async () => { + const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(email: string) { + return { + status: "invited", + email: email, + projects: ["example-openapi"] + } + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn({ + user: { + id: "1234", + email: "foo@example.com" + }, + account: { + provider: "nodemailer", + providerAccountId: "foo@example.com" + } }) expect(didLogin).toBeTruthy() }) +test("It redirects user to OAuth error when attempting to login as a guest with an e-mail address for a user that has previously logged in using GitHub", async () => { + const sut = new LogInHandler({ + userRepository: { + async findByEmail(email) { + return { + id: 1234, + email: email, + name: "John Doe", + image: null, + accounts: [{ provider: "github" }] + } + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) {}, + async delete(_userId) {}, + } + }) + const result = await sut.handleLogIn({ + user: { + id: "1234", + email: "foo@example.com" + }, + account: { + provider: "nodemailer", + providerAccountId: "foo@example.com" + } + }) + expect(result).toBe("/api/auth/signin?error=OAuthAccountNotLinked") +}) + test("It disallows logging in using a GitHub Account without an access token", async () => { const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -41,16 +248,41 @@ test("It disallows logging in using a GitHub Account without an access token", a async delete(_userId) {}, } }) - const didLogin = await sut.handleLogIn("1234", { - provider: "github", - providerAccountId: "foo@example.com", - refresh_token: "bar" + const didLogin = await sut.handleLogIn({ + user: { + id: "1234" + }, + account: { + provider: "github", + providerAccountId: "foo@example.com", + refresh_token: "bar" + } }) expect(didLogin).toBeFalsy() }) test("It disallows logging in using a GitHub Account without a refresh token", async () => { const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -59,16 +291,41 @@ test("It disallows logging in using a GitHub Account without a refresh token", a async delete(_userId) {}, } }) - const didLogin = await sut.handleLogIn("1234", { - provider: "github", - providerAccountId: "foo@example.com", - access_token: "foo" + const didLogin = await sut.handleLogIn({ + user: { + id: "1234" + }, + account: { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo" + } }) expect(didLogin).toBeFalsy() }) test("It allows logging in when using a GitHub account that has an access token and a refresh token", async () => { const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -77,20 +334,45 @@ test("It allows logging in when using a GitHub account that has an access token async delete(_userId) {}, } }) - const didLogin = await sut.handleLogIn("1234", { - provider: "github", - providerAccountId: "foo@example.com", - access_token: "foo", - refresh_token: "bar" + const didLogin = await sut.handleLogIn({ + user: { + id: "1234" + }, + account: { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo", + refresh_token: "bar" + } }) expect(didLogin).toBeTruthy() }) -test("It persists access token and refresh token when logging in with GitHub account", async () => { +test("It persists access token and refresh token when logging in with a GitHub account with a valid user ID", async () => { let persistedUserId: string | undefined let persistedAccessToken: string | undefined let persistedRefreshToken: string | undefined const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -103,11 +385,16 @@ test("It persists access token and refresh token when logging in with GitHub acc async delete(_userId) {}, } }) - const didLogin = await sut.handleLogIn("1234", { - provider: "github", - providerAccountId: "foo@example.com", - access_token: "foo", - refresh_token: "bar" + const didLogin = await sut.handleLogIn({ + user: { + id: "1234" + }, + account: { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo", + refresh_token: "bar" + } }) expect(didLogin).toBeTruthy() expect(persistedUserId).toBe("1234") @@ -115,9 +402,78 @@ test("It persists access token and refresh token when logging in with GitHub acc expect(persistedRefreshToken).toBe("bar") }) +test("It skips persisting access token and refresh token when logging in with a GitHub account with a temporary user ID", async () => { + let didPersistTokens = false + const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, + oauthTokenRepository: { + async get(_userId) { + throw new Error("Not implemented") + }, + async set(_userId, _token) { + didPersistTokens = true + }, + async delete(_userId) {}, + } + }) + const didLogin = await sut.handleLogIn({ + user: { + // A temporary user ID is any string that is not an integer. + id: "3d3e2ac4-6b06-41b3-8340-d210c733c62d" + }, + account: { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo", + refresh_token: "bar" + } + }) + expect(didLogin).toBeTruthy() + expect(didPersistTokens).toBeFalsy() +}) + test("It disallows logging in when failing to persist access token and refresh token for GitHub account", async () => { let didAttemptToPersistTokens = false const sut = new LogInHandler({ + userRepository: { + async findByEmail(_email) { + return undefined + }, + }, + guestRepository: { + async getAll() { + return [] + }, + async findByEmail(_email: string) { + return undefined + }, + async create(_email: string, _projects: string[]) { + throw new Error("Not implemented") + }, + async removeByEmail(_email: string) {}, + async getProjectsForEmail(_email: string) { + return [] + } + }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -129,11 +485,16 @@ test("It disallows logging in when failing to persist access token and refresh t async delete(_userId) {}, } }) - const didLogin = await sut.handleLogIn("1234", { - provider: "github", - providerAccountId: "foo@example.com", - access_token: "foo", - refresh_token: "bar" + const didLogin = await sut.handleLogIn({ + user: { + id: "1234" + }, + account: { + provider: "github", + providerAccountId: "foo@example.com", + access_token: "foo", + refresh_token: "bar" + } }) expect(didLogin).toBeFalsy() expect(didAttemptToPersistTokens).toBeTruthy() diff --git a/src/composition.ts b/src/composition.ts index 0610bb65..7a4718dd 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -144,8 +144,8 @@ export const auth = NextAuth({ strategy: "database" }, callbacks: { - async signIn({ user, account, email }) { - return await logInHandler.handleLogIn(user, account, email) + async signIn({ user, account }) { + return await logInHandler.handleLogIn({ user, account }) }, async session({ session, user }) { session.user.id = user.id diff --git a/src/features/auth/domain/log-in/ILogInHandler.ts b/src/features/auth/domain/log-in/ILogInHandler.ts index b7103fdf..671bd4fc 100644 --- a/src/features/auth/domain/log-in/ILogInHandler.ts +++ b/src/features/auth/domain/log-in/ILogInHandler.ts @@ -10,10 +10,6 @@ export interface IAccount { readonly refresh_token?: string } -export interface IEmail { - readonly verificationRequest?: boolean -} - export default interface ILogInHandler { - handleLogIn(user: IUser, account: IAccount | null, email?: IEmail): Promise + handleLogIn(params: { user: IUser, account: IAccount | null }): Promise } diff --git a/src/features/auth/domain/log-in/LogInHandler.ts b/src/features/auth/domain/log-in/LogInHandler.ts index 76937d31..e179c7c6 100644 --- a/src/features/auth/domain/log-in/LogInHandler.ts +++ b/src/features/auth/domain/log-in/LogInHandler.ts @@ -1,4 +1,4 @@ -import { ILogInHandler, IUser, IAccount, IEmail } from "." +import { ILogInHandler, IUser, IAccount } from "." import { IGuestRepository, IUserRepository } from "@/features/admin/domain" import { IOAuthTokenRepository } from "../oauth-token" import saneParseInt from "@/common/utils/saneParseInt" @@ -18,12 +18,12 @@ export default class LogInHandler implements ILogInHandler { this.oauthTokenRepository = config.oauthTokenRepository } - async handleLogIn(user: IUser, account: IAccount | null, email?: IEmail) { + async handleLogIn({ user, account }: { user: IUser, account: IAccount | null }) { if (!account) { return false } if (account.provider === "github") { - return await this.handleLogInForGitHubUser(user, account) + return await this.handleLogInForGitHubUser({ user, account }) } else if (account.provider === "nodemailer") { return await this.handleLogInForGuestUser(user) } else { @@ -32,7 +32,7 @@ export default class LogInHandler implements ILogInHandler { } } - private async handleLogInForGitHubUser(user: IUser, account: IAccount) { + private async handleLogInForGitHubUser({ user, account }: { user: IUser, account: IAccount }) { if (!user.id) { return false } @@ -45,10 +45,20 @@ export default class LogInHandler implements ILogInHandler { return false } let userId = saneParseInt(user.id) - if (userId) { + if (!userId) { + // We do not have a valid user ID, meaning this is the first time the user logs in. + // When logging in for the first time, the user has a temporary ID that we cannot + // look up in our database, so we rely on Auth.js to persist the access token and + // refresh token. This is intended according to Auth.js' documentation: + // https://authjs.dev/reference/nextjs#signin + return true + } + try { await this.oauthTokenRepository.set(`${userId}`, { accessToken, refreshToken }) + return true + } catch (error) { + return false } - return true } private async handleLogInForGuestUser(user: IUser) { diff --git a/src/features/auth/domain/log-in/index.ts b/src/features/auth/domain/log-in/index.ts index 1092abce..4daa92b0 100644 --- a/src/features/auth/domain/log-in/index.ts +++ b/src/features/auth/domain/log-in/index.ts @@ -1,2 +1,2 @@ -export type { default as ILogInHandler, IUser, IAccount, IEmail } from "./ILogInHandler" +export type { default as ILogInHandler, IUser, IAccount } from "./ILogInHandler" export { default as LogInHandler } from "./LogInHandler" From 5b8d3f473ed29466d3ed7b21da1880fa87df0cdd Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 12:06:12 +0200 Subject: [PATCH 080/191] Add deployment to staging --- .../workflows/build-and-deploy-staging.yml | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .github/workflows/build-and-deploy-staging.yml diff --git a/.github/workflows/build-and-deploy-staging.yml b/.github/workflows/build-and-deploy-staging.yml new file mode 100644 index 00000000..0eea287c --- /dev/null +++ b/.github/workflows/build-and-deploy-staging.yml @@ -0,0 +1,82 @@ +name: "[Staging] Build and Deploy" + +on: + workflow_dispatch: {} + +env: + AWS_REGION: eu-central-1 + ECR_REPOSITORY: shapedocs + ECS_SERVICE: StagingApp-AppService7F8F0CA1-jCcia0OvXEXa + ECS_CLUSTER: StagingApp-EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3-L9xrshUBnmqe + ECS_TASK_DEFINITION_NAME: StagingAppAppServiceTaskDef1613562E + CONTAINER_NAME: web + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_SHAPE_DOCS }} + +jobs: + build: + name: Build and Deploy + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Install AWS credentials from 1Password + run: | + AWS_ACCESS_KEY_ID=$(op read "op://Shape Docs GitHub Actions/AWS GitHub Actions User/access_key_id") + AWS_SECRET_ACCESS_KEY=$(op read "op://Shape Docs GitHub Actions/AWS GitHub Actions User/secret_access_key") + echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> $GITHUB_ENV + echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> $GITHUB_ENV + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4.0.1 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2.0.1 + + - name: Fetch task definition + id: fetch-task-definition + run: | + aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION_NAME }} --region ${{ env.AWS_REGION }} | jq .taskDefinition > task-definition.json + jq . task-definition.json + + - name: Create .env.local + run: | + cp .env.example .env.local + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + run: | + # Build a docker image and push it to ECR so that it can + # be deployed to ECS + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1.2.0 + with: + task-definition: task-definition.json + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.build-image.outputs.image }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1.4.11 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true From 5a13ce95feab27c115786778639223c18418323b Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 12:13:38 +0200 Subject: [PATCH 081/191] Remove unused workflow --- .github/workflows/build-docker-image.yml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .github/workflows/build-docker-image.yml diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml deleted file mode 100644 index dca98fda..00000000 --- a/.github/workflows/build-docker-image.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: "Build Docker image" - -on: - workflow_dispatch: {} - -jobs: - build: - name: Build - runs-on: ubuntu-22.04 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Build the app and Docker image - run: | - docker build -t shapedocs . From b222caa98e5178878037875498154db352aecc93 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 13:05:36 +0200 Subject: [PATCH 082/191] Add env vars needed for build to succeed --- .github/workflows/build-and-deploy-staging.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build-and-deploy-staging.yml b/.github/workflows/build-and-deploy-staging.yml index 0eea287c..a4d273d4 100644 --- a/.github/workflows/build-and-deploy-staging.yml +++ b/.github/workflows/build-and-deploy-staging.yml @@ -11,6 +11,16 @@ env: ECS_TASK_DEFINITION_NAME: StagingAppAppServiceTaskDef1613562E CONTAINER_NAME: web OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_SHAPE_DOCS }} + # /start: needed for build to succeed + NEXT_TELEMETRY_DISABLED: 1 + NEXTAUTH_URL: http://dev.local:3000 + NEXTAUTH_SECRET: 336f7a926310cff425cea29556dce2a98859b8d234aa27968696c2e6f1cb7d34 + GITHUB_CLIENT_ID: this-is-our-github-client-id + GITHUB_CLIENT_SECRET: this-is-our-github-client-secret + GITHUB_APP_ID: 12345 + GITHUB_PRIVATE_KEY_BASE_64: LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNCS2NaL3UyL3dubDA4R1BjeXdiSVc2QVdKdnhVL2o0V0g2UnkxODVPSHZDd0FBQUpBYW01MzdHcHVkCit3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQktjWi91Mi93bmwwOEdQY3l3YklXNkFXSnZ4VS9qNFdINlJ5MTg1T0h2Q3cKQUFBRUJ6T2htRGpuY3R0QSt3ai9USHRnWnN6MklRWjdLM2ovb0Z1OEZBNTMxTDhrcHhuKzdiL0NlWFR3WTl6TEJzaGJvQgpZbS9GVCtQaFlmcEhMWHprNGU4TEFBQUFDMlp2YjBCb1lYQmxMbVJyQVFJPQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K + GITHUB_WEBHOOK_SECRET: super-duper-secret + # /end: needed for the build to succeed jobs: build: From a1810f9d0fd2a25284a7fb20566b8be367f621d6 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 13:05:53 +0200 Subject: [PATCH 083/191] Use node20 + npm for build --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e55824e7..720c4b5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-alpine AS base +FROM node:20-alpine AS base # Install dependencies only when needed FROM base AS deps @@ -27,10 +27,10 @@ COPY . . # Uncomment the following line in case you want to disable telemetry during the build. # ENV NEXT_TELEMETRY_DISABLED 1 -RUN yarn build +# RUN yarn build # If using npm comment out above and use below instead -# RUN npm run build +RUN npm run build # Production image, copy all the files and run next FROM base AS runner From b58c1b1eaac1bf0d3e5c87c93e0b02ce91af9c34 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 13:06:20 +0200 Subject: [PATCH 084/191] Fix type for WebhookEventName --- src/features/hooks/data/GitHubHookHandler.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/features/hooks/data/GitHubHookHandler.ts b/src/features/hooks/data/GitHubHookHandler.ts index 16ff71b7..66a826aa 100644 --- a/src/features/hooks/data/GitHubHookHandler.ts +++ b/src/features/hooks/data/GitHubHookHandler.ts @@ -1,6 +1,7 @@ import { NextRequest } from "next/server" -import { Webhooks, EmitterWebhookEventName } from "@octokit/webhooks" +import { Webhooks } from "@octokit/webhooks" import { IPullRequestEventHandler } from "../domain" +import { WebhookEventName } from "@octokit/webhooks/dist-types/types" interface GitHubHookHandlerConfig { readonly secret: string @@ -20,7 +21,7 @@ class GitHubHookHandler { async handle(req: NextRequest): Promise { await this.webhooks.verifyAndReceive({ id: req.headers.get('X-GitHub-Delivery') as string, - name: req.headers.get('X-GitHub-Event') as EmitterWebhookEventName, + name: req.headers.get('X-GitHub-Event') as WebhookEventName, payload: await req.text(), signature: req.headers.get('X-Hub-Signature') as string, }).catch((error) => { From 531b4f65002fc36847e69a3309f4e1900a649169 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 13:06:50 +0200 Subject: [PATCH 085/191] Remove invalid import --- src/app/api/user/repository-access/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/api/user/repository-access/route.ts b/src/app/api/user/repository-access/route.ts index 39b61ff4..585d134b 100644 --- a/src/app/api/user/repository-access/route.ts +++ b/src/app/api/user/repository-access/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server" import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" -import { session, repositoryAccessReader } from "@/composition" +import { session } from "@/composition" export async function GET() { const isAuthenticated = await session.getIsAuthenticated() @@ -14,7 +14,7 @@ export async function GET() { return makeAPIErrorResponse(401, "Unauthorized") } try { - const repositoryNames = await repositoryAccessReader.getRepositoryNames(userId) + const repositoryNames = [] as string[] return NextResponse.json({repositories: repositoryNames}) /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ } catch (error: any) { From 8aa663eac83022e125be503d837700f9c9bb8ac4 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 13:08:45 +0200 Subject: [PATCH 086/191] Remove copy env var step from deploy workflow --- .github/workflows/build-and-deploy-staging.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build-and-deploy-staging.yml b/.github/workflows/build-and-deploy-staging.yml index a4d273d4..6248cd3f 100644 --- a/.github/workflows/build-and-deploy-staging.yml +++ b/.github/workflows/build-and-deploy-staging.yml @@ -57,10 +57,6 @@ jobs: aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION_NAME }} --region ${{ env.AWS_REGION }} | jq .taskDefinition > task-definition.json jq . task-definition.json - - name: Create .env.local - run: | - cp .env.example .env.local - - name: Build, tag, and push image to Amazon ECR id: build-image env: From 5942928370508931cf62a9fbafe888cb9ca9f50b Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 14:45:22 +0200 Subject: [PATCH 087/191] Add IS_BUILD_PROCESS flag Indicates to code that we are in a build environment which does not have access to e.g. a database. --- .../workflows/build-and-deploy-staging.yml | 1 + src/composition.ts | 4 ++- .../admin/data/DummyGuestRepository.ts | 25 +++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/features/admin/data/DummyGuestRepository.ts diff --git a/.github/workflows/build-and-deploy-staging.yml b/.github/workflows/build-and-deploy-staging.yml index 6248cd3f..3652eff1 100644 --- a/.github/workflows/build-and-deploy-staging.yml +++ b/.github/workflows/build-and-deploy-staging.yml @@ -20,6 +20,7 @@ env: GITHUB_APP_ID: 12345 GITHUB_PRIVATE_KEY_BASE_64: LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNCS2NaL3UyL3dubDA4R1BjeXdiSVc2QVdKdnhVL2o0V0g2UnkxODVPSHZDd0FBQUpBYW01MzdHcHVkCit3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQktjWi91Mi93bmwwOEdQY3l3YklXNkFXSnZ4VS9qNFdINlJ5MTg1T0h2Q3cKQUFBRUJ6T2htRGpuY3R0QSt3ai9USHRnWnN6MklRWjdLM2ovb0Z1OEZBNTMxTDhrcHhuKzdiL0NlWFR3WTl6TEJzaGJvQgpZbS9GVCtQaFlmcEhMWHprNGU4TEFBQUFDMlp2YjBCb1lYQmxMbVJyQVFJPQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K GITHUB_WEBHOOK_SECRET: super-duper-secret + IS_BUILD_PROCESS: true # used to signal to the code that it is being built in a CI/CD environment and does not have access to e.g. DB # /end: needed for the build to succeed jobs: diff --git a/src/composition.ts b/src/composition.ts index 7a4718dd..d93a5a80 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -49,10 +49,12 @@ import { MagicLinkEmailSender } from "./features/admin/data" import { + Guest, IGuestInviter, IGuestRepository, IUserRepository } from "./features/admin/domain" +import DummyGuestRepository from "./features/admin/data/DummyGuestRepository" const { SHAPE_DOCS_BASE_URL, @@ -99,7 +101,7 @@ const oauthTokenRepository = new FallbackOAuthTokenRepository({ const userRepository: IUserRepository = new DbUserRepository({ db }) -export const guestRepository: IGuestRepository = new DbGuestRepository({ db }) +export const guestRepository = (process.env.IS_BUILD_PROCESS !== undefined) ? new DummyGuestRepository : new DbGuestRepository({ db }) const logInHandler = new LogInHandler({ userRepository, guestRepository, oauthTokenRepository }) diff --git a/src/features/admin/data/DummyGuestRepository.ts b/src/features/admin/data/DummyGuestRepository.ts new file mode 100644 index 00000000..e4ffd18a --- /dev/null +++ b/src/features/admin/data/DummyGuestRepository.ts @@ -0,0 +1,25 @@ +import { Guest, IGuestRepository } from "../domain" + +/** + * This is a dummy implementation of the guest repository that returns stati data. + * + * It is used in the NextJS build process where pages are prerendered and where we do not + * have access to a database. + */ +export default class DummyGuestRepository implements IGuestRepository { + getAll(): Promise { + return Promise.resolve([]) + } + findByEmail(email: string): Promise { + throw new Error("Method not implemented."); + } + create(email: string, projects: string[]): Promise { + throw new Error("Method not implemented."); + } + removeByEmail(email: string): Promise { + throw new Error("Method not implemented."); + } + getProjectsForEmail(email: string): Promise { + throw new Error("Method not implemented."); + } +} From c4b8ac68312972d5f5dfb42ad7fffa0ccac30fab Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Fri, 28 Jun 2024 14:47:59 +0200 Subject: [PATCH 088/191] Add IS_BUILD_PROCESS flag to build workflow --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 40bd1a85..2e021646 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,7 @@ env: GITHUB_APP_ID: 12345 GITHUB_PRIVATE_KEY_BASE_64: LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNCS2NaL3UyL3dubDA4R1BjeXdiSVc2QVdKdnhVL2o0V0g2UnkxODVPSHZDd0FBQUpBYW01MzdHcHVkCit3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQktjWi91Mi93bmwwOEdQY3l3YklXNkFXSnZ4VS9qNFdINlJ5MTg1T0h2Q3cKQUFBRUJ6T2htRGpuY3R0QSt3ai9USHRnWnN6MklRWjdLM2ovb0Z1OEZBNTMxTDhrcHhuKzdiL0NlWFR3WTl6TEJzaGJvQgpZbS9GVCtQaFlmcEhMWHprNGU4TEFBQUFDMlp2YjBCb1lYQmxMbVJyQVFJPQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K GITHUB_WEBHOOK_SECRET: super-duper-secret + IS_BUILD_PROCESS: true jobs: build: name: Build From d4c3f838ee3966bb627c08677ce4e18d1cf6538d Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 2 Jul 2024 09:31:07 +0200 Subject: [PATCH 089/191] Log from DummyGuestRepo --- src/features/admin/data/DummyGuestRepository.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/features/admin/data/DummyGuestRepository.ts b/src/features/admin/data/DummyGuestRepository.ts index e4ffd18a..c7c2883c 100644 --- a/src/features/admin/data/DummyGuestRepository.ts +++ b/src/features/admin/data/DummyGuestRepository.ts @@ -7,6 +7,10 @@ import { Guest, IGuestRepository } from "../domain" * have access to a database. */ export default class DummyGuestRepository implements IGuestRepository { + constructor() { + console.warn("DummyGuestRepository initialized. No data will be stored.") + } + getAll(): Promise { return Promise.resolve([]) } From 8bd21bcd1113170ec9974bd142f0373c85257e3c Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 2 Jul 2024 09:31:21 +0200 Subject: [PATCH 090/191] Add back .env.example --- .env.example | 20 ++++++++++++++++++++ README.md | 23 ++--------------------- 2 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..f2ae4f2c --- /dev/null +++ b/.env.example @@ -0,0 +1,20 @@ +NEXTAUTH_URL=http://localhost:3000 +NEXTAUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' +GITHUB_CLIENT_ID='GitHub App client ID' +GITHUB_CLIENT_SECRET='GitHub App client secret' +GITHUB_APP_ID='123456' +GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key - see README.md for more info' +GITHUB_WEBHOOK_SECRET='preshared secret also put in app conf in GitHub' +NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' +SHAPE_DOCS_BASE_URL=http://localhost:3000 +GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' +GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' +GITHUB_ORGANIZATION_NAME='testorg' +REDIS_URL=localhost +POSTGRESQL_HOST=localhost +POSTGRESQL_USER=dbuser +POSTGRESQL_DB=db +SMTP_HOST=smtp.domain.com +SMTP_USER=smtpuser +SMTP_PASS=smtppass +FROM_EMAIL=docs@shape.dk diff --git a/README.md b/README.md index e5b4f7e8..e41f91b5 100644 --- a/README.md +++ b/README.md @@ -12,28 +12,9 @@ Portal displaying our projects that are documented with OpenAPI. Hosted on [docs ## 💻 Running the App Locally -Create a file named `.env.local` in the root of the project with the following contents. Make sure to replace any placeholders and generate a random secret using OpenSSL. +Copy `.env.example` to `.env.local` in the root of the project. Make sure to replace any placeholders and generate a random secret using OpenSSL. -``` -NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' -SHAPE_DOCS_BASE_URL='https://docs.shapetools.io' -AUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' -NEXTAUTH_URL_INTERNAL='https://docs.shapetools.io' -GITHUB_CLIENT_ID='GitHub App client ID' -GITHUB_CLIENT_SECRET='GitHub App client secret' -GITHUB_APP_ID='the GitHub App id' -GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key' -GITHUB_WEBHOOK_SECRET='preshared secret also put in app conf in GitHub' -GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' -GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' -GITHUB_ORGANIZATION_NAME='shapehq' -REDIS_URL='' -SMTP_HOST='' -SMTP_USER='' -SMTP_PASS='' -``` - -Each environment variable is described in the table below. +The table below contains explations for environment variables. The list is not pre-emptive and `.env.example` contains the full list. |Environment Variable|Description| |-|-| From 682ba7ca95890d18996c3995177e3f99ed69df03 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 2 Jul 2024 09:31:34 +0200 Subject: [PATCH 091/191] Configure docker build --- Dockerfile | 8 +++++++- next.config.js | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 720c4b5a..85ecaba1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,10 +25,16 @@ COPY . . # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. -# ENV NEXT_TELEMETRY_DISABLED 1 +ENV NEXT_TELEMETRY_DISABLED 1 # RUN yarn build +# Copy example env vars (needed for build process - picked up by Next) +COPY .env.example .env + +# Signal to code that we are in the build process (e.g. to disable database access) +ENV IS_BUILD_PROCESS true + # If using npm comment out above and use below instead RUN npm run build diff --git a/next.config.js b/next.config.js index 5373e6a7..761f61ab 100644 --- a/next.config.js +++ b/next.config.js @@ -4,7 +4,9 @@ const nextConfig = { // Allows production builds to successfully complete even if it has linting errors. // This is only OK because we do linting as part of our CI setup. ignoreDuringBuilds: true, - } + }, + // Output standalone to be used for Docker builds. + output: 'standalone', } module.exports = nextConfig From 648cf6b1f1c060a4c604e0c05b85953d5043f3a3 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 2 Jul 2024 11:26:42 +0200 Subject: [PATCH 092/191] Add health route back --- src/app/api/health/route.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/app/api/health/route.ts diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts new file mode 100644 index 00000000..fead38a1 --- /dev/null +++ b/src/app/api/health/route.ts @@ -0,0 +1,5 @@ +import { NextResponse } from "next/server" + +export const GET = async (): Promise => { + return NextResponse.json({ status: "Healthy" }) +} From 80e5f81cffc0b15faf9c663349731f427a7dc0c5 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 2 Jul 2024 20:58:18 +0200 Subject: [PATCH 093/191] Configure pool with database password --- src/composition.ts | 2 ++ types/env.d.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index d93a5a80..e658c9a8 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -66,6 +66,7 @@ const { REDIS_URL, POSTGRESQL_HOST, POSTGRESQL_USER, + POSTGRESQL_PASSWORD, POSTGRESQL_DB, SMTP_HOST, SMTP_USER, @@ -86,6 +87,7 @@ const gitHubAppCredentials = { const pool = new Pool({ host: POSTGRESQL_HOST, user: POSTGRESQL_USER, + password: POSTGRESQL_PASSWORD, database: POSTGRESQL_DB, max: 20, idleTimeoutMillis: 30000, diff --git a/types/env.d.ts b/types/env.d.ts index ef71a2f8..9b912c54 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -15,10 +15,11 @@ namespace NodeJS { REDIS_URL: string POSTGRESQL_HOST: string POSTGRESQL_USER: string - POSTGRESQL_DB: string, - SMTP_HOST: string, - SMTP_USER: string, - SMTP_PASS: string, - FROM_EMAIL: string | undefined, + POSTGRESQL_PASSWORD: string + POSTGRESQL_DB: string + SMTP_HOST: string + SMTP_USER: string + SMTP_PASS: string + FROM_EMAIL: string | undefined } } From 83d0554b386a1dc52df2387474d5e1ffc7349682 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Wed, 3 Jul 2024 14:51:08 +0200 Subject: [PATCH 094/191] Configure transport in MagicLinkEmailSender manually --- src/composition.ts | 9 ++- .../admin/data/MagicLinkEmailSender.ts | 58 ++++++++----------- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index e658c9a8..679e1446 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -139,7 +139,14 @@ export const auth = NextAuth({ from: fromEmail, name: "email", sendVerificationRequest: async (params) => { - const sender = new MagicLinkEmailSender() + const sender = new MagicLinkEmailSender({ + server: { + host: SMTP_HOST, + user: SMTP_USER, + pass: SMTP_PASS, + }, + from: fromEmail + }) await sender.sendMagicLink(params) } }), diff --git a/src/features/admin/data/MagicLinkEmailSender.ts b/src/features/admin/data/MagicLinkEmailSender.ts index 279f9d6e..451230d0 100644 --- a/src/features/admin/data/MagicLinkEmailSender.ts +++ b/src/features/admin/data/MagicLinkEmailSender.ts @@ -1,46 +1,36 @@ -import { createTransport } from "nodemailer" -import type { Transport, TransportOptions } from "nodemailer" -import * as JSONTransport from "nodemailer/lib/json-transport/index.js" -import * as SendmailTransport from "nodemailer/lib/sendmail-transport/index.js" -import * as SESTransport from "nodemailer/lib/ses-transport/index.js" -import * as SMTPTransport from "nodemailer/lib/smtp-transport/index.js" -import * as SMTPPool from "nodemailer/lib/smtp-pool/index.js" -import * as StreamTransport from "nodemailer/lib/stream-transport/index.js" - -type AllTransportOptions = - | string - | SMTPTransport - | SMTPTransport.Options - | SMTPPool - | SMTPPool.Options - | SendmailTransport - | SendmailTransport.Options - | StreamTransport - | StreamTransport.Options - | JSONTransport - | JSONTransport.Options - | SESTransport - | SESTransport.Options - | Transport - | TransportOptions +import { Transporter, createTransport } from "nodemailer" export default class MagicLinkEmailSender { - constructor() {} + private readonly transport: Transporter + private readonly from: string + + constructor(config: { + server: { + host: string, + user: string, + pass: string + }, + from: string + }) { + this.transport = createTransport({ + host: config.server.host, + auth: { + user: config.server.user, + pass: config.server.pass + } + }) + this.from = config.from + } async sendMagicLink(params: { identifier: string expires: Date url: string - provider: { - from: string - server?: AllTransportOptions - } }): Promise { - const { identifier, expires, url, provider } = params - const transport = createTransport(provider.server) - const result = await transport.sendMail({ + const { identifier, expires, url } = params + const result = await this.transport.sendMail({ to: identifier, - from: provider.from, + from: this.from, subject: "Sign in to Shape Docs", text: text({ url }), html: html({ url, expires }), From 6b93ab832f26e13936afe4fdff5327db02002f1a Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Wed, 3 Jul 2024 14:51:24 +0200 Subject: [PATCH 095/191] Use node:20.8.1-alpine for Docker build --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 85ecaba1..a6573935 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine AS base +FROM node:20.8.1-alpine AS base # Install dependencies only when needed FROM base AS deps From 4474e3cd4b5fa2a6f0fc070b1acdaf06ce0dd685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 08:52:34 +0200 Subject: [PATCH 096/191] Fixes AuthjsAccountsOAuthTokenRepository unit tests --- ...AuthjsAccountsOAuthTokenRepository.test.ts | 46 +++++-------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/__test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts b/__test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts index b45a12bc..685c2c02 100644 --- a/__test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts +++ b/__test__/auth/AuthjsAccountsOAuthTokenRepository.test.ts @@ -31,11 +31,8 @@ test("It gets token for user ID and provider", async () => { expect(queryProvider).toEqual("github") }) -test("It sets token for user ID and provider", async () => { - let queryUserId: string | undefined - let queryProvider: string | undefined - let queryAccessToken: string | undefined - let queryRefreshToken: string | undefined +test("It does not set token", async () => { + let didSetToken = false const sut = new AuthjsAccountsOAuthTokenRepository({ provider: "github", db: { @@ -47,30 +44,18 @@ test("It sets token for user ID and provider", async () => { async disconnect() {}, } }, - async query(_query, values: any[] = []) { - queryProvider = values[0] - queryUserId = values[1] - queryAccessToken = values[2] - queryRefreshToken = values[3] - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } + async query(_query, _values: any[] = []) { + didSetToken = true + return { rows: [] } } } }) await sut.set("1234", { accessToken: "foo", refreshToken: "bar" }) - expect(queryUserId).toEqual("1234") - expect(queryProvider).toEqual("github") - expect(queryAccessToken).toEqual("foo") - expect(queryRefreshToken).toEqual("bar") + expect(didSetToken).toBeFalsy() }) -test("It deletes token for user ID and provider", async () => { - let queryUserId: string | undefined - let queryProvider: string | undefined +test("It does not delete token", async () => { + let didDeleteToken = false const sut = new AuthjsAccountsOAuthTokenRepository({ provider: "github", db: { @@ -82,19 +67,12 @@ test("It deletes token for user ID and provider", async () => { async disconnect() {}, } }, - async query(_query, values: any[] = []) { - queryProvider = values[0] - queryUserId = values[1] - return { - rows: [{ - access_token: "foo", - refresh_token: "bar" - }] - } + async query(_query, _values: any[] = []) { + didDeleteToken = true + return { rows: [] } } } }) await sut.delete("1234") - expect(queryUserId).toEqual("1234") - expect(queryProvider).toEqual("github") + expect(didDeleteToken).toBeFalsy() }) From cb1eb4eb205bdb0607ea62d5aea6ccc5e965eb24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 08:55:30 +0200 Subject: [PATCH 097/191] Streamlines indentation --- __test__/admin/EmailGuestInviter.test.ts | 94 ++++++++++++------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts index f57c1bf7..06bc9b8a 100644 --- a/__test__/admin/EmailGuestInviter.test.ts +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -1,57 +1,57 @@ import nodemailer from "nodemailer" import { EmailGuestInviter } from "../../src/features/admin/data" -jest.mock('nodemailer', () => { - return { - createTransport: jest.fn().mockReturnValue({ - sendMail: jest.fn(), - }), - } +jest.mock("nodemailer", () => { + return { + createTransport: jest.fn().mockReturnValue({ + sendMail: jest.fn() + }) + } }) -describe('EmailGuestInviter', () => { - describe('constructor', () => { - it('should create a transporter', () => { - const sut = new EmailGuestInviter({ - url: "https://docs.shapetools.io", - server: { - host: 'smtp.example.com', - user: 'user', - pass: 'pass', - }, - from: 'some@email.dk', - }) +describe("EmailGuestInviter", () => { + describe("constructor", () => { + it("should create a transporter", () => { + const sut = new EmailGuestInviter({ + url: "https://docs.shapetools.io", + server: { + host: "smtp.example.com", + user: "user", + pass: "pass" + }, + from: "some@email.dk" + }) - expect(nodemailer.createTransport).toHaveBeenCalledWith({ - host: 'smtp.example.com', - auth: { - user: 'user', - pass: 'pass', - }, - }) - }) + expect(nodemailer.createTransport).toHaveBeenCalledWith({ + host: "smtp.example.com", + auth: { + user: "user", + pass: "pass" + } + }) }) + }) - describe('inviteGuestByEmail', () => { - it('should send an invite', async () => { - const sut = new EmailGuestInviter({ - url: "https://docs.shapetools.io", - server: { - host: 'smtp.example.com', - user: 'user', - pass: 'pass', - }, - from: 'some@email.dk', - }) - - sut.inviteGuestByEmail('guest@email.dk') - - expect(nodemailer.createTransport().sendMail).toHaveBeenCalledWith({ - to: 'guest@email.dk', - from: 'some@email.dk', - subject: 'You have been invited to join Shape Docs', - html: expect.any(String), // difficult to test the exact content - }); - }) + describe("inviteGuestByEmail", () => { + it("should send an invite", async () => { + const sut = new EmailGuestInviter({ + url: "https://docs.shapetools.io", + server: { + host: "smtp.example.com", + user: "user", + pass: "pass" + }, + from: "some@email.dk" + }) + + sut.inviteGuestByEmail("guest@email.dk") + + expect(nodemailer.createTransport().sendMail).toHaveBeenCalledWith({ + to: "guest@email.dk", + from: "some@email.dk", + subject: "You have been invited to join Shape Docs", + html: expect.any(String) // difficult to test the exact content + }) }) + }) }) From 331daf7ea0de7985a093ba55e2a78045f73f8938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 08:57:36 +0200 Subject: [PATCH 098/191] Fixes EmailGuestInviter tests --- __test__/admin/EmailGuestInviter.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts index 06bc9b8a..788f9b0f 100644 --- a/__test__/admin/EmailGuestInviter.test.ts +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -49,7 +49,8 @@ describe("EmailGuestInviter", () => { expect(nodemailer.createTransport().sendMail).toHaveBeenCalledWith({ to: "guest@email.dk", from: "some@email.dk", - subject: "You have been invited to join Shape Docs", + subject: "You have been invited Shape Docs", + text: expect.any(String), // difficult to test the exact content html: expect.any(String) // difficult to test the exact content }) }) From 6f6b978bbf9719898444661396d2b87ddf2c4a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 08:58:24 +0200 Subject: [PATCH 099/191] Removes unused variable --- __test__/admin/EmailGuestInviter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts index 788f9b0f..23ae091c 100644 --- a/__test__/admin/EmailGuestInviter.test.ts +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -12,7 +12,7 @@ jest.mock("nodemailer", () => { describe("EmailGuestInviter", () => { describe("constructor", () => { it("should create a transporter", () => { - const sut = new EmailGuestInviter({ + new EmailGuestInviter({ url: "https://docs.shapetools.io", server: { host: "smtp.example.com", From f4f7c89ab63b094770387d2d13b6feea709b9a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 09:44:35 +0200 Subject: [PATCH 100/191] Requires FROM_EMAIL environment variabel --- .env.example | 2 +- src/composition.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index f2ae4f2c..63d14f26 100644 --- a/.env.example +++ b/.env.example @@ -17,4 +17,4 @@ POSTGRESQL_DB=db SMTP_HOST=smtp.domain.com SMTP_USER=smtpuser SMTP_PASS=smtppass -FROM_EMAIL=docs@shape.dk +FROM_EMAIL=Framna Docs diff --git a/src/composition.ts b/src/composition.ts index 679e1446..5eeafff8 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -107,8 +107,9 @@ export const guestRepository = (process.env.IS_BUILD_PROCESS !== undefined) ? ne const logInHandler = new LogInHandler({ userRepository, guestRepository, oauthTokenRepository }) -// Must be a verified email in AWS SES. -const fromEmail = FROM_EMAIL || "Shape Docs " +if (!FROM_EMAIL) { + throw new Error("FROM_EMAIL environment variable must be set to an e-mail verified in AWS SES") +} export const auth = NextAuth({ adapter: PostgresAdapter(pool), @@ -136,7 +137,7 @@ export const auth = NextAuth({ pass: SMTP_PASS, } }, - from: fromEmail, + from: FROM_EMAIL, name: "email", sendVerificationRequest: async (params) => { const sender = new MagicLinkEmailSender({ @@ -145,7 +146,7 @@ export const auth = NextAuth({ user: SMTP_USER, pass: SMTP_PASS, }, - from: fromEmail + from: FROM_EMAIL }) await sender.sendMagicLink(params) } @@ -259,5 +260,5 @@ export const guestInviter: IGuestInviter = new EmailGuestInviter({ user: SMTP_USER, pass: SMTP_PASS, }, - from: fromEmail + from: FROM_EMAIL }) From a185db1c73445f5b6a111e8fb297c56df25c2ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 10:19:38 +0200 Subject: [PATCH 101/191] Moves page metadata into environment variables --- .env.example | 9 +++++---- src/app/layout.tsx | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 63d14f26..b63f8497 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,13 @@ -NEXTAUTH_URL=http://localhost:3000 +SHAPE_DOCS_BASE_URL=http://localhost:3000 +NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' +NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION=Documentation for Shape's APIs +NEXTAUTH_URL_INTERNAL=http://localhost:3000 NEXTAUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' GITHUB_CLIENT_ID='GitHub App client ID' GITHUB_CLIENT_SECRET='GitHub App client secret' GITHUB_APP_ID='123456' GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key - see README.md for more info' -GITHUB_WEBHOOK_SECRET='preshared secret also put in app conf in GitHub' -NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' -SHAPE_DOCS_BASE_URL=http://localhost:3000 +GITHUB_WEBHOOK_SECRET='preshared secret also put in app configuration in GitHub' GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' GITHUB_ORGANIZATION_NAME='testorg' diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 16dd6cc4..f221724d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,8 +8,8 @@ import "@fortawesome/fontawesome-svg-core/styles.css" fontAwesomeConfig.autoAddCss = false export const metadata: Metadata = { - title: "Shape Docs", - description: "Documentation for Shape\"s APIs", + title: process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE, + description: process.env.NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION } export default function RootLayout({ children }: { children: React.ReactNode }) { From 93d4db854ae156b5ccbbecbf9834eacdbd26cd2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 10:22:21 +0200 Subject: [PATCH 102/191] Reorders environment variables --- .env.example | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index b63f8497..b1e87ee9 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,9 @@ SHAPE_DOCS_BASE_URL=http://localhost:3000 +FROM_EMAIL=Framna Docs NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION=Documentation for Shape's APIs NEXTAUTH_URL_INTERNAL=http://localhost:3000 NEXTAUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' -GITHUB_CLIENT_ID='GitHub App client ID' -GITHUB_CLIENT_SECRET='GitHub App client secret' -GITHUB_APP_ID='123456' -GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key - see README.md for more info' -GITHUB_WEBHOOK_SECRET='preshared secret also put in app configuration in GitHub' -GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' -GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' -GITHUB_ORGANIZATION_NAME='testorg' REDIS_URL=localhost POSTGRESQL_HOST=localhost POSTGRESQL_USER=dbuser @@ -18,4 +11,11 @@ POSTGRESQL_DB=db SMTP_HOST=smtp.domain.com SMTP_USER=smtpuser SMTP_PASS=smtppass -FROM_EMAIL=Framna Docs +GITHUB_WEBHOOK_SECRET='preshared secret also put in app configuration in GitHub' +GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' +GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' +GITHUB_ORGANIZATION_NAME='testorg' +GITHUB_CLIENT_ID='GitHub App client ID' +GITHUB_CLIENT_SECRET='GitHub App client secret' +GITHUB_APP_ID='123456' +GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key - see README.md for more info' From 096f8606890ec388d05dcdbdc03119ea31e2b95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 10:23:48 +0200 Subject: [PATCH 103/191] Removes quotes --- .env.example | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index b1e87ee9..7e5e5569 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,9 @@ SHAPE_DOCS_BASE_URL=http://localhost:3000 FROM_EMAIL=Framna Docs -NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' +NEXT_PUBLIC_SHAPE_DOCS_TITLE=Shape Docs NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION=Documentation for Shape's APIs NEXTAUTH_URL_INTERNAL=http://localhost:3000 -NEXTAUTH_SECRET='use [openssl rand -base64 32] to generate a 32 bytes value' +NEXTAUTH_SECRET=use [openssl rand -base64 32] to generate a 32 bytes value REDIS_URL=localhost POSTGRESQL_HOST=localhost POSTGRESQL_USER=dbuser @@ -11,11 +11,11 @@ POSTGRESQL_DB=db SMTP_HOST=smtp.domain.com SMTP_USER=smtpuser SMTP_PASS=smtppass -GITHUB_WEBHOOK_SECRET='preshared secret also put in app configuration in GitHub' -GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' -GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' -GITHUB_ORGANIZATION_NAME='testorg' -GITHUB_CLIENT_ID='GitHub App client ID' -GITHUB_CLIENT_SECRET='GitHub App client secret' -GITHUB_APP_ID='123456' -GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key - see README.md for more info' +GITHUB_WEBHOOK_SECRET=preshared secret also put in app configuration in GitHub +GITHUB_WEBHOK_REPOSITORY_ALLOWLIST= +GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST= +GITHUB_ORGANIZATION_NAME=testorg +GITHUB_CLIENT_ID=GitHub App client ID +GITHUB_CLIENT_SECRET=GitHub App client secret +GITHUB_APP_ID=123456 +GITHUB_PRIVATE_KEY_BASE_64=base 64 encoded version of the private key - see README.md for more info From 945e3d1c127542300c51f1372338191c2a104ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 10:26:41 +0200 Subject: [PATCH 104/191] Reads title from environment variable --- src/features/sidebar/view/SidebarHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/sidebar/view/SidebarHeader.tsx b/src/features/sidebar/view/SidebarHeader.tsx index 6d046eb3..848f7806 100644 --- a/src/features/sidebar/view/SidebarHeader.tsx +++ b/src/features/sidebar/view/SidebarHeader.tsx @@ -28,7 +28,7 @@ export default function SidebarHeader() { letterSpacing: 1 }} > - Shape Docs + {process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE} From 1ad35ba4fb4a954a0a0c4ae3a7cd322805852923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 10:27:46 +0200 Subject: [PATCH 105/191] Renames duck.png to logo.png --- public/{duck.png => images/logo.png} | Bin public/{images/duck.png => logo.png} | Bin src/composition.ts | 2 +- src/features/admin/data/EmailGuestInviter.ts | 2 +- src/features/admin/data/MagicLinkEmailSender.ts | 2 +- src/features/sidebar/view/SidebarHeader.tsx | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) rename public/{duck.png => images/logo.png} (100%) rename public/{images/duck.png => logo.png} (100%) diff --git a/public/duck.png b/public/images/logo.png similarity index 100% rename from public/duck.png rename to public/images/logo.png diff --git a/public/images/duck.png b/public/logo.png similarity index 100% rename from public/images/duck.png rename to public/logo.png diff --git a/src/composition.ts b/src/composition.ts index 5eeafff8..1171f6a2 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -115,7 +115,7 @@ export const auth = NextAuth({ adapter: PostgresAdapter(pool), secret: process.env.NEXTAUTH_SECRET, theme: { - logo: "/images/duck.png", + logo: "/images/logo.png", colorScheme: "light", brandColor: "black" }, diff --git a/src/features/admin/data/EmailGuestInviter.ts b/src/features/admin/data/EmailGuestInviter.ts index a490bdd8..8ac6aeb2 100644 --- a/src/features/admin/data/EmailGuestInviter.ts +++ b/src/features/admin/data/EmailGuestInviter.ts @@ -58,7 +58,7 @@ function html({ url }: { url: string }) { style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;"> - Shape Docs logo + Shape Docs logo diff --git a/src/features/admin/data/MagicLinkEmailSender.ts b/src/features/admin/data/MagicLinkEmailSender.ts index 451230d0..511b3def 100644 --- a/src/features/admin/data/MagicLinkEmailSender.ts +++ b/src/features/admin/data/MagicLinkEmailSender.ts @@ -61,7 +61,7 @@ function html({ url, expires }: { url: string, expires: Date }) { style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;"> - Shape Docs logo + Shape Docs logo diff --git a/src/features/sidebar/view/SidebarHeader.tsx b/src/features/sidebar/view/SidebarHeader.tsx index 848f7806..4fa6319f 100644 --- a/src/features/sidebar/view/SidebarHeader.tsx +++ b/src/features/sidebar/view/SidebarHeader.tsx @@ -15,7 +15,7 @@ export default function SidebarHeader() { }} > Duck Date: Tue, 9 Jul 2024 10:28:03 +0200 Subject: [PATCH 106/191] Removes Shape logos --- public/images/shape-logo-text.png | Bin 62435 -> 0 bytes public/images/shape-logo.png | Bin 89812 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 public/images/shape-logo-text.png delete mode 100644 public/images/shape-logo.png diff --git a/public/images/shape-logo-text.png b/public/images/shape-logo-text.png deleted file mode 100644 index 0b6acc33d369e60763d60ff14e3fc3c9fa5dd7ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62435 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%0AF*G?w1{m|XVO?Q2&4o7FCV~72S7@8nK5Dz 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 z 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 diff --git a/public/images/shape-logo.png b/public/images/shape-logo.png deleted file mode 100644 index 913f770d38ff47274e8192711625edfb00949f12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89812 zcmX`T1yoy27cLxvyA^jS?(SZsxLa{|EA9j>?(R-;cPZ{rpt!rc+n@Kl|Gio3WF}`# zCYj8ZXYc(?B9s)QkP+|^00028jI@Lb000Dcfy{6)|207-c;E)gLR4N90H}*ad^3Uu zzqU2gk};Q;2hf50Z~!O>e87KqA;6aq1i}C7OG3~9ApdtCJY$#@0P6oAqX2Hf50?L) z5W$n{gIfpyFc0{DM*snNkpHjG{NFHQHTEuW1MeuU?E(P&o&aB05OB_f{Q!UnKt@7T z%>&}B3%=7--Ib|ZaeZ}iFCctolC*3qZ8WYkxU-ZzksNJOL|=bne0tA8PVNf@d5~c( zx`Yy@e8C$gGWsrBGIg088(g>(b{MXNk|

s9IcIhr@#Bsjh{8$G;7W+zRiG%X9wu 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 |JZ9kcJyt;Y=by| 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

-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 From bbaae76b97175e79e61f757a5f42ead1fe2120ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 10:28:52 +0200 Subject: [PATCH 107/191] Removes shapehq from unit tests --- ...ingCommentCheckingPullRequestEventHandler.test.ts | 10 +++++----- .../hooks/PostCommentPullRequestEventHandler.test.ts | 6 +++--- ...sitoryNameCheckingPullRequestEventHandler.test.ts | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts index ff56d18f..bd5f38bd 100644 --- a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts +++ b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts @@ -13,7 +13,7 @@ test("It fetches comments from the repository", async () => { }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -35,7 +35,7 @@ test("It does calls decorated event handler if a comment does not exist in the r }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -60,7 +60,7 @@ test("It does not call the event handler if a comment already exists in the repo }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -85,7 +85,7 @@ test("It calls the event handler if a comment exists matching the needle domain }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -110,7 +110,7 @@ test("It calls the event handler if the repository contains a comment from a bot }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 diff --git a/__test__/hooks/PostCommentPullRequestEventHandler.test.ts b/__test__/hooks/PostCommentPullRequestEventHandler.test.ts index 82fb91f4..a02841a0 100644 --- a/__test__/hooks/PostCommentPullRequestEventHandler.test.ts +++ b/__test__/hooks/PostCommentPullRequestEventHandler.test.ts @@ -12,7 +12,7 @@ test("It adds a comment to the repository", async () => { }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -32,7 +32,7 @@ test("It adds a comment containing a link to the documentation", async () => { }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -52,7 +52,7 @@ test("It removes the \"openapi\" suffix of the repository name", async () => { }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo-openapi", ref: "bar", pullRequestNumber: 1337 diff --git a/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts b/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts index bfb42db3..9e2b94a1 100644 --- a/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts +++ b/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts @@ -9,7 +9,7 @@ test("It does not call event handler when repository name does not have \"-opena }, [], []) await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -26,7 +26,7 @@ test("It does not call event handler when repository name contains \"-openapi\" }, [], []) await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo-openapi-bar", ref: "bar", pullRequestNumber: 1337 @@ -43,7 +43,7 @@ test("It calls event handler when no repositories have been allowed or disallowe }, [], []) await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo-openapi", ref: "bar", pullRequestNumber: 1337 @@ -60,7 +60,7 @@ test("It does not call event handler for repository that is not on the allowlist }, ["example-openapi"], []) await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "foo", ref: "bar", pullRequestNumber: 1337 @@ -77,7 +77,7 @@ test("It does not call event handler for repository that is on the disallowlist" }, [], ["example-openapi"]) await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "example-openapi", ref: "bar", pullRequestNumber: 1337 @@ -94,7 +94,7 @@ test("It lets the disallowlist takes precedence over the allowlist", async () => }, ["example-openapi"], ["example-openapi"]) await sut.pullRequestOpened({ appInstallationId: 42, - repositoryOwner: "shapehq", + repositoryOwner: "acme", repositoryName: "example-openapi", ref: "bar", pullRequestNumber: 1337 From 0dd671a35729fd4254772229ce8dbd9c49541c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 10:30:10 +0200 Subject: [PATCH 108/191] Removes Shape Docs from unit tests --- __test__/admin/EmailGuestInviter.test.ts | 2 +- __test__/projects/updateWindowTitle.test.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts index 23ae091c..0fa2a472 100644 --- a/__test__/admin/EmailGuestInviter.test.ts +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -49,7 +49,7 @@ describe("EmailGuestInviter", () => { expect(nodemailer.createTransport().sendMail).toHaveBeenCalledWith({ to: "guest@email.dk", from: "some@email.dk", - subject: "You have been invited Shape Docs", + subject: expect.any(String), // difficult to test the exact content text: expect.any(String), // difficult to test the exact content html: expect.any(String) // difficult to test the exact content }) diff --git a/__test__/projects/updateWindowTitle.test.ts b/__test__/projects/updateWindowTitle.test.ts index 1f7692b2..dcc778c5 100644 --- a/__test__/projects/updateWindowTitle.test.ts +++ b/__test__/projects/updateWindowTitle.test.ts @@ -4,16 +4,16 @@ test("It uses default title when there is no selection", async () => { const store: { title: string } = { title: "" } updateWindowTitle({ storage: store, - defaultTitle: "Shape Docs" + defaultTitle: "Demo Docs" }) - expect(store.title).toEqual("Shape Docs") + expect(store.title).toEqual("Demo Docs") }) test("It leaves out specification when the specification has a generic name", async () => { const store: { title: string } = { title: "" } updateWindowTitle({ storage: store, - defaultTitle: "Shape Docs", + defaultTitle: "Demo Docs", project: { id: "foo", name: "foo", @@ -47,7 +47,7 @@ test("It leaves out version when it is the defualt version", async () => { const store: { title: string } = { title: "" } updateWindowTitle({ storage: store, - defaultTitle: "Shape Docs", + defaultTitle: "Demo Docs", project: { id: "foo", name: "foo", @@ -77,7 +77,7 @@ test("It adds version when it is not the defualt version", async () => { const store: { title: string } = { title: "" } updateWindowTitle({ storage: store, - defaultTitle: "Shape Docs", + defaultTitle: "Demo Docs", project: { id: "foo", name: "foo", From 4b5c630851f2931b594c3572c297205404034819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 11:02:27 +0200 Subject: [PATCH 109/191] Makes website title configurable in EmailGuestInviter --- __test__/admin/EmailGuestInviter.test.ts | 2 ++ src/composition.ts | 2 ++ src/features/admin/data/EmailGuestInviter.ts | 24 ++++++++++++-------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts index 0fa2a472..b892edd3 100644 --- a/__test__/admin/EmailGuestInviter.test.ts +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -13,6 +13,7 @@ describe("EmailGuestInviter", () => { describe("constructor", () => { it("should create a transporter", () => { new EmailGuestInviter({ + websiteTitle: "Demo Docs", url: "https://docs.shapetools.io", server: { host: "smtp.example.com", @@ -35,6 +36,7 @@ describe("EmailGuestInviter", () => { describe("inviteGuestByEmail", () => { it("should send an invite", async () => { const sut = new EmailGuestInviter({ + websiteTitle: "Demo Docs", url: "https://docs.shapetools.io", server: { host: "smtp.example.com", diff --git a/src/composition.ts b/src/composition.ts index 1171f6a2..012a4cb4 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -57,6 +57,7 @@ import { import DummyGuestRepository from "./features/admin/data/DummyGuestRepository" const { + NEXT_PUBLIC_SHAPE_DOCS_TITLE, SHAPE_DOCS_BASE_URL, GITHUB_APP_ID, GITHUB_CLIENT_ID, @@ -254,6 +255,7 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( ) export const guestInviter: IGuestInviter = new EmailGuestInviter({ + websiteTitle: NEXT_PUBLIC_SHAPE_DOCS_TITLE, url: SHAPE_DOCS_BASE_URL, server: { host: SMTP_HOST, diff --git a/src/features/admin/data/EmailGuestInviter.ts b/src/features/admin/data/EmailGuestInviter.ts index 8ac6aeb2..ac70c231 100644 --- a/src/features/admin/data/EmailGuestInviter.ts +++ b/src/features/admin/data/EmailGuestInviter.ts @@ -2,11 +2,13 @@ import { Transporter, createTransport } from "nodemailer"; import { IGuestInviter } from "../domain"; export default class EmailGuestInviter implements IGuestInviter { + private readonly websiteTitle: string private readonly url: string private readonly transport: Transporter private readonly from: string constructor(config: { + websiteTitle: string, url: string, server: { host: string, @@ -15,6 +17,7 @@ export default class EmailGuestInviter implements IGuestInviter { }, from: string }) { + this.websiteTitle = config.websiteTitle this.url = config.url this.transport = createTransport({ host: config.server.host, @@ -27,22 +30,23 @@ export default class EmailGuestInviter implements IGuestInviter { } async inviteGuestByEmail(email: string): Promise { + const websiteTitle = this.websiteTitle const url = this.url await this.transport.sendMail({ to: email, from: this.from, - subject: "You have been invited Shape Docs", - text: text({ url }), - html: html({ url }) + subject: `You have been invited ${websiteTitle}`, + text: text({ websiteTitle, url }), + html: html({ websiteTitle, url }) }) } } -function text({ url }: { url: string }) { - return `You have been invited to Shape Docs\n\nSign in with your e-mail on ${url}\n\n` +function text({ websiteTitle, url }: { websiteTitle: string, url: string }) { + return `You have been invited to ${websiteTitle}\n\nSign in with your e-mail on ${url}\n\n` } -function html({ url }: { url: string }) { +function html({ websiteTitle, url }: { websiteTitle: String, url: string }) { const imageHost = "http://docs.shapetools.io" const displayURL = url.replace(/https?:\/\//gi, "") const color = { @@ -58,19 +62,19 @@ function html({ url }: { url: string }) { style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;"> - Shape Docs logo + ${websiteTitle} logo - You have been invited to Shape Docs + You have been invited to ${websiteTitle} - Shape Docs uses magic links for signing in,
so you don't need to remember a password.
+ ${websiteTitle} uses magic links for signing in,
so you don't need to remember a password.
@@ -85,7 +89,7 @@ function html({ url }: { url: string }) { Go to Shape Docs + style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; padding: 20px 30px; display: inline-block; font-weight: semibold;">Go to ${websiteTitle} From 75f8524f37b8bee667d6c5f1850915ba6c3326bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 11:07:58 +0200 Subject: [PATCH 110/191] Makes website title configurable in MagicLinkEmailSender --- src/composition.ts | 1 + .../admin/data/MagicLinkEmailSender.ts | 34 +++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index 012a4cb4..8e768b3c 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -147,6 +147,7 @@ export const auth = NextAuth({ user: SMTP_USER, pass: SMTP_PASS, }, + websiteTitle: NEXT_PUBLIC_SHAPE_DOCS_TITLE, from: FROM_EMAIL }) await sender.sendMagicLink(params) diff --git a/src/features/admin/data/MagicLinkEmailSender.ts b/src/features/admin/data/MagicLinkEmailSender.ts index 511b3def..e7756237 100644 --- a/src/features/admin/data/MagicLinkEmailSender.ts +++ b/src/features/admin/data/MagicLinkEmailSender.ts @@ -2,6 +2,7 @@ import { Transporter, createTransport } from "nodemailer" export default class MagicLinkEmailSender { private readonly transport: Transporter + private readonly websiteTitle: string private readonly from: string constructor(config: { @@ -10,6 +11,7 @@ export default class MagicLinkEmailSender { user: string, pass: string }, + websiteTitle: string, from: string }) { this.transport = createTransport({ @@ -19,21 +21,26 @@ export default class MagicLinkEmailSender { pass: config.server.pass } }) + this.websiteTitle = config.websiteTitle this.from = config.from } - async sendMagicLink(params: { + async sendMagicLink({ + identifier, + expires, + url + }: { identifier: string expires: Date url: string }): Promise { - const { identifier, expires, url } = params + const websiteTitle = this.websiteTitle const result = await this.transport.sendMail({ to: identifier, from: this.from, - subject: "Sign in to Shape Docs", - text: text({ url }), - html: html({ url, expires }), + subject: `Sign in to ${websiteTitle}`, + text: text({ websiteTitle, url }), + html: html({ websiteTitle, url, expires }), }) const failed = result.rejected.concat(result.pending).filter(Boolean) if (failed.length) { @@ -42,11 +49,18 @@ export default class MagicLinkEmailSender { } } -function text({ url }: { url: string }) { - return `Sign in to Shape Docs.\n${url}\n\n` +function text({ websiteTitle, url }: { websiteTitle: string, url: string }) { + return `Sign in to ${websiteTitle}.\n${url}\n\n` } -function html({ url, expires }: { url: string, expires: Date }) { +function html({ + websiteTitle, + url,expires +}: { + websiteTitle: string, + url: string, + expires: Date +}) { const imageHost = "http://docs.shapetools.io" const color = { background: "#f9f9f9", @@ -61,13 +75,13 @@ function html({ url, expires }: { url: string, expires: Date }) { style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;"> - Shape Docs logo + ${websiteTitle} logo - Sign in to Shape Docs + Sign in to ${websiteTitle} From b1485de01bf46b701884b0087a2611bb91593903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 11:08:34 +0200 Subject: [PATCH 111/191] Removes unneeded parantheses --- src/composition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/composition.ts b/src/composition.ts index 8e768b3c..d9639637 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -140,7 +140,7 @@ export const auth = NextAuth({ }, from: FROM_EMAIL, name: "email", - sendVerificationRequest: async (params) => { + sendVerificationRequest: async params => { const sender = new MagicLinkEmailSender({ server: { host: SMTP_HOST, From 5120ba81a94786f2faf87eebc2b865b1a266065e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 11:25:56 +0200 Subject: [PATCH 112/191] Makes website title configurable in GitHubCommentFactory --- ...entCheckingPullRequestEventHandler.test.ts | 116 ++++++++++-------- __test__/hooks/GitHubCommentFactory.test.ts | 25 +++- ...PostCommentPullRequestEventHandler.test.ts | 57 ++------- src/app/api/hooks/github/route.ts | 23 ++-- ...gCommentCheckingPullRequestEventHandler.ts | 10 +- .../hooks/domain/GitHubCommentFactory.ts | 42 +++++-- .../hooks/domain/IGitHubCommentFactory.ts | 9 ++ .../PostCommentPullRequestEventHandler.ts | 21 ++-- src/features/hooks/domain/index.ts | 1 + 9 files changed, 171 insertions(+), 133 deletions(-) create mode 100644 src/features/hooks/domain/IGitHubCommentFactory.ts diff --git a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts index bd5f38bd..0980a13e 100644 --- a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts +++ b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts @@ -3,14 +3,18 @@ import { ExistingCommentCheckingPullRequestEventHandler } from "../../src/featur test("It fetches comments from the repository", async () => { let didFetchComments = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ - async pullRequestOpened() {} - }, { - async getComments() { - didFetchComments = true - return [] + eventHandler: { + async pullRequestOpened() {} }, - async addComment() {} - }, "https://docs.shapetools.io") + commentRepository: { + async getComments() { + didFetchComments = true + return [] + }, + async addComment() {} + }, + needleDomain: "https://docs.shapetools.io" + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -24,15 +28,19 @@ test("It fetches comments from the repository", async () => { test("It does calls decorated event handler if a comment does not exist in the repository", async () => { let didCallEventHandler = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, { - async getComments() { - return [] + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + commentRepository: { + async getComments() { + return [] + }, + async addComment() {} }, - async addComment() {} - }, "https://docs.shapetools.io") + needleDomain: "https://docs.shapetools.io" + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -46,18 +54,22 @@ test("It does calls decorated event handler if a comment does not exist in the r test("It does not call the event handler if a comment already exists in the repository", async () => { let didCallEventHandler = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, { - async getComments() { - return [{ - body: "The documentation is available on https://docs.shapetools.io", - isFromBot: true - }] + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + commentRepository: { + async getComments() { + return [{ + body: "The documentation is available on https://docs.shapetools.io", + isFromBot: true + }] + }, + async addComment() {} }, - async addComment() {} - }, "https://docs.shapetools.io") + needleDomain: "https://docs.shapetools.io" + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -71,18 +83,22 @@ test("It does not call the event handler if a comment already exists in the repo test("It calls the event handler if a comment exists matching the needle domain but that comment is not from a bot", async () => { let didCallEventHandler = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, { - async getComments() { - return [{ - body: "The documentation is available on https://docs.shapetools.io", - isFromBot: false - }] + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + commentRepository: { + async getComments() { + return [{ + body: "The documentation is available on https://docs.shapetools.io", + isFromBot: false + }] + }, + async addComment() {} }, - async addComment() {} - }, "https://docs.shapetools.io") + needleDomain: "https://docs.shapetools.io" + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -96,18 +112,22 @@ test("It calls the event handler if a comment exists matching the needle domain test("It calls the event handler if the repository contains a comment from a bot but that comment does not contain the needle domain", async () => { let didCallEventHandler = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, { - async getComments() { - return [{ - body: "Hello world!", - isFromBot: true - }] + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } }, - async addComment() {} - }, "https://docs.shapetools.io") + commentRepository: { + async getComments() { + return [{ + body: "Hello world!", + isFromBot: true + }] + }, + async addComment() {} + }, + needleDomain: "https://docs.shapetools.io" + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", diff --git a/__test__/hooks/GitHubCommentFactory.test.ts b/__test__/hooks/GitHubCommentFactory.test.ts index 4857c717..2bd61090 100644 --- a/__test__/hooks/GitHubCommentFactory.test.ts +++ b/__test__/hooks/GitHubCommentFactory.test.ts @@ -1,8 +1,25 @@ import { GitHubCommentFactory } from "../../src/features/hooks/domain" test("It includes a link to the documentation", async () => { - const text = GitHubCommentFactory.makeDocumentationPreviewReadyComment( - "https://docs.shapetools.io/foo/bar" - ) - expect(text).toContain("https://docs.shapetools.io/foo/bar") + const sut = new GitHubCommentFactory({ + websiteTitle: "Demo Docs", + domain: "https://example.com" + }) + const text = sut.makeDocumentationPreviewReadyComment({ + repositoryName: "foo", + ref: "bar" + }) + expect(text).toContain("https://example.com/foo/bar") +}) + +test("It removes the \"openapi\" suffix of the repository name", async () => { + const sut = new GitHubCommentFactory({ + websiteTitle: "Demo Docs", + domain: "https://example.com" + }) + const text = sut.makeDocumentationPreviewReadyComment({ + repositoryName: "foo-openapi", + ref: "bar" + }) + expect(text).toContain("https://example.com/foo/bar") }) \ No newline at end of file diff --git a/__test__/hooks/PostCommentPullRequestEventHandler.test.ts b/__test__/hooks/PostCommentPullRequestEventHandler.test.ts index a02841a0..60f88adc 100644 --- a/__test__/hooks/PostCommentPullRequestEventHandler.test.ts +++ b/__test__/hooks/PostCommentPullRequestEventHandler.test.ts @@ -3,33 +3,20 @@ import { PostCommentPullRequestEventHandler } from "../../src/features/hooks/dom test("It adds a comment to the repository", async () => { let didAddComment = false const sut = new PostCommentPullRequestEventHandler({ - async getComments() { - return [] + commentRepository: { + async getComments() { + return [] + }, + async addComment() { + didAddComment = true + } }, - async addComment() { - didAddComment = true + commentFactory: { + makeDocumentationPreviewReadyComment() { + return "This is the comment" + } } - }, "https://docs.shapetools.io") - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 }) - expect(didAddComment).toBeTruthy() -}) - -test("It adds a comment containing a link to the documentation", async () => { - let commentBody: string | undefined - const sut = new PostCommentPullRequestEventHandler({ - async getComments() { - return [] - }, - async addComment(operation) { - commentBody = operation.body - } - }, "https://docs.shapetools.io") await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -37,25 +24,5 @@ test("It adds a comment containing a link to the documentation", async () => { ref: "bar", pullRequestNumber: 1337 }) - expect(commentBody).toContain("https://docs.shapetools.io/foo/bar") -}) - -test("It removes the \"openapi\" suffix of the repository name", async () => { - let commentBody: string | undefined - const sut = new PostCommentPullRequestEventHandler({ - async getComments() { - return [] - }, - async addComment(operation) { - commentBody = operation.body - } - }, "https://docs.shapetools.io") - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo-openapi", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(commentBody).toContain("https://docs.shapetools.io/foo/bar") + expect(didAddComment).toBeTruthy() }) diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index d1fd0b6e..4ce81638 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -6,11 +6,13 @@ import { import { PostCommentPullRequestEventHandler, RepositoryNameCheckingPullRequestEventHandler, - ExistingCommentCheckingPullRequestEventHandler + ExistingCommentCheckingPullRequestEventHandler, + GitHubCommentFactory } from "@/features/hooks/domain" import { gitHubClient } from "@/composition" const { + NEXT_PUBLIC_SHAPE_DOCS_TITLE, SHAPE_DOCS_BASE_URL, GITHUB_WEBHOOK_SECRET, GITHUB_WEBHOK_REPOSITORY_ALLOWLIST, @@ -30,14 +32,17 @@ const disallowedRepositoryNames = listFromCommaSeparatedString(GITHUB_WEBHOK_REP const hookHandler = new GitHubHookHandler({ secret: GITHUB_WEBHOOK_SECRET, pullRequestEventHandler: new RepositoryNameCheckingPullRequestEventHandler( - new ExistingCommentCheckingPullRequestEventHandler( - new PostCommentPullRequestEventHandler( - new GitHubPullRequestCommentRepository(gitHubClient), - SHAPE_DOCS_BASE_URL - ), - new GitHubPullRequestCommentRepository(gitHubClient), - SHAPE_DOCS_BASE_URL - ), + new ExistingCommentCheckingPullRequestEventHandler({ + eventHandler: new PostCommentPullRequestEventHandler({ + commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), + commentFactory: new GitHubCommentFactory({ + websiteTitle: NEXT_PUBLIC_SHAPE_DOCS_TITLE, + domain: SHAPE_DOCS_BASE_URL + }) + }), + commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), + needleDomain: SHAPE_DOCS_BASE_URL + }), allowedRepositoryNames, disallowedRepositoryNames ) diff --git a/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts b/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts index 98447c4c..d787541f 100644 --- a/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts +++ b/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts @@ -6,14 +6,14 @@ export default class ExistingCommentCheckingPullRequestEventHandler implements I private readonly commentRepository: IPullRequestCommentRepository private readonly needleDomain: string - constructor( + constructor(config: { eventHandler: IPullRequestEventHandler, commentRepository: IPullRequestCommentRepository, needleDomain: string - ) { - this.eventHandler = eventHandler - this.commentRepository = commentRepository - this.needleDomain = needleDomain + }) { + this.eventHandler = config.eventHandler + this.commentRepository = config.commentRepository + this.needleDomain = config.needleDomain } async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { diff --git a/src/features/hooks/domain/GitHubCommentFactory.ts b/src/features/hooks/domain/GitHubCommentFactory.ts index 27f27f3d..10f96a59 100644 --- a/src/features/hooks/domain/GitHubCommentFactory.ts +++ b/src/features/hooks/domain/GitHubCommentFactory.ts @@ -1,16 +1,34 @@ -export default class GitHubCommentFactory { - static makeDocumentationPreviewReadyComment(link: string): string { - return `### 📖 Documentation Preview +import IGitHubCommentFactory from "./IGitHubCommentFactory" + +export default class GitHubCommentFactory implements IGitHubCommentFactory { + private readonly websiteTitle: string + private readonly domain: string -These edits are available for preview at [Shape Docs](${link}). + constructor(config: { websiteTitle: string, domain: string }) { + this.websiteTitle = config.websiteTitle + this.domain = config.domain + } - - - - - - - -
Status:✅ Ready!
Preview URL:${link}
` + makeDocumentationPreviewReadyComment({ + repositoryName, + ref + }: { + repositoryName: string + ref: string + }): string { + const projectId = repositoryName.replace(/-openapi$/, "") + const link = `${this.domain}/${projectId}/${ref}` + return `### 📖 Documentation Preview + + These edits are available for preview at [${this.websiteTitle}](${link}). + + + + + + + + +
Status:✅ Ready!
Preview URL:${link}
` } } diff --git a/src/features/hooks/domain/IGitHubCommentFactory.ts b/src/features/hooks/domain/IGitHubCommentFactory.ts new file mode 100644 index 00000000..86e40f04 --- /dev/null +++ b/src/features/hooks/domain/IGitHubCommentFactory.ts @@ -0,0 +1,9 @@ +export default interface IGitHubCommentFactory { + makeDocumentationPreviewReadyComment({ + repositoryName, + ref + }: { + repositoryName: string, + ref: string + }): string +} diff --git a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts index f7ea34d3..43eba53a 100644 --- a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts +++ b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts @@ -1,23 +1,24 @@ import IPullRequestEventHandler, { IPullRequestOpenedEvent } from "./IPullRequestEventHandler" import IPullRequestCommentRepository from "./IPullRequestCommentRepository" -import GitHubCommentFactory from "./GitHubCommentFactory" +import IGitHubCommentFactory from "./IGitHubCommentFactory" export default class PostCommentPullRequestEventHandler implements IPullRequestEventHandler { private readonly commentRepository: IPullRequestCommentRepository - private readonly domain: string + private readonly commentFactory: IGitHubCommentFactory - constructor( + constructor(config: { commentRepository: IPullRequestCommentRepository, - domain: string - ) { - this.commentRepository = commentRepository - this.domain = domain + commentFactory: IGitHubCommentFactory + }) { + this.commentRepository = config.commentRepository + this.commentFactory = config.commentFactory } async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { - const projectId = event.repositoryName.replace(/-openapi$/, "") - const link = `${this.domain}/${projectId}/${event.ref}` - const commentBody = GitHubCommentFactory.makeDocumentationPreviewReadyComment(link) + const commentBody = this.commentFactory.makeDocumentationPreviewReadyComment({ + repositoryName: event.repositoryName, + ref: event.ref + }) await this.commentRepository.addComment({ appInstallationId: event.appInstallationId, repositoryOwner: event.repositoryOwner, diff --git a/src/features/hooks/domain/index.ts b/src/features/hooks/domain/index.ts index 5bc8aa4a..673bd7c6 100644 --- a/src/features/hooks/domain/index.ts +++ b/src/features/hooks/domain/index.ts @@ -1,5 +1,6 @@ export { default as ExistingCommentCheckingPullRequestEventHandler } from "./ExistingCommentCheckingPullRequestEventHandler" export { default as GitHubCommentFactory } from "./GitHubCommentFactory" +export type { default as IGitHubCommentFactory } from "./IGitHubCommentFactory" export type { default as IPullRequestCommentRepository } from "./IPullRequestCommentRepository" export type { default as IPullRequestEventHandler } from "./IPullRequestEventHandler" export { default as PostCommentPullRequestEventHandler } from "./PostCommentPullRequestEventHandler" From 3ad7ccf7fa837c0ccbe599f8c53712ed207a20b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 11:27:03 +0200 Subject: [PATCH 113/191] Removes Framna reference --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 7e5e5569..86b20a20 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ SHAPE_DOCS_BASE_URL=http://localhost:3000 -FROM_EMAIL=Framna Docs +FROM_EMAIL=Shape Docs NEXT_PUBLIC_SHAPE_DOCS_TITLE=Shape Docs NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION=Documentation for Shape's APIs NEXTAUTH_URL_INTERNAL=http://localhost:3000 From 81f5699ecf29e46ef11eae6086a274028eb60c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 11:28:37 +0200 Subject: [PATCH 114/191] Removes references to shapetools.io from unit tests --- __test__/admin/EmailGuestInviter.test.ts | 4 ++-- ...gCommentCheckingPullRequestEventHandler.test.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts index b892edd3..4600eec5 100644 --- a/__test__/admin/EmailGuestInviter.test.ts +++ b/__test__/admin/EmailGuestInviter.test.ts @@ -14,7 +14,7 @@ describe("EmailGuestInviter", () => { it("should create a transporter", () => { new EmailGuestInviter({ websiteTitle: "Demo Docs", - url: "https://docs.shapetools.io", + url: "https://example.com", server: { host: "smtp.example.com", user: "user", @@ -37,7 +37,7 @@ describe("EmailGuestInviter", () => { it("should send an invite", async () => { const sut = new EmailGuestInviter({ websiteTitle: "Demo Docs", - url: "https://docs.shapetools.io", + url: "https://example.com", server: { host: "smtp.example.com", user: "user", diff --git a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts index 0980a13e..c13f8048 100644 --- a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts +++ b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts @@ -13,7 +13,7 @@ test("It fetches comments from the repository", async () => { }, async addComment() {} }, - needleDomain: "https://docs.shapetools.io" + needleDomain: "https://example.com" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -39,7 +39,7 @@ test("It does calls decorated event handler if a comment does not exist in the r }, async addComment() {} }, - needleDomain: "https://docs.shapetools.io" + needleDomain: "https://example.com" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -62,13 +62,13 @@ test("It does not call the event handler if a comment already exists in the repo commentRepository: { async getComments() { return [{ - body: "The documentation is available on https://docs.shapetools.io", + body: "The documentation is available on https://example.com", isFromBot: true }] }, async addComment() {} }, - needleDomain: "https://docs.shapetools.io" + needleDomain: "https://example.com" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -91,13 +91,13 @@ test("It calls the event handler if a comment exists matching the needle domain commentRepository: { async getComments() { return [{ - body: "The documentation is available on https://docs.shapetools.io", + body: "The documentation is available on https://example.com", isFromBot: false }] }, async addComment() {} }, - needleDomain: "https://docs.shapetools.io" + needleDomain: "https://example.com" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -126,7 +126,7 @@ test("It calls the event handler if the repository contains a comment from a bot }, async addComment() {} }, - needleDomain: "https://docs.shapetools.io" + needleDomain: "https://example.com" }) await sut.pullRequestOpened({ appInstallationId: 42, From 292205206feedfed15e7895e8de4acb6e3dde2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 11:36:21 +0200 Subject: [PATCH 115/191] Removes unneeded asset --- public/logo.png | Bin 513355 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 public/logo.png diff --git a/public/logo.png b/public/logo.png deleted file mode 100644 index 9895edf604905bb97350fbba66f3cf0861e7032f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 513355 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

oUaWNT?q$Nj*hy$hpGY}YWm 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

CV+qB6jExORc7d-nXh*QiHn0mfKaDu{RVmtW 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 Date: Tue, 9 Jul 2024 11:59:41 +0200 Subject: [PATCH 116/191] Removes unneeded semicolons --- src/features/admin/data/EmailGuestInviter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/admin/data/EmailGuestInviter.ts b/src/features/admin/data/EmailGuestInviter.ts index ac70c231..7f73e275 100644 --- a/src/features/admin/data/EmailGuestInviter.ts +++ b/src/features/admin/data/EmailGuestInviter.ts @@ -1,5 +1,5 @@ -import { Transporter, createTransport } from "nodemailer"; -import { IGuestInviter } from "../domain"; +import { Transporter, createTransport } from "nodemailer" +import { IGuestInviter } from "../domain" export default class EmailGuestInviter implements IGuestInviter { private readonly websiteTitle: string From 9a60122cd13750466ea8ad2c1491852770efe859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:06:26 +0200 Subject: [PATCH 117/191] Uses base64 encoded logo in mails --- src/common/images/base64EncodedLogo.ts | 7 + src/features/admin/data/EmailGuestInviter.ts | 130 +++++++++--------- .../admin/data/MagicLinkEmailSender.ts | 127 +++++++++-------- 3 files changed, 134 insertions(+), 130 deletions(-) create mode 100644 src/common/images/base64EncodedLogo.ts diff --git a/src/common/images/base64EncodedLogo.ts b/src/common/images/base64EncodedLogo.ts new file mode 100644 index 00000000..888a5ef1 --- /dev/null +++ b/src/common/images/base64EncodedLogo.ts @@ -0,0 +1,7 @@ +import fs from "fs" + +let str = "unavailable" +if (process.env.NODE_ENV !== "test") { + str = fs.readFileSync("public/images/logo.png", "base64") +} +export default str diff --git a/src/features/admin/data/EmailGuestInviter.ts b/src/features/admin/data/EmailGuestInviter.ts index 7f73e275..d7ab579f 100644 --- a/src/features/admin/data/EmailGuestInviter.ts +++ b/src/features/admin/data/EmailGuestInviter.ts @@ -1,5 +1,6 @@ import { Transporter, createTransport } from "nodemailer" import { IGuestInviter } from "../domain" +import base64EncodedLogo from "@/common/images/base64EncodedLogo" export default class EmailGuestInviter implements IGuestInviter { private readonly websiteTitle: string @@ -30,77 +31,74 @@ export default class EmailGuestInviter implements IGuestInviter { } async inviteGuestByEmail(email: string): Promise { - const websiteTitle = this.websiteTitle - const url = this.url await this.transport.sendMail({ to: email, from: this.from, - subject: `You have been invited ${websiteTitle}`, - text: text({ websiteTitle, url }), - html: html({ websiteTitle, url }) + subject: `You have been invited ${this.websiteTitle}`, + text: this.makeText(), + html: this.makeHtml() }) } -} - -function text({ websiteTitle, url }: { websiteTitle: string, url: string }) { - return `You have been invited to ${websiteTitle}\n\nSign in with your e-mail on ${url}\n\n` -} + + private makeText() { + return `You have been invited to ${this.websiteTitle}\n\nSign in with your e-mail on ${this.url}\n\n` + } -function html({ websiteTitle, url }: { websiteTitle: String, url: string }) { - const imageHost = "http://docs.shapetools.io" - const displayURL = url.replace(/https?:\/\//gi, "") - const color = { - background: "#f9f9f9", - text: "#000", - mainBackground: "#fff", - buttonBackground: "#0D6DDB", - buttonText: "#fff" + private makeHtml() { + const displayURL = this.url.replace(/https?:\/\//gi, "") + const color = { + background: "#f9f9f9", + text: "#000", + mainBackground: "#fff", + buttonBackground: "#0D6DDB", + buttonText: "#fff" + } + return ` + + + + + + + + + + + + + + + + + + + + +
+ ${this.websiteTitle} logo +
+ You have been invited to ${this.websiteTitle} +
+ ${this.websiteTitle} uses magic links for signing in,
so you don't need to remember a password.
+
+ Visit ${displayURL} and enter your email to sign in. +
+ + + + +
Go to ${this.websiteTitle}
+
+ If you did not request this email, you can safely ignore it. +
+ + ` } - return ` - - - - - - - - - - - - - - - - - - - - -
- ${websiteTitle} logo -
- You have been invited to ${websiteTitle} -
- ${websiteTitle} uses magic links for signing in,
so you don't need to remember a password.
-
- Visit ${displayURL} and enter your email to sign in. -
- - - - -
Go to ${websiteTitle}
-
- If you did not request this email, you can safely ignore it. -
- -` } diff --git a/src/features/admin/data/MagicLinkEmailSender.ts b/src/features/admin/data/MagicLinkEmailSender.ts index e7756237..9e8d576c 100644 --- a/src/features/admin/data/MagicLinkEmailSender.ts +++ b/src/features/admin/data/MagicLinkEmailSender.ts @@ -1,4 +1,5 @@ import { Transporter, createTransport } from "nodemailer" +import base64EncodedLogo from "@/common/images/base64EncodedLogo" export default class MagicLinkEmailSender { private readonly transport: Transporter @@ -39,78 +40,76 @@ export default class MagicLinkEmailSender { to: identifier, from: this.from, subject: `Sign in to ${websiteTitle}`, - text: text({ websiteTitle, url }), - html: html({ websiteTitle, url, expires }), + text: this.makeText({ url }), + html: this.makeHtml({ url, expires }), }) const failed = result.rejected.concat(result.pending).filter(Boolean) if (failed.length) { throw new Error(`Email (${failed.join(", ")}) could not be sent`) } } -} - -function text({ websiteTitle, url }: { websiteTitle: string, url: string }) { - return `Sign in to ${websiteTitle}.\n${url}\n\n` -} -function html({ - websiteTitle, - url,expires -}: { - websiteTitle: string, - url: string, - expires: Date -}) { - const imageHost = "http://docs.shapetools.io" - const color = { - background: "#f9f9f9", - text: "#000", - mainBackground: "#fff", - buttonBackground: "#0D6DDB", - buttonText: "#fff" + private makeText({ url }: { url: string }) { + return `Sign in to ${this.websiteTitle}.\n${url}\n\n` } - return ` - - - - - - - - - - - - - - - - - -
- ${websiteTitle} logo -
- Sign in to ${websiteTitle} -
- The link can only be used once
and expires on ${formatDate(expires)}. -
- - - - -
Sign - in
-
- If you did not request this email, you can safely ignore it. -
- -` + + private makeHtml({ + url, + expires + }: { + url: string, + expires: Date + }) { + const color = { + background: "#f9f9f9", + text: "#000", + mainBackground: "#fff", + buttonBackground: "#0D6DDB", + buttonText: "#fff" + } + return ` + + + + + + + + + + + + + + + + + +
+ ${this.websiteTitle} logo +
+ Sign in to ${this.websiteTitle} +
+ The link can only be used once
and expires on ${formatDate(expires)}. +
+ + + + +
Sign + in
+
+ If you did not request this email, you can safely ignore it. +
+ + ` +} } function formatDate(date: Date) { From 1bbb9e2130e4e212655d6c1eaf65d6056539fcf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:06:55 +0200 Subject: [PATCH 118/191] Fixes typos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e41f91b5..c9c59afe 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Portal displaying our projects that are documented with OpenAPI. Hosted on [docs Copy `.env.example` to `.env.local` in the root of the project. Make sure to replace any placeholders and generate a random secret using OpenSSL. -The table below contains explations for environment variables. The list is not pre-emptive and `.env.example` contains the full list. +The table below contains explanations for environment variables. The list is not preemptive and `.env.example` contains the full list. |Environment Variable|Description| |-|-| From 9cce21ec33d6c5920c256237395c0a9f51f2ba37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:08:32 +0200 Subject: [PATCH 119/191] Fixes reference to unavailable image --- src/features/sidebar/view/SidebarHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/sidebar/view/SidebarHeader.tsx b/src/features/sidebar/view/SidebarHeader.tsx index 4fa6319f..42fd8783 100644 --- a/src/features/sidebar/view/SidebarHeader.tsx +++ b/src/features/sidebar/view/SidebarHeader.tsx @@ -15,7 +15,7 @@ export default function SidebarHeader() { }} > Duck Date: Tue, 9 Jul 2024 12:16:57 +0200 Subject: [PATCH 120/191] Fixes incorrect filename --- __test__/projects/GitHubProjectDataSource.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index 2be08923..2e4dbef2 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -489,7 +489,7 @@ test("It filters away tags with no specifications", async () => { expect(projects[0].versions.length).toEqual(2) }) -test("It reads image from .shape-config.yml", async () => { +test("It reads image from .shape-docs.yml", async () => { const sut = new GitHubProjectDataSource({ organizationName: "foo", graphQlClient: { @@ -608,7 +608,7 @@ test("It filters away tags with no specifications", async () => { expect(projects[0].versions.length).toEqual(2) }) -test("It reads display name from .shape-config.yml", async () => { +test("It reads display name from .shape-docs.yml", async () => { const sut = new GitHubProjectDataSource({ organizationName: "foo", graphQlClient: { @@ -659,7 +659,7 @@ test("It reads display name from .shape-config.yml", async () => { expect(projects[0].displayName).toEqual("Hello World") }) -test("It reads image from .shape-config.yml", async () => { +test("It reads image from .shape-docs.yml", async () => { const sut = new GitHubProjectDataSource({ organizationName: "foo", graphQlClient: { @@ -708,7 +708,7 @@ test("It reads image from .shape-config.yml", async () => { expect(projects[0].imageURL).toEqual("/api/blob/acme/foo-openapi/icon.png?ref=12345678") }) -test("It reads display name from .shape-config.yaml", async () => { +test("It reads display name from .shape-docs.yaml", async () => { const sut = new GitHubProjectDataSource({ organizationName: "foo", graphQlClient: { @@ -759,7 +759,7 @@ test("It reads display name from .shape-config.yaml", async () => { expect(projects[0].displayName).toEqual("Hello World") }) -test("It reads image from .shape-config.yaml", async () => { +test("It reads image from .shape-docs.yaml", async () => { const sut = new GitHubProjectDataSource({ organizationName: "foo", graphQlClient: { From 2b84f7ea39f625938d99632b54480e26619d90fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:27:36 +0200 Subject: [PATCH 121/191] Makes .shape-docs.yml filename configurable --- .env.example | 1 + .../projects/GitHubProjectDataSource.test.ts | 32 ++++++++++++++++--- src/composition.ts | 4 ++- .../projects/data/GitHubProjectDataSource.ts | 9 ++++-- src/features/projects/domain/getSelection.ts | 2 +- types/env.d.ts | 1 + 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index 86b20a20..0777262b 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ SHAPE_DOCS_BASE_URL=http://localhost:3000 +SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME=.shape-docs.yml FROM_EMAIL=Shape Docs NEXT_PUBLIC_SHAPE_DOCS_TITLE=Shape Docs NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION=Documentation for Shape's APIs diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index 2e4dbef2..9e6907e4 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -5,6 +5,7 @@ import { test("It loads repositories from data source", async () => { let didLoadRepositories = false const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -23,6 +24,7 @@ test("It loads repositories from data source", async () => { test("It maps projects including branches and tags", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -109,6 +111,7 @@ test("It maps projects including branches and tags", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -169,6 +172,7 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It supports multiple OpenAPI specifications on a branch", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -269,6 +273,7 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -329,6 +334,7 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It filters away projects with no versions", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -363,6 +369,7 @@ test("It filters away projects with no versions", async () => { test("It filters away branches with no specifications", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -421,6 +428,7 @@ test("It filters away branches with no specifications", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -489,8 +497,9 @@ test("It filters away tags with no specifications", async () => { expect(projects[0].versions.length).toEqual(2) }) -test("It reads image from .shape-docs.yml", async () => { +test("It reads image from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -540,6 +549,7 @@ test("It reads image from .shape-docs.yml", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -608,8 +618,9 @@ test("It filters away tags with no specifications", async () => { expect(projects[0].versions.length).toEqual(2) }) -test("It reads display name from .shape-docs.yml", async () => { +test("It reads display name from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -659,8 +670,9 @@ test("It reads display name from .shape-docs.yml", async () => { expect(projects[0].displayName).toEqual("Hello World") }) -test("It reads image from .shape-docs.yml", async () => { +test("It reads image from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -708,8 +720,9 @@ test("It reads image from .shape-docs.yml", async () => { expect(projects[0].imageURL).toEqual("/api/blob/acme/foo-openapi/icon.png?ref=12345678") }) -test("It reads display name from .shape-docs.yaml", async () => { +test("It reads display name from configuration file with .yaml extension", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -759,8 +772,9 @@ test("It reads display name from .shape-docs.yaml", async () => { expect(projects[0].displayName).toEqual("Hello World") }) -test("It reads image from .shape-docs.yaml", async () => { +test("It reads image from configuration file with .yaml extension", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -810,6 +824,7 @@ test("It reads image from .shape-docs.yaml", async () => { test("It sorts projects alphabetically", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -916,6 +931,7 @@ test("It sorts projects alphabetically", async () => { test("It sorts versions alphabetically", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -1001,6 +1017,7 @@ test("It sorts versions alphabetically", async () => { test("It prioritizes main, master, develop, and development branch names when sorting verisons", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -1112,6 +1129,7 @@ test("It prioritizes main, master, develop, and development branch names when so test("It identifies the default branch in returned versions", async () => { const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -1199,6 +1217,7 @@ test("It adds remote versions from the project configuration", async () => { url: https://example.com/louie.yml ` const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -1269,6 +1288,7 @@ test("It modifies ID of remote version if the ID already exists", async () => { url: https://example.com/hello.yml ` const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -1355,6 +1375,7 @@ test("It lets users specify the ID of a remote version", async () => { url: https://example.com/baz.yml ` const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { @@ -1409,6 +1430,7 @@ test("It lets users specify the ID of a remote specification", async () => { url: https://example.com/baz.yml ` const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", organizationName: "foo", graphQlClient: { async graphql() { diff --git a/src/composition.ts b/src/composition.ts index d9639637..a38203a0 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -59,6 +59,7 @@ import DummyGuestRepository from "./features/admin/data/DummyGuestRepository" const { NEXT_PUBLIC_SHAPE_DOCS_TITLE, SHAPE_DOCS_BASE_URL, + SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, GITHUB_APP_ID, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, @@ -244,7 +245,8 @@ export const projectRepository = new ProjectRepository( export const projectDataSource = new CachingProjectDataSource({ dataSource: new GitHubProjectDataSource({ graphQlClient: userGitHubClient, - organizationName: GITHUB_ORGANIZATION_NAME + organizationName: GITHUB_ORGANIZATION_NAME, + projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME }), repository: projectRepository }) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index 568855e7..1bf15440 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -28,15 +28,18 @@ interface IGitHubGraphQLClient { export default class GitHubProjectDataSource implements IProjectDataSource { private readonly graphQlClient: IGitHubGraphQLClient private readonly organizationName: string + private readonly projectConfigurationFilename: string constructor( config: { graphQlClient: IGitHubGraphQLClient, - organizationName: string + organizationName: string, + projectConfigurationFilename: string } ) { this.graphQlClient = config.graphQlClient this.organizationName = config.organizationName + this.projectConfigurationFilename = config.projectConfigurationFilename.replace(/\.ya?ml$/, "") } async getProjects(): Promise { @@ -247,10 +250,10 @@ export default class GitHubProjectDataSource implements IProjectDataSource { } } } - configYml: object(expression: "HEAD:.shape-docs.yml") { + configYml: object(expression: "HEAD:${this.projectConfigurationFilename}.yml") { ...ConfigParts } - configYaml: object(expression: "HEAD:.shape-docs.yaml") { + configYaml: object(expression: "HEAD:${this.projectConfigurationFilename}.yaml") { ...ConfigParts } branches: refs(refPrefix: "refs/heads/", first: 100) { diff --git a/src/features/projects/domain/getSelection.ts b/src/features/projects/domain/getSelection.ts index b76559ad..09dc3587 100644 --- a/src/features/projects/domain/getSelection.ts +++ b/src/features/projects/domain/getSelection.ts @@ -34,7 +34,7 @@ export default function getSelection({ if (versionId) { version = project.versions.find(e => e.id == versionId) if (!version && specificationId && !isSpecificationIdFilename(specificationId)) { - // With the introduction of remote versions that are specified in the .shape-docs.yml + // With the introduction of remote versions that are specified in the .yml // configuration file, it has become impossible to tell if the last component in a URL // is the specification ID or if it belongs to the version ID. Previously, we required // specification IDs to end with either ".yml" or ".yaml" but that no longer makes diff --git a/types/env.d.ts b/types/env.d.ts index 9b912c54..90b194c5 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -1,5 +1,6 @@ namespace NodeJS { interface ProcessEnv { + SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: string NEXT_PUBLIC_SHAPE_DOCS_TITLE: string SHAPE_DOCS_BASE_URL: string AUTH_SECRET: string From 451cb01cd380f49df8febcc4755d1d08415f5960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:30:01 +0200 Subject: [PATCH 122/191] Tests file extension --- .../projects/GitHubProjectDataSource.test.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index 9e6907e4..6d2b5e7f 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -1474,3 +1474,66 @@ test("It lets users specify the ID of a remote specification", async () => { }] }]) }) + +test("It queries for both .yml and .yaml file extension with specifying .yml extension", async () => { + let query: string | undefined + const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", + organizationName: "foo", + graphQlClient: { + async graphql(request) { + query = request.query + return { + search: { + results: [] + } + } + } + } + }) + await sut.getProjects() + expect(query).toContain(".demo-docs.yml") + expect(query).toContain(".demo-docs.yaml") +}) + +test("It queries for both .yml and .yaml file extension with specifying .yaml extension", async () => { + let query: string | undefined + const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs.yml", + organizationName: "foo", + graphQlClient: { + async graphql(request) { + query = request.query + return { + search: { + results: [] + } + } + } + } + }) + await sut.getProjects() + expect(query).toContain(".demo-docs.yml") + expect(query).toContain(".demo-docs.yaml") +}) + +test("It queries for both .yml and .yaml file extension with no extension", async () => { + let query: string | undefined + const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs", + organizationName: "foo", + graphQlClient: { + async graphql(request) { + query = request.query + return { + search: { + results: [] + } + } + } + } + }) + await sut.getProjects() + expect(query).toContain(".demo-docs.yml") + expect(query).toContain(".demo-docs.yaml") +}) From cf8ad6523080c4513f8b19511c097ffc94e801ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:32:33 +0200 Subject: [PATCH 123/191] Reorders env type --- types/env.d.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/types/env.d.ts b/types/env.d.ts index 90b194c5..1d1549ff 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -1,26 +1,26 @@ namespace NodeJS { interface ProcessEnv { + SHAPE_DOCS_BASE_URL: string SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: string + FROM_EMAIL: string | undefined NEXT_PUBLIC_SHAPE_DOCS_TITLE: string - SHAPE_DOCS_BASE_URL: string - AUTH_SECRET: string - NEXTAUTH_URL: string - GITHUB_CLIENT_ID: string - GITHUB_CLIENT_SECRET: string - GITHUB_APP_ID: string - GITHUB_PRIVATE_KEY_BASE_64: string - GITHUB_WEBHOOK_SECRET: string - GITHUB_WEBHOK_REPOSITORY_ALLOWLIST?: string - GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST?: string - GITHUB_ORGANIZATION_NAME: string + NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: string + NEXTAUTH_URL_INTERNAL: string + NEXTAUTH_SECRET: string REDIS_URL: string POSTGRESQL_HOST: string POSTGRESQL_USER: string - POSTGRESQL_PASSWORD: string POSTGRESQL_DB: string SMTP_HOST: string SMTP_USER: string SMTP_PASS: string - FROM_EMAIL: string | undefined + GITHUB_WEBHOOK_SECRET: string + GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: string + GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: string + GITHUB_ORGANIZATION_NAME: string + GITHUB_CLIENT_ID: string + GITHUB_CLIENT_SECRET: string + GITHUB_APP_ID: string + GITHUB_PRIVATE_KEY_BASE_64: string } } From 8476060e30e9e4656497c84bb09629fe896bca34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:33:25 +0200 Subject: [PATCH 124/191] Ensures environment variables are read-only --- types/env.d.ts | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/types/env.d.ts b/types/env.d.ts index 1d1549ff..f37949e4 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -1,26 +1,26 @@ namespace NodeJS { interface ProcessEnv { - SHAPE_DOCS_BASE_URL: string - SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: string - FROM_EMAIL: string | undefined - NEXT_PUBLIC_SHAPE_DOCS_TITLE: string - NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: string - NEXTAUTH_URL_INTERNAL: string - NEXTAUTH_SECRET: string - REDIS_URL: string - POSTGRESQL_HOST: string - POSTGRESQL_USER: string - POSTGRESQL_DB: string - SMTP_HOST: string - SMTP_USER: string - SMTP_PASS: string - GITHUB_WEBHOOK_SECRET: string - GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: string - GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: string - GITHUB_ORGANIZATION_NAME: string - GITHUB_CLIENT_ID: string - GITHUB_CLIENT_SECRET: string - GITHUB_APP_ID: string - GITHUB_PRIVATE_KEY_BASE_64: string + readonly SHAPE_DOCS_BASE_URL: string + readonly SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: string + readonly FROM_EMAIL: string | undefined + readonly NEXT_PUBLIC_SHAPE_DOCS_TITLE: string + readonly NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: string + readonly NEXTAUTH_URL_INTERNAL: string + readonly NEXTAUTH_SECRET: string + readonly REDIS_URL: string + readonly POSTGRESQL_HOST: string + readonly POSTGRESQL_USER: string + readonly POSTGRESQL_DB: string + readonly SMTP_HOST: string + readonly SMTP_USER: string + readonly SMTP_PASS: string + readonly GITHUB_WEBHOOK_SECRET: string + readonly GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: string + readonly GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: string + readonly GITHUB_ORGANIZATION_NAME: string + readonly GITHUB_CLIENT_ID: string + readonly GITHUB_CLIENT_SECRET: string + readonly GITHUB_APP_ID: string + readonly GITHUB_PRIVATE_KEY_BASE_64: string } } From 113d9d0700201bc8aef9bd79c72335de8c87c779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 12:35:12 +0200 Subject: [PATCH 125/191] Fixes alt text --- src/features/sidebar/view/SidebarHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/sidebar/view/SidebarHeader.tsx b/src/features/sidebar/view/SidebarHeader.tsx index 42fd8783..1703a18e 100644 --- a/src/features/sidebar/view/SidebarHeader.tsx +++ b/src/features/sidebar/view/SidebarHeader.tsx @@ -16,7 +16,7 @@ export default function SidebarHeader() { > Duck Date: Tue, 9 Jul 2024 12:35:55 +0200 Subject: [PATCH 126/191] Ensures session is read-only --- types/@next-auth.d.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/types/@next-auth.d.ts b/types/@next-auth.d.ts index 18355b40..e933af28 100644 --- a/types/@next-auth.d.ts +++ b/types/@next-auth.d.ts @@ -2,11 +2,11 @@ import NextAuth from "next-auth" declare module "next-auth" { interface Session { - user: { - id: string - email: string - name?: string - image?: string + readonly user: { + readonly id: string + readonly email: string + readonly name?: string + readonly image?: string } } } From 45868bde3c82e4dabf633e69e0a137481c817aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 13:53:45 +0200 Subject: [PATCH 127/191] Fetches projects for all user's organisations --- src/common/github/IGitHubClient.ts | 2 +- src/composition.ts | 1 - .../projects/data/GitHubProjectDataSource.ts | 37 +++++++++++++++---- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/common/github/IGitHubClient.ts b/src/common/github/IGitHubClient.ts index 1b349fc6..569b6bd6 100644 --- a/src/common/github/IGitHubClient.ts +++ b/src/common/github/IGitHubClient.ts @@ -1,7 +1,7 @@ export type GraphQLQueryRequest = { readonly query: string /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - readonly variables: {[key: string]: any} + readonly variables?: {[key: string]: any} } export type GraphQlQueryResponse = { diff --git a/src/composition.ts b/src/composition.ts index a38203a0..d050e795 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -245,7 +245,6 @@ export const projectRepository = new ProjectRepository( export const projectDataSource = new CachingProjectDataSource({ dataSource: new GitHubProjectDataSource({ graphQlClient: userGitHubClient, - organizationName: GITHUB_ORGANIZATION_NAME, projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME }), repository: projectRepository diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index 1bf15440..f88dede1 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -13,7 +13,7 @@ import { export type GitHubGraphQLClientRequest = { readonly query: string /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - readonly variables: {[key: string]: any} + readonly variables?: {[key: string]: any} } export type GitHubGraphQLClientResponse = { @@ -27,23 +27,21 @@ interface IGitHubGraphQLClient { export default class GitHubProjectDataSource implements IProjectDataSource { private readonly graphQlClient: IGitHubGraphQLClient - private readonly organizationName: string private readonly projectConfigurationFilename: string constructor( config: { graphQlClient: IGitHubGraphQLClient, - organizationName: string, projectConfigurationFilename: string } ) { this.graphQlClient = config.graphQlClient - this.organizationName = config.organizationName this.projectConfigurationFilename = config.projectConfigurationFilename.replace(/\.ya?ml$/, "") } async getProjects(): Promise { - const repositories = await this.getRepositories() + const logins = await this.getLogins() + const repositories = await this.getRepositories({ logins }) return repositories.map(repository => { return this.mapProject(repository) }) @@ -231,7 +229,29 @@ export default class GitHubProjectDataSource implements IProjectDataSource { .replace(/[^A-Za-z0-9-]/g, "") } - private async getRepositories(): Promise { + private async getLogins(): Promise { + const request = { + query: `query { + viewer { + login + organizations(first: 100) { + nodes { + login + } + } + } + }` + } + const response = await this.graphQlClient.graphql(request) + const viewer = response.viewer + const organizations = viewer.organizations.nodes.map((e: { login: string }) => e.login) + return [viewer.login].concat(organizations) + } + + private async getRepositories({ logins }: { logins: string[] }): Promise { + if (logins.length === 0) { + return [] + } const request = { query: ` query Repositories($searchQuery: String!) { @@ -295,10 +315,11 @@ export default class GitHubProjectDataSource implements IProjectDataSource { } `, variables: { - searchQuery: `org:${this.organizationName} openapi in:name` + searchQuery: `user:${logins[0]} openapi in:name` } } const response = await this.graphQlClient.graphql(request) - return response.search.results + const nextResults = await this.getRepositories({ logins: logins.slice(1) }) + return response.search.results.concat(nextResults) } } From 1224458e140a7f8c91f7df6992d8b2bf2a5d44e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 14:55:46 +0200 Subject: [PATCH 128/191] Fixes GitHubProjectDataSource tests --- .../projects/GitHubProjectDataSource.test.ts | 150 +++++++++++++++--- src/composition.ts | 6 +- .../projects/data/GitHubLoginDataSource.ts | 38 +++++ .../projects/data/GitHubProjectDataSource.ts | 53 ++----- .../projects/data/IGitHubGraphQLClient.ts | 14 ++ .../projects/data/IGitHubLoginDataSource.ts | 3 + src/features/projects/data/index.ts | 4 + 7 files changed, 200 insertions(+), 68 deletions(-) create mode 100644 src/features/projects/data/GitHubLoginDataSource.ts create mode 100644 src/features/projects/data/IGitHubGraphQLClient.ts create mode 100644 src/features/projects/data/IGitHubLoginDataSource.ts diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index 6d2b5e7f..838e28b7 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -6,7 +6,11 @@ test("It loads repositories from data source", async () => { let didLoadRepositories = false const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { didLoadRepositories = true @@ -25,7 +29,11 @@ test("It loads repositories from data source", async () => { test("It maps projects including branches and tags", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -112,7 +120,11 @@ test("It maps projects including branches and tags", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -173,7 +185,11 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It supports multiple OpenAPI specifications on a branch", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -274,7 +290,11 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -335,7 +355,11 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It filters away projects with no versions", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -370,7 +394,11 @@ test("It filters away projects with no versions", async () => { test("It filters away branches with no specifications", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -429,7 +457,11 @@ test("It filters away branches with no specifications", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -500,7 +532,11 @@ test("It filters away tags with no specifications", async () => { test("It reads image from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -550,7 +586,11 @@ test("It reads image from configuration file with .yml extension", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -621,7 +661,11 @@ test("It filters away tags with no specifications", async () => { test("It reads display name from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -673,7 +717,11 @@ test("It reads display name from configuration file with .yml extension", async test("It reads image from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -723,7 +771,11 @@ test("It reads image from configuration file with .yml extension", async () => { test("It reads display name from configuration file with .yaml extension", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -775,7 +827,11 @@ test("It reads display name from configuration file with .yaml extension", async test("It reads image from configuration file with .yaml extension", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -825,7 +881,11 @@ test("It reads image from configuration file with .yaml extension", async () => test("It sorts projects alphabetically", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -932,7 +992,11 @@ test("It sorts projects alphabetically", async () => { test("It sorts versions alphabetically", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -1018,7 +1082,11 @@ test("It sorts versions alphabetically", async () => { test("It prioritizes main, master, develop, and development branch names when sorting verisons", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -1130,7 +1198,11 @@ test("It prioritizes main, master, develop, and development branch names when so test("It identifies the default branch in returned versions", async () => { const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -1218,7 +1290,11 @@ test("It adds remote versions from the project configuration", async () => { ` const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -1289,7 +1365,11 @@ test("It modifies ID of remote version if the ID already exists", async () => { ` const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -1376,7 +1456,11 @@ test("It lets users specify the ID of a remote version", async () => { ` const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -1431,7 +1515,11 @@ test("It lets users specify the ID of a remote specification", async () => { ` const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql() { return { @@ -1479,7 +1567,11 @@ test("It queries for both .yml and .yaml file extension with specifying .yml ext let query: string | undefined const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql(request) { query = request.query @@ -1500,7 +1592,11 @@ test("It queries for both .yml and .yaml file extension with specifying .yaml ex let query: string | undefined const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs.yml", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql(request) { query = request.query @@ -1521,7 +1617,11 @@ test("It queries for both .yml and .yaml file extension with no extension", asyn let query: string | undefined const sut = new GitHubProjectDataSource({ projectConfigurationFilename: ".demo-docs", - organizationName: "foo", + loginsDataSource: { + async getLogins() { + return ["acme"] + } + }, graphQlClient: { async graphql(request) { query = request.query diff --git a/src/composition.ts b/src/composition.ts index d050e795..ebd47d7d 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -15,7 +15,8 @@ import { SessionMutexFactory } from "@/common" import { - GitHubProjectDataSource + GitHubProjectDataSource, + GitHubLoginDataSource } from "@/features/projects/data" import { CachingProjectDataSource, @@ -244,6 +245,9 @@ export const projectRepository = new ProjectRepository( export const projectDataSource = new CachingProjectDataSource({ dataSource: new GitHubProjectDataSource({ + loginsDataSource: new GitHubLoginDataSource({ + graphQlClient: userGitHubClient + }), graphQlClient: userGitHubClient, projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME }), diff --git a/src/features/projects/data/GitHubLoginDataSource.ts b/src/features/projects/data/GitHubLoginDataSource.ts new file mode 100644 index 00000000..6c1a03db --- /dev/null +++ b/src/features/projects/data/GitHubLoginDataSource.ts @@ -0,0 +1,38 @@ +import IGitHubLoginDataSource from "./IGitHubLoginDataSource" +import IGitHubGraphQLClient from "./IGitHubGraphQLClient" + +export default class GitHubLoginDataSource implements IGitHubLoginDataSource { + private readonly graphQlClient: IGitHubGraphQLClient + + constructor(config: { graphQlClient: IGitHubGraphQLClient }) { + this.graphQlClient = config.graphQlClient + } + + async getLogins(): Promise { + const request = { + query: `query { + viewer { + login + organizations(first: 100) { + nodes { + login + } + } + } + }` + } + const response = await this.graphQlClient.graphql(request) + if (!response.viewer) { + throw new Error("viewer property not found in response") + } + if (!response.viewer.login) { + throw new Error("login property not found on viewer in response") + } + if (!response.viewer.organizations) { + throw new Error("organizations property not found on viewer in response") + } + const viewer = response.viewer + const organizations = viewer.organizations.nodes.map((e: { login: string }) => e.login) + return [viewer.login].concat(organizations) + } +} diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index f88dede1..aff25611 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -1,46 +1,34 @@ import GitHubProjectRepository, { GitHubProjectRepositoryRef } from "./GitHubProjectRepository" +import IGitHubLoginDataSource from "./IGitHubLoginDataSource" +import IGitHubGraphQLClient from "./IGitHubGraphQLClient" import { Project, Version, IProjectConfig, IProjectDataSource, ProjectConfigParser, - ProjectConfigRemoteVersion, + ProjectConfigRemoteVersion } from "../domain" -export type GitHubGraphQLClientRequest = { - readonly query: string - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - readonly variables?: {[key: string]: any} -} - -export type GitHubGraphQLClientResponse = { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - readonly [key: string]: any -} - -interface IGitHubGraphQLClient { - graphql(request: GitHubGraphQLClientRequest): Promise -} - export default class GitHubProjectDataSource implements IProjectDataSource { + private readonly loginsDataSource: IGitHubLoginDataSource private readonly graphQlClient: IGitHubGraphQLClient private readonly projectConfigurationFilename: string - constructor( - config: { - graphQlClient: IGitHubGraphQLClient, - projectConfigurationFilename: string - } - ) { + constructor(config: { + loginsDataSource: IGitHubLoginDataSource, + graphQlClient: IGitHubGraphQLClient, + projectConfigurationFilename: string + }) { + this.loginsDataSource = config.loginsDataSource this.graphQlClient = config.graphQlClient this.projectConfigurationFilename = config.projectConfigurationFilename.replace(/\.ya?ml$/, "") } async getProjects(): Promise { - const logins = await this.getLogins() + const logins = await this.loginsDataSource.getLogins() const repositories = await this.getRepositories({ logins }) return repositories.map(repository => { return this.mapProject(repository) @@ -229,25 +217,6 @@ export default class GitHubProjectDataSource implements IProjectDataSource { .replace(/[^A-Za-z0-9-]/g, "") } - private async getLogins(): Promise { - const request = { - query: `query { - viewer { - login - organizations(first: 100) { - nodes { - login - } - } - } - }` - } - const response = await this.graphQlClient.graphql(request) - const viewer = response.viewer - const organizations = viewer.organizations.nodes.map((e: { login: string }) => e.login) - return [viewer.login].concat(organizations) - } - private async getRepositories({ logins }: { logins: string[] }): Promise { if (logins.length === 0) { return [] diff --git a/src/features/projects/data/IGitHubGraphQLClient.ts b/src/features/projects/data/IGitHubGraphQLClient.ts new file mode 100644 index 00000000..47345fd0 --- /dev/null +++ b/src/features/projects/data/IGitHubGraphQLClient.ts @@ -0,0 +1,14 @@ +export type GitHubGraphQLClientRequest = { + readonly query: string + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + readonly variables?: {[key: string]: any} +} + +export type GitHubGraphQLClientResponse = { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + readonly [key: string]: any +} + +export default interface IGitHubGraphQLClient { + graphql(request: GitHubGraphQLClientRequest): Promise +} diff --git a/src/features/projects/data/IGitHubLoginDataSource.ts b/src/features/projects/data/IGitHubLoginDataSource.ts new file mode 100644 index 00000000..fa880775 --- /dev/null +++ b/src/features/projects/data/IGitHubLoginDataSource.ts @@ -0,0 +1,3 @@ +export default interface IGitHubLoginDataSource { + getLogins(): Promise +} diff --git a/src/features/projects/data/index.ts b/src/features/projects/data/index.ts index 8199a31a..cf44de31 100644 --- a/src/features/projects/data/index.ts +++ b/src/features/projects/data/index.ts @@ -1,2 +1,6 @@ export { default as GitHubProjectDataSource } from "./GitHubProjectDataSource" +export * from "./GitHubProjectDataSource" export { default as useProjects } from "./useProjects" +export type { default as IGitHubLoginDataSource } from "./IGitHubLoginDataSource" +export { default as GitHubLoginDataSource } from "./GitHubLoginDataSource" +export * from "./IGitHubGraphQLClient" From c0333ca1ff5498e9833064ad6fbcba5d419409c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 15:00:23 +0200 Subject: [PATCH 129/191] Adds unit test --- .../projects/GitHubProjectDataSource.test.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index 838e28b7..99538d15 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -1637,3 +1637,32 @@ test("It queries for both .yml and .yaml file extension with no extension", asyn expect(query).toContain(".demo-docs.yml") expect(query).toContain(".demo-docs.yaml") }) + +test("It loads projects for all logins", async () => { + let searchQueries: string[] = [] + const sut = new GitHubProjectDataSource({ + projectConfigurationFilename: ".demo-docs", + loginsDataSource: { + async getLogins() { + return ["acme", "somecorp", "techsystems"] + } + }, + graphQlClient: { + async graphql(request) { + if (request.variables?.searchQuery) { + searchQueries.push(request.variables.searchQuery) + } + return { + search: { + results: [] + } + } + } + } + }) + await sut.getProjects() + expect(searchQueries.length).toEqual(3) + expect(searchQueries[0]).toBe("user:acme openapi in:name") + expect(searchQueries[1]).toBe("user:somecorp openapi in:name") + expect(searchQueries[2]).toBe("user:techsystems openapi in:name") +}) From 255ece452bb8d1f6326654b42bfe1bc76a4d2075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 15:59:32 +0200 Subject: [PATCH 130/191] Removes extraneous whitespace --- src/common/db/PostgreSQLDB.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/db/PostgreSQLDB.ts b/src/common/db/PostgreSQLDB.ts index 3b41451f..8e197a33 100644 --- a/src/common/db/PostgreSQLDB.ts +++ b/src/common/db/PostgreSQLDB.ts @@ -1,7 +1,6 @@ import { Pool, PoolClient, QueryResult } from "pg" import IDB, { IDBConnection, IDBQueryResult, IDBRow } from "./IDB" - export class PostgreSQLDBConnection implements IDBConnection { private readonly client: PoolClient From 2b68e30c8073861bbff86b904e2bf10d421355c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 16:32:09 +0200 Subject: [PATCH 131/191] Removes GITHUB_ORGANIZATION_NAME environment variable --- .env.example | 1 - README.md | 1 - ...GitHubOrganizationSessionValidator.test.ts | 131 ------------------ __test__/auth/SessionValidity.test.ts | 28 ---- infrastructure/aws/lib/app-stack.ts | 1 - src/app/api/user/session-validity/route.ts | 26 ---- src/composition.ts | 11 +- .../GitHubOrganizationSessionValidator.ts | 67 --------- .../session-validity/SessionValidity.ts | 15 -- .../auth/domain/session-validity/index.ts | 2 - .../session-validity/useSessionValidity.ts | 19 --- src/features/auth/view/SessionBarrier.tsx | 7 - ...NonGitHubAccountAccessTokenInvalidPage.tsx | 8 +- .../auth/view/client/SessionBarrier.tsx | 26 +--- types/env.d.ts | 1 - 15 files changed, 7 insertions(+), 337 deletions(-) delete mode 100644 __test__/auth/GitHubOrganizationSessionValidator.test.ts delete mode 100644 __test__/auth/SessionValidity.test.ts delete mode 100644 src/app/api/user/session-validity/route.ts delete mode 100644 src/features/auth/domain/session-validity/GitHubOrganizationSessionValidator.ts delete mode 100644 src/features/auth/domain/session-validity/useSessionValidity.ts diff --git a/.env.example b/.env.example index 0777262b..5009f278 100644 --- a/.env.example +++ b/.env.example @@ -15,7 +15,6 @@ SMTP_PASS=smtppass GITHUB_WEBHOOK_SECRET=preshared secret also put in app configuration in GitHub GITHUB_WEBHOK_REPOSITORY_ALLOWLIST= GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST= -GITHUB_ORGANIZATION_NAME=testorg GITHUB_CLIENT_ID=GitHub App client ID GITHUB_CLIENT_SECRET=GitHub App client secret GITHUB_APP_ID=123456 diff --git a/README.md b/README.md index c9c59afe..0f7520ca 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ The table below contains explanations for environment variables. The list is not |GITHUB_WEBHOOK_SECRET|Secret shared with the GitHub app to validate a webhook call.| |GITHUB_WEBHOK_REPOSITORY_ALLOWLIST|Comma-separated list of repositories from which webhook calls should be accepted. Leave empty to accept calls from all repositories.| |GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST|Comma-separated list of repositories from which webhook calls should be ignored. The list of disallowed repositories takes precedence over the list of allowed repositories.| -|GITHUB_ORGANIZATION_NAME|Name of the organization to show repositories for.| |REDIS_URL|The URL to the Redis store.| |SMTP_HOST|Hostname for SMTP server used for sending magic links and guest invitation.| |SMTP_USER|Username for SMTP server used for sending magic links and guest invitation.| diff --git a/__test__/auth/GitHubOrganizationSessionValidator.test.ts b/__test__/auth/GitHubOrganizationSessionValidator.test.ts deleted file mode 100644 index f6c9df2b..00000000 --- a/__test__/auth/GitHubOrganizationSessionValidator.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - GitHubOrganizationSessionValidator, - SessionValidity - } from "../../src/features/auth/domain" - -test("It requests organization membership status for the specified organization", async () => { - let queriedOrganizationName: string | undefined - const sut = new GitHubOrganizationSessionValidator({ - acceptedOrganization: "foo", - organizationMembershipStatusReader: { - async getOrganizationMembershipStatus(request) { - queriedOrganizationName = request.organizationName - return { state: "active" } - } - }, - accountProviderReader: { - async getAccountProvider() { - return "github" - } - } - }) - await sut.validateSession() - expect(queriedOrganizationName).toBe("foo") -}) - -test("It considers session valid when membership state is \"active\"", async () => { - const sut = new GitHubOrganizationSessionValidator({ - acceptedOrganization: "foo", - organizationMembershipStatusReader: { - async getOrganizationMembershipStatus() { - return { state: "active" } - } - }, - accountProviderReader: { - async getAccountProvider() { - return "github" - } - } - }) - const sessionValidity = await sut.validateSession() - expect(sessionValidity).toEqual(SessionValidity.VALID) -}) - -test("It considers user not to be part of the organization when membership state is \"pending\"", async () => { - const sut = new GitHubOrganizationSessionValidator({ - acceptedOrganization: "foo", - organizationMembershipStatusReader: { - async getOrganizationMembershipStatus() { - return { state: "pending" } - } - }, - accountProviderReader: { - async getAccountProvider() { - return "github" - } - } - }) - const sessionValidity = await sut.validateSession() - expect(sessionValidity).toEqual(SessionValidity.OUTSIDE_GITHUB_ORGANIZATION) -}) - -test("It considers user not to be part of the organization when receiving HTTP 404", async () => { - const sut = new GitHubOrganizationSessionValidator({ - acceptedOrganization: "foo", - organizationMembershipStatusReader: { - async getOrganizationMembershipStatus() { - throw { status: 404, message: "User is not member of organization"} - } - }, - accountProviderReader: { - async getAccountProvider() { - return "github" - } - } - }) - const sessionValidity = await sut.validateSession() - expect(sessionValidity).toEqual(SessionValidity.OUTSIDE_GITHUB_ORGANIZATION) -}) - -test("It considers organization to have blocked the GitHub app when receiving HTTP 403", async () => { - const sut = new GitHubOrganizationSessionValidator({ - acceptedOrganization: "foo", - organizationMembershipStatusReader: { - async getOrganizationMembershipStatus() { - throw { status: 403, message: "Organization has blocked GitHub app"} - } - }, - accountProviderReader: { - async getAccountProvider() { - return "github" - } - } - }) - const sessionValidity = await sut.validateSession() - expect(sessionValidity).toEqual(SessionValidity.GITHUB_APP_BLOCKED) -}) - -test("It forwards error when getting membership status throws unknown error", async () => { - const sut = new GitHubOrganizationSessionValidator({ - acceptedOrganization: "foo", - organizationMembershipStatusReader: { - async getOrganizationMembershipStatus() { - throw { status: 500 } - } - }, - accountProviderReader: { - async getAccountProvider() { - return "github" - } - } - }) - await expect(sut.validateSession()).rejects.toEqual({ status: 500 }) -}) - -test("It considers session valid when the account provider is not \"github\"", async () => { - const sut = new GitHubOrganizationSessionValidator({ - acceptedOrganization: "foo", - organizationMembershipStatusReader: { - async getOrganizationMembershipStatus() { - throw { status: "pending" } - } - }, - accountProviderReader: { - async getAccountProvider() { - return "email" - } - } - }) - const sessionValidity = await sut.validateSession() - expect(sessionValidity).toEqual(SessionValidity.VALID) -}) diff --git a/__test__/auth/SessionValidity.test.ts b/__test__/auth/SessionValidity.test.ts deleted file mode 100644 index c411f973..00000000 --- a/__test__/auth/SessionValidity.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - mergeSessionValidity, - SessionValidity -} from "../../src/features/auth/domain" - -test("It returns invalid validity when left-hand side validity indicates that the session is invalid", async () => { - const sut = mergeSessionValidity( - SessionValidity.INVALID_ACCESS_TOKEN, - SessionValidity.VALID - ) - expect(sut).toEqual(SessionValidity.INVALID_ACCESS_TOKEN) -}) - -test("It returns invalid validity when right-hand side validity indicates that the session is invalid", async () => { - const sut = mergeSessionValidity( - SessionValidity.VALID, - SessionValidity.INVALID_ACCESS_TOKEN - ) - expect(sut).toEqual(SessionValidity.INVALID_ACCESS_TOKEN) -}) - -test("It returns valid validity when both validities indicate that the session is valid", async () => { - const sut = mergeSessionValidity( - SessionValidity.VALID, - SessionValidity.VALID - ) - expect(sut).toEqual(SessionValidity.VALID) -}) diff --git a/infrastructure/aws/lib/app-stack.ts b/infrastructure/aws/lib/app-stack.ts index ae018ddc..cf051b7e 100644 --- a/infrastructure/aws/lib/app-stack.ts +++ b/infrastructure/aws/lib/app-stack.ts @@ -30,7 +30,6 @@ export class AppStack extends cdk.Stack { "GITHUB_APP_ID", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", - "GITHUB_ORGANIZATION_NAME", "GITHUB_PRIVATE_KEY_BASE_64", "GITHUB_WEBHOK_REPOSITORY_ALLOWLIST", "GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST", diff --git a/src/app/api/user/session-validity/route.ts b/src/app/api/user/session-validity/route.ts deleted file mode 100644 index c1084311..00000000 --- a/src/app/api/user/session-validity/route.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NextResponse } from "next/server" -import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" -import { session, delayedSessionValidator } from "@/composition" - -export async function GET() { - const isAuthenticated = await session.getIsAuthenticated() - if (!isAuthenticated) { - return makeUnauthenticatedAPIErrorResponse() - } - try { - await session.getUserId() - } catch { - return makeAPIErrorResponse(401, "Unauthorized") - } - try { - const sessionValidity = await delayedSessionValidator.validateSession() - return NextResponse.json({sessionValidity}) - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - } catch (error: any) { - if (error.message) { - return makeAPIErrorResponse(500, error.message) - } else { - return makeAPIErrorResponse(500, "Unknown error") - } - } -} \ No newline at end of file diff --git a/src/composition.ts b/src/composition.ts index ebd47d7d..26efbf33 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -32,7 +32,6 @@ import { CompositeLogOutHandler, ErrorIgnoringLogOutHandler, FallbackOAuthTokenRepository, - GitHubOrganizationSessionValidator, GuestOAuthTokenDataSource, GuestOAuthTokenRefresher, LockingOAuthTokenRefresher, @@ -65,7 +64,6 @@ const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_PRIVATE_KEY_BASE_64, - GITHUB_ORGANIZATION_NAME, REDIS_URL, POSTGRESQL_HOST, POSTGRESQL_USER, @@ -83,8 +81,7 @@ const gitHubAppCredentials = { clientSecret: GITHUB_CLIENT_SECRET, privateKey: Buffer .from(GITHUB_PRIVATE_KEY_BASE_64, "base64") - .toString("utf-8"), - organization: GITHUB_ORGANIZATION_NAME, + .toString("utf-8") } const pool = new Pool({ @@ -227,12 +224,6 @@ export const blockingSessionValidator = new OAuthTokenSessionValidator({ oauthTokenDataSource }) -export const delayedSessionValidator = new GitHubOrganizationSessionValidator({ - acceptedOrganization: GITHUB_ORGANIZATION_NAME, - organizationMembershipStatusReader: userGitHubClient, - accountProviderReader: session -}) - const projectUserDataRepository = new KeyValueUserDataRepository( new RedisKeyValueStore(REDIS_URL), "projects" diff --git a/src/features/auth/domain/session-validity/GitHubOrganizationSessionValidator.ts b/src/features/auth/domain/session-validity/GitHubOrganizationSessionValidator.ts deleted file mode 100644 index 0c69504c..00000000 --- a/src/features/auth/domain/session-validity/GitHubOrganizationSessionValidator.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { AccountProvider } from "@/common" -import SessionValidity from "./SessionValidity" - -type OrganizationMembershipStatus = { - readonly state: "active" | "pending" -} - -interface IAccountProviderReader { - getAccountProvider(): Promise -} - -interface IOrganizationMembershipStatusReader { - getOrganizationMembershipStatus( - request: { organizationName: string } - ): Promise -} - -export default class GitHubOrganizationSessionValidator { - private readonly acceptedOrganization: string - private readonly organizationMembershipStatusReader: IOrganizationMembershipStatusReader - private readonly accountProviderReader: IAccountProviderReader - - constructor( - config: { - acceptedOrganization: string - organizationMembershipStatusReader: IOrganizationMembershipStatusReader, - accountProviderReader: IAccountProviderReader - } - ) { - this.acceptedOrganization = config.acceptedOrganization - this.organizationMembershipStatusReader = config.organizationMembershipStatusReader - this.accountProviderReader = config.accountProviderReader - } - - async validateSession(): Promise { - const accountProvider = await this.accountProviderReader.getAccountProvider() - if (accountProvider !== "github") { - // Only validate GitHub sessions and consider any other valid. - return SessionValidity.VALID - } - try { - const response = await this.organizationMembershipStatusReader.getOrganizationMembershipStatus({ - organizationName: this.acceptedOrganization - }) - if (response.state == "active") { - return SessionValidity.VALID - } else { - return SessionValidity.OUTSIDE_GITHUB_ORGANIZATION - } - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - } catch (error: any) { - if (error.status) { - if (error.status == 404) { - return SessionValidity.OUTSIDE_GITHUB_ORGANIZATION - } else if (error.status == 403) { - return SessionValidity.GITHUB_APP_BLOCKED - } else if (error.status == 401) { - return SessionValidity.INVALID_ACCESS_TOKEN - } else { - throw error - } - } else { - throw error - } - } - } -} diff --git a/src/features/auth/domain/session-validity/SessionValidity.ts b/src/features/auth/domain/session-validity/SessionValidity.ts index ba461768..39ac2977 100644 --- a/src/features/auth/domain/session-validity/SessionValidity.ts +++ b/src/features/auth/domain/session-validity/SessionValidity.ts @@ -1,21 +1,6 @@ enum SessionValidity { VALID = "valid", INVALID_ACCESS_TOKEN = "invalid_access_token", - OUTSIDE_GITHUB_ORGANIZATION = "outside_github_organization", - GITHUB_APP_BLOCKED = "github_app_blocked" } export default SessionValidity - -export function mergeSessionValidity( - lhs: SessionValidity, - rhs: SessionValidity -): SessionValidity { - if (lhs != SessionValidity.VALID) { - return lhs - } else if (rhs != SessionValidity.VALID) { - return rhs - } else { - return SessionValidity.VALID - } -} diff --git a/src/features/auth/domain/session-validity/index.ts b/src/features/auth/domain/session-validity/index.ts index b5b8f96c..bdbaaa79 100644 --- a/src/features/auth/domain/session-validity/index.ts +++ b/src/features/auth/domain/session-validity/index.ts @@ -1,6 +1,4 @@ -export { default as GitHubOrganizationSessionValidator } from "./GitHubOrganizationSessionValidator" export { default as OAuthTokenSessionValidator } from "./OAuthTokenSessionValidator" export { default as SessionValidity } from "./SessionValidity" export * from "./SessionValidity" export { default as useRepositoryAccess } from "./useRepositoryAccess" -export { default as useSessionValidity } from "./useSessionValidity" diff --git a/src/features/auth/domain/session-validity/useSessionValidity.ts b/src/features/auth/domain/session-validity/useSessionValidity.ts deleted file mode 100644 index b334450e..00000000 --- a/src/features/auth/domain/session-validity/useSessionValidity.ts +++ /dev/null @@ -1,19 +0,0 @@ -"use client" - -import useSWR from "swr" -import { fetcher } from "@/common" -import SessionValidity from "./SessionValidity" - -type SessionValidityContainer = { sessionValidity: SessionValidity } - -export default function useSessionValidity() { - const { data, error, isLoading } = useSWR( - "/api/user/session-validity", - fetcher - ) - return { - sessionValidity: data?.sessionValidity || SessionValidity.VALID, - isLoading, - error - } -} diff --git a/src/features/auth/view/SessionBarrier.tsx b/src/features/auth/view/SessionBarrier.tsx index 648ef1b2..406b6120 100644 --- a/src/features/auth/view/SessionBarrier.tsx +++ b/src/features/auth/view/SessionBarrier.tsx @@ -2,11 +2,6 @@ import { ReactNode } from "react" import { session, blockingSessionValidator } from "@/composition" import ClientSessionBarrier from "./client/SessionBarrier" -const { - NEXT_PUBLIC_SHAPE_DOCS_TITLE, - GITHUB_ORGANIZATION_NAME -} = process.env - export default async function SessionBarrier({ children }: { @@ -17,8 +12,6 @@ export default async function SessionBarrier({ return ( {children} diff --git a/src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx b/src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx index aeb27af9..3c54f761 100644 --- a/src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx +++ b/src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx @@ -4,11 +4,7 @@ import InvalidSessionPage from "./InvalidSessionPage" import LoadingIndicator from "@/common/loading/DelayedLoadingIndicator" import { useRepositoryAccess } from "../../domain" -export default function NonGitHubAccountAccessTokenInvalidPage({ - organizationName -}: { - organizationName: string -}) { +export default function NonGitHubAccountAccessTokenInvalidPage() { const {repositories, isLoading, error} = useRepositoryAccess() if (isLoading) { return ( @@ -20,7 +16,7 @@ export default function NonGitHubAccountAccessTokenInvalidPage({ if (error) { return ( - It was not possible to obtain access to the projects on the {organizationName} organization on GitHub. + It was not possible to obtain access to the repositories on GitHub. ) } diff --git a/src/features/auth/view/client/SessionBarrier.tsx b/src/features/auth/view/client/SessionBarrier.tsx index 22a58120..acbd439b 100644 --- a/src/features/auth/view/client/SessionBarrier.tsx +++ b/src/features/auth/view/client/SessionBarrier.tsx @@ -3,51 +3,33 @@ import { ReactNode } from "react" import { AccountProvider } from "@/common" import { - SessionValidity, - mergeSessionValidity, - useSessionValidity + SessionValidity } from "../../domain" import NonGitHubAccountAccessTokenInvalidPage from "./NonGitHubAccountAccessTokenInvalidPage" import InvalidSessionPage from "./InvalidSessionPage" export default function SessionBarrier({ accountProvider, - siteName, - organizationName, - sessionValidity: fastSessionValidity, + sessionValidity, children }: { accountProvider: AccountProvider - siteName: string - organizationName: string sessionValidity: SessionValidity children: ReactNode }) { - const { sessionValidity: delayedSessionValidity } = useSessionValidity() - const sessionValidity = mergeSessionValidity( - fastSessionValidity, - delayedSessionValidity - ) switch (sessionValidity) { case SessionValidity.VALID: return <>{children} case SessionValidity.INVALID_ACCESS_TOKEN: switch (accountProvider) { case "email": - return + return case "github": return ( - It was not possible to obtain access to the projects on the {organizationName} organization on GitHub. + It was not possible to obtain access to the repositories on GitHub. ) } - case SessionValidity.OUTSIDE_GITHUB_ORGANIZATION: - case SessionValidity.GITHUB_APP_BLOCKED: - return ( - - Access to {siteName} requires that your account is an active member of the {organizationName} organization on GitHub. - - ) } } diff --git a/types/env.d.ts b/types/env.d.ts index f37949e4..30748275 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -17,7 +17,6 @@ namespace NodeJS { readonly GITHUB_WEBHOOK_SECRET: string readonly GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: string readonly GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: string - readonly GITHUB_ORGANIZATION_NAME: string readonly GITHUB_CLIENT_ID: string readonly GITHUB_CLIENT_SECRET: string readonly GITHUB_APP_ID: string From 211748c99512a327513ae0ae0d44479584eefca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 9 Jul 2024 16:36:41 +0200 Subject: [PATCH 132/191] Removes unused imports --- src/composition.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index 26efbf33..7152f5a1 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -49,9 +49,7 @@ import { MagicLinkEmailSender } from "./features/admin/data" import { - Guest, IGuestInviter, - IGuestRepository, IUserRepository } from "./features/admin/domain" import DummyGuestRepository from "./features/admin/data/DummyGuestRepository" From d5479f9831b7e92b4d345ff51adff1b0ae9ef0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 09:02:47 +0200 Subject: [PATCH 133/191] Improves formatting --- src/common/github/GitHubClient.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/common/github/GitHubClient.ts b/src/common/github/GitHubClient.ts index 048fe183..36b1d8ac 100644 --- a/src/common/github/GitHubClient.ts +++ b/src/common/github/GitHubClient.ts @@ -24,15 +24,13 @@ export default class GitHubClient implements IGitHubClient { private readonly oauthTokenDataSource: IGitHubOAuthTokenDataSource private readonly installationAuthenticator: InstallationAuthenticator - constructor( - config: { - appId: string - clientId: string - clientSecret: string - privateKey: string - oauthTokenDataSource: IGitHubOAuthTokenDataSource - } - ) { + constructor(config: { + appId: string + clientId: string + clientSecret: string + privateKey: string + oauthTokenDataSource: IGitHubOAuthTokenDataSource + }) { this.oauthTokenDataSource = config.oauthTokenDataSource const appAuth = createAppAuth({ appId: config.appId, From 36bc08a8a24903597f3968cdca010d5407db018a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 14:53:15 +0200 Subject: [PATCH 134/191] Removes code related to guests --- .env.example | 4 - README.md | 28 -- __test__/admin/EmailGuestInviter.test.ts | 60 ---- ...oviderTypeBasedOAuthTokenRefresher.test.ts | 59 ---- .../auth/FallbackOAuthTokenRepository.test.ts | 2 +- .../auth/GuestOAuthTokenDataSource.test.ts | 178 ---------- .../auth/GuestOAuthTokenRefresher.test.ts | 35 -- .../auth/LockingAccessTokenRefresher.test.ts | 15 +- __test__/auth/LogInHandler.test.ts | 321 ------------------ __test__/auth/OAuthTokenDataSource.test.ts | 75 ++++ .../auth/OAuthTokenSessionValidator.test.ts | 10 +- .../PersistingOAuthTokenDataSource.test.ts | 314 ----------------- create-tables.sql | 17 +- drop-tables.sql | 1 - infrastructure/aws/lib/app-stack.ts | 4 - src/app/admin/guests/page.tsx | 7 - src/app/api/user/repository-access/route.ts | 27 -- src/common/session/AuthjsSession.ts | 40 +-- src/common/session/ISession.ts | 4 - src/common/session/index.ts | 2 +- src/composition.ts | 106 +----- src/features/admin/data/DbGuestRepository.ts | 84 ----- src/features/admin/data/DbUserRepository.ts | 54 --- .../admin/data/DummyGuestRepository.ts | 29 -- src/features/admin/data/EmailGuestInviter.ts | 104 ------ .../admin/data/MagicLinkEmailSender.ts | 126 ------- src/features/admin/data/index.ts | 4 - src/features/admin/domain/Account.ts | 5 - src/features/admin/domain/Guest.ts | 7 - src/features/admin/domain/IGuestInviter.ts | 3 - src/features/admin/domain/IGuestRepository.ts | 9 - src/features/admin/domain/IUserRepository.ts | 5 - src/features/admin/domain/User.ts | 11 - src/features/admin/domain/index.ts | 6 - src/features/admin/view/Actions.tsx | 61 ---- src/features/admin/view/AdminPage.tsx | 60 ---- src/features/admin/view/EditGuestForm.tsx | 11 - src/features/admin/view/InviteGuestForm.tsx | 28 -- src/features/admin/view/RemoveGuestForm.tsx | 19 -- ...GitHubInstallationAccessTokenDataSource.ts | 53 --- src/features/auth/data/index.ts | 1 - .../auth/domain/log-in/LogInHandler.ts | 31 +- .../auth/domain/oauth-token/OAuthToken.ts | 2 +- .../data-source/GuestOAuthTokenDataSource.ts | 35 -- .../data-source/OAuthTokenDataSource.ts | 18 + .../PersistingOAuthTokenDataSource.ts | 55 --- src/features/auth/domain/oauth-token/index.ts | 5 +- ...untProviderTypeBasedOAuthTokenRefresher.ts | 27 -- .../refresher/GuestOAuthTokenRefresher.ts | 17 - .../refresher/LockingOAuthTokenRefresher.ts | 10 +- .../session-validity/SessionValidity.ts | 2 +- .../auth/domain/session-validity/index.ts | 1 - .../session-validity/useRepositoryAccess.ts | 18 - src/features/auth/view/SessionBarrier.tsx | 3 +- ...NonGitHubAccountAccessTokenInvalidPage.tsx | 50 --- .../auth/view/client/SessionBarrier.tsx | 23 +- src/features/projects/view/ProjectsPage.tsx | 2 - .../projects/view/client/ProjectsPage.tsx | 3 - .../view/toolbar/TrailingToolbarItem.tsx | 6 +- src/features/user/view/SettingsList.tsx | 3 - types/env.d.ts | 4 - 61 files changed, 146 insertions(+), 2158 deletions(-) delete mode 100644 __test__/admin/EmailGuestInviter.test.ts delete mode 100644 __test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts delete mode 100644 __test__/auth/GuestOAuthTokenDataSource.test.ts delete mode 100644 __test__/auth/GuestOAuthTokenRefresher.test.ts create mode 100644 __test__/auth/OAuthTokenDataSource.test.ts delete mode 100644 __test__/auth/PersistingOAuthTokenDataSource.test.ts delete mode 100644 src/app/admin/guests/page.tsx delete mode 100644 src/app/api/user/repository-access/route.ts delete mode 100644 src/features/admin/data/DbGuestRepository.ts delete mode 100644 src/features/admin/data/DbUserRepository.ts delete mode 100644 src/features/admin/data/DummyGuestRepository.ts delete mode 100644 src/features/admin/data/EmailGuestInviter.ts delete mode 100644 src/features/admin/data/MagicLinkEmailSender.ts delete mode 100644 src/features/admin/data/index.ts delete mode 100644 src/features/admin/domain/Account.ts delete mode 100644 src/features/admin/domain/Guest.ts delete mode 100644 src/features/admin/domain/IGuestInviter.ts delete mode 100644 src/features/admin/domain/IGuestRepository.ts delete mode 100644 src/features/admin/domain/IUserRepository.ts delete mode 100644 src/features/admin/domain/User.ts delete mode 100644 src/features/admin/domain/index.ts delete mode 100644 src/features/admin/view/Actions.tsx delete mode 100644 src/features/admin/view/AdminPage.tsx delete mode 100644 src/features/admin/view/EditGuestForm.tsx delete mode 100644 src/features/admin/view/InviteGuestForm.tsx delete mode 100644 src/features/admin/view/RemoveGuestForm.tsx delete mode 100644 src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts delete mode 100644 src/features/auth/domain/oauth-token/data-source/GuestOAuthTokenDataSource.ts create mode 100644 src/features/auth/domain/oauth-token/data-source/OAuthTokenDataSource.ts delete mode 100644 src/features/auth/domain/oauth-token/data-source/PersistingOAuthTokenDataSource.ts delete mode 100644 src/features/auth/domain/oauth-token/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts delete mode 100644 src/features/auth/domain/oauth-token/refresher/GuestOAuthTokenRefresher.ts delete mode 100644 src/features/auth/domain/session-validity/useRepositoryAccess.ts delete mode 100644 src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx diff --git a/.env.example b/.env.example index 5009f278..9e95c267 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,5 @@ SHAPE_DOCS_BASE_URL=http://localhost:3000 SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME=.shape-docs.yml -FROM_EMAIL=Shape Docs NEXT_PUBLIC_SHAPE_DOCS_TITLE=Shape Docs NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION=Documentation for Shape's APIs NEXTAUTH_URL_INTERNAL=http://localhost:3000 @@ -9,9 +8,6 @@ REDIS_URL=localhost POSTGRESQL_HOST=localhost POSTGRESQL_USER=dbuser POSTGRESQL_DB=db -SMTP_HOST=smtp.domain.com -SMTP_USER=smtpuser -SMTP_PASS=smtppass GITHUB_WEBHOOK_SECRET=preshared secret also put in app configuration in GitHub GITHUB_WEBHOK_REPOSITORY_ALLOWLIST= GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST= diff --git a/README.md b/README.md index 0f7520ca..2cb27f30 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,6 @@ The table below contains explanations for environment variables. The list is not |GITHUB_WEBHOK_REPOSITORY_ALLOWLIST|Comma-separated list of repositories from which webhook calls should be accepted. Leave empty to accept calls from all repositories.| |GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST|Comma-separated list of repositories from which webhook calls should be ignored. The list of disallowed repositories takes precedence over the list of allowed repositories.| |REDIS_URL|The URL to the Redis store.| -|SMTP_HOST|Hostname for SMTP server used for sending magic links and guest invitation.| -|SMTP_USER|Username for SMTP server used for sending magic links and guest invitation.| -|SMTP_PASS|Password for SMTP server used for sending magic links and guest invitation.| -|FROM_EMAIL|Sender email for magic links and guest invitations.| Be aware that the GitHub private key must be PKCS8. GitHub creates PKCS1 keys, so we must manually convert the key from PKCS1 to PKCS8 before base64 encoding it. This can be done as follows: @@ -61,22 +57,6 @@ Finally, open the application on https://dev.local:3000. See `create-tables.sql` -## SMTP Setup - -Magic links are sent via email. - -Configure email sending via SMTP using the `SMTP_*` environment variables. - -Follow this guide for AWS SES: [Using the Amazon SES SMTP interface to send email](https://docs.aws.amazon.com/ses/latest/dg/send-email-smtp.html). - -Example (for AWS SES): - -``` -SMTP_HOST="email-smtp.eu-central-1.amazonaws.com" -SMTP_USER="3AH7TB7N4I4JDIK2A66E" -SMTP_PASS="SX/vT1W7q9d44Oe2fmEURYIWqttgBNbhrtuMDb6CBBBg" -``` - ## 🚀 Deploying the App The app is hosted on Heroku in two different environments. @@ -91,11 +71,3 @@ Each environment is deployed by merging changes into their respective branch. He ## 📖 Getting Started with Shape Docs Details on getting started showing documentation on Shape Docs can be [found on our Confluence](https://shapedk.atlassian.net/wiki/spaces/DEVELOPERS/pages/3795615745/Shape+Docs). - -## ✨ Design - -This section documents key decisions that were made during the design of Shape Docs. - -### Multiple Authentication Providers - -Users are allowed to sign in with multiple authentication providers. They can sign in using either GitHub or a magic link. This is key for employees who are also invited as guests on the email associated with their GitHub account. When they sign in using a magic link, they will have access to the repositories that their GitHub account has access to. For users who are both invited as guests and have access through their GitHub account, the access granted by their GitHub account takes priority. diff --git a/__test__/admin/EmailGuestInviter.test.ts b/__test__/admin/EmailGuestInviter.test.ts deleted file mode 100644 index 4600eec5..00000000 --- a/__test__/admin/EmailGuestInviter.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import nodemailer from "nodemailer" -import { EmailGuestInviter } from "../../src/features/admin/data" - -jest.mock("nodemailer", () => { - return { - createTransport: jest.fn().mockReturnValue({ - sendMail: jest.fn() - }) - } -}) - -describe("EmailGuestInviter", () => { - describe("constructor", () => { - it("should create a transporter", () => { - new EmailGuestInviter({ - websiteTitle: "Demo Docs", - url: "https://example.com", - server: { - host: "smtp.example.com", - user: "user", - pass: "pass" - }, - from: "some@email.dk" - }) - - expect(nodemailer.createTransport).toHaveBeenCalledWith({ - host: "smtp.example.com", - auth: { - user: "user", - pass: "pass" - } - }) - }) - }) - - describe("inviteGuestByEmail", () => { - it("should send an invite", async () => { - const sut = new EmailGuestInviter({ - websiteTitle: "Demo Docs", - url: "https://example.com", - server: { - host: "smtp.example.com", - user: "user", - pass: "pass" - }, - from: "some@email.dk" - }) - - sut.inviteGuestByEmail("guest@email.dk") - - expect(nodemailer.createTransport().sendMail).toHaveBeenCalledWith({ - to: "guest@email.dk", - from: "some@email.dk", - subject: expect.any(String), // difficult to test the exact content - text: expect.any(String), // difficult to test the exact content - html: expect.any(String) // difficult to test the exact content - }) - }) - }) -}) diff --git a/__test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts b/__test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts deleted file mode 100644 index 55fdd886..00000000 --- a/__test__/auth/AccountProviderTypeBasedOAuthTokenRefresher.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { AccountProviderTypeBasedOAuthTokenRefresher } from "../../src/features/auth/domain" - -test("It refreshes using email refresher when account provider is \"email\"", async () => { - let didRefreshUsingEmailStrategy = false - let didRefreshUsingGitHubStrategy = false - const sut = new AccountProviderTypeBasedOAuthTokenRefresher({ - accountProviderReader: { - async getAccountProvider() { - return "email" - } - }, - strategy: { - email: { - async refreshOAuthToken(_oauthToken) { - didRefreshUsingEmailStrategy = true - return { accessToken: "foo "} - } - }, - github: { - async refreshOAuthToken(_oauthToken) { - didRefreshUsingGitHubStrategy = true - return { accessToken: "foo "} - } - } - } - }) - await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) - expect(didRefreshUsingEmailStrategy).toBeTruthy() - expect(didRefreshUsingGitHubStrategy).toBeFalsy() -}) - -test("It refreshes using GitHub refresher when account provider is \"github\"", async () => { - let didRefreshUsingEmailStrategy = false - let didRefreshUsingGitHubStrategy = false - const sut = new AccountProviderTypeBasedOAuthTokenRefresher({ - accountProviderReader: { - async getAccountProvider() { - return "github" - } - }, - strategy: { - email: { - async refreshOAuthToken(_oauthToken) { - didRefreshUsingEmailStrategy = true - return { accessToken: "foo "} - } - }, - github: { - async refreshOAuthToken(_oauthToken) { - didRefreshUsingGitHubStrategy = true - return { accessToken: "foo "} - } - } - } - }) - await sut.refreshOAuthToken({ accessToken: "foo", refreshToken: "bar" }) - expect(didRefreshUsingEmailStrategy).toBeFalsy() - expect(didRefreshUsingGitHubStrategy).toBeTruthy() -}) diff --git a/__test__/auth/FallbackOAuthTokenRepository.test.ts b/__test__/auth/FallbackOAuthTokenRepository.test.ts index d51e1458..7784172b 100644 --- a/__test__/auth/FallbackOAuthTokenRepository.test.ts +++ b/__test__/auth/FallbackOAuthTokenRepository.test.ts @@ -119,7 +119,7 @@ test("It sets value in primary repository only", async () => { async delete(_userId) {} } }) - await sut.set("1234", { accessToken: "foo" }) + await sut.set("1234", { accessToken: "foo", refreshToken: "bar" }) expect(didSetInPrimaryRepository).toBeTruthy() expect(didSetInSecondaryRepository).toBeFalsy() }) diff --git a/__test__/auth/GuestOAuthTokenDataSource.test.ts b/__test__/auth/GuestOAuthTokenDataSource.test.ts deleted file mode 100644 index eaecc57f..00000000 --- a/__test__/auth/GuestOAuthTokenDataSource.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { GuestOAuthTokenDataSource } from "../../src/features/auth/domain" - -test("It gets the user's account provider type", async () => { - let didReadAccountProvider = false - const sut = new GuestOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getAccountProvider() { - didReadAccountProvider = true - return "email" - }, - async getEmail() { - return "foo@example.com" - }, - }, - gitHubInstallationAccessTokenDataSource: { - async getAccessToken(_repositoryNames) { - return "foo" - }, - }, - guestRepository: { - async getAll() { - throw new Error("Not implemented") - }, - async findByEmail(_email) { - throw new Error("Not implemented") - }, - async create(_email, _projects) { - throw new Error("Not implemented") - }, - async removeByEmail(_email) { - throw new Error("Not implemented") - }, - async getProjectsForEmail(_email) { - return [] - } - } - }) - await sut.getOAuthToken() - expect(didReadAccountProvider).toBeTruthy() -}) - -test("It throws error if account provider type is not \"email\"", async () => { - const sut = new GuestOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getAccountProvider() { - return "github" - }, - async getEmail() { - return "foo@example.com" - }, - }, - gitHubInstallationAccessTokenDataSource: { - async getAccessToken(_repositoryNames) { - return "foo" - }, - }, - guestRepository: { - async getAll() { - throw new Error("Not implemented") - }, - async findByEmail(_email) { - throw new Error("Not implemented") - }, - async create(_email, _projects) { - throw new Error("Not implemented") - }, - async removeByEmail(_email) { - throw new Error("Not implemented") - }, - async getProjectsForEmail(_email) { - throw new Error("Not implemented") - } - } - }) - await expect(sut.getOAuthToken()).rejects.toThrow() -}) - -test("It gets the user's projects", async () => { - let didGetProjects = false - const sut = new GuestOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getAccountProvider() { - return "email" - }, - async getEmail() { - return "foo@example.com" - }, - }, - gitHubInstallationAccessTokenDataSource: { - async getAccessToken(_repositoryNames) { - return "foo" - }, - }, - guestRepository: { - async getAll() { - throw new Error("Not implemented") - }, - async findByEmail(_email) { - throw new Error("Not implemented") - }, - async create(_email, _projects) { - throw new Error("Not implemented") - }, - async removeByEmail(_email) { - throw new Error("Not implemented") - }, - async getProjectsForEmail(_email) { - didGetProjects = true - return [] - } - } - }) - await sut.getOAuthToken() - expect(didGetProjects).toBeTruthy() -}) - -test("It creates a GitHub installation access token with access to the user's projects", async () => { - let repositoriesInAccessToken: string[] = [] - const sut = new GuestOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getAccountProvider() { - return "email" - }, - async getEmail() { - return "foo@example.com" - }, - }, - gitHubInstallationAccessTokenDataSource: { - async getAccessToken(repositoryNames) { - repositoriesInAccessToken = repositoryNames - return "foo" - }, - }, - guestRepository: { - async getAll() { - throw new Error("Not implemented") - }, - async findByEmail(_email) { - throw new Error("Not implemented") - }, - async create(_email, _projects) { - throw new Error("Not implemented") - }, - async removeByEmail(_email) { - throw new Error("Not implemented") - }, - async getProjectsForEmail(_email) { - return ["foo", "bar"] - } - } - }) - await sut.getOAuthToken() - expect(repositoriesInAccessToken).toEqual(["foo", "bar"]) -}) diff --git a/__test__/auth/GuestOAuthTokenRefresher.test.ts b/__test__/auth/GuestOAuthTokenRefresher.test.ts deleted file mode 100644 index 9486a5ec..00000000 --- a/__test__/auth/GuestOAuthTokenRefresher.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { GuestOAuthTokenRefresher } from "../../src/features/auth/domain" - -test("It gets OAuth token from data source", async () => { - let didCallDataSource = false - const sut = new GuestOAuthTokenRefresher({ - dataSource: { - async getOAuthToken() { - didCallDataSource = true - return { accessToken: "foo" } - } - } - }) - await sut.refreshOAuthToken({ - accessToken: "foo" - }) - expect(didCallDataSource).toBeTruthy() -}) - -test("It throws an error when the provided OAuth token contains a refresh token", async () => { - const sut = new GuestOAuthTokenRefresher({ - dataSource: { - async getOAuthToken() { - return { accessToken: "foo" } - } - } - }) - await expect( - sut.refreshOAuthToken({ - accessToken: "foo", - refreshToken: "bar" - }) - ) - .rejects - .toThrow() -}) diff --git a/__test__/auth/LockingAccessTokenRefresher.test.ts b/__test__/auth/LockingAccessTokenRefresher.test.ts index 119e7b50..00ee777f 100644 --- a/__test__/auth/LockingAccessTokenRefresher.test.ts +++ b/__test__/auth/LockingAccessTokenRefresher.test.ts @@ -15,7 +15,10 @@ test("It acquires a lock", async () => { }, oauthTokenRefresher: { async refreshOAuthToken(_refreshToken) { - return { accessToken: "newAccessToken" } + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } } }) @@ -38,7 +41,10 @@ test("It releases the acquired lock", async () => { }, oauthTokenRefresher: { async refreshOAuthToken(_refreshToken) { - return { accessToken: "newAccessToken" } + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } } }) @@ -60,7 +66,10 @@ test("It refreshes access token", async () => { oauthTokenRefresher: { async refreshOAuthToken(_refreshToken) { didRefreshAccessToken = true - return { accessToken: "newAccessToken" } + return { + accessToken: "newAccessToken", + refreshToken: "newRefreshToken" + } } } }) diff --git a/__test__/auth/LogInHandler.test.ts b/__test__/auth/LogInHandler.test.ts index 759e5308..d1dd0c0f 100644 --- a/__test__/auth/LogInHandler.test.ts +++ b/__test__/auth/LogInHandler.test.ts @@ -2,26 +2,6 @@ import { LogInHandler } from "../../src/features/auth/domain" test("It disallows logging in when account is undefined", async () => { const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -37,209 +17,8 @@ test("It disallows logging in when account is undefined", async () => { expect(didLogin).toBeFalsy() }) -test("It disallows logging in when guest with no mail", async () => { - const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, - oauthTokenRepository: { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {}, - } - }) - const didLogin = await sut.handleLogIn({ - user: { - id: "1234" - }, - account: { - provider: "nodemailer", - providerAccountId: "foo@example.com" - } - }) - expect(didLogin).toBeFalsy() -}) - -test("It disallows logging in guest that has not been invited", async () => { - const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, - oauthTokenRepository: { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {}, - } - }) - const didLogin = await sut.handleLogIn({ - user: { - id: "1234", - email: "foo@example.com" - }, - account: { - provider: "nodemailer", - providerAccountId: "foo@example.com" - } - }) - expect(didLogin).toBeFalsy() -}) - -test("It allows logging in guest who has been invited", async () => { - const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(email: string) { - return { - status: "invited", - email: email, - projects: ["example-openapi"] - } - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, - oauthTokenRepository: { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {}, - } - }) - const didLogin = await sut.handleLogIn({ - user: { - id: "1234", - email: "foo@example.com" - }, - account: { - provider: "nodemailer", - providerAccountId: "foo@example.com" - } - }) - expect(didLogin).toBeTruthy() -}) - -test("It redirects user to OAuth error when attempting to login as a guest with an e-mail address for a user that has previously logged in using GitHub", async () => { - const sut = new LogInHandler({ - userRepository: { - async findByEmail(email) { - return { - id: 1234, - email: email, - name: "John Doe", - image: null, - accounts: [{ provider: "github" }] - } - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, - oauthTokenRepository: { - async get(_userId) { - throw new Error("Not implemented") - }, - async set(_userId, _token) {}, - async delete(_userId) {}, - } - }) - const result = await sut.handleLogIn({ - user: { - id: "1234", - email: "foo@example.com" - }, - account: { - provider: "nodemailer", - providerAccountId: "foo@example.com" - } - }) - expect(result).toBe("/api/auth/signin?error=OAuthAccountNotLinked") -}) - test("It disallows logging in using a GitHub Account without an access token", async () => { const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -263,26 +42,6 @@ test("It disallows logging in using a GitHub Account without an access token", a test("It disallows logging in using a GitHub Account without a refresh token", async () => { const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -306,26 +65,6 @@ test("It disallows logging in using a GitHub Account without a refresh token", a test("It allows logging in when using a GitHub account that has an access token and a refresh token", async () => { const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -353,26 +92,6 @@ test("It persists access token and refresh token when logging in with a GitHub a let persistedAccessToken: string | undefined let persistedRefreshToken: string | undefined const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -405,26 +124,6 @@ test("It persists access token and refresh token when logging in with a GitHub a test("It skips persisting access token and refresh token when logging in with a GitHub account with a temporary user ID", async () => { let didPersistTokens = false const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") @@ -454,26 +153,6 @@ test("It skips persisting access token and refresh token when logging in with a test("It disallows logging in when failing to persist access token and refresh token for GitHub account", async () => { let didAttemptToPersistTokens = false const sut = new LogInHandler({ - userRepository: { - async findByEmail(_email) { - return undefined - }, - }, - guestRepository: { - async getAll() { - return [] - }, - async findByEmail(_email: string) { - return undefined - }, - async create(_email: string, _projects: string[]) { - throw new Error("Not implemented") - }, - async removeByEmail(_email: string) {}, - async getProjectsForEmail(_email: string) { - return [] - } - }, oauthTokenRepository: { async get(_userId) { throw new Error("Not implemented") diff --git a/__test__/auth/OAuthTokenDataSource.test.ts b/__test__/auth/OAuthTokenDataSource.test.ts new file mode 100644 index 00000000..28390b61 --- /dev/null +++ b/__test__/auth/OAuthTokenDataSource.test.ts @@ -0,0 +1,75 @@ +import { OAuthTokenDataSource } from "../../src/features/auth/domain" + +test("It reads OAuth token for user's ID", async () => { + let readUserId: string | undefined + const sut = new OAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + } + }, + repository: { + async get(userId) { + readUserId = userId + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + await sut.getOAuthToken() + expect(readUserId).toBe("1234") +}) + +test("It reads OAuth token", async () => { + const sut = new OAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + } + }, + repository: { + async get(_userId) { + return { + accessToken: "foo", + refreshToken: "bar" + } + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + const oauthToken = await sut.getOAuthToken() + expect(oauthToken.accessToken).toBe("foo") + expect(oauthToken.refreshToken).toBe("bar") +}) + +test("It forwards errors from repository", async () => { + const sut = new OAuthTokenDataSource({ + session: { + async getIsAuthenticated() { + return true + }, + async getUserId() { + return "1234" + } + }, + repository: { + async get(_userId) { + throw new Error("OAuth token was not found") + }, + async set(_userId, _token) {}, + async delete(_userId) {} + } + }) + expect(sut.getOAuthToken()).rejects.toThrow() +}) diff --git a/__test__/auth/OAuthTokenSessionValidator.test.ts b/__test__/auth/OAuthTokenSessionValidator.test.ts index 13156405..2abadbbd 100644 --- a/__test__/auth/OAuthTokenSessionValidator.test.ts +++ b/__test__/auth/OAuthTokenSessionValidator.test.ts @@ -6,7 +6,10 @@ test("It reads the access token", async () => { oauthTokenDataSource: { async getOAuthToken() { didReadOAuthToken = true - return { accessToken: "foo" } + return { + accessToken: "foo", + refreshToken: "bar" + } } } }) @@ -18,7 +21,10 @@ test("It considers session valid when it can read an access token", async () => const sut = new OAuthTokenSessionValidator({ oauthTokenDataSource: { async getOAuthToken() { - return { accessToken: "foo" } + return { + accessToken: "foo", + refreshToken: "bar" + } } } }) diff --git a/__test__/auth/PersistingOAuthTokenDataSource.test.ts b/__test__/auth/PersistingOAuthTokenDataSource.test.ts deleted file mode 100644 index ce32965b..00000000 --- a/__test__/auth/PersistingOAuthTokenDataSource.test.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { PersistingOAuthTokenDataSource, OAuthToken } from "../../src/features/auth/domain" - -test("It skips obtaining an OAuth token from the data source if one already exists in the repository", async () => { - let didGetOAuthTokenFromDataSource = false - const sut = new PersistingOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProvider() { - return "github" - } - }, - dataSource: { - async getOAuthToken() { - didGetOAuthTokenFromDataSource = true - return { accessToken: "new-github-access-token" } - } - }, - repository: { - async get(_userId) { - return { accessToken: "foo" } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() {}, - async release() {} - } - } - } - }) - await sut.getOAuthToken() - expect(didGetOAuthTokenFromDataSource).toBeFalsy() -}) - -test("It skips obtaining an OAuth token from the data source if one has been stored while waiting to acquire the lock", async () => { - let didAcquireLock = false - let didGetOAuthTokenFromDataSource = false - const sut = new PersistingOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProvider() { - return "github" - } - }, - dataSource: { - async getOAuthToken() { - throw new Error("Not found") - } - }, - repository: { - async get(_userId) { - if (didAcquireLock) { - return { accessToken: "new-github-access-token" } - } else { - throw new Error("Not found") - } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() { - didAcquireLock = true - }, - async release() {} - } - } - } - }) - await sut.getOAuthToken() - expect(didAcquireLock).toBeTruthy() - expect(didGetOAuthTokenFromDataSource).toBeFalsy() -}) - -test("It does not acquire lock if an OAuth token already exists", async () => { - let didAcquireLock = false - const sut = new PersistingOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProvider() { - return "github" - } - }, - dataSource: { - async getOAuthToken() { - return { accessToken: "new-github-access-token" } - } - }, - repository: { - async get(_userId) { - return { accessToken: "foo" } - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() { - didAcquireLock = true - }, - async release() {} - } - } - } - }) - await sut.getOAuthToken() - expect(didAcquireLock).toBeFalsy() -}) - -test("It obtains OAuth token from data source if one does not exist in the repository", async () => { - let didGetOAuthTokenFromDataSource = false - const sut = new PersistingOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProvider() { - return "github" - } - }, - dataSource: { - async getOAuthToken() { - didGetOAuthTokenFromDataSource = true - return { accessToken: "new-github-access-token" } - } - }, - repository: { - async get(_userId) { - throw new Error("Not found") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() {}, - async release() {} - } - } - } - }) - const oauthToken = await sut.getOAuthToken() - expect(didGetOAuthTokenFromDataSource).toBeTruthy() - expect(oauthToken.accessToken).toBe("new-github-access-token") -}) - -test("It persists the OAuth token obtained from the data source", async () => { - let persistedOAuthToken: OAuthToken | undefined - const sut = new PersistingOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProvider() { - return "github" - } - }, - dataSource: { - async getOAuthToken() { - return { accessToken: "new-github-access-token" } - } - }, - repository: { - async get(_userId) { - throw new Error("Not found") - }, - async set(_userId, token) { - persistedOAuthToken = token - }, - async delete(_userId) {} - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() {}, - async release() {} - } - } - } - }) - await sut.getOAuthToken() - expect(persistedOAuthToken?.accessToken).toBe("new-github-access-token") -}) - -test("It acquires the lock", async () => { - let didAcquireLock = false - const sut = new PersistingOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProvider() { - return "github" - } - }, - dataSource: { - async getOAuthToken() { - return { accessToken: "new-github-access-token" } - } - }, - repository: { - async get(_userId) { - throw new Error("Not found") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() { - didAcquireLock = true - }, - async release() {} - } - } - } - }) - await sut.getOAuthToken() - expect(didAcquireLock).toBeTruthy() -}) - -test("It releases the lock", async () => { - let didAcquireLock = false - const sut = new PersistingOAuthTokenDataSource({ - session: { - async getIsAuthenticated() { - return true - }, - async getUserId() { - return "1234" - }, - async getEmail() { - return "john@example.com" - }, - async getAccountProvider() { - return "github" - } - }, - dataSource: { - async getOAuthToken() { - return { accessToken: "new-github-access-token" } - } - }, - repository: { - async get(_userId) { - throw new Error("Not found") - }, - async set(_userId, _token) {}, - async delete(_userId) {} - }, - mutexFactory: { - async makeMutex() { - return { - async acquire() {}, - async release() { - didAcquireLock = true - } - } - } - } - }) - await sut.getOAuthToken() - expect(didAcquireLock).toBeTruthy() -}) diff --git a/create-tables.sql b/create-tables.sql index 73fbaf14..12dffb24 100644 --- a/create-tables.sql +++ b/create-tables.sql @@ -1,12 +1,3 @@ -CREATE TABLE verification_token -( - identifier TEXT NOT NULL, - expires TIMESTAMPTZ NOT NULL, - token TEXT NOT NULL, - - PRIMARY KEY (identifier, token) -); - CREATE TABLE accounts ( id SERIAL, @@ -51,14 +42,8 @@ CREATE TABLE oauth_tokens user_id VARCHAR(255) NOT NULL, provider VARCHAR(255) NOT NULL, access_token VARCHAR(255) NOT NULL, - refresh_token VARCHAR(255) NULL, + refresh_token VARCHAR(255) NOT NULL, last_updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (user_id, provider) ); - -CREATE TABLE guests ( - id SERIAL PRIMARY KEY, - email VARCHAR(255) UNIQUE NOT NULL, - projects jsonb -); diff --git a/drop-tables.sql b/drop-tables.sql index b2b03219..fbc46117 100644 --- a/drop-tables.sql +++ b/drop-tables.sql @@ -3,4 +3,3 @@ DROP TABLE verification_token; DROP TABLE accounts; DROP TABLE sessions; DROP TABLE users; -DROP TABLE guests; diff --git a/infrastructure/aws/lib/app-stack.ts b/infrastructure/aws/lib/app-stack.ts index cf051b7e..b92a44a4 100644 --- a/infrastructure/aws/lib/app-stack.ts +++ b/infrastructure/aws/lib/app-stack.ts @@ -45,10 +45,6 @@ export class AppStack extends cdk.Stack { "AUTH0_SECRET", // TODO: Remove once we have transitioned to NEXTAUTH "NEXTAUTH_SECRET", "NEXTAUTH_URL", // TODO: Could be part of config along with certificate issuing - // SMTP for sending emails - "SMTP_HOST", - "SMTP_USER", - "SMTP_PASS", // Other "SHAPE_DOCS_BASE_URL", // TODO: Could be part of config along with certificate issuing ] diff --git a/src/app/admin/guests/page.tsx b/src/app/admin/guests/page.tsx deleted file mode 100644 index 0c559a54..00000000 --- a/src/app/admin/guests/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import AdminPage from "@/features/admin/view/AdminPage"; - -export default async function Page() { - return ( - - ) -} diff --git a/src/app/api/user/repository-access/route.ts b/src/app/api/user/repository-access/route.ts deleted file mode 100644 index 585d134b..00000000 --- a/src/app/api/user/repository-access/route.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NextResponse } from "next/server" -import { makeAPIErrorResponse, makeUnauthenticatedAPIErrorResponse } from "@/common" -import { session } from "@/composition" - -export async function GET() { - const isAuthenticated = await session.getIsAuthenticated() - if (!isAuthenticated) { - return makeUnauthenticatedAPIErrorResponse() - } - let userId: string - try { - userId = await session.getUserId() - } catch { - return makeAPIErrorResponse(401, "Unauthorized") - } - try { - const repositoryNames = [] as string[] - return NextResponse.json({repositories: repositoryNames}) - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - } catch (error: any) { - if (error.message) { - return makeAPIErrorResponse(500, error.message) - } else { - return makeAPIErrorResponse(500, "Unknown error") - } - } -} diff --git a/src/common/session/AuthjsSession.ts b/src/common/session/AuthjsSession.ts index 6136adc6..5d3ff360 100644 --- a/src/common/session/AuthjsSession.ts +++ b/src/common/session/AuthjsSession.ts @@ -1,14 +1,11 @@ import { NextAuthResult } from "next-auth" -import { IDB, UnauthorizedError } from "@/common" -import ISession, { AccountProvider } from "./ISession" +import { UnauthorizedError } from "@/common" +import ISession from "./ISession" export default class AuthjsSession implements ISession { - private readonly db: IDB private readonly auth: NextAuthResult - private accountProvider: AccountProvider | undefined - constructor(config: { db: IDB, auth: NextAuthResult }) { - this.db = config.db + constructor(config: { auth: NextAuthResult }) { this.auth = config.auth } @@ -26,35 +23,4 @@ export default class AuthjsSession implements ISession { } return session.user.id } - - async getEmail(): Promise { - const { auth } = this.auth - const session = await auth() - if (!session || !session.user || !session.user.email) { - throw new UnauthorizedError("User's email is unavailable because the user is not authenticated.") - } - return session.user.email - } - - async getAccountProvider(): Promise { - if (this.accountProvider) { - return this.accountProvider - } - const accountProvider = await this.readAccountProviderFromDB() - this.accountProvider = accountProvider - return accountProvider - } - - private async readAccountProviderFromDB(): Promise { - const userId = await this.getUserId() - const result = await this.db.query( - "SELECT true FROM accounts WHERE \"userId\" = $1 AND provider = $2 LIMIT 1", - [userId, "github"] - ) - if (result.rows.length > 0) { - return "github" - } else { - return "email" - } - } } diff --git a/src/common/session/ISession.ts b/src/common/session/ISession.ts index c4578835..ad7410e2 100644 --- a/src/common/session/ISession.ts +++ b/src/common/session/ISession.ts @@ -1,8 +1,4 @@ -export type AccountProvider = "github" | "email" - export default interface ISession { getIsAuthenticated(): Promise getUserId(): Promise - getEmail(): Promise - getAccountProvider(): Promise } diff --git a/src/common/session/index.ts b/src/common/session/index.ts index ef88e64a..5ba5fb15 100644 --- a/src/common/session/index.ts +++ b/src/common/session/index.ts @@ -1,2 +1,2 @@ export { default as AuthjsSession } from "./AuthjsSession" -export type { default as ISession, AccountProvider } from "./ISession" +export type { default as ISession } from "./ISession" diff --git a/src/composition.ts b/src/composition.ts index 7152f5a1..45e2c387 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -1,7 +1,6 @@ import { Pool } from "pg" import NextAuth from "next-auth" import GithubProvider from "next-auth/providers/github" -import EmailProvider from "next-auth/providers/nodemailer" import PostgresAdapter from "@auth/pg-adapter" import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/key-value-store/RedisKeyValueStore" @@ -23,40 +22,23 @@ import { ProjectRepository } from "@/features/projects/domain" import { - GitHubOAuthTokenRefresher, - GitHubInstallationAccessTokenDataSource + GitHubOAuthTokenRefresher } from "@/features/auth/data" import { - AccountProviderTypeBasedOAuthTokenRefresher, AuthjsAccountsOAuthTokenRepository, CompositeLogOutHandler, ErrorIgnoringLogOutHandler, FallbackOAuthTokenRepository, - GuestOAuthTokenDataSource, - GuestOAuthTokenRefresher, LockingOAuthTokenRefresher, LogInHandler, - PersistingOAuthTokenDataSource, + OAuthTokenDataSource, PersistingOAuthTokenRefresher, OAuthTokenRepository, OAuthTokenSessionValidator, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" -import { - DbGuestRepository, - DbUserRepository, - EmailGuestInviter, - MagicLinkEmailSender -} from "./features/admin/data" -import { - IGuestInviter, - IUserRepository -} from "./features/admin/domain" -import DummyGuestRepository from "./features/admin/data/DummyGuestRepository" const { - NEXT_PUBLIC_SHAPE_DOCS_TITLE, - SHAPE_DOCS_BASE_URL, SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, GITHUB_APP_ID, GITHUB_CLIENT_ID, @@ -66,11 +48,7 @@ const { POSTGRESQL_HOST, POSTGRESQL_USER, POSTGRESQL_PASSWORD, - POSTGRESQL_DB, - SMTP_HOST, - SMTP_USER, - SMTP_PASS, - FROM_EMAIL, + POSTGRESQL_DB } = process.env const gitHubAppCredentials = { @@ -99,15 +77,7 @@ const oauthTokenRepository = new FallbackOAuthTokenRepository({ secondaryRepository: new AuthjsAccountsOAuthTokenRepository({ db, provider: "github" }) }) -const userRepository: IUserRepository = new DbUserRepository({ db }) - -export const guestRepository = (process.env.IS_BUILD_PROCESS !== undefined) ? new DummyGuestRepository : new DbGuestRepository({ db }) - -const logInHandler = new LogInHandler({ userRepository, guestRepository, oauthTokenRepository }) - -if (!FROM_EMAIL) { - throw new Error("FROM_EMAIL environment variable must be set to an e-mail verified in AWS SES") -} +const logInHandler = new LogInHandler({ oauthTokenRepository }) export const auth = NextAuth({ adapter: PostgresAdapter(pool), @@ -126,30 +96,7 @@ export const auth = NextAuth({ scope: "repo" } } - }), - EmailProvider({ - server: { - host: SMTP_HOST, - auth: { - user: SMTP_USER, - pass: SMTP_PASS, - } - }, - from: FROM_EMAIL, - name: "email", - sendVerificationRequest: async params => { - const sender = new MagicLinkEmailSender({ - server: { - host: SMTP_HOST, - user: SMTP_USER, - pass: SMTP_PASS, - }, - websiteTitle: NEXT_PUBLIC_SHAPE_DOCS_TITLE, - from: FROM_EMAIL - }) - await sender.sendMagicLink(params) - } - }), + }) ], session: { strategy: "database" @@ -165,25 +112,11 @@ export const auth = NextAuth({ } }) -export const session: ISession = new AuthjsSession({ db, auth }) +export const session: ISession = new AuthjsSession({ auth }) -const guestOAuthTokenDataSource = new GuestOAuthTokenDataSource({ +const oauthTokenDataSource = new OAuthTokenDataSource({ session, - guestRepository, - gitHubInstallationAccessTokenDataSource: new GitHubInstallationAccessTokenDataSource( - gitHubAppCredentials - ) -}) - -const oauthTokenDataSource = new PersistingOAuthTokenDataSource({ - session, - mutexFactory: new SessionMutexFactory({ - baseKey: "mutexPersistingOAuthTokenDataSource", - mutexFactory: new RedisKeyedMutexFactory(REDIS_URL), - userIdReader: session - }), - repository: oauthTokenRepository, - dataSource: guestOAuthTokenDataSource + repository: oauthTokenRepository }) const oauthTokenRefresher = new LockingOAuthTokenRefresher({ @@ -195,15 +128,7 @@ const oauthTokenRefresher = new LockingOAuthTokenRefresher({ oauthTokenRefresher: new PersistingOAuthTokenRefresher({ userIdReader: session, oauthTokenRepository, - oauthTokenRefresher: new AccountProviderTypeBasedOAuthTokenRefresher({ - accountProviderReader: session, - strategy: { - github: new GitHubOAuthTokenRefresher(gitHubAppCredentials), - email: new GuestOAuthTokenRefresher({ - dataSource: guestOAuthTokenDataSource - }) - } - }) + oauthTokenRefresher: new GitHubOAuthTokenRefresher(gitHubAppCredentials) }) }) @@ -215,7 +140,7 @@ export const gitHubClient = new GitHubClient({ export const userGitHubClient = new OAuthTokenRefreshingGitHubClient({ gitHubClient, oauthTokenDataSource, - oauthTokenRefresher, + oauthTokenRefresher }) export const blockingSessionValidator = new OAuthTokenSessionValidator({ @@ -248,14 +173,3 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( new UserDataCleanUpLogOutHandler(session, projectUserDataRepository) ]) ) - -export const guestInviter: IGuestInviter = new EmailGuestInviter({ - websiteTitle: NEXT_PUBLIC_SHAPE_DOCS_TITLE, - url: SHAPE_DOCS_BASE_URL, - server: { - host: SMTP_HOST, - user: SMTP_USER, - pass: SMTP_PASS, - }, - from: FROM_EMAIL -}) diff --git a/src/features/admin/data/DbGuestRepository.ts b/src/features/admin/data/DbGuestRepository.ts deleted file mode 100644 index 80ede323..00000000 --- a/src/features/admin/data/DbGuestRepository.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { IDB } from "@/common" -import { Guest, IGuestRepository } from "../domain" - -export default class DbGuestRepository implements IGuestRepository { - private readonly db: IDB - - constructor(config: { db: IDB }) { - this.db = config.db - } - - /** - * Uses users table owned by Authjs to determine if a guest is active = there's a user with the same email - * - * @returns all guests including their status - */ - async getAll(): Promise { - const sql = ` - SELECT - g.*, - CASE - WHEN u.email IS NULL THEN 'invited' - ELSE 'active' - END as status - FROM guests g - LEFT JOIN users u ON g.email = u.email - ORDER BY g.email ASC - ` - const result = await this.db.query(sql) - return result.rows - } - - /** - * Find a guest by email - * - * @param email Email of the guest - * @returns The guest or undefined if not found - */ - async findByEmail(email: string): Promise { - const sql = "SELECT * FROM guests WHERE email = $1" - const result = await this.db.query(sql, [email]) - return result.rows[0] - } - - /** - * Create a guest - * - * @param email Email of the guest - * @param projects List of projects the guest should be associated with - * @returns Newly created guest - */ - async create(email: string, projects: string[]): Promise { - const sql = "INSERT INTO guests (email, projects) VALUES ($1, $2)" - await this.db.query(sql, [email, JSON.stringify(projects)]) - - const insertedGuest = await this.findByEmail(email) - if (!insertedGuest) { - throw new Error("Guest not found after insert") - } - - return insertedGuest - } - - /** - * Remove a guest by email - * - * @param email Email of the guest - */ - async removeByEmail(email: string): Promise { - const sql = "DELETE FROM guests WHERE email = $1" - await this.db.query(sql, [email]) - } - - /** - * Get projects for a guest - * - * @param email Email of the guest - * @returns The projects the guest is associated with. If the guest is not found, an empty array is returned. - */ - async getProjectsForEmail(email: string): Promise { - const sql = "SELECT projects FROM guests WHERE email = $1" - const result = await this.db.query(sql, [email]) - return result.rows[0] ? result.rows[0].projects : [] - } -} diff --git a/src/features/admin/data/DbUserRepository.ts b/src/features/admin/data/DbUserRepository.ts deleted file mode 100644 index 9f3e760c..00000000 --- a/src/features/admin/data/DbUserRepository.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { IDB } from "@/common" -import { User, IUserRepository } from "../domain" - -type UserRow = { - readonly id: number - readonly name: string | null - readonly email: string | null - readonly image: string | null - readonly account_provider: string | null -} - -export default class DbUserRepository implements IUserRepository { - private readonly db: IDB - - constructor(config: { db: IDB }) { - this.db = config.db - } - - async findByEmail(email: string): Promise { - const sql = ` - SELECT u.id, u.name, u.email, u.image, a.provider as account_provider - FROM users u - LEFT JOIN accounts a ON u.id = a."userId" - WHERE u.email = $1 - ` - const result = await this.db.query(sql, [email]) - const users = this.groupUserRows(result.rows) - if (users.length == 0) { - return undefined - } - return users[0] - } - - private groupUserRows(rows: UserRow[]): User[] { - const userMap = new Map() - for (const row of rows) { - let user = userMap.get(row.id) - if (!user) { - user = { - id: row.id, - name: row.name, - email: row.email, - image: row.image, - accounts: [] - } - userMap.set(row.id, user) - } - if (row.account_provider) { - user.accounts.push({ provider: row.account_provider }) - } - } - return Array.from(userMap.values()) - } -} \ No newline at end of file diff --git a/src/features/admin/data/DummyGuestRepository.ts b/src/features/admin/data/DummyGuestRepository.ts deleted file mode 100644 index c7c2883c..00000000 --- a/src/features/admin/data/DummyGuestRepository.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Guest, IGuestRepository } from "../domain" - -/** - * This is a dummy implementation of the guest repository that returns stati data. - * - * It is used in the NextJS build process where pages are prerendered and where we do not - * have access to a database. - */ -export default class DummyGuestRepository implements IGuestRepository { - constructor() { - console.warn("DummyGuestRepository initialized. No data will be stored.") - } - - getAll(): Promise { - return Promise.resolve([]) - } - findByEmail(email: string): Promise { - throw new Error("Method not implemented."); - } - create(email: string, projects: string[]): Promise { - throw new Error("Method not implemented."); - } - removeByEmail(email: string): Promise { - throw new Error("Method not implemented."); - } - getProjectsForEmail(email: string): Promise { - throw new Error("Method not implemented."); - } -} diff --git a/src/features/admin/data/EmailGuestInviter.ts b/src/features/admin/data/EmailGuestInviter.ts deleted file mode 100644 index d7ab579f..00000000 --- a/src/features/admin/data/EmailGuestInviter.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Transporter, createTransport } from "nodemailer" -import { IGuestInviter } from "../domain" -import base64EncodedLogo from "@/common/images/base64EncodedLogo" - -export default class EmailGuestInviter implements IGuestInviter { - private readonly websiteTitle: string - private readonly url: string - private readonly transport: Transporter - private readonly from: string - - constructor(config: { - websiteTitle: string, - url: string, - server: { - host: string, - user: string, - pass: string - }, - from: string - }) { - this.websiteTitle = config.websiteTitle - this.url = config.url - this.transport = createTransport({ - host: config.server.host, - auth: { - user: config.server.user, - pass: config.server.pass - } - }) - this.from = config.from - } - - async inviteGuestByEmail(email: string): Promise { - await this.transport.sendMail({ - to: email, - from: this.from, - subject: `You have been invited ${this.websiteTitle}`, - text: this.makeText(), - html: this.makeHtml() - }) - } - - private makeText() { - return `You have been invited to ${this.websiteTitle}\n\nSign in with your e-mail on ${this.url}\n\n` - } - - private makeHtml() { - const displayURL = this.url.replace(/https?:\/\//gi, "") - const color = { - background: "#f9f9f9", - text: "#000", - mainBackground: "#fff", - buttonBackground: "#0D6DDB", - buttonText: "#fff" - } - return ` - - - - - - - - - - - - - - - - - - - - -
- ${this.websiteTitle} logo -
- You have been invited to ${this.websiteTitle} -
- ${this.websiteTitle} uses magic links for signing in,
so you don't need to remember a password.
-
- Visit ${displayURL} and enter your email to sign in. -
- - - - -
Go to ${this.websiteTitle}
-
- If you did not request this email, you can safely ignore it. -
- - ` - } -} diff --git a/src/features/admin/data/MagicLinkEmailSender.ts b/src/features/admin/data/MagicLinkEmailSender.ts deleted file mode 100644 index 9e8d576c..00000000 --- a/src/features/admin/data/MagicLinkEmailSender.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Transporter, createTransport } from "nodemailer" -import base64EncodedLogo from "@/common/images/base64EncodedLogo" - -export default class MagicLinkEmailSender { - private readonly transport: Transporter - private readonly websiteTitle: string - private readonly from: string - - constructor(config: { - server: { - host: string, - user: string, - pass: string - }, - websiteTitle: string, - from: string - }) { - this.transport = createTransport({ - host: config.server.host, - auth: { - user: config.server.user, - pass: config.server.pass - } - }) - this.websiteTitle = config.websiteTitle - this.from = config.from - } - - async sendMagicLink({ - identifier, - expires, - url - }: { - identifier: string - expires: Date - url: string - }): Promise { - const websiteTitle = this.websiteTitle - const result = await this.transport.sendMail({ - to: identifier, - from: this.from, - subject: `Sign in to ${websiteTitle}`, - text: this.makeText({ url }), - html: this.makeHtml({ url, expires }), - }) - const failed = result.rejected.concat(result.pending).filter(Boolean) - if (failed.length) { - throw new Error(`Email (${failed.join(", ")}) could not be sent`) - } - } - - private makeText({ url }: { url: string }) { - return `Sign in to ${this.websiteTitle}.\n${url}\n\n` - } - - private makeHtml({ - url, - expires - }: { - url: string, - expires: Date - }) { - const color = { - background: "#f9f9f9", - text: "#000", - mainBackground: "#fff", - buttonBackground: "#0D6DDB", - buttonText: "#fff" - } - return ` - - - - - - - - - - - - - - - - - -
- ${this.websiteTitle} logo -
- Sign in to ${this.websiteTitle} -
- The link can only be used once
and expires on ${formatDate(expires)}. -
- - - - -
Sign - in
-
- If you did not request this email, you can safely ignore it. -
- - ` -} -} - -function formatDate(date: Date) { - const day = date.getDate() - const monthNames = [ - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December" - ] - const month = monthNames[date.getMonth()] - const year = date.getFullYear() - const hours = date.getHours().toString().padStart(2, '0') - const minutes = date.getMinutes().toString().padStart(2, '0') - return `${month} ${day}, ${year}, at ${hours}:${minutes}` -} diff --git a/src/features/admin/data/index.ts b/src/features/admin/data/index.ts deleted file mode 100644 index 3a918381..00000000 --- a/src/features/admin/data/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { default as DbGuestRepository } from "./DbGuestRepository" -export { default as DbUserRepository } from "./DbUserRepository" -export { default as EmailGuestInviter } from "./EmailGuestInviter" -export { default as MagicLinkEmailSender } from "./MagicLinkEmailSender" \ No newline at end of file diff --git a/src/features/admin/domain/Account.ts b/src/features/admin/domain/Account.ts deleted file mode 100644 index 16e6f547..00000000 --- a/src/features/admin/domain/Account.ts +++ /dev/null @@ -1,5 +0,0 @@ -type Account = { - readonly provider: string -} - -export default Account diff --git a/src/features/admin/domain/Guest.ts b/src/features/admin/domain/Guest.ts deleted file mode 100644 index bb953c03..00000000 --- a/src/features/admin/domain/Guest.ts +++ /dev/null @@ -1,7 +0,0 @@ -type Guest = { - email: string; - status: "active" | "invited"; - projects: string[]; -}; - -export default Guest diff --git a/src/features/admin/domain/IGuestInviter.ts b/src/features/admin/domain/IGuestInviter.ts deleted file mode 100644 index 2b4fd734..00000000 --- a/src/features/admin/domain/IGuestInviter.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IGuestInviter { - inviteGuestByEmail: (email: string) => Promise -} diff --git a/src/features/admin/domain/IGuestRepository.ts b/src/features/admin/domain/IGuestRepository.ts deleted file mode 100644 index 2326779f..00000000 --- a/src/features/admin/domain/IGuestRepository.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Guest from "./Guest" - -export default interface IGuestRepository { - getAll(): Promise - findByEmail(email: string): Promise - create(email: string, projects: string[]): Promise - removeByEmail(email: string): Promise - getProjectsForEmail(email: string): Promise -} diff --git a/src/features/admin/domain/IUserRepository.ts b/src/features/admin/domain/IUserRepository.ts deleted file mode 100644 index e59b5db4..00000000 --- a/src/features/admin/domain/IUserRepository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import User from "./User" - -export default interface IUserRepository { - findByEmail(email: string): Promise -} diff --git a/src/features/admin/domain/User.ts b/src/features/admin/domain/User.ts deleted file mode 100644 index 92145db8..00000000 --- a/src/features/admin/domain/User.ts +++ /dev/null @@ -1,11 +0,0 @@ -import Account from "./Account" - -type User = { - readonly id: number - readonly name: string | null - readonly email: string | null - readonly image: string | null - readonly accounts: Account[] -} - -export default User diff --git a/src/features/admin/domain/index.ts b/src/features/admin/domain/index.ts deleted file mode 100644 index cad74df7..00000000 --- a/src/features/admin/domain/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type { default as Account } from "./Account" -export type { default as Guest } from "./Guest" -export type { default as IGuestRepository } from "./IGuestRepository" -export type { default as IGuestInviter } from "./IGuestInviter" -export type { default as IUserRepository } from "./IUserRepository" -export type { default as User } from "./User" diff --git a/src/features/admin/view/Actions.tsx b/src/features/admin/view/Actions.tsx deleted file mode 100644 index eac20093..00000000 --- a/src/features/admin/view/Actions.tsx +++ /dev/null @@ -1,61 +0,0 @@ -'use server' - -import { guestInviter, guestRepository } from "@/composition" -import { revalidatePath } from "next/cache" -import { z } from 'zod' - -const sendInviteSchema = z.object({ - email: z.string().email(), - projects: z.string().min(1).transform(v => v.trim().split(",")) -}) - -export interface SendInviteResult { - error?: string, - success: boolean, -} - -/** -* Server action to send an invite -*/ -export const sendInvite = async (prevState: any, formData: FormData): Promise => { - const validatedFields = sendInviteSchema.safeParse({ - email: formData.get('email'), - projects: formData.get('projects'), - }) - - if (!validatedFields.success) { - return { - error: validatedFields.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', '), - success: false, - } - } - - try { - await guestRepository.create(validatedFields.data.email, validatedFields.data.projects) - await guestInviter.inviteGuestByEmail(validatedFields.data.email) - - revalidatePath('/admin/guests') - - return { - success: true, - } - } catch (error: unknown) { - return { - success: false, - error: error instanceof Error ? error.message : 'An error occurred', - } - } -} - -/** - * Server action to remove a guest - */ -export const removeGuest = async (formData: FormData): Promise => { - 'use server' - - const email = formData.get('email') as string - - await guestRepository.removeByEmail(email) - - revalidatePath('/admin/guests') -} diff --git a/src/features/admin/view/AdminPage.tsx b/src/features/admin/view/AdminPage.tsx deleted file mode 100644 index f2faa6ef..00000000 --- a/src/features/admin/view/AdminPage.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { guestRepository } from "@/composition" -import { Box, ButtonGroup, Chip, Paper, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material" -import Table from "@mui/material/Table" -import { InviteGuestForm } from "./InviteGuestForm" -import { RemoveGuestForm } from "./RemoveGuestForm" -import { EditGuestForm } from "./EditGuestForm" - -const AdminPage = async () => { - const guests = await guestRepository.getAll() - - return ( - - Admin - - - Invite Guest - - Invite a guest to access the platform. An invitation email will be sent to the provided email address. - - - - Guests - - - Manage guests who have access to the platform. - - - - - - - Email - Status - Projects - Actions - - - - {guests.map((row, index) => ( - - {row.email} - - {row.projects.join(", ")} - - - - - - - - ))} - -
-
-
-
- ) -} - -export default AdminPage diff --git a/src/features/admin/view/EditGuestForm.tsx b/src/features/admin/view/EditGuestForm.tsx deleted file mode 100644 index 33070104..00000000 --- a/src/features/admin/view/EditGuestForm.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client' - -import { Button } from "@mui/material"; - -export const EditGuestForm = () => { - return ( -

- - - ) -} diff --git a/src/features/admin/view/InviteGuestForm.tsx b/src/features/admin/view/InviteGuestForm.tsx deleted file mode 100644 index ab67a972..00000000 --- a/src/features/admin/view/InviteGuestForm.tsx +++ /dev/null @@ -1,28 +0,0 @@ -'use client' - -import { useFormState } from 'react-dom' -import { SendInviteResult, sendInvite } from './Actions' -import { Alert, Button, FormGroup, TextField } from '@mui/material' - -const initialState: SendInviteResult = { - success: false, -} - -export const InviteGuestForm = () => { - const [state, formAction] = useFormState(sendInvite, initialState) - - return ( -
- {state.error && Failed to invite guest: {state.error}} - {state.success && - - Guest was invited successfully 🥳 - } - - - - - -
- ) -} diff --git a/src/features/admin/view/RemoveGuestForm.tsx b/src/features/admin/view/RemoveGuestForm.tsx deleted file mode 100644 index e27499a3..00000000 --- a/src/features/admin/view/RemoveGuestForm.tsx +++ /dev/null @@ -1,19 +0,0 @@ -'use client' - -import { Button } from "@mui/material"; -import { removeGuest } from "./Actions"; - -export const RemoveGuestForm = ({ email }: { email: string }) => { - const onClick = (e: React.MouseEvent) => { - e.preventDefault() - if (confirm(`Please confirm that you want to remove ${email}`) === false) return - e.currentTarget.form?.submit() - } - - return ( -
- - -
- ) -} \ No newline at end of file diff --git a/src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts b/src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts deleted file mode 100644 index 49e922f5..00000000 --- a/src/features/auth/data/GitHubInstallationAccessTokenDataSource.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Octokit } from "octokit" -import { createAppAuth } from "@octokit/auth-app" - -type GitHubInstallationAccessTokenDataSourceConfig = { - readonly appId: string - readonly clientId: string - readonly clientSecret: string - readonly privateKey: string - readonly organization: string -} - -export default class GitHubInstallationAccessTokenDataSource { - private readonly config: GitHubInstallationAccessTokenDataSourceConfig - - constructor(config: GitHubInstallationAccessTokenDataSourceConfig) { - this.config = config - } - - async getAccessToken(repositoryNames: string[]): Promise { - if (repositoryNames.length == 0) { - throw new Error("Must provide at least one repository name when creating a GitHub installation access token.") - } - const auth = createAppAuth({ - appId: this.config.appId, - clientId: this.config.clientId, - clientSecret: this.config.clientSecret, - privateKey: this.config.privateKey - }) - const appAuth = await auth({ type: "app" }) - const octokit = new Octokit({ auth: appAuth.token }) - const response = await octokit.rest.apps.getOrgInstallation({ - org: this.config.organization - }) - const installation = response.data - try { - const installationAuth = await auth({ - type: "installation", - installationId: installation.id, - repositoryNames: repositoryNames - }) - return installationAuth.token - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - } catch (error: any) { - if (error.status && error.status == 422) { - // One or more of the repositories do not exist. We log the error - // and create an access token with access to know repositories. - console.error("Cannot log in user as one or more repositories do not exist: " + repositoryNames.join(", ")) - console.error(error) - } - throw error - } - } -} diff --git a/src/features/auth/data/index.ts b/src/features/auth/data/index.ts index f8808118..a1b0236b 100644 --- a/src/features/auth/data/index.ts +++ b/src/features/auth/data/index.ts @@ -1,2 +1 @@ -export { default as GitHubInstallationAccessTokenDataSource } from "./GitHubInstallationAccessTokenDataSource" export { default as GitHubOAuthTokenRefresher } from "./GitHubOAuthTokenRefresher" diff --git a/src/features/auth/domain/log-in/LogInHandler.ts b/src/features/auth/domain/log-in/LogInHandler.ts index e179c7c6..04023277 100644 --- a/src/features/auth/domain/log-in/LogInHandler.ts +++ b/src/features/auth/domain/log-in/LogInHandler.ts @@ -1,20 +1,11 @@ import { ILogInHandler, IUser, IAccount } from "." -import { IGuestRepository, IUserRepository } from "@/features/admin/domain" import { IOAuthTokenRepository } from "../oauth-token" import saneParseInt from "@/common/utils/saneParseInt" export default class LogInHandler implements ILogInHandler { - private readonly userRepository: IUserRepository - private readonly guestRepository: IGuestRepository private readonly oauthTokenRepository: IOAuthTokenRepository - constructor(config: { - userRepository: IUserRepository, - guestRepository: IGuestRepository, - oauthTokenRepository: IOAuthTokenRepository - }) { - this.userRepository = config.userRepository - this.guestRepository = config.guestRepository + constructor(config: { oauthTokenRepository: IOAuthTokenRepository }) { this.oauthTokenRepository = config.oauthTokenRepository } @@ -24,8 +15,6 @@ export default class LogInHandler implements ILogInHandler { } if (account.provider === "github") { return await this.handleLogInForGitHubUser({ user, account }) - } else if (account.provider === "nodemailer") { - return await this.handleLogInForGuestUser(user) } else { console.error("Unhandled account provider: " + account.provider) return false @@ -60,22 +49,4 @@ export default class LogInHandler implements ILogInHandler { return false } } - - private async handleLogInForGuestUser(user: IUser) { - if (!user.email) { - return false - } - const existingUser = await this.userRepository.findByEmail(user.email) - if (existingUser && existingUser.accounts.length > 0) { - // The user is already authenticated with an identity provider, - // so we'll ask them to use that instead. - return "/api/auth/signin?error=OAuthAccountNotLinked" - } - const guest = await this.guestRepository.findByEmail(user.email) - if (!guest) { - // The e-mail address has not been invited as a guest. - return false - } - return true - } } diff --git a/src/features/auth/domain/oauth-token/OAuthToken.ts b/src/features/auth/domain/oauth-token/OAuthToken.ts index ae776734..a7af5025 100644 --- a/src/features/auth/domain/oauth-token/OAuthToken.ts +++ b/src/features/auth/domain/oauth-token/OAuthToken.ts @@ -2,7 +2,7 @@ import { z } from "zod" export const OAuthTokenSchema = z.object({ accessToken: z.string(), - refreshToken: z.string().optional() + refreshToken: z.string() }) type OAuthToken = z.infer diff --git a/src/features/auth/domain/oauth-token/data-source/GuestOAuthTokenDataSource.ts b/src/features/auth/domain/oauth-token/data-source/GuestOAuthTokenDataSource.ts deleted file mode 100644 index b6b77262..00000000 --- a/src/features/auth/domain/oauth-token/data-source/GuestOAuthTokenDataSource.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { ISession } from "@/common" -import { IGuestRepository } from "@/features/admin/domain" -import { OAuthToken } from ".." -import IOAuthTokenDataSource from "./IOAuthTokenDataSource" - -interface IGitHubInstallationAccessTokenDataSource { - getAccessToken(repositoryNames: string[]): Promise -} - -export default class GuestOAuthTokenDataSource implements IOAuthTokenDataSource { - private readonly session: ISession - private readonly guestRepository: IGuestRepository - private readonly gitHubInstallationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource - - constructor(config: { - session: ISession, - guestRepository: IGuestRepository, - gitHubInstallationAccessTokenDataSource: IGitHubInstallationAccessTokenDataSource - }) { - this.session = config.session - this.guestRepository = config.guestRepository - this.gitHubInstallationAccessTokenDataSource = config.gitHubInstallationAccessTokenDataSource - } - - async getOAuthToken(): Promise { - const accountProvider = await this.session.getAccountProvider() - if (accountProvider !== "email") { - throw new Error(`Cannot get access token for accounts with the "${accountProvider}" provider.`) - } - const email = await this.session.getEmail() - const repositoryNames = await this.guestRepository.getProjectsForEmail(email) - const accessToken = await this.gitHubInstallationAccessTokenDataSource.getAccessToken(repositoryNames) - return { accessToken } - } -} diff --git a/src/features/auth/domain/oauth-token/data-source/OAuthTokenDataSource.ts b/src/features/auth/domain/oauth-token/data-source/OAuthTokenDataSource.ts new file mode 100644 index 00000000..429bd5fb --- /dev/null +++ b/src/features/auth/domain/oauth-token/data-source/OAuthTokenDataSource.ts @@ -0,0 +1,18 @@ +import { ISession } from "@/common" +import { OAuthToken, IOAuthTokenRepository } from ".." +import IOAuthTokenDataSource from "./IOAuthTokenDataSource" + +export default class PersistingOAuthTokenDataSource implements IOAuthTokenDataSource { + private readonly session: ISession + private readonly repository: IOAuthTokenRepository + + constructor(config: { session: ISession, repository: IOAuthTokenRepository }) { + this.session = config.session + this.repository = config.repository + } + + async getOAuthToken(): Promise { + const userId = await this.session.getUserId() + return await this.repository.get(userId) + } +} diff --git a/src/features/auth/domain/oauth-token/data-source/PersistingOAuthTokenDataSource.ts b/src/features/auth/domain/oauth-token/data-source/PersistingOAuthTokenDataSource.ts deleted file mode 100644 index 36b3d878..00000000 --- a/src/features/auth/domain/oauth-token/data-source/PersistingOAuthTokenDataSource.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ISession, IMutexFactory, withMutex } from "@/common" -import { OAuthToken, IOAuthTokenRepository } from ".." -import IOAuthTokenDataSource from "./IOAuthTokenDataSource" - -export default class PersistingOAuthTokenDataSource implements IOAuthTokenDataSource { - private readonly session: ISession - private readonly mutexFactory: IMutexFactory - private readonly repository: IOAuthTokenRepository - private readonly dataSource: IOAuthTokenDataSource - - constructor(config: { - session: ISession, - mutexFactory: IMutexFactory, - repository: IOAuthTokenRepository, - dataSource: IOAuthTokenDataSource - }) { - this.session = config.session - this.mutexFactory = config.mutexFactory - this.repository = config.repository - this.dataSource = config.dataSource - } - - async getOAuthToken(): Promise { - // Immediately try to get the OAuth token to avoid a mutex. - const existingOAuthToken = await this.getExistingOAuthToken() - if (existingOAuthToken) { - return existingOAuthToken - } - const mutex = await this.mutexFactory.makeMutex() - return await withMutex(mutex, async () => { - // Make a second attempt to get the OAuth token as it might have been created in the mean time. - const existingOAuthToken = await this.getExistingOAuthToken() - if (existingOAuthToken) { - return existingOAuthToken - } else { - return await this.getNewOAuthToken() - } - }) - } - - private async getExistingOAuthToken(): Promise { - const userId = await this.session.getUserId() - try { - return await this.repository.get(userId) - } catch {} - return null - } - - private async getNewOAuthToken(): Promise { - const userId = await this.session.getUserId() - const oauthToken = await this.dataSource.getOAuthToken() - await this.repository.set(userId, oauthToken) - return oauthToken - } -} diff --git a/src/features/auth/domain/oauth-token/index.ts b/src/features/auth/domain/oauth-token/index.ts index d1c32042..0717f64c 100644 --- a/src/features/auth/domain/oauth-token/index.ts +++ b/src/features/auth/domain/oauth-token/index.ts @@ -1,9 +1,6 @@ export type { default as OAuthToken } from "./OAuthToken" -export { default as GuestOAuthTokenDataSource } from "./data-source/GuestOAuthTokenDataSource" export type { default as IOAuthTokenDataSource } from "./data-source/IOAuthTokenDataSource" -export { default as PersistingOAuthTokenDataSource } from "./data-source/PersistingOAuthTokenDataSource" -export { default as AccountProviderTypeBasedOAuthTokenRefresher } from "./refresher/AccountProviderTypeBasedOAuthTokenRefresher" -export { default as GuestOAuthTokenRefresher } from "./refresher/GuestOAuthTokenRefresher" +export { default as OAuthTokenDataSource } from "./data-source/OAuthTokenDataSource" export type { default as IOAuthTokenRefresher } from "./refresher/IOAuthTokenRefresher" export { default as LockingOAuthTokenRefresher } from "./refresher/LockingOAuthTokenRefresher" export { default as PersistingOAuthTokenRefresher } from "./refresher/PersistingOAuthTokenRefresher" diff --git a/src/features/auth/domain/oauth-token/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts deleted file mode 100644 index b3ee5b75..00000000 --- a/src/features/auth/domain/oauth-token/refresher/AccountProviderTypeBasedOAuthTokenRefresher.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { AccountProvider } from "@/common" -import { OAuthToken } from ".." -import IOAuthTokenRefresher from "./IOAuthTokenRefresher" - -interface IAccountProviderReader { - getAccountProvider(): Promise -} - -type Strategy = { [key in AccountProvider]: IOAuthTokenRefresher } - -export default class AccountProviderStrategyOAuthTokenRefresher implements IOAuthTokenRefresher { - private readonly accountProviderReader: IAccountProviderReader - private readonly strategy: Strategy - - constructor(config: { - accountProviderReader: IAccountProviderReader, - strategy: Strategy - }) { - this.accountProviderReader = config.accountProviderReader - this.strategy = config.strategy - } - - async refreshOAuthToken(oldOAuthToken: OAuthToken): Promise { - const accountProvider = await this.accountProviderReader.getAccountProvider() - return await this.strategy[accountProvider].refreshOAuthToken(oldOAuthToken) - } -} diff --git a/src/features/auth/domain/oauth-token/refresher/GuestOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/GuestOAuthTokenRefresher.ts deleted file mode 100644 index e3bed8bb..00000000 --- a/src/features/auth/domain/oauth-token/refresher/GuestOAuthTokenRefresher.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { OAuthToken, IOAuthTokenDataSource } from ".." -import IOAuthTokenRefresher from "./IOAuthTokenRefresher" - -export default class GuestOAuthTokenRefresher implements IOAuthTokenRefresher { - private readonly dataSource: IOAuthTokenDataSource - - constructor(config: { dataSource: IOAuthTokenDataSource }) { - this.dataSource = config.dataSource - } - - async refreshOAuthToken(oldOAuthToken: OAuthToken): Promise { - if (oldOAuthToken.refreshToken) { - throw new Error("The refresher should not be used when a refresh token is available") - } - return await this.dataSource.getOAuthToken() - } -} diff --git a/src/features/auth/domain/oauth-token/refresher/LockingOAuthTokenRefresher.ts b/src/features/auth/domain/oauth-token/refresher/LockingOAuthTokenRefresher.ts index 1e691fab..31323f6f 100644 --- a/src/features/auth/domain/oauth-token/refresher/LockingOAuthTokenRefresher.ts +++ b/src/features/auth/domain/oauth-token/refresher/LockingOAuthTokenRefresher.ts @@ -5,12 +5,10 @@ export default class LockingOAuthTokenRefresher implements IOAuthTokenRefresher private readonly mutexFactory: IMutexFactory private readonly oauthTokenRefresher: IOAuthTokenRefresher - constructor( - config: { - mutexFactory: IMutexFactory - oauthTokenRefresher: IOAuthTokenRefresher - } - ) { + constructor(config: { + mutexFactory: IMutexFactory + oauthTokenRefresher: IOAuthTokenRefresher + }) { this.mutexFactory = config.mutexFactory this.oauthTokenRefresher = config.oauthTokenRefresher } diff --git a/src/features/auth/domain/session-validity/SessionValidity.ts b/src/features/auth/domain/session-validity/SessionValidity.ts index 39ac2977..0d2e18ca 100644 --- a/src/features/auth/domain/session-validity/SessionValidity.ts +++ b/src/features/auth/domain/session-validity/SessionValidity.ts @@ -1,6 +1,6 @@ enum SessionValidity { VALID = "valid", - INVALID_ACCESS_TOKEN = "invalid_access_token", + INVALID_ACCESS_TOKEN = "invalid_access_token" } export default SessionValidity diff --git a/src/features/auth/domain/session-validity/index.ts b/src/features/auth/domain/session-validity/index.ts index bdbaaa79..0d39e2d3 100644 --- a/src/features/auth/domain/session-validity/index.ts +++ b/src/features/auth/domain/session-validity/index.ts @@ -1,4 +1,3 @@ export { default as OAuthTokenSessionValidator } from "./OAuthTokenSessionValidator" export { default as SessionValidity } from "./SessionValidity" export * from "./SessionValidity" -export { default as useRepositoryAccess } from "./useRepositoryAccess" diff --git a/src/features/auth/domain/session-validity/useRepositoryAccess.ts b/src/features/auth/domain/session-validity/useRepositoryAccess.ts deleted file mode 100644 index bb41d33d..00000000 --- a/src/features/auth/domain/session-validity/useRepositoryAccess.ts +++ /dev/null @@ -1,18 +0,0 @@ -"use client" - -import useSWR from "swr" -import { fetcher } from "@/common" - -type RepositoriesContainer = { repositories: string[] } - -export default function useRepositoryAccess() { - const { data, error, isLoading } = useSWR( - "/api/user/repository-access", - fetcher - ) - return { - repositories: data?.repositories || [], - isLoading, - error - } -} diff --git a/src/features/auth/view/SessionBarrier.tsx b/src/features/auth/view/SessionBarrier.tsx index 406b6120..68e4c3c8 100644 --- a/src/features/auth/view/SessionBarrier.tsx +++ b/src/features/auth/view/SessionBarrier.tsx @@ -7,11 +7,10 @@ export default async function SessionBarrier({ }: { children: ReactNode }) { - const accountProvider = await session.getAccountProvider() const sessionValidity = await blockingSessionValidator.validateSession() return ( {children} diff --git a/src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx b/src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx deleted file mode 100644 index 3c54f761..00000000 --- a/src/features/auth/view/client/NonGitHubAccountAccessTokenInvalidPage.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client" - -import InvalidSessionPage from "./InvalidSessionPage" -import LoadingIndicator from "@/common/loading/DelayedLoadingIndicator" -import { useRepositoryAccess } from "../../domain" - -export default function NonGitHubAccountAccessTokenInvalidPage() { - const {repositories, isLoading, error} = useRepositoryAccess() - if (isLoading) { - return ( - - - - ) - } - if (error) { - return ( - - It was not possible to obtain access to the repositories on GitHub. - - ) - } - if (repositories.length == 0) { - return ( - - Your account does not have access to any projects. - - ) - } - const repositoryNamesHTML = makeRepositoryNamesHTML(repositories) - const html = `It was not possible to obtain access to ${repositoryNamesHTML}.` - return ( - -
- - ) -} - -function makeRepositoryNamesHTML(repositories: string[]): string { - const copiedRepositories = [...repositories] - if (copiedRepositories.length == 1) { - return `${copiedRepositories[0]}` - } else { - const last = copiedRepositories.pop() - return copiedRepositories - .map(e => `${e}`) - .join(", ") - + `, and ${last}` - } -} \ No newline at end of file diff --git a/src/features/auth/view/client/SessionBarrier.tsx b/src/features/auth/view/client/SessionBarrier.tsx index acbd439b..55408cfd 100644 --- a/src/features/auth/view/client/SessionBarrier.tsx +++ b/src/features/auth/view/client/SessionBarrier.tsx @@ -1,19 +1,13 @@ "use client" import { ReactNode } from "react" -import { AccountProvider } from "@/common" -import { - SessionValidity -} from "../../domain" -import NonGitHubAccountAccessTokenInvalidPage from "./NonGitHubAccountAccessTokenInvalidPage" +import { SessionValidity } from "../../domain" import InvalidSessionPage from "./InvalidSessionPage" export default function SessionBarrier({ - accountProvider, sessionValidity, children }: { - accountProvider: AccountProvider sessionValidity: SessionValidity children: ReactNode }) { @@ -21,15 +15,10 @@ export default function SessionBarrier({ case SessionValidity.VALID: return <>{children} case SessionValidity.INVALID_ACCESS_TOKEN: - switch (accountProvider) { - case "email": - return - case "github": - return ( - - It was not possible to obtain access to the repositories on GitHub. - - ) - } + return ( + + It was not possible to obtain access to the repositories on GitHub. + + ) } } diff --git a/src/features/projects/view/ProjectsPage.tsx b/src/features/projects/view/ProjectsPage.tsx index d800e5d7..ebcceaa4 100644 --- a/src/features/projects/view/ProjectsPage.tsx +++ b/src/features/projects/view/ProjectsPage.tsx @@ -9,11 +9,9 @@ export default async function ProjectsPage({ projectRepository: ProjectRepository path: string }) { - const enableGitHubLinks = await session.getAccountProvider() == "github" const projects = await projectRepository.get() return ( diff --git a/src/features/projects/view/client/ProjectsPage.tsx b/src/features/projects/view/client/ProjectsPage.tsx index e218a26d..84a6d2da 100644 --- a/src/features/projects/view/client/ProjectsPage.tsx +++ b/src/features/projects/view/client/ProjectsPage.tsx @@ -18,11 +18,9 @@ import { } from "../../domain" export default function ProjectsPage({ - enableGitHubLinks, projects: serverProjects, path }: { - enableGitHubLinks: boolean projects?: Project[] path: string }) { @@ -93,7 +91,6 @@ export default function ProjectsPage({ } toolbarTrailingItem={project && version && specification && void, onSelectSpecification: (specificationId: string) => void }) => { - const projectNameURL = enableGitHubLinks ? version.url || project.url : undefined + const projectNameURL = version.url || project.url return ( <> - {enableGitHubLinks && specification.editURL && + {specification.editURL && { }}> - - Manage guests - signOut()}> Log out diff --git a/types/env.d.ts b/types/env.d.ts index 30748275..0451c3f6 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -2,7 +2,6 @@ namespace NodeJS { interface ProcessEnv { readonly SHAPE_DOCS_BASE_URL: string readonly SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: string - readonly FROM_EMAIL: string | undefined readonly NEXT_PUBLIC_SHAPE_DOCS_TITLE: string readonly NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: string readonly NEXTAUTH_URL_INTERNAL: string @@ -11,9 +10,6 @@ namespace NodeJS { readonly POSTGRESQL_HOST: string readonly POSTGRESQL_USER: string readonly POSTGRESQL_DB: string - readonly SMTP_HOST: string - readonly SMTP_USER: string - readonly SMTP_PASS: string readonly GITHUB_WEBHOOK_SECRET: string readonly GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: string readonly GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: string From be8e0708ef390586b744f4fc345f61927461141b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 14:59:30 +0200 Subject: [PATCH 135/191] Sorts imports --- src/composition.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/composition.ts b/src/composition.ts index 45e2c387..b3cbccd3 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -14,8 +14,8 @@ import { SessionMutexFactory } from "@/common" import { - GitHubProjectDataSource, - GitHubLoginDataSource + GitHubLoginDataSource, + GitHubProjectDataSource } from "@/features/projects/data" import { CachingProjectDataSource, @@ -32,9 +32,9 @@ import { LockingOAuthTokenRefresher, LogInHandler, OAuthTokenDataSource, - PersistingOAuthTokenRefresher, OAuthTokenRepository, OAuthTokenSessionValidator, + PersistingOAuthTokenRefresher, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" From 73f7e69e08795046850d9346077443dc788f9bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 15:03:20 +0200 Subject: [PATCH 136/191] Uses named arguments --- .../user-data/KeyValueUserDataRepository.ts | 6 +++--- src/composition.ts | 16 ++++++++-------- .../projects/domain/ProjectRepository.ts | 10 ++++------ 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/common/user-data/KeyValueUserDataRepository.ts b/src/common/user-data/KeyValueUserDataRepository.ts index 95b05734..081854b2 100644 --- a/src/common/user-data/KeyValueUserDataRepository.ts +++ b/src/common/user-data/KeyValueUserDataRepository.ts @@ -5,9 +5,9 @@ export default class KeyValueUserDataRepository implements IUserDataRepository { diff --git a/src/composition.ts b/src/composition.ts index b3cbccd3..b1f98fc6 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -147,15 +147,15 @@ export const blockingSessionValidator = new OAuthTokenSessionValidator({ oauthTokenDataSource }) -const projectUserDataRepository = new KeyValueUserDataRepository( - new RedisKeyValueStore(REDIS_URL), - "projects" -) +const projectUserDataRepository = new KeyValueUserDataRepository({ + store: new RedisKeyValueStore(REDIS_URL), + baseKey: "projects" +}) -export const projectRepository = new ProjectRepository( - session, - projectUserDataRepository -) +export const projectRepository = new ProjectRepository({ + userIDReader: session, + repository: projectUserDataRepository +}) export const projectDataSource = new CachingProjectDataSource({ dataSource: new GitHubProjectDataSource({ diff --git a/src/features/projects/domain/ProjectRepository.ts b/src/features/projects/domain/ProjectRepository.ts index baf5ea69..79bd6859 100644 --- a/src/features/projects/domain/ProjectRepository.ts +++ b/src/features/projects/domain/ProjectRepository.ts @@ -6,15 +6,13 @@ interface IUserIDReader { getUserId(): Promise } -type Repository = IUserDataRepository - export default class ProjectRepository implements IProjectRepository { private readonly userIDReader: IUserIDReader - private readonly repository: Repository + private readonly repository: IUserDataRepository - constructor(userIDReader: IUserIDReader, repository: Repository) { - this.userIDReader = userIDReader - this.repository = repository + constructor(config: { userIDReader: IUserIDReader, repository: IUserDataRepository }) { + this.userIDReader = config.userIDReader + this.repository = config.repository } async get(): Promise { From 149a4ff6e299cd5f7b2b1831f8a7138a6c0307c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 15:18:53 +0200 Subject: [PATCH 137/191] Gracefully fails reading cached projects --- src/features/projects/domain/ProjectRepository.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/features/projects/domain/ProjectRepository.ts b/src/features/projects/domain/ProjectRepository.ts index 79bd6859..75ad4680 100644 --- a/src/features/projects/domain/ProjectRepository.ts +++ b/src/features/projects/domain/ProjectRepository.ts @@ -21,7 +21,12 @@ export default class ProjectRepository implements IProjectRepository { if (!string) { return undefined } - return ZodJSONCoder.decode(ProjectSchema.array(), string) + try { + return ZodJSONCoder.decode(ProjectSchema.array(), string) + } catch (err) { + console.error(err) + return undefined + } } async set(projects: Project[]): Promise { From 3ae83de24d1af0b77215ac236cfb01db960c640e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 15:19:04 +0200 Subject: [PATCH 138/191] Adds owner and ownerUrl to projects --- src/features/projects/data/GitHubProjectDataSource.ts | 2 ++ src/features/projects/domain/Project.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index aff25611..bbc79c2c 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -64,10 +64,12 @@ export default class GitHubProjectDataSource implements IProjectDataSource { const defaultName = repository.name.replace(/-openapi$/, "") return { id: defaultName, + owner: repository.owner.login, name: defaultName, displayName: config?.name || defaultName, versions, imageURL: imageURL, + ownerUrl: `https://github.com/${repository.owner.login}`, url: `https://github.com/${repository.owner.login}/${repository.name}` } } diff --git a/src/features/projects/domain/Project.ts b/src/features/projects/domain/Project.ts index fb05e3a1..172d057a 100644 --- a/src/features/projects/domain/Project.ts +++ b/src/features/projects/domain/Project.ts @@ -7,7 +7,9 @@ export const ProjectSchema = z.object({ displayName: z.string(), versions: VersionSchema.array(), imageURL: z.string().optional(), - url: z.string().optional() + url: z.string().optional(), + owner: z.string(), + ownerUrl: z.string() }) type Project = z.infer From a7f5d576c4571450e5b56c86e9570f6c60f79961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 15:19:08 +0200 Subject: [PATCH 139/191] Shows owner in path --- .../view/toolbar/TrailingToolbarItem.tsx | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/features/projects/view/toolbar/TrailingToolbarItem.tsx b/src/features/projects/view/toolbar/TrailingToolbarItem.tsx index 0bbf746c..ba1a3bec 100644 --- a/src/features/projects/view/toolbar/TrailingToolbarItem.tsx +++ b/src/features/projects/view/toolbar/TrailingToolbarItem.tsx @@ -27,18 +27,30 @@ const TrailingToolbarItem = ({ alignItems="center" sx={{ display: { sm: "flex", md: "none" } }} > - + + / + - + / + + /> / - {text} + Test {text} ) } From ea08e6e9399e3b190e2173b910c27b19b39a3caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 15:28:12 +0200 Subject: [PATCH 140/191] Fixes unit tests --- .../KeyValueUserDataRepository.test.ts | 78 +++++++++++-------- .../projects/CachingProjectDataSource.test.ts | 4 +- .../projects/GitHubProjectDataSource.test.ts | 8 +- __test__/projects/getSelection.test.ts | 52 +++++++++---- __test__/projects/projectNavigator.test.ts | 8 +- __test__/projects/updateWindowTitle.test.ts | 12 ++- 6 files changed, 108 insertions(+), 54 deletions(-) diff --git a/__test__/common/userData/KeyValueUserDataRepository.test.ts b/__test__/common/userData/KeyValueUserDataRepository.test.ts index 09afadf3..9e73391f 100644 --- a/__test__/common/userData/KeyValueUserDataRepository.test.ts +++ b/__test__/common/userData/KeyValueUserDataRepository.test.ts @@ -3,14 +3,17 @@ import { KeyValueUserDataRepository } from "@/common" test("It reads the expected key", async () => { let readKey: string | undefined const sut = new KeyValueUserDataRepository({ - async get(key) { - readKey = key - return "" + store: { + async get(key) { + readKey = key + return "" + }, + async set() {}, + async setExpiring() {}, + async delete() {} }, - async set() {}, - async setExpiring() {}, - async delete() {} - }, "foo") + baseKey: "foo" + }) await sut.get("123") expect(readKey).toBe("foo[123]") }) @@ -18,15 +21,18 @@ test("It reads the expected key", async () => { test("It stores values under the expected key", async () => { let storedKey: string | undefined const sut = new KeyValueUserDataRepository({ - async get() { - return "" - }, - async set(key) { - storedKey = key - }, - async setExpiring() {}, - async delete() {} - }, "foo") + store: { + async get() { + return "" + }, + async set(key) { + storedKey = key + }, + async setExpiring() {}, + async delete() {} + }, + baseKey: "foo" + }) await sut.set("123", "bar") expect(storedKey).toBe("foo[123]") }) @@ -35,16 +41,19 @@ test("It stores values under the expected key with expected time to live", async let storedKey: string | undefined let storedTimeToLive: number | undefined const sut = new KeyValueUserDataRepository({ - async get() { - return "" - }, - async set() {}, - async setExpiring(key, _value, timeToLive) { - storedKey = key - storedTimeToLive = timeToLive + store: { + async get() { + return "" + }, + async set() {}, + async setExpiring(key, _value, timeToLive) { + storedKey = key + storedTimeToLive = timeToLive + }, + async delete() {} }, - async delete() {} - }, "foo") + baseKey: "foo" + }) await sut.setExpiring("123", "bar", 24 * 3600) expect(storedKey).toBe("foo[123]") expect(storedTimeToLive).toBe(24 * 3600) @@ -53,15 +62,18 @@ test("It stores values under the expected key with expected time to live", async test("It deletes the expected key", async () => { let deletedKey: string | undefined const sut = new KeyValueUserDataRepository({ - async get() { - return "" + store: { + async get() { + return "" + }, + async set() {}, + async setExpiring() {}, + async delete(key) { + deletedKey = key + } }, - async set() {}, - async setExpiring() {}, - async delete(key) { - deletedKey = key - } - }, "foo") + baseKey: "foo" + }) await sut.delete("123") expect(deletedKey).toBe("foo[123]") }) diff --git a/__test__/projects/CachingProjectDataSource.test.ts b/__test__/projects/CachingProjectDataSource.test.ts index bf4c0cea..0a9fc418 100644 --- a/__test__/projects/CachingProjectDataSource.test.ts +++ b/__test__/projects/CachingProjectDataSource.test.ts @@ -23,7 +23,9 @@ test("It caches projects read from the data source", async () => { name: "world.yml", url: "https://example.com/world.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] let cachedProjects: Project[] | undefined const sut = new CachingProjectDataSource({ diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index 99538d15..0730253b 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -113,7 +113,9 @@ test("It maps projects including branches and tags", async () => { }], url: "https://github.com/acme/foo/tree/1.0", isDefault: false - }] + }], + owner: "acme", + ownerUrl: "https://github.com/acme" }]) }) @@ -283,7 +285,9 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { }], url: "https://github.com/acme/foo/tree/1.0", isDefault: false - }] + }], + owner: "acme", + ownerUrl: "https://github.com/acme" }]) }) diff --git a/__test__/projects/getSelection.test.ts b/__test__/projects/getSelection.test.ts index a3979b44..609de1ca 100644 --- a/__test__/projects/getSelection.test.ts +++ b/__test__/projects/getSelection.test.ts @@ -16,7 +16,9 @@ test("It selects the first project when there is only one project and path is em name: "hello.yml", url: "https://example.com/hello.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("foo") @@ -31,7 +33,9 @@ test("It selects the first version and specification of the specified project", id: "foo", name: "foo", displayName: "foo", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }, { id: "bar", name: "bar", @@ -54,7 +58,9 @@ test("It selects the first version and specification of the specified project", name: "baz2", isDefault: false, specifications: [] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("bar") @@ -69,7 +75,9 @@ test("It selects the first specification of the specified project and version", id: "foo", name: "foo", displayName: "foo", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }, { id: "bar", name: "bar", @@ -88,7 +96,9 @@ test("It selects the first specification of the specified project and version", name: "hello1.yml", url: "https://example.com/hello.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("bar") @@ -103,7 +113,9 @@ test("It selects the specification of the specified version", () => { id: "foo", name: "foo", displayName: "foo", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }, { id: "bar", name: "bar", @@ -126,7 +138,9 @@ test("It selects the specification of the specified version", () => { name: "hello2.yml", url: "https://example.com/hello.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("bar") @@ -141,7 +155,9 @@ test("It selects the specified project, version, and specification", () => { id: "foo", name: "foo", displayName: "foo", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }, { id: "bar", name: "bar", @@ -164,7 +180,9 @@ test("It selects the specified project, version, and specification", () => { name: "hello2.yml", url: "https://example.com/hello.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("bar") @@ -179,7 +197,9 @@ test("It returns a undefined project, version, and specification when the select id: "bar", name: "bar", displayName: "bar", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project).toBeUndefined() @@ -199,7 +219,9 @@ test("It returns a undefined version and specification when the selected version name: "baz", isDefault: false, specifications: [] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("foo") @@ -223,7 +245,9 @@ test("It returns a undefined specification when the selected specification canno name: "hello.yml", url: "https://example.com/hello.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("foo") @@ -247,7 +271,9 @@ test("It moves specification ID to version ID if needed", () => { name: "hello.yml", url: "https://example.com/hello.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" }] }) expect(sut.project!.id).toEqual("foo") diff --git a/__test__/projects/projectNavigator.test.ts b/__test__/projects/projectNavigator.test.ts index e80d4d26..0cfd26bb 100644 --- a/__test__/projects/projectNavigator.test.ts +++ b/__test__/projects/projectNavigator.test.ts @@ -42,7 +42,9 @@ test("It navigates to first specification when changing version", async () => { name: "world.yml", url: "https://example.com/world.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" } let pushedPath: string | undefined const sut = new ProjectNavigator({ @@ -101,7 +103,9 @@ test("It finds a specification with the same name when changing version", async name: "jupiter.yml", url: "https://example.com/jupiter.yml" }] - }] + }], + owner: "acme", + ownerUrl: "https://example.com/acme" } let pushedPath: string | undefined const sut = new ProjectNavigator({ diff --git a/__test__/projects/updateWindowTitle.test.ts b/__test__/projects/updateWindowTitle.test.ts index dcc778c5..5521f406 100644 --- a/__test__/projects/updateWindowTitle.test.ts +++ b/__test__/projects/updateWindowTitle.test.ts @@ -18,7 +18,9 @@ test("It leaves out specification when the specification has a generic name", as id: "foo", name: "foo", displayName: "foo", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }, version: { id: "bar", @@ -52,7 +54,9 @@ test("It leaves out version when it is the defualt version", async () => { id: "foo", name: "foo", displayName: "foo", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }, version: { id: "bar", @@ -82,7 +86,9 @@ test("It adds version when it is not the defualt version", async () => { id: "foo", name: "foo", displayName: "foo", - versions: [] + versions: [], + owner: "acme", + ownerUrl: "https://example.com/acme" }, version: { id: "bar", From 871e8b3685ee72a2c7ff88a38259d0b86e8bd549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 Jul 2024 16:02:02 +0200 Subject: [PATCH 141/191] Adds owner to URL --- .../projects/GitHubProjectDataSource.test.ts | 12 +++--- __test__/projects/getSelection.test.ts | 40 +++++++++++-------- __test__/projects/projectNavigator.test.ts | 22 +++++----- .../projects/data/GitHubProjectDataSource.ts | 2 +- .../projects/domain/ProjectNavigator.ts | 26 ++++++------ src/features/projects/domain/getSelection.ts | 36 +++++++++-------- .../projects/view/client/ProjectsPage.tsx | 7 ++-- 7 files changed, 80 insertions(+), 65 deletions(-) diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index 0730253b..f46d67ac 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -87,7 +87,7 @@ test("It maps projects including branches and tags", async () => { }) const projects = await sut.getProjects() expect(projects).toEqual([{ - id: "foo", + id: "acme-foo", name: "foo", displayName: "foo", url: "https://github.com/acme/foo", @@ -179,7 +179,7 @@ test("It removes \"-openapi\" suffix from project name", async () => { } }) const projects = await sut.getProjects() - expect(projects[0].id).toEqual("foo") + expect(projects[0].id).toEqual("acme-foo") expect(projects[0].name).toEqual("foo") expect(projects[0].displayName).toEqual("foo") }) @@ -249,7 +249,7 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { }) const projects = await sut.getProjects() expect(projects).toEqual([{ - id: "foo", + id: "acme-foo", name: "foo", displayName: "foo", url: "https://github.com/acme/foo", @@ -351,7 +351,7 @@ test("It removes \"-openapi\" suffix from project name", async () => { } }) const projects = await sut.getProjects() - expect(projects[0].id).toEqual("foo") + expect(projects[0].id).toEqual("acme-foo") expect(projects[0].name).toEqual("foo") expect(projects[0].displayName).toEqual("foo") }) @@ -713,7 +713,7 @@ test("It reads display name from configuration file with .yml extension", async } }) const projects = await sut.getProjects() - expect(projects[0].id).toEqual("foo") + expect(projects[0].id).toEqual("acme-foo") expect(projects[0].name).toEqual("foo") expect(projects[0].displayName).toEqual("Hello World") }) @@ -823,7 +823,7 @@ test("It reads display name from configuration file with .yaml extension", async } }) const projects = await sut.getProjects() - expect(projects[0].id).toEqual("foo") + expect(projects[0].id).toEqual("acme-foo") expect(projects[0].name).toEqual("foo") expect(projects[0].displayName).toEqual("Hello World") }) diff --git a/__test__/projects/getSelection.test.ts b/__test__/projects/getSelection.test.ts index 609de1ca..a5a544ca 100644 --- a/__test__/projects/getSelection.test.ts +++ b/__test__/projects/getSelection.test.ts @@ -21,14 +21,15 @@ test("It selects the first project when there is only one project and path is em ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("foo") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("foo") expect(sut.version!.id).toEqual("bar") expect(sut.specification!.id).toEqual("hello") }) test("It selects the first version and specification of the specified project", () => { const sut = getSelection({ - path: "/bar", + path: "/acme/bar", projects: [{ id: "foo", name: "foo", @@ -63,14 +64,15 @@ test("It selects the first version and specification of the specified project", ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("bar") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("bar") expect(sut.version!.id).toEqual("baz1") expect(sut.specification!.id).toEqual("hello1") }) test("It selects the first specification of the specified project and version", () => { const sut = getSelection({ - path: "/bar/baz2", + path: "/acme/bar/baz2", projects: [{ id: "foo", name: "foo", @@ -101,14 +103,15 @@ test("It selects the first specification of the specified project and version", ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("bar") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("bar") expect(sut.version!.id).toEqual("baz2") expect(sut.specification!.id).toEqual("hello1") }) test("It selects the specification of the specified version", () => { const sut = getSelection({ - path: "/bar/baz2", + path: "/acme/bar/baz2", projects: [{ id: "foo", name: "foo", @@ -143,14 +146,15 @@ test("It selects the specification of the specified version", () => { ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("bar") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("bar") expect(sut.version!.id).toEqual("baz2") expect(sut.specification!.id).toEqual("hello1") }) test("It selects the specified project, version, and specification", () => { const sut = getSelection({ - path: "/bar/baz2/hello2", + path: "/acme/bar/baz2/hello2", projects: [{ id: "foo", name: "foo", @@ -185,14 +189,15 @@ test("It selects the specified project, version, and specification", () => { ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("bar") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("bar") expect(sut.version!.id).toEqual("baz2") expect(sut.specification!.id).toEqual("hello2") }) test("It returns a undefined project, version, and specification when the selected project cannot be found", () => { const sut = getSelection({ - path: "/foo", + path: "/acme/foo", projects: [{ id: "bar", name: "bar", @@ -209,7 +214,7 @@ test("It returns a undefined project, version, and specification when the select test("It returns a undefined version and specification when the selected version cannot be found", () => { const sut = getSelection({ - path: "/foo/bar", + path: "/acme/foo/bar", projects: [{ id: "foo", name: "foo", @@ -224,14 +229,15 @@ test("It returns a undefined version and specification when the selected version ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("foo") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("foo") expect(sut.version).toBeUndefined() expect(sut.specification).toBeUndefined() }) test("It returns a undefined specification when the selected specification cannot be found", () => { const sut = getSelection({ - path: "/foo/bar/baz", + path: "/acme/foo/bar/baz", projects: [{ id: "foo", name: "foo", @@ -250,14 +256,15 @@ test("It returns a undefined specification when the selected specification canno ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("foo") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("foo") expect(sut.version!.id).toEqual("bar") expect(sut.specification).toBeUndefined() }) test("It moves specification ID to version ID if needed", () => { const sut = getSelection({ - path: "/foo/bar/baz", + path: "/acme/foo/bar/baz", projects: [{ id: "foo", name: "foo", @@ -276,7 +283,8 @@ test("It moves specification ID to version ID if needed", () => { ownerUrl: "https://example.com/acme" }] }) - expect(sut.project!.id).toEqual("foo") + expect(sut.project!.owner).toEqual("acme") + expect(sut.project!.name).toEqual("foo") expect(sut.version!.id).toEqual("bar/baz") expect(sut.specification!.id).toEqual("hello") }) diff --git a/__test__/projects/projectNavigator.test.ts b/__test__/projects/projectNavigator.test.ts index 0cfd26bb..4cc0cb6a 100644 --- a/__test__/projects/projectNavigator.test.ts +++ b/__test__/projects/projectNavigator.test.ts @@ -15,8 +15,8 @@ test("It navigates to the correct path", async () => { replace: () => {} } }) - sut.navigate("foo", "bar", "hello.yml") - expect(pushedPath).toEqual("/foo/bar/hello.yml") + sut.navigate("acme", "foo", "bar", "hello.yml") + expect(pushedPath).toEqual("/acme/foo/bar/hello.yml") }) test("It navigates to first specification when changing version", async () => { @@ -61,7 +61,7 @@ test("It navigates to first specification when changing version", async () => { } }) sut.navigateToVersion(project, "hello", "baz.yml") - expect(pushedPath).toEqual("/foo/hello/world.yml") + expect(pushedPath).toEqual("/acme/foo/hello/world.yml") }) test("It finds a specification with the same name when changing version", async () => { @@ -122,7 +122,7 @@ test("It finds a specification with the same name when changing version", async } }) sut.navigateToVersion(project, "baz", "earth.yml") - expect(pushedPath).toEqual("/foo/baz/earth.yml") + expect(pushedPath).toEqual("/acme/foo/baz/earth.yml") }) test("It skips navigating when URL matches selection", async () => { @@ -130,7 +130,7 @@ test("It skips navigating when URL matches selection", async () => { const sut = new ProjectNavigator({ pathnameReader: { get pathname() { - return "/foo/bar/baz" + return "/acme/foo/bar/baz" } }, router: { @@ -141,7 +141,8 @@ test("It skips navigating when URL matches selection", async () => { } }) sut.navigateIfNeeded({ - projectId: "foo", + projectOwner: "acme", + projectName: "foo", versionId: "bar", specificationId: "baz" }) @@ -164,7 +165,8 @@ test("It navigates when project ID in URL does not match ID of selected project" } }) sut.navigateIfNeeded({ - projectId: "foo", + projectOwner: "acme", + projectName: "foo", versionId: "bar", specificationId: "baz" }) @@ -187,7 +189,8 @@ test("It navigates when version ID in URL does not match ID of selected version" } }) sut.navigateIfNeeded({ - projectId: "foo", + projectOwner: "acme", + projectName: "foo", versionId: "bar", specificationId: "baz" }) @@ -210,7 +213,8 @@ test("It navigates when specification ID in URL does not match ID of selected sp } }) sut.navigateIfNeeded({ - projectId: "foo", + projectOwner: "acme", + projectName: "foo", versionId: "bar", specificationId: "baz" }) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index bbc79c2c..7b2fc583 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -63,7 +63,7 @@ export default class GitHubProjectDataSource implements IProjectDataSource { }) const defaultName = repository.name.replace(/-openapi$/, "") return { - id: defaultName, + id: `${repository.owner.login}-${defaultName}`, owner: repository.owner.login, name: defaultName, displayName: config?.name || defaultName, diff --git a/src/features/projects/domain/ProjectNavigator.ts b/src/features/projects/domain/ProjectNavigator.ts index f7bf9e6f..aa87c239 100644 --- a/src/features/projects/domain/ProjectNavigator.ts +++ b/src/features/projects/domain/ProjectNavigator.ts @@ -34,32 +34,32 @@ export default class ProjectNavigator { return e.name == preferredSpecificationName }) if (candidateSpecification) { - this.router.push(`/${project.id}/${newVersion.id}/${candidateSpecification.id}`) + this.router.push(`/${project.owner}/${project.name}/${newVersion.id}/${candidateSpecification.id}`) } else { const firstSpecification = newVersion.specifications[0] - this.router.push(`/${project.id}/${newVersion.id}/${firstSpecification.id}`) + this.router.push(`/${project.owner}/${project.name}/${newVersion.id}/${firstSpecification.id}`) } } navigate( - projectId: string, + projectOwner: string, + projectName: string, versionId: string, specificationId: string ) { - this.router.push(`/${projectId}/${versionId}/${specificationId}`) + this.router.push(`/${projectOwner}/${projectName}/${versionId}/${specificationId}`) } - navigateIfNeeded( - selection: { - projectId?: string, - versionId?: string, - specificationId?: string - } - ) { - if (!selection.projectId || !selection.versionId || !selection.specificationId) { + navigateIfNeeded(selection: { + projectOwner?: string + projectName?: string + versionId?: string + specificationId?: string + }) { + if (!selection.projectOwner || !selection.projectName || !selection.versionId || !selection.specificationId) { return } - const path = `/${selection.projectId}/${selection.versionId}/${selection.specificationId}` + const path = `/${selection.projectOwner}/${selection.projectName}/${selection.versionId}/${selection.specificationId}` if (path !== this.pathnameReader.pathname) { this.router.replace(path) } diff --git a/src/features/projects/domain/getSelection.ts b/src/features/projects/domain/getSelection.ts index 09dc3587..087dc6f9 100644 --- a/src/features/projects/domain/getSelection.ts +++ b/src/features/projects/domain/getSelection.ts @@ -16,16 +16,18 @@ export default function getSelection({ if (path.startsWith("/")) { path = path.substring(1) } - const { projectId: _projectId, versionId, specificationId } = guessSelection(path) + const { owner: _owner, projectName: _projectName, versionId, specificationId } = guessSelection(path) // If no project is selected and the user only has a single project then we select that. - let projectId = _projectId - if (!projectId && projects.length == 1) { - projectId = projects[0].id + let owner = _owner + let projectName = _projectName + if (!projectName && projects.length == 1) { + owner = projects[0].owner + projectName = projects[0].name } - if (!projectId) { + if (!projectName) { return {} } - const project = projects.find(e => e.id == projectId) + const project = projects.find(e => e.owner == owner && e.name == projectName) if (!project) { return {} } @@ -65,20 +67,20 @@ export default function getSelection({ function guessSelection(pathname: string) { const comps = pathname.split("/") - if (comps.length == 0) { + if (comps.length == 0 || comps.length == 1) { return {} - } else if (comps.length == 1) { - return { projectId: comps[0] } - } else if (comps.length == 2) { - return { - projectId: comps[0], - versionId: comps[1] - } + } + const owner = comps[0] + const projectName = comps[1] + if (comps.length == 2) { + return { owner, projectName } + } else if (comps.length == 3) { + const versionId = comps[2] + return { owner, projectName, versionId } } else { - const projectId = comps[0] - const versionId = comps.slice(1, -1).join("/") + const versionId = comps.slice(2, -1).join("/") const specificationId = comps[comps.length - 1] - return { projectId, versionId, specificationId } + return { owner, projectName, versionId, specificationId } } } diff --git a/src/features/projects/view/client/ProjectsPage.tsx b/src/features/projects/view/client/ProjectsPage.tsx index 84a6d2da..951d4df6 100644 --- a/src/features/projects/view/client/ProjectsPage.tsx +++ b/src/features/projects/view/client/ProjectsPage.tsx @@ -43,7 +43,8 @@ export default function ProjectsPage({ useEffect(() => { // Ensure the URL reflects the current selection of project, version, and specification. projectNavigator.navigateIfNeeded({ - projectId: project?.id, + projectOwner: project?.owner, + projectName: project?.name, versionId: version?.id, specificationId: specification?.id }) @@ -60,13 +61,13 @@ export default function ProjectsPage({ } const version = project.versions[0] const specification = version.specifications[0] - projectNavigator.navigate(project.id, version.id, specification.id) + projectNavigator.navigate(project.owner, project.name, version.id, specification.id) } const selectVersion = (versionId: string) => { projectNavigator.navigateToVersion(project!, versionId, specification!.name) } const selectSpecification = (specificationId: string) => { - projectNavigator.navigate(project!.id, version!.id, specificationId) + projectNavigator.navigate(project!.owner, project!.name, version!.id, specificationId) } const canCloseSidebar = project !== undefined const toggleSidebar = (isOpen: boolean) => { From 4209903bcea3c55e82992a0f79ea6f48ac538055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 11 Jul 2024 10:30:18 +0200 Subject: [PATCH 142/191] Improves formatting --- src/features/projects/data/GitHubLoginDataSource.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/projects/data/GitHubLoginDataSource.ts b/src/features/projects/data/GitHubLoginDataSource.ts index 6c1a03db..3d60d1bf 100644 --- a/src/features/projects/data/GitHubLoginDataSource.ts +++ b/src/features/projects/data/GitHubLoginDataSource.ts @@ -10,7 +10,8 @@ export default class GitHubLoginDataSource implements IGitHubLoginDataSource { async getLogins(): Promise { const request = { - query: `query { + query: ` + query { viewer { login organizations(first: 100) { From 4d7d7a93278a80045a1b22022351aed8677a7177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Thu, 11 Jul 2024 11:33:56 +0200 Subject: [PATCH 143/191] Loads all user's repositories --- .../projects/data/GitHubProjectDataSource.ts | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index 7b2fc583..89b0b6e7 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -220,13 +220,44 @@ export default class GitHubProjectDataSource implements IProjectDataSource { } private async getRepositories({ logins }: { logins: string[] }): Promise { - if (logins.length === 0) { - return [] - } + let searchQueries: string[] = [] + // Search for all private repositories the user has access to. This is needed to find + // repositories for external collaborators who do not belong to an organization. + searchQueries.push("openapi in:name is:private") + // Search for public repositories belonging to a user or organization. + searchQueries = searchQueries.concat(logins.map(login => `openapi in:name user:${login} is:public`)) + return await Promise.all(searchQueries.map(searchQuery => { + return this.getRepositoriesForSearchQuery({ searchQuery }) + })) + .then(e => e.flat()) + .then(repositories => { + // GitHub's search API does not enable searching for repositories whose name ends with "-openapi", + // only repositories whose names include "openapi" so we filter the results ourselves. + return repositories.filter(repository => { + return repository.name.endsWith("-openapi") + }) + }) + .then(repositories => { + // Ensure we don't have duplicates in the resulting repositories. + const uniqueIdentifiers = new Set() + return repositories.filter(repository => { + const identifier = `${repository.owner.login}-${repository.name}` + const alreadyAdded = uniqueIdentifiers.has(identifier) + uniqueIdentifiers.add(identifier) + return !alreadyAdded + }) + }) + } + + private async getRepositoriesForSearchQuery(params: { + searchQuery: string, + cursor?: string + }): Promise { + const { searchQuery, cursor } = params const request = { query: ` - query Repositories($searchQuery: String!) { - search(query: $searchQuery, type: REPOSITORY, first: 100) { + query Repositories($searchQuery: String!, $cursor: String) { + search(query: $searchQuery, type: REPOSITORY, first: 100, after: $cursor) { results: nodes { ... on Repository { name @@ -255,6 +286,11 @@ export default class GitHubProjectDataSource implements IProjectDataSource { } } } + + pageInfo { + hasNextPage + endCursor + } } } @@ -285,12 +321,20 @@ export default class GitHubProjectDataSource implements IProjectDataSource { } } `, - variables: { - searchQuery: `user:${logins[0]} openapi in:name` - } + variables: { searchQuery, cursor } } const response = await this.graphQlClient.graphql(request) - const nextResults = await this.getRepositories({ logins: logins.slice(1) }) + if (!response.search || !response.search.results || !response.search.pageInfo) { + return [] + } + const pageInfo = response.search.pageInfo + if (!pageInfo.hasNextPage || !pageInfo.endCursor) { + return response.search.results + } + const nextResults = await this.getRepositoriesForSearchQuery({ + searchQuery, + cursor: pageInfo.endCursor + }) return response.search.results.concat(nextResults) } } From 2b13d1648f3ad2cd6886537393ffafddaa7d44ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 09:13:51 +0200 Subject: [PATCH 144/191] Adds REPOSITORY_NAME_SUFFIX --- .env.example | 1 + src/app/api/hooks/github/route.ts | 9 ++++++--- src/composition.ts | 2 ++ src/features/hooks/domain/GitHubCommentFactory.ts | 10 ++++++++-- ...positoryNameCheckingPullRequestEventHandler.ts | 15 +++++++++------ .../projects/data/GitHubProjectDataSource.ts | 13 +++++++++---- types/env.d.ts | 1 + 7 files changed, 36 insertions(+), 15 deletions(-) diff --git a/.env.example b/.env.example index 9e95c267..26fdbfbb 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,7 @@ REDIS_URL=localhost POSTGRESQL_HOST=localhost POSTGRESQL_USER=dbuser POSTGRESQL_DB=db +REPOSITORY_NAME_SUFFIX=-openapi GITHUB_WEBHOOK_SECRET=preshared secret also put in app configuration in GitHub GITHUB_WEBHOK_REPOSITORY_ALLOWLIST= GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST= diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index 4ce81638..b62bf5f5 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -14,6 +14,7 @@ import { gitHubClient } from "@/composition" const { NEXT_PUBLIC_SHAPE_DOCS_TITLE, SHAPE_DOCS_BASE_URL, + REPOSITORY_NAME_SUFFIX, GITHUB_WEBHOOK_SECRET, GITHUB_WEBHOK_REPOSITORY_ALLOWLIST, GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST @@ -31,11 +32,12 @@ const disallowedRepositoryNames = listFromCommaSeparatedString(GITHUB_WEBHOK_REP const hookHandler = new GitHubHookHandler({ secret: GITHUB_WEBHOOK_SECRET, - pullRequestEventHandler: new RepositoryNameCheckingPullRequestEventHandler( - new ExistingCommentCheckingPullRequestEventHandler({ + pullRequestEventHandler: new RepositoryNameCheckingPullRequestEventHandler({ + eventHandler: new ExistingCommentCheckingPullRequestEventHandler({ eventHandler: new PostCommentPullRequestEventHandler({ commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), commentFactory: new GitHubCommentFactory({ + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, websiteTitle: NEXT_PUBLIC_SHAPE_DOCS_TITLE, domain: SHAPE_DOCS_BASE_URL }) @@ -43,9 +45,10 @@ const hookHandler = new GitHubHookHandler({ commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), needleDomain: SHAPE_DOCS_BASE_URL }), + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, allowedRepositoryNames, disallowedRepositoryNames - ) + }) }) export const POST = async (req: NextRequest): Promise => { diff --git a/src/composition.ts b/src/composition.ts index b1f98fc6..f4f2c407 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -40,6 +40,7 @@ import { const { SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, + REPOSITORY_NAME_SUFFIX, GITHUB_APP_ID, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, @@ -163,6 +164,7 @@ export const projectDataSource = new CachingProjectDataSource({ graphQlClient: userGitHubClient }), graphQlClient: userGitHubClient, + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME }), repository: projectRepository diff --git a/src/features/hooks/domain/GitHubCommentFactory.ts b/src/features/hooks/domain/GitHubCommentFactory.ts index 10f96a59..b4dfe2ef 100644 --- a/src/features/hooks/domain/GitHubCommentFactory.ts +++ b/src/features/hooks/domain/GitHubCommentFactory.ts @@ -1,10 +1,16 @@ import IGitHubCommentFactory from "./IGitHubCommentFactory" export default class GitHubCommentFactory implements IGitHubCommentFactory { + private readonly repositoryNameSuffix: string private readonly websiteTitle: string private readonly domain: string - constructor(config: { websiteTitle: string, domain: string }) { + constructor(config: { + repositoryNameSuffix: string, + websiteTitle: string, + domain: string + }) { + this.repositoryNameSuffix = config.repositoryNameSuffix this.websiteTitle = config.websiteTitle this.domain = config.domain } @@ -16,7 +22,7 @@ export default class GitHubCommentFactory implements IGitHubCommentFactory { repositoryName: string ref: string }): string { - const projectId = repositoryName.replace(/-openapi$/, "") + const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") const link = `${this.domain}/${projectId}/${ref}` return `### 📖 Documentation Preview diff --git a/src/features/hooks/domain/RepositoryNameCheckingPullRequestEventHandler.ts b/src/features/hooks/domain/RepositoryNameCheckingPullRequestEventHandler.ts index cf45fa81..50d66947 100644 --- a/src/features/hooks/domain/RepositoryNameCheckingPullRequestEventHandler.ts +++ b/src/features/hooks/domain/RepositoryNameCheckingPullRequestEventHandler.ts @@ -2,17 +2,20 @@ import IPullRequestEventHandler, { IPullRequestOpenedEvent } from "./IPullReques export default class RepositoryNameCheckingPullRequestEventHandler implements IPullRequestEventHandler { private readonly eventHandler: IPullRequestEventHandler + private readonly repositoryNameSuffix: string private readonly allowedRepositoryNames: string[] private readonly disallowedRepositoryNames: string[] - constructor( + constructor(config: { eventHandler: IPullRequestEventHandler, + repositoryNameSuffix: string, allowedRepositoryNames?: string[], disallowedRepositoryNames?: string[] - ) { - this.eventHandler = eventHandler - this.allowedRepositoryNames = allowedRepositoryNames || [] - this.disallowedRepositoryNames = disallowedRepositoryNames || [] + }) { + this.eventHandler = config.eventHandler + this.repositoryNameSuffix = config.repositoryNameSuffix + this.allowedRepositoryNames = config.allowedRepositoryNames || [] + this.disallowedRepositoryNames = config.disallowedRepositoryNames || [] } async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { @@ -29,7 +32,7 @@ export default class RepositoryNameCheckingPullRequestEventHandler implements IP } private repositoryNameHasExpectedSuffix(repositoryName: string) { - return repositoryName.match(/-openapi$/) + return repositoryName.endsWith(this.repositoryNameSuffix) } private isAllowedRepositoryName(repositoryName: string) { diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index 89b0b6e7..da61f97f 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -15,15 +15,18 @@ import { export default class GitHubProjectDataSource implements IProjectDataSource { private readonly loginsDataSource: IGitHubLoginDataSource private readonly graphQlClient: IGitHubGraphQLClient + private readonly repositoryNameSuffix: string private readonly projectConfigurationFilename: string constructor(config: { loginsDataSource: IGitHubLoginDataSource, graphQlClient: IGitHubGraphQLClient, + repositoryNameSuffix: string, projectConfigurationFilename: string }) { this.loginsDataSource = config.loginsDataSource this.graphQlClient = config.graphQlClient + this.repositoryNameSuffix = config.repositoryNameSuffix this.projectConfigurationFilename = config.projectConfigurationFilename.replace(/\.ya?ml$/, "") } @@ -61,7 +64,7 @@ export default class GitHubProjectDataSource implements IProjectDataSource { ).filter(version => { return version.specifications.length > 0 }) - const defaultName = repository.name.replace(/-openapi$/, "") + const defaultName = repository.name.replace(new RegExp(this.repositoryNameSuffix + "$"), "") return { id: `${repository.owner.login}-${defaultName}`, owner: repository.owner.login, @@ -223,9 +226,11 @@ export default class GitHubProjectDataSource implements IProjectDataSource { let searchQueries: string[] = [] // Search for all private repositories the user has access to. This is needed to find // repositories for external collaborators who do not belong to an organization. - searchQueries.push("openapi in:name is:private") + searchQueries.push(`"${this.repositoryNameSuffix}" in:name is:private`) // Search for public repositories belonging to a user or organization. - searchQueries = searchQueries.concat(logins.map(login => `openapi in:name user:${login} is:public`)) + searchQueries = searchQueries.concat(logins.map(login => { + return `"${this.repositoryNameSuffix}" in:name user:${login} is:public` + })) return await Promise.all(searchQueries.map(searchQuery => { return this.getRepositoriesForSearchQuery({ searchQuery }) })) @@ -234,7 +239,7 @@ export default class GitHubProjectDataSource implements IProjectDataSource { // GitHub's search API does not enable searching for repositories whose name ends with "-openapi", // only repositories whose names include "openapi" so we filter the results ourselves. return repositories.filter(repository => { - return repository.name.endsWith("-openapi") + return repository.name.endsWith(this.repositoryNameSuffix) }) }) .then(repositories => { diff --git a/types/env.d.ts b/types/env.d.ts index 0451c3f6..c21d62ae 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -10,6 +10,7 @@ namespace NodeJS { readonly POSTGRESQL_HOST: string readonly POSTGRESQL_USER: string readonly POSTGRESQL_DB: string + readonly REPOSITORY_NAME_SUFFIX: string readonly GITHUB_WEBHOOK_SECRET: string readonly GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: string readonly GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: string From 800a4bfe222064b0c3fd7215a113a96cf1d5f7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 09:47:15 +0200 Subject: [PATCH 145/191] Removes unneeded commas --- src/features/hooks/domain/GitHubCommentFactory.ts | 4 ++-- src/features/hooks/domain/IGitHubCommentFactory.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/hooks/domain/GitHubCommentFactory.ts b/src/features/hooks/domain/GitHubCommentFactory.ts index b4dfe2ef..213e9a77 100644 --- a/src/features/hooks/domain/GitHubCommentFactory.ts +++ b/src/features/hooks/domain/GitHubCommentFactory.ts @@ -6,8 +6,8 @@ export default class GitHubCommentFactory implements IGitHubCommentFactory { private readonly domain: string constructor(config: { - repositoryNameSuffix: string, - websiteTitle: string, + repositoryNameSuffix: string + websiteTitle: string domain: string }) { this.repositoryNameSuffix = config.repositoryNameSuffix diff --git a/src/features/hooks/domain/IGitHubCommentFactory.ts b/src/features/hooks/domain/IGitHubCommentFactory.ts index 86e40f04..b1740492 100644 --- a/src/features/hooks/domain/IGitHubCommentFactory.ts +++ b/src/features/hooks/domain/IGitHubCommentFactory.ts @@ -3,7 +3,7 @@ export default interface IGitHubCommentFactory { repositoryName, ref }: { - repositoryName: string, + repositoryName: string ref: string }): string } From eb4a86de9d663d266f9c95574c3e04ac54e6629c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 09:49:00 +0200 Subject: [PATCH 146/191] Renames websiteTitle to siteName --- __test__/hooks/GitHubCommentFactory.test.ts | 4 ++-- src/app/api/hooks/github/route.ts | 2 +- src/features/hooks/domain/GitHubCommentFactory.ts | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/__test__/hooks/GitHubCommentFactory.test.ts b/__test__/hooks/GitHubCommentFactory.test.ts index 2bd61090..b7357d56 100644 --- a/__test__/hooks/GitHubCommentFactory.test.ts +++ b/__test__/hooks/GitHubCommentFactory.test.ts @@ -2,7 +2,7 @@ import { GitHubCommentFactory } from "../../src/features/hooks/domain" test("It includes a link to the documentation", async () => { const sut = new GitHubCommentFactory({ - websiteTitle: "Demo Docs", + siteName: "Demo Docs", domain: "https://example.com" }) const text = sut.makeDocumentationPreviewReadyComment({ @@ -14,7 +14,7 @@ test("It includes a link to the documentation", async () => { test("It removes the \"openapi\" suffix of the repository name", async () => { const sut = new GitHubCommentFactory({ - websiteTitle: "Demo Docs", + siteName: "Demo Docs", domain: "https://example.com" }) const text = sut.makeDocumentationPreviewReadyComment({ diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index b62bf5f5..ee7ab3c0 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -38,7 +38,7 @@ const hookHandler = new GitHubHookHandler({ commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), commentFactory: new GitHubCommentFactory({ repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - websiteTitle: NEXT_PUBLIC_SHAPE_DOCS_TITLE, + siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, domain: SHAPE_DOCS_BASE_URL }) }), diff --git a/src/features/hooks/domain/GitHubCommentFactory.ts b/src/features/hooks/domain/GitHubCommentFactory.ts index 213e9a77..ab5c62a2 100644 --- a/src/features/hooks/domain/GitHubCommentFactory.ts +++ b/src/features/hooks/domain/GitHubCommentFactory.ts @@ -2,16 +2,16 @@ import IGitHubCommentFactory from "./IGitHubCommentFactory" export default class GitHubCommentFactory implements IGitHubCommentFactory { private readonly repositoryNameSuffix: string - private readonly websiteTitle: string + private readonly siteName: string private readonly domain: string constructor(config: { repositoryNameSuffix: string - websiteTitle: string + siteName: string domain: string }) { this.repositoryNameSuffix = config.repositoryNameSuffix - this.websiteTitle = config.websiteTitle + this.siteName = config.siteName this.domain = config.domain } @@ -26,7 +26,7 @@ export default class GitHubCommentFactory implements IGitHubCommentFactory { const link = `${this.domain}/${projectId}/${ref}` return `### 📖 Documentation Preview - These edits are available for preview at [${this.websiteTitle}](${link}). + These edits are available for preview at [${this.siteName}](${link}). From fb2e6694352234696fc3ec4fb2ae7646149a797c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 10:10:27 +0200 Subject: [PATCH 147/191] Fixes unit tests --- __test__/hooks/GitHubCommentFactory.test.ts | 2 + ...ameCheckingPullRequestEventHandler.test.ts | 78 +++++++++----- .../projects/GitHubProjectDataSource.test.ts | 101 +++++++++++------- .../projects/data/GitHubProjectDataSource.ts | 5 +- 4 files changed, 124 insertions(+), 62 deletions(-) diff --git a/__test__/hooks/GitHubCommentFactory.test.ts b/__test__/hooks/GitHubCommentFactory.test.ts index b7357d56..1d47c7db 100644 --- a/__test__/hooks/GitHubCommentFactory.test.ts +++ b/__test__/hooks/GitHubCommentFactory.test.ts @@ -2,6 +2,7 @@ import { GitHubCommentFactory } from "../../src/features/hooks/domain" test("It includes a link to the documentation", async () => { const sut = new GitHubCommentFactory({ + repositoryNameSuffix: "-openapi", siteName: "Demo Docs", domain: "https://example.com" }) @@ -14,6 +15,7 @@ test("It includes a link to the documentation", async () => { test("It removes the \"openapi\" suffix of the repository name", async () => { const sut = new GitHubCommentFactory({ + repositoryNameSuffix: "-openapi", siteName: "Demo Docs", domain: "https://example.com" }) diff --git a/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts b/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts index 9e2b94a1..a71a6b6d 100644 --- a/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts +++ b/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts @@ -3,10 +3,15 @@ import { RepositoryNameCheckingPullRequestEventHandler } from "../../src/feature test("It does not call event handler when repository name does not have \"-openapi\" suffix", async () => { let didCallEventHandler = false const sut = new RepositoryNameCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, [], []) + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + repositoryNameSuffix: "-openapi", + allowedRepositoryNames: [], + disallowedRepositoryNames: [] + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -20,10 +25,15 @@ test("It does not call event handler when repository name does not have \"-opena test("It does not call event handler when repository name contains \"-openapi\" but it is not the last part of the repository name", async () => { let didCallEventHandler = false const sut = new RepositoryNameCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, [], []) + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + repositoryNameSuffix: "-openapi", + allowedRepositoryNames: [], + disallowedRepositoryNames: [] + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -37,10 +47,15 @@ test("It does not call event handler when repository name contains \"-openapi\" test("It calls event handler when no repositories have been allowed or disallowed", async () => { let didCallEventHandler = false const sut = new RepositoryNameCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, [], []) + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + repositoryNameSuffix: "-openapi", + allowedRepositoryNames: [], + disallowedRepositoryNames: [] + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -54,10 +69,15 @@ test("It calls event handler when no repositories have been allowed or disallowe test("It does not call event handler for repository that is not on the allowlist", async () => { let didCallEventHandler = false const sut = new RepositoryNameCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, ["example-openapi"], []) + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + repositoryNameSuffix: "-openapi", + allowedRepositoryNames: ["example-openapi"], + disallowedRepositoryNames: [] + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -71,10 +91,15 @@ test("It does not call event handler for repository that is not on the allowlist test("It does not call event handler for repository that is on the disallowlist", async () => { let didCallEventHandler = false const sut = new RepositoryNameCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, [], ["example-openapi"]) + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + repositoryNameSuffix: "-openapi", + allowedRepositoryNames: [], + disallowedRepositoryNames: ["example-openapi"] + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", @@ -88,10 +113,15 @@ test("It does not call event handler for repository that is on the disallowlist" test("It lets the disallowlist takes precedence over the allowlist", async () => { let didCallEventHandler = false const sut = new RepositoryNameCheckingPullRequestEventHandler({ - async pullRequestOpened() { - didCallEventHandler = true - } - }, ["example-openapi"], ["example-openapi"]) + eventHandler: { + async pullRequestOpened() { + didCallEventHandler = true + } + }, + repositoryNameSuffix: "-openapi", + allowedRepositoryNames: ["example-openapi"], + disallowedRepositoryNames: ["example-openapi"] + }) await sut.pullRequestOpened({ appInstallationId: 42, repositoryOwner: "acme", diff --git a/__test__/projects/GitHubProjectDataSource.test.ts b/__test__/projects/GitHubProjectDataSource.test.ts index f46d67ac..b4aeb857 100644 --- a/__test__/projects/GitHubProjectDataSource.test.ts +++ b/__test__/projects/GitHubProjectDataSource.test.ts @@ -5,6 +5,7 @@ import { test("It loads repositories from data source", async () => { let didLoadRepositories = false const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -28,6 +29,7 @@ test("It loads repositories from data source", async () => { test("It maps projects including branches and tags", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -39,7 +41,7 @@ test("It maps projects including branches and tags", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -90,17 +92,17 @@ test("It maps projects including branches and tags", async () => { id: "acme-foo", name: "foo", displayName: "foo", - url: "https://github.com/acme/foo", + url: "https://github.com/acme/foo-openapi", versions: [{ id: "main", name: "main", specifications: [{ id: "openapi.yml", name: "openapi.yml", - url: "/api/blob/acme/foo/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo/edit/main/openapi.yml" + url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", + editURL: "https://github.com/acme/foo-openapi/edit/main/openapi.yml" }], - url: "https://github.com/acme/foo/tree/main", + url: "https://github.com/acme/foo-openapi/tree/main", isDefault: true }, { id: "1.0", @@ -108,10 +110,10 @@ test("It maps projects including branches and tags", async () => { specifications: [{ id: "openapi.yml", name: "openapi.yml", - url: "/api/blob/acme/foo/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo/edit/1.0/openapi.yml" + url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", + editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml" }], - url: "https://github.com/acme/foo/tree/1.0", + url: "https://github.com/acme/foo-openapi/tree/1.0", isDefault: false }], owner: "acme", @@ -121,6 +123,7 @@ test("It maps projects including branches and tags", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -186,6 +189,7 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It supports multiple OpenAPI specifications on a branch", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -197,7 +201,7 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -252,27 +256,27 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { id: "acme-foo", name: "foo", displayName: "foo", - url: "https://github.com/acme/foo", + url: "https://github.com/acme/foo-openapi", versions: [{ id: "main", name: "main", specifications: [{ id: "foo-service.yml", name: "foo-service.yml", - url: "/api/blob/acme/foo/foo-service.yml?ref=12345678", - editURL: "https://github.com/acme/foo/edit/main/foo-service.yml" + url: "/api/blob/acme/foo-openapi/foo-service.yml?ref=12345678", + editURL: "https://github.com/acme/foo-openapi/edit/main/foo-service.yml" }, { id: "bar-service.yml", name: "bar-service.yml", - url: "/api/blob/acme/foo/bar-service.yml?ref=12345678", - editURL: "https://github.com/acme/foo/edit/main/bar-service.yml" + url: "/api/blob/acme/foo-openapi/bar-service.yml?ref=12345678", + editURL: "https://github.com/acme/foo-openapi/edit/main/bar-service.yml" }, { id: "baz-service.yml", name: "baz-service.yml", - url: "/api/blob/acme/foo/baz-service.yml?ref=12345678", - editURL: "https://github.com/acme/foo/edit/main/baz-service.yml" + url: "/api/blob/acme/foo-openapi/baz-service.yml?ref=12345678", + editURL: "https://github.com/acme/foo-openapi/edit/main/baz-service.yml" }], - url: "https://github.com/acme/foo/tree/main", + url: "https://github.com/acme/foo-openapi/tree/main", isDefault: true }, { id: "1.0", @@ -280,10 +284,10 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { specifications: [{ id: "openapi.yml", name: "openapi.yml", - url: "/api/blob/acme/foo/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo/edit/1.0/openapi.yml" + url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", + editURL: "https://github.com/acme/foo-openapi/edit/1.0/openapi.yml" }], - url: "https://github.com/acme/foo/tree/1.0", + url: "https://github.com/acme/foo-openapi/tree/1.0", isDefault: false }], owner: "acme", @@ -293,6 +297,7 @@ test("It supports multiple OpenAPI specifications on a branch", async () => { test("It removes \"-openapi\" suffix from project name", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -358,6 +363,7 @@ test("It removes \"-openapi\" suffix from project name", async () => { test("It filters away projects with no versions", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -397,6 +403,7 @@ test("It filters away projects with no versions", async () => { test("It filters away branches with no specifications", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -460,6 +467,7 @@ test("It filters away branches with no specifications", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -535,6 +543,7 @@ test("It filters away tags with no specifications", async () => { test("It reads image from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -589,6 +598,7 @@ test("It reads image from configuration file with .yml extension", async () => { test("It filters away tags with no specifications", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -664,6 +674,7 @@ test("It filters away tags with no specifications", async () => { test("It reads display name from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -720,6 +731,7 @@ test("It reads display name from configuration file with .yml extension", async test("It reads image from configuration file with .yml extension", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -774,6 +786,7 @@ test("It reads image from configuration file with .yml extension", async () => { test("It reads display name from configuration file with .yaml extension", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -830,6 +843,7 @@ test("It reads display name from configuration file with .yaml extension", async test("It reads image from configuration file with .yaml extension", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -884,6 +898,7 @@ test("It reads image from configuration file with .yaml extension", async () => test("It sorts projects alphabetically", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -895,7 +910,7 @@ test("It sorts projects alphabetically", async () => { return { search: { results: [{ - name: "cathrine", + name: "cathrine-openapi", owner: { login: "acme" }, @@ -924,7 +939,7 @@ test("It sorts projects alphabetically", async () => { edges: [] } }, { - name: "anne", + name: "anne-openapi", owner: { login: "acme" }, @@ -953,7 +968,7 @@ test("It sorts projects alphabetically", async () => { edges: [] } }, { - name: "bobby", + name: "bobby-openapi", owner: { login: "acme" }, @@ -995,6 +1010,7 @@ test("It sorts projects alphabetically", async () => { test("It sorts versions alphabetically", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1006,7 +1022,7 @@ test("It sorts versions alphabetically", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -1085,6 +1101,7 @@ test("It sorts versions alphabetically", async () => { test("It prioritizes main, master, develop, and development branch names when sorting verisons", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1096,7 +1113,7 @@ test("It prioritizes main, master, develop, and development branch names when so return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -1201,6 +1218,7 @@ test("It prioritizes main, master, develop, and development branch names when so test("It identifies the default branch in returned versions", async () => { const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1212,7 +1230,7 @@ test("It identifies the default branch in returned versions", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -1293,6 +1311,7 @@ test("It adds remote versions from the project configuration", async () => { url: https://example.com/louie.yml ` const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1304,7 +1323,7 @@ test("It adds remote versions from the project configuration", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -1368,6 +1387,7 @@ test("It modifies ID of remote version if the ID already exists", async () => { url: https://example.com/hello.yml ` const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1379,7 +1399,7 @@ test("It modifies ID of remote version if the ID already exists", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -1420,13 +1440,13 @@ test("It modifies ID of remote version if the ID already exists", async () => { expect(projects[0].versions).toEqual([{ id: "bar", name: "bar", - url: "https://github.com/acme/foo/tree/bar", + url: "https://github.com/acme/foo-openapi/tree/bar", isDefault: true, specifications: [{ id: "openapi.yml", name: "openapi.yml", - url: "/api/blob/acme/foo/openapi.yml?ref=12345678", - editURL: "https://github.com/acme/foo/edit/bar/openapi.yml" + url: "/api/blob/acme/foo-openapi/openapi.yml?ref=12345678", + editURL: "https://github.com/acme/foo-openapi/edit/bar/openapi.yml" }] }, { id: "bar1", @@ -1459,6 +1479,7 @@ test("It lets users specify the ID of a remote version", async () => { url: https://example.com/baz.yml ` const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1470,7 +1491,7 @@ test("It lets users specify the ID of a remote version", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -1518,6 +1539,7 @@ test("It lets users specify the ID of a remote specification", async () => { url: https://example.com/baz.yml ` const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1529,7 +1551,7 @@ test("It lets users specify the ID of a remote specification", async () => { return { search: { results: [{ - name: "foo", + name: "foo-openapi", owner: { login: "acme" }, @@ -1570,6 +1592,7 @@ test("It lets users specify the ID of a remote specification", async () => { test("It queries for both .yml and .yaml file extension with specifying .yml extension", async () => { let query: string | undefined const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1595,6 +1618,7 @@ test("It queries for both .yml and .yaml file extension with specifying .yml ext test("It queries for both .yml and .yaml file extension with specifying .yaml extension", async () => { let query: string | undefined const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs.yml", loginsDataSource: { async getLogins() { @@ -1620,6 +1644,7 @@ test("It queries for both .yml and .yaml file extension with specifying .yaml ex test("It queries for both .yml and .yaml file extension with no extension", async () => { let query: string | undefined const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs", loginsDataSource: { async getLogins() { @@ -1645,6 +1670,7 @@ test("It queries for both .yml and .yaml file extension with no extension", asyn test("It loads projects for all logins", async () => { let searchQueries: string[] = [] const sut = new GitHubProjectDataSource({ + repositoryNameSuffix: "-openapi", projectConfigurationFilename: ".demo-docs", loginsDataSource: { async getLogins() { @@ -1665,8 +1691,9 @@ test("It loads projects for all logins", async () => { } }) await sut.getProjects() - expect(searchQueries.length).toEqual(3) - expect(searchQueries[0]).toBe("user:acme openapi in:name") - expect(searchQueries[1]).toBe("user:somecorp openapi in:name") - expect(searchQueries[2]).toBe("user:techsystems openapi in:name") + expect(searchQueries.length).toEqual(4) + expect(searchQueries).toContain("\"-openapi\" in:name is:private") + expect(searchQueries).toContain("\"-openapi\" in:name user:acme is:public") + expect(searchQueries).toContain("\"-openapi\" in:name user:somecorp is:public") + expect(searchQueries).toContain("\"-openapi\" in:name user:techsystems is:public") }) diff --git a/src/features/projects/data/GitHubProjectDataSource.ts b/src/features/projects/data/GitHubProjectDataSource.ts index da61f97f..848c4859 100644 --- a/src/features/projects/data/GitHubProjectDataSource.ts +++ b/src/features/projects/data/GitHubProjectDataSource.ts @@ -329,10 +329,13 @@ export default class GitHubProjectDataSource implements IProjectDataSource { variables: { searchQuery, cursor } } const response = await this.graphQlClient.graphql(request) - if (!response.search || !response.search.results || !response.search.pageInfo) { + if (!response.search || !response.search.results) { return [] } const pageInfo = response.search.pageInfo + if (!pageInfo) { + return response.search.results + } if (!pageInfo.hasNextPage || !pageInfo.endCursor) { return response.search.results } From ec8f8b8516327860b6c753797561753e18b17016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 10:13:07 +0200 Subject: [PATCH 148/191] Fixes formatting --- .../hooks/domain/GitHubCommentFactory.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/features/hooks/domain/GitHubCommentFactory.ts b/src/features/hooks/domain/GitHubCommentFactory.ts index ab5c62a2..92110ac1 100644 --- a/src/features/hooks/domain/GitHubCommentFactory.ts +++ b/src/features/hooks/domain/GitHubCommentFactory.ts @@ -25,16 +25,16 @@ export default class GitHubCommentFactory implements IGitHubCommentFactory { const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") const link = `${this.domain}/${projectId}/${ref}` return `### 📖 Documentation Preview - - These edits are available for preview at [${this.siteName}](${link}). - -
- - - - - - -
Status:✅ Ready!
Preview URL:${link}
` + +These edits are available for preview at [${this.siteName}](${link}). + + + + + + + + +
Status:✅ Ready!
Preview URL:${link}
` } } From c39d4d129d5c9ae353005be304bc939614d7faea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 10:33:58 +0200 Subject: [PATCH 149/191] Uses GitHub app ID to find our comment in PR --- src/app/api/hooks/github/route.ts | 3 ++- src/common/github/GitHubClient.ts | 10 ++++++---- src/common/github/IGitHubClient.ts | 4 +++- .../ExistingCommentCheckingPullRequestEventHandler.ts | 11 ++++++----- .../hooks/domain/IPullRequestCommentRepository.ts | 4 +++- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index ee7ab3c0..aa4f1458 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -15,6 +15,7 @@ const { NEXT_PUBLIC_SHAPE_DOCS_TITLE, SHAPE_DOCS_BASE_URL, REPOSITORY_NAME_SUFFIX, + GITHUB_APP_ID, GITHUB_WEBHOOK_SECRET, GITHUB_WEBHOK_REPOSITORY_ALLOWLIST, GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST @@ -43,7 +44,7 @@ const hookHandler = new GitHubHookHandler({ }) }), commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), - needleDomain: SHAPE_DOCS_BASE_URL + gitHubAppId: GITHUB_APP_ID }), repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, allowedRepositoryNames, diff --git a/src/common/github/GitHubClient.ts b/src/common/github/GitHubClient.ts index 36b1d8ac..f48f223c 100644 --- a/src/common/github/GitHubClient.ts +++ b/src/common/github/GitHubClient.ts @@ -72,10 +72,12 @@ export default class GitHubClient implements IGitHubClient { }) const result: PullRequestComment[] = [] for await (const comment of comments) { - result.push({ - body: comment.body || "", - isFromBot: comment.user?.type == "Bot" - }) + const isFromBot = comment.user?.type == "Bot" + let gitHubApp: { id: string } | undefined + if (comment.performed_via_github_app) { + gitHubApp = { id: comment.performed_via_github_app.id.toString() } + } + result.push({ isFromBot, gitHubApp }) } return result } diff --git a/src/common/github/IGitHubClient.ts b/src/common/github/IGitHubClient.ts index 569b6bd6..fce46b83 100644 --- a/src/common/github/IGitHubClient.ts +++ b/src/common/github/IGitHubClient.ts @@ -15,7 +15,9 @@ export type RepositoryContent = { export type PullRequestComment = { readonly isFromBot: boolean - readonly body: string + readonly gitHubApp?: { + readonly id: string + } } export type GetRepositoryContentRequest = { diff --git a/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts b/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts index d787541f..dc431eb8 100644 --- a/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts +++ b/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts @@ -4,23 +4,24 @@ import IPullRequestCommentRepository from "./IPullRequestCommentRepository" export default class ExistingCommentCheckingPullRequestEventHandler implements IPullRequestEventHandler { private readonly eventHandler: IPullRequestEventHandler private readonly commentRepository: IPullRequestCommentRepository - private readonly needleDomain: string + private readonly gitHubAppId: string constructor(config: { eventHandler: IPullRequestEventHandler, commentRepository: IPullRequestCommentRepository, - needleDomain: string + gitHubAppId: string }) { this.eventHandler = config.eventHandler this.commentRepository = config.commentRepository - this.needleDomain = config.needleDomain + this.gitHubAppId = config.gitHubAppId } async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { const comments = await this.commentRepository.getComments(event) - const containsOurComment = comments.find(comment => { - return comment.isFromBot && comment.body.includes(this.needleDomain) + const ourComment = comments.find(comment => { + return comment.isFromBot && comment.gitHubApp?.id == this.gitHubAppId }) + const containsOurComment = ourComment !== undefined if (containsOurComment) { return } diff --git a/src/features/hooks/domain/IPullRequestCommentRepository.ts b/src/features/hooks/domain/IPullRequestCommentRepository.ts index 6ed2e534..de548e9b 100644 --- a/src/features/hooks/domain/IPullRequestCommentRepository.ts +++ b/src/features/hooks/domain/IPullRequestCommentRepository.ts @@ -1,6 +1,8 @@ export type PullRequestComment = { readonly isFromBot: boolean - readonly body: string + readonly gitHubApp?: { + readonly id: string + } } export type GetPullRequestCommentsOperation = { From 7d93aeacc45d3672a075bb325b0fb52eb1750ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 10:42:21 +0200 Subject: [PATCH 150/191] Fixes unit tests --- ...entCheckingPullRequestEventHandler.test.ts | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts index c13f8048..0939af29 100644 --- a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts +++ b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts @@ -13,7 +13,7 @@ test("It fetches comments from the repository", async () => { }, async addComment() {} }, - needleDomain: "https://example.com" + gitHubAppId: "appid1234" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -25,7 +25,7 @@ test("It fetches comments from the repository", async () => { expect(didFetchComments).toBeTruthy() }) -test("It does calls decorated event handler if a comment does not exist in the repository", async () => { +test("It calls decorated event handler if a comment does not exist in the repository", async () => { let didCallEventHandler = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ eventHandler: { @@ -39,7 +39,7 @@ test("It does calls decorated event handler if a comment does not exist in the r }, async addComment() {} }, - needleDomain: "https://example.com" + gitHubAppId: "appid1234" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -62,13 +62,15 @@ test("It does not call the event handler if a comment already exists in the repo commentRepository: { async getComments() { return [{ - body: "The documentation is available on https://example.com", - isFromBot: true + isFromBot: true, + gitHubApp: { + id: "appid1234" + } }] }, async addComment() {} }, - needleDomain: "https://example.com" + gitHubAppId: "appid1234" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -80,7 +82,7 @@ test("It does not call the event handler if a comment already exists in the repo expect(didCallEventHandler).toBeFalsy() }) -test("It calls the event handler if a comment exists matching the needle domain but that comment is not from a bot", async () => { +test("It calls the event handler if a comment exists with our GitHub app ID but that comment is not from a bot", async () => { let didCallEventHandler = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ eventHandler: { @@ -91,13 +93,15 @@ test("It calls the event handler if a comment exists matching the needle domain commentRepository: { async getComments() { return [{ - body: "The documentation is available on https://example.com", - isFromBot: false + isFromBot: false, + gitHubApp: { + id: "appid1234" + } }] }, async addComment() {} }, - needleDomain: "https://example.com" + gitHubAppId: "appid1234" }) await sut.pullRequestOpened({ appInstallationId: 42, @@ -109,7 +113,7 @@ test("It calls the event handler if a comment exists matching the needle domain expect(didCallEventHandler).toBeTruthy() }) -test("It calls the event handler if the repository contains a comment from a bot but that comment does not contain the needle domain", async () => { +test("It calls the event handler if the repository contains a comment from a bot but that comment is not from our GitHub app", async () => { let didCallEventHandler = false const sut = new ExistingCommentCheckingPullRequestEventHandler({ eventHandler: { @@ -120,13 +124,15 @@ test("It calls the event handler if the repository contains a comment from a bot commentRepository: { async getComments() { return [{ - body: "Hello world!", - isFromBot: true + isFromBot: true, + gitHubApp: { + id: "someotherapp" + } }] }, async addComment() {} }, - needleDomain: "https://example.com" + gitHubAppId: "appid1234" }) await sut.pullRequestOpened({ appInstallationId: 42, From 06ed7359ab899d19c65898f24dff6160bdad7019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 10:47:47 +0200 Subject: [PATCH 151/191] Fixes URLs in PRs --- src/features/hooks/domain/GitHubCommentFactory.ts | 9 ++++----- src/features/hooks/domain/IGitHubCommentFactory.ts | 6 ++---- .../hooks/domain/PostCommentPullRequestEventHandler.ts | 1 + 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/features/hooks/domain/GitHubCommentFactory.ts b/src/features/hooks/domain/GitHubCommentFactory.ts index 92110ac1..bee03cd3 100644 --- a/src/features/hooks/domain/GitHubCommentFactory.ts +++ b/src/features/hooks/domain/GitHubCommentFactory.ts @@ -15,15 +15,14 @@ export default class GitHubCommentFactory implements IGitHubCommentFactory { this.domain = config.domain } - makeDocumentationPreviewReadyComment({ - repositoryName, - ref - }: { + makeDocumentationPreviewReadyComment(params: { + owner: string repositoryName: string ref: string }): string { + const { owner, repositoryName, ref } = params const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") - const link = `${this.domain}/${projectId}/${ref}` + const link = `${this.domain}/${owner}/${projectId}/${ref}` return `### 📖 Documentation Preview These edits are available for preview at [${this.siteName}](${link}). diff --git a/src/features/hooks/domain/IGitHubCommentFactory.ts b/src/features/hooks/domain/IGitHubCommentFactory.ts index b1740492..23c700d9 100644 --- a/src/features/hooks/domain/IGitHubCommentFactory.ts +++ b/src/features/hooks/domain/IGitHubCommentFactory.ts @@ -1,8 +1,6 @@ export default interface IGitHubCommentFactory { - makeDocumentationPreviewReadyComment({ - repositoryName, - ref - }: { + makeDocumentationPreviewReadyComment(request: { + owner: string repositoryName: string ref: string }): string diff --git a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts index 43eba53a..7e18bda6 100644 --- a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts +++ b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts @@ -16,6 +16,7 @@ export default class PostCommentPullRequestEventHandler implements IPullRequestE async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { const commentBody = this.commentFactory.makeDocumentationPreviewReadyComment({ + owner: event.repositoryOwner, repositoryName: event.repositoryName, ref: event.ref }) From 7e2c2e2bbc64c7d49f76031bad075383addbb349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 10:57:32 +0200 Subject: [PATCH 152/191] Streamlines quotes --- src/features/hooks/data/GitHubHookHandler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/hooks/data/GitHubHookHandler.ts b/src/features/hooks/data/GitHubHookHandler.ts index 66a826aa..e8ca8f3f 100644 --- a/src/features/hooks/data/GitHubHookHandler.ts +++ b/src/features/hooks/data/GitHubHookHandler.ts @@ -20,10 +20,10 @@ class GitHubHookHandler { async handle(req: NextRequest): Promise { await this.webhooks.verifyAndReceive({ - id: req.headers.get('X-GitHub-Delivery') as string, - name: req.headers.get('X-GitHub-Event') as WebhookEventName, + id: req.headers.get("X-GitHub-Delivery") as string, + name: req.headers.get("X-GitHub-Event") as WebhookEventName, payload: await req.text(), - signature: req.headers.get('X-Hub-Signature') as string, + signature: req.headers.get("X-Hub-Signature") as string, }).catch((error) => { console.error(`Error: ${error.message}`) return false From 93dd488c0c413df2b90d62317b23e0ecdbae09ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:11:21 +0200 Subject: [PATCH 153/191] Updates PR comments --- src/app/api/hooks/github/route.ts | 37 ++-- src/common/github/GitHubClient.ts | 33 +++- src/common/github/IGitHubClient.ts | 30 ++++ .../OAuthTokenRefreshingGitHubClient.ts | 19 ++- src/common/github/index.ts | 1 + src/features/hooks/data/GitHubHookHandler.ts | 14 +- .../GitHubPullRequestCommentRepository.ts | 35 ---- src/features/hooks/data/index.ts | 1 - ...gCommentCheckingPullRequestEventHandler.ts | 30 ---- .../FilteringPullRequestEventHandler.ts | 37 ++++ .../hooks/domain/GitHubCommentFactory.ts | 39 ----- .../hooks/domain/IGitHubCommentFactory.ts | 7 - .../domain/IPullRequestCommentRepository.ts | 26 --- .../hooks/domain/IPullRequestEventHandler.ts | 18 ++ .../PostCommentPullRequestEventHandler.ts | 159 ++++++++++++++++-- ...andler.ts => RepositoryNameEventFilter.ts} | 29 ++-- src/features/hooks/domain/index.ts | 7 +- 17 files changed, 321 insertions(+), 201 deletions(-) delete mode 100644 src/features/hooks/data/GitHubPullRequestCommentRepository.ts delete mode 100644 src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts create mode 100644 src/features/hooks/domain/FilteringPullRequestEventHandler.ts delete mode 100644 src/features/hooks/domain/GitHubCommentFactory.ts delete mode 100644 src/features/hooks/domain/IGitHubCommentFactory.ts delete mode 100644 src/features/hooks/domain/IPullRequestCommentRepository.ts rename src/features/hooks/domain/{RepositoryNameCheckingPullRequestEventHandler.ts => RepositoryNameEventFilter.ts} (58%) diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index aa4f1458..c2b59517 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -1,19 +1,18 @@ import { NextRequest, NextResponse } from "next/server" import { - GitHubHookHandler, - GitHubPullRequestCommentRepository + GitHubHookHandler } from "@/features/hooks/data" import { PostCommentPullRequestEventHandler, - RepositoryNameCheckingPullRequestEventHandler, - ExistingCommentCheckingPullRequestEventHandler, - GitHubCommentFactory + FilteringPullRequestEventHandler, + RepositoryNameEventFilter } from "@/features/hooks/domain" import { gitHubClient } from "@/composition" const { NEXT_PUBLIC_SHAPE_DOCS_TITLE, SHAPE_DOCS_BASE_URL, + SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, REPOSITORY_NAME_SUFFIX, GITHUB_APP_ID, GITHUB_WEBHOOK_SECRET, @@ -33,22 +32,20 @@ const disallowedRepositoryNames = listFromCommaSeparatedString(GITHUB_WEBHOK_REP const hookHandler = new GitHubHookHandler({ secret: GITHUB_WEBHOOK_SECRET, - pullRequestEventHandler: new RepositoryNameCheckingPullRequestEventHandler({ - eventHandler: new ExistingCommentCheckingPullRequestEventHandler({ - eventHandler: new PostCommentPullRequestEventHandler({ - commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), - commentFactory: new GitHubCommentFactory({ - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, - domain: SHAPE_DOCS_BASE_URL - }) - }), - commentRepository: new GitHubPullRequestCommentRepository(gitHubClient), - gitHubAppId: GITHUB_APP_ID + pullRequestEventHandler: new FilteringPullRequestEventHandler({ + filter: new RepositoryNameEventFilter({ + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, + allowedRepositoryNames, + disallowedRepositoryNames }), - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - allowedRepositoryNames, - disallowedRepositoryNames + eventHandler: new PostCommentPullRequestEventHandler({ + siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, + domain: SHAPE_DOCS_BASE_URL, + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, + projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, + gitHubAppId: GITHUB_APP_ID, + gitHubClient: gitHubClient + }) }) }) diff --git a/src/common/github/GitHubClient.ts b/src/common/github/GitHubClient.ts index f48f223c..18f74b28 100644 --- a/src/common/github/GitHubClient.ts +++ b/src/common/github/GitHubClient.ts @@ -5,11 +5,14 @@ import IGitHubClient, { GraphQlQueryResponse, GetRepositoryContentRequest, GetPullRequestCommentsRequest, + GetPullRequestFilesRequest, AddCommentToPullRequestRequest, + UpdatePullRequestCommentRequest, GetOrganizationMembershipStatusRequest, GetOrganizationMembershipStatusRequestResponse, RepositoryContent, - PullRequestComment + PullRequestComment, + PullRequestFile } from "./IGitHubClient" interface IGitHubOAuthTokenDataSource { @@ -62,6 +65,19 @@ export default class GitHubClient implements IGitHubClient { return { downloadURL: item.download_url } } + async getPullRequestFiles(request: GetPullRequestFilesRequest): Promise { + const auth = await this.installationAuthenticator(request.appInstallationId) + const octokit = new Octokit({ auth: auth.token }) + const files = await octokit.paginate(octokit.rest.pulls.listFiles, { + owner: request.repositoryOwner, + repo: request.repositoryName, + pull_number: request.pullRequestNumber, + }) + return files.map(file => { + return { filename: file.filename, status: file.status } + }) + } + async getPullRequestComments(request: GetPullRequestCommentsRequest): Promise { const auth = await this.installationAuthenticator(request.appInstallationId) const octokit = new Octokit({ auth: auth.token }) @@ -72,12 +88,14 @@ export default class GitHubClient implements IGitHubClient { }) const result: PullRequestComment[] = [] for await (const comment of comments) { + const id = comment.id + const body = comment.body const isFromBot = comment.user?.type == "Bot" let gitHubApp: { id: string } | undefined if (comment.performed_via_github_app) { gitHubApp = { id: comment.performed_via_github_app.id.toString() } } - result.push({ isFromBot, gitHubApp }) + result.push({ id, body, isFromBot, gitHubApp }) } return result } @@ -93,6 +111,17 @@ export default class GitHubClient implements IGitHubClient { }) } + async updatePullRequestComment(request: UpdatePullRequestCommentRequest): Promise { + const auth = await this.installationAuthenticator(request.appInstallationId) + const octokit = new Octokit({ auth: auth.token }) + await octokit.rest.issues.updateComment({ + comment_id: request.commentId, + owner: request.repositoryOwner, + repo: request.repositoryName, + body: request.body + }) + } + async getOrganizationMembershipStatus( request: GetOrganizationMembershipStatusRequest ): Promise { diff --git a/src/common/github/IGitHubClient.ts b/src/common/github/IGitHubClient.ts index fce46b83..6cace33a 100644 --- a/src/common/github/IGitHubClient.ts +++ b/src/common/github/IGitHubClient.ts @@ -13,7 +13,20 @@ export type RepositoryContent = { readonly downloadURL: string } +export type PullRequestFile = { + readonly filename: string + readonly status: "added" + | "removed" + | "modified" + | "renamed" + | "copied" + | "changed" + | "unchanged" +} + export type PullRequestComment = { + readonly id: number + readonly body?: string readonly isFromBot: boolean readonly gitHubApp?: { readonly id: string @@ -27,6 +40,13 @@ export type GetRepositoryContentRequest = { readonly ref: string | undefined } +export type GetPullRequestFilesRequest = { + readonly appInstallationId: number + readonly repositoryOwner: string + readonly repositoryName: string + readonly pullRequestNumber: number +} + export type GetPullRequestCommentsRequest = { readonly appInstallationId: number readonly repositoryOwner: string @@ -42,6 +62,14 @@ export type AddCommentToPullRequestRequest = { readonly body: string } +export type UpdatePullRequestCommentRequest = { + readonly appInstallationId: number + readonly repositoryOwner: string + readonly repositoryName: string + readonly commentId: number + readonly body: string +} + export type GetOrganizationMembershipStatusRequest = { readonly organizationName: string } @@ -53,7 +81,9 @@ export type GetOrganizationMembershipStatusRequestResponse = { export default interface IGitHubClient { graphql(request: GraphQLQueryRequest): Promise getRepositoryContent(request: GetRepositoryContentRequest): Promise + getPullRequestFiles(request: GetPullRequestFilesRequest): Promise getPullRequestComments(request: GetPullRequestCommentsRequest): Promise addCommentToPullRequest(request: AddCommentToPullRequestRequest): Promise + updatePullRequestComment(request: UpdatePullRequestCommentRequest): Promise getOrganizationMembershipStatus(request: GetOrganizationMembershipStatusRequest): Promise } diff --git a/src/common/github/OAuthTokenRefreshingGitHubClient.ts b/src/common/github/OAuthTokenRefreshingGitHubClient.ts index 2d4ec2c8..59222897 100644 --- a/src/common/github/OAuthTokenRefreshingGitHubClient.ts +++ b/src/common/github/OAuthTokenRefreshingGitHubClient.ts @@ -7,8 +7,11 @@ import IGitHubClient, { GetOrganizationMembershipStatusRequest, GetOrganizationMembershipStatusRequestResponse, AddCommentToPullRequestRequest, + UpdatePullRequestCommentRequest, + GetPullRequestFilesRequest, RepositoryContent, - PullRequestComment + PullRequestComment, + PullRequestFile } from "./IGitHubClient" const HttpErrorSchema = z.object({ @@ -52,18 +55,30 @@ export default class OAuthTokenRefreshingGitHubClient implements IGitHubClient { }) } + async getPullRequestFiles(request: GetPullRequestFilesRequest): Promise { + return await this.send(async () => { + return await this.gitHubClient.getPullRequestFiles(request) + }) + } + async getPullRequestComments(request: GetPullRequestCommentsRequest): Promise { return await this.send(async () => { return await this.gitHubClient.getPullRequestComments(request) }) } - async addCommentToPullRequest(request: AddCommentToPullRequestRequest): Promise { + async addCommentToPullRequest(request: AddCommentToPullRequestRequest) { return await this.send(async () => { return await this.gitHubClient.addCommentToPullRequest(request) }) } + async updatePullRequestComment(request: UpdatePullRequestCommentRequest) { + return await this.send(async () => { + return await this.gitHubClient.updatePullRequestComment(request) + }) + } + async getOrganizationMembershipStatus( request: GetOrganizationMembershipStatusRequest ): Promise { diff --git a/src/common/github/index.ts b/src/common/github/index.ts index b5aef3bd..44efbb31 100644 --- a/src/common/github/index.ts +++ b/src/common/github/index.ts @@ -1,3 +1,4 @@ export { default as OAuthTokenRefreshingGitHubClient } from "./OAuthTokenRefreshingGitHubClient" export { default as GitHubClient } from "./GitHubClient" export type { default as IGitHubClient } from "./IGitHubClient" +export * from "./IGitHubClient" diff --git a/src/features/hooks/data/GitHubHookHandler.ts b/src/features/hooks/data/GitHubHookHandler.ts index e8ca8f3f..f548f330 100644 --- a/src/features/hooks/data/GitHubHookHandler.ts +++ b/src/features/hooks/data/GitHubHookHandler.ts @@ -47,7 +47,19 @@ class GitHubHookHandler { if (!payload.installation) { throw new Error("Payload does not contain information about the app installation.") } - await this.pullRequestEventHandler.pullRequestOpened({ + await this.pullRequestEventHandler.pullRequestReopened({ + appInstallationId: payload.installation.id, + repositoryOwner: payload.repository.owner.login, + repositoryName: payload.repository.name, + ref: payload.pull_request.head.ref, + pullRequestNumber: payload.pull_request.number + }) + }) + this.webhooks.on("pull_request.synchronize", async ({ payload }) => { + if (!payload.installation) { + throw new Error("Payload does not contain information about the app installation.") + } + await this.pullRequestEventHandler.pullRequestSynchronized({ appInstallationId: payload.installation.id, repositoryOwner: payload.repository.owner.login, repositoryName: payload.repository.name, diff --git a/src/features/hooks/data/GitHubPullRequestCommentRepository.ts b/src/features/hooks/data/GitHubPullRequestCommentRepository.ts deleted file mode 100644 index 3e074d59..00000000 --- a/src/features/hooks/data/GitHubPullRequestCommentRepository.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { IGitHubClient } from "@/common" -import IPullRequestCommentRepository, { - GetPullRequestCommentsOperation, - AddPullRequestCommentOperation, - PullRequestComment -} from "../domain/IPullRequestCommentRepository" - -export default class GitHubPullRequestCommentRepository implements IPullRequestCommentRepository { - readonly gitHubClient: IGitHubClient - - constructor(gitHubClient: IGitHubClient) { - this.gitHubClient = gitHubClient - } - - async getComments( - operation: GetPullRequestCommentsOperation - ): Promise { - return await this.gitHubClient.getPullRequestComments({ - appInstallationId: operation.appInstallationId, - repositoryOwner: operation.repositoryOwner, - repositoryName: operation.repositoryName, - pullRequestNumber: operation.pullRequestNumber, - }) - } - - async addComment(operation: AddPullRequestCommentOperation): Promise { - await this.gitHubClient.addCommentToPullRequest({ - appInstallationId: operation.appInstallationId, - repositoryOwner: operation.repositoryOwner, - repositoryName: operation.repositoryName, - pullRequestNumber: operation.pullRequestNumber, - body: operation.body - }) - } -} diff --git a/src/features/hooks/data/index.ts b/src/features/hooks/data/index.ts index 3e641922..bc0ca6aa 100644 --- a/src/features/hooks/data/index.ts +++ b/src/features/hooks/data/index.ts @@ -1,2 +1 @@ export { default as GitHubHookHandler } from "./GitHubHookHandler" -export { default as GitHubPullRequestCommentRepository } from "./GitHubPullRequestCommentRepository" \ No newline at end of file diff --git a/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts b/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts deleted file mode 100644 index dc431eb8..00000000 --- a/src/features/hooks/domain/ExistingCommentCheckingPullRequestEventHandler.ts +++ /dev/null @@ -1,30 +0,0 @@ -import IPullRequestEventHandler, { IPullRequestOpenedEvent } from "./IPullRequestEventHandler" -import IPullRequestCommentRepository from "./IPullRequestCommentRepository" - -export default class ExistingCommentCheckingPullRequestEventHandler implements IPullRequestEventHandler { - private readonly eventHandler: IPullRequestEventHandler - private readonly commentRepository: IPullRequestCommentRepository - private readonly gitHubAppId: string - - constructor(config: { - eventHandler: IPullRequestEventHandler, - commentRepository: IPullRequestCommentRepository, - gitHubAppId: string - }) { - this.eventHandler = config.eventHandler - this.commentRepository = config.commentRepository - this.gitHubAppId = config.gitHubAppId - } - - async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { - const comments = await this.commentRepository.getComments(event) - const ourComment = comments.find(comment => { - return comment.isFromBot && comment.gitHubApp?.id == this.gitHubAppId - }) - const containsOurComment = ourComment !== undefined - if (containsOurComment) { - return - } - await this.eventHandler.pullRequestOpened(event) - } -} diff --git a/src/features/hooks/domain/FilteringPullRequestEventHandler.ts b/src/features/hooks/domain/FilteringPullRequestEventHandler.ts new file mode 100644 index 00000000..00f787cf --- /dev/null +++ b/src/features/hooks/domain/FilteringPullRequestEventHandler.ts @@ -0,0 +1,37 @@ +import IPullRequestEventHandler, { + IPullRequestOpenedEvent, + IPullRequestReopenedEvent, + IPullRequestSynchronizedEvent +} from "./IPullRequestEventHandler" + +interface IFilter { + includeEvent(event: { repositoryName: string }): boolean +} + +export default class FilteringPullRequestEventHandler implements IPullRequestEventHandler { + private readonly filter: IFilter + private readonly eventHandler: IPullRequestEventHandler + + constructor(config: { filter: IFilter, eventHandler: IPullRequestEventHandler }) { + this.filter = config.filter + this.eventHandler = config.eventHandler + } + + async pullRequestOpened(event: IPullRequestOpenedEvent) { + if (this.filter.includeEvent(event)) { + await this.eventHandler.pullRequestOpened(event) + } + } + + async pullRequestReopened(event: IPullRequestReopenedEvent) { + if (this.filter.includeEvent(event)) { + await this.eventHandler.pullRequestReopened(event) + } + } + + async pullRequestSynchronized(event: IPullRequestSynchronizedEvent) { + if (this.filter.includeEvent(event)) { + await this.eventHandler.pullRequestSynchronized(event) + } + } +} diff --git a/src/features/hooks/domain/GitHubCommentFactory.ts b/src/features/hooks/domain/GitHubCommentFactory.ts deleted file mode 100644 index bee03cd3..00000000 --- a/src/features/hooks/domain/GitHubCommentFactory.ts +++ /dev/null @@ -1,39 +0,0 @@ -import IGitHubCommentFactory from "./IGitHubCommentFactory" - -export default class GitHubCommentFactory implements IGitHubCommentFactory { - private readonly repositoryNameSuffix: string - private readonly siteName: string - private readonly domain: string - - constructor(config: { - repositoryNameSuffix: string - siteName: string - domain: string - }) { - this.repositoryNameSuffix = config.repositoryNameSuffix - this.siteName = config.siteName - this.domain = config.domain - } - - makeDocumentationPreviewReadyComment(params: { - owner: string - repositoryName: string - ref: string - }): string { - const { owner, repositoryName, ref } = params - const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") - const link = `${this.domain}/${owner}/${projectId}/${ref}` - return `### 📖 Documentation Preview - -These edits are available for preview at [${this.siteName}](${link}). - - - - - - - - -
Status:✅ Ready!
Preview URL:${link}
` - } -} diff --git a/src/features/hooks/domain/IGitHubCommentFactory.ts b/src/features/hooks/domain/IGitHubCommentFactory.ts deleted file mode 100644 index 23c700d9..00000000 --- a/src/features/hooks/domain/IGitHubCommentFactory.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface IGitHubCommentFactory { - makeDocumentationPreviewReadyComment(request: { - owner: string - repositoryName: string - ref: string - }): string -} diff --git a/src/features/hooks/domain/IPullRequestCommentRepository.ts b/src/features/hooks/domain/IPullRequestCommentRepository.ts deleted file mode 100644 index de548e9b..00000000 --- a/src/features/hooks/domain/IPullRequestCommentRepository.ts +++ /dev/null @@ -1,26 +0,0 @@ -export type PullRequestComment = { - readonly isFromBot: boolean - readonly gitHubApp?: { - readonly id: string - } -} - -export type GetPullRequestCommentsOperation = { - readonly appInstallationId: number - readonly repositoryOwner: string - readonly repositoryName: string - readonly pullRequestNumber: number -} - -export type AddPullRequestCommentOperation = { - readonly appInstallationId: number - readonly repositoryOwner: string - readonly repositoryName: string - readonly pullRequestNumber: number - readonly body: string -} - -export default interface IPullRequestCommentRepository { - getComments(operation: GetPullRequestCommentsOperation): Promise - addComment(operation: AddPullRequestCommentOperation): Promise -} diff --git a/src/features/hooks/domain/IPullRequestEventHandler.ts b/src/features/hooks/domain/IPullRequestEventHandler.ts index 4f9d6490..0b1d3962 100644 --- a/src/features/hooks/domain/IPullRequestEventHandler.ts +++ b/src/features/hooks/domain/IPullRequestEventHandler.ts @@ -6,6 +6,24 @@ export interface IPullRequestOpenedEvent { readonly pullRequestNumber: number } +export interface IPullRequestReopenedEvent { + readonly appInstallationId: number + readonly repositoryOwner: string + readonly repositoryName: string + readonly ref: string + readonly pullRequestNumber: number +} + +export interface IPullRequestSynchronizedEvent { + readonly appInstallationId: number + readonly repositoryOwner: string + readonly repositoryName: string + readonly ref: string + readonly pullRequestNumber: number +} + export default interface IPullRequestEventHandler { pullRequestOpened(event: IPullRequestOpenedEvent): Promise + pullRequestReopened(event: IPullRequestReopenedEvent): Promise + pullRequestSynchronized(event: IPullRequestSynchronizedEvent): Promise } diff --git a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts index 7e18bda6..5c93cc09 100644 --- a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts +++ b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts @@ -1,31 +1,158 @@ -import IPullRequestEventHandler, { IPullRequestOpenedEvent } from "./IPullRequestEventHandler" -import IPullRequestCommentRepository from "./IPullRequestCommentRepository" -import IGitHubCommentFactory from "./IGitHubCommentFactory" +import IPullRequestEventHandler, { + IPullRequestOpenedEvent, + IPullRequestReopenedEvent, + IPullRequestSynchronizedEvent +} from "./IPullRequestEventHandler" +import { IGitHubClient, PullRequestFile } from "@/common" export default class PostCommentPullRequestEventHandler implements IPullRequestEventHandler { - private readonly commentRepository: IPullRequestCommentRepository - private readonly commentFactory: IGitHubCommentFactory + private readonly domain: string + private readonly siteName: string + private readonly repositoryNameSuffix: string + private readonly projectConfigurationFilename: string + private readonly gitHubAppId: string + private readonly gitHubClient: IGitHubClient + private readonly fileExtensionRegex = /\.ya?ml$/ constructor(config: { - commentRepository: IPullRequestCommentRepository, - commentFactory: IGitHubCommentFactory + domain: string + siteName: string + repositoryNameSuffix: string + projectConfigurationFilename: string + gitHubAppId: string + gitHubClient: IGitHubClient }) { - this.commentRepository = config.commentRepository - this.commentFactory = config.commentFactory + this.domain = config.domain + this.siteName = config.siteName + this.repositoryNameSuffix = config.repositoryNameSuffix + this.projectConfigurationFilename = config.projectConfigurationFilename + this.gitHubAppId = config.gitHubAppId + this.gitHubClient = config.gitHubClient } async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { - const commentBody = this.commentFactory.makeDocumentationPreviewReadyComment({ + await this.processEvent(event) + } + + async pullRequestReopened(event: IPullRequestReopenedEvent) { + await this.processEvent(event) + } + + async pullRequestSynchronized(event: IPullRequestSynchronizedEvent) { + await this.processEvent(event) + } + + private async processEvent(event: { + appInstallationId: number + repositoryOwner: string + repositoryName: string + ref: string + pullRequestNumber: number + }) { + const files = await this.getChangedYamlFiles(event) + if (files.length == 0) { + // Do nothing if no OpenAPI files were updated. + return + } + const commentBody = this.makeCommentBody({ + files, owner: event.repositoryOwner, repositoryName: event.repositoryName, ref: event.ref }) - await this.commentRepository.addComment({ - appInstallationId: event.appInstallationId, - repositoryOwner: event.repositoryOwner, - repositoryName: event.repositoryName, - pullRequestNumber: event.pullRequestNumber, - body: commentBody + const existingComment = await this.getExistingComment(event) + if (existingComment && existingComment.body !== commentBody) { + await this.gitHubClient.updatePullRequestComment({ + appInstallationId: event.appInstallationId, + commentId: existingComment.id, + repositoryOwner: event.repositoryOwner, + repositoryName: event.repositoryName, + body: commentBody + }) + } else if (!existingComment) { + await this.gitHubClient.addCommentToPullRequest({ + appInstallationId: event.appInstallationId, + repositoryOwner: event.repositoryOwner, + repositoryName: event.repositoryName, + pullRequestNumber: event.pullRequestNumber, + body: commentBody + }) + } + } + + private async getChangedYamlFiles(request: { + appInstallationId: number, + repositoryOwner: string + repositoryName: string + pullRequestNumber: number + }) { + const files = await this.gitHubClient.getPullRequestFiles({ + appInstallationId: request.appInstallationId, + repositoryOwner: request.repositoryOwner, + repositoryName: request.repositoryName, + pullRequestNumber: request.pullRequestNumber }) + return files + .filter(file => file.filename.match(this.fileExtensionRegex)) + .filter(file => file.status != "unchanged") + } + + private async getExistingComment(request: { + appInstallationId: number, + repositoryOwner: string + repositoryName: string + pullRequestNumber: number + }) { + const comments = await this.gitHubClient.getPullRequestComments({ + appInstallationId: request.appInstallationId, + repositoryOwner: request.repositoryOwner, + repositoryName: request.repositoryName, + pullRequestNumber: request.pullRequestNumber, + }) + return comments.find(comment => { + return comment.isFromBot && comment.gitHubApp?.id == this.gitHubAppId + }) + } + + private makeCommentBody(params: { + files: PullRequestFile[] + owner: string + repositoryName: string + ref: string + }): string { + const { files, owner, repositoryName, ref } = params + const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") + let rows: { title: string, status: string, link: string }[] = [] + // Make sure we don't include the project configuration file. + const baseConfigFilename = this.projectConfigurationFilename.replace(this.fileExtensionRegex, "") + const changedFiles = files.filter(file => file.filename.replace(this.fileExtensionRegex, "") != baseConfigFilename) + for (const file of changedFiles) { + let status = "" + if (file.status == "added") { + status += "Added" + } else if (file.status == "removed") { + status += "Remvoed" + } else if (file.status == "renamed") { + status += "Renamed" + } else if (file.status == "changed" || file.status == "modified") { + status += "Changed" + } + let link = "" + if (file.status != "removed") { + const url = `${this.domain}/${owner}/${projectId}/${ref}/${file.filename}` + link += `
${url}` + } + rows.push({ title: file.filename, status, link }) + } + let result = `### 📖 Documentation Preview` + result += `\n\n` + result += `The changes are now ready to previewed on ${this.siteName} 🚀` + if (rows.length > 0) { + const rowsHTML = rows + .map(row => `${row.title}${row.link}${row.status}`) + .join("\n") + result = `${result}\n\n${rowsHTML}
` + } + return result } } diff --git a/src/features/hooks/domain/RepositoryNameCheckingPullRequestEventHandler.ts b/src/features/hooks/domain/RepositoryNameEventFilter.ts similarity index 58% rename from src/features/hooks/domain/RepositoryNameCheckingPullRequestEventHandler.ts rename to src/features/hooks/domain/RepositoryNameEventFilter.ts index 50d66947..7e121e94 100644 --- a/src/features/hooks/domain/RepositoryNameCheckingPullRequestEventHandler.ts +++ b/src/features/hooks/domain/RepositoryNameEventFilter.ts @@ -1,34 +1,29 @@ -import IPullRequestEventHandler, { IPullRequestOpenedEvent } from "./IPullRequestEventHandler" - -export default class RepositoryNameCheckingPullRequestEventHandler implements IPullRequestEventHandler { - private readonly eventHandler: IPullRequestEventHandler +export default class RepositoryNameEventFilter { private readonly repositoryNameSuffix: string private readonly allowedRepositoryNames: string[] private readonly disallowedRepositoryNames: string[] constructor(config: { - eventHandler: IPullRequestEventHandler, - repositoryNameSuffix: string, - allowedRepositoryNames?: string[], - disallowedRepositoryNames?: string[] + repositoryNameSuffix: string + allowedRepositoryNames: string[] + disallowedRepositoryNames: string[] }) { - this.eventHandler = config.eventHandler this.repositoryNameSuffix = config.repositoryNameSuffix - this.allowedRepositoryNames = config.allowedRepositoryNames || [] - this.disallowedRepositoryNames = config.disallowedRepositoryNames || [] + this.allowedRepositoryNames = config.allowedRepositoryNames + this.disallowedRepositoryNames = config.disallowedRepositoryNames } - async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { + includeEvent(event: { repositoryName: string }) { if (!this.repositoryNameHasExpectedSuffix(event.repositoryName)) { - return + return false } if (!this.isAllowedRepositoryName(event.repositoryName)) { - return + return false } if (this.isDisallowedRepositoryName(event.repositoryName)) { - return + return false } - return await this.eventHandler.pullRequestOpened(event) + return true } private repositoryNameHasExpectedSuffix(repositoryName: string) { @@ -45,4 +40,4 @@ export default class RepositoryNameCheckingPullRequestEventHandler implements IP private isDisallowedRepositoryName(repositoryName: string) { return this.disallowedRepositoryNames.includes(repositoryName) } -} +} \ No newline at end of file diff --git a/src/features/hooks/domain/index.ts b/src/features/hooks/domain/index.ts index 673bd7c6..bfd3bc5c 100644 --- a/src/features/hooks/domain/index.ts +++ b/src/features/hooks/domain/index.ts @@ -1,7 +1,4 @@ -export { default as ExistingCommentCheckingPullRequestEventHandler } from "./ExistingCommentCheckingPullRequestEventHandler" -export { default as GitHubCommentFactory } from "./GitHubCommentFactory" -export type { default as IGitHubCommentFactory } from "./IGitHubCommentFactory" -export type { default as IPullRequestCommentRepository } from "./IPullRequestCommentRepository" export type { default as IPullRequestEventHandler } from "./IPullRequestEventHandler" export { default as PostCommentPullRequestEventHandler } from "./PostCommentPullRequestEventHandler" -export { default as RepositoryNameCheckingPullRequestEventHandler } from "./RepositoryNameCheckingPullRequestEventHandler" +export { default as FilteringPullRequestEventHandler } from "./FilteringPullRequestEventHandler" +export { default as RepositoryNameEventFilter } from "./RepositoryNameEventFilter" From 04f049c70478c1ffa95b52fda02eaa49bcd4c0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:17:58 +0200 Subject: [PATCH 154/191] Allowlist and disallowlist checks full repository names --- .../hooks/domain/FilteringPullRequestEventHandler.ts | 2 +- src/features/hooks/domain/RepositoryNameEventFilter.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/features/hooks/domain/FilteringPullRequestEventHandler.ts b/src/features/hooks/domain/FilteringPullRequestEventHandler.ts index 00f787cf..9afde828 100644 --- a/src/features/hooks/domain/FilteringPullRequestEventHandler.ts +++ b/src/features/hooks/domain/FilteringPullRequestEventHandler.ts @@ -5,7 +5,7 @@ import IPullRequestEventHandler, { } from "./IPullRequestEventHandler" interface IFilter { - includeEvent(event: { repositoryName: string }): boolean + includeEvent(event: { repositoryOwner: string, repositoryName: string }): boolean } export default class FilteringPullRequestEventHandler implements IPullRequestEventHandler { diff --git a/src/features/hooks/domain/RepositoryNameEventFilter.ts b/src/features/hooks/domain/RepositoryNameEventFilter.ts index 7e121e94..aace2631 100644 --- a/src/features/hooks/domain/RepositoryNameEventFilter.ts +++ b/src/features/hooks/domain/RepositoryNameEventFilter.ts @@ -13,14 +13,15 @@ export default class RepositoryNameEventFilter { this.disallowedRepositoryNames = config.disallowedRepositoryNames } - includeEvent(event: { repositoryName: string }) { + includeEvent(event: { repositoryOwner: string, repositoryName: string }) { + const fullRepositoryName = `${event.repositoryOwner}/${event.repositoryName}` if (!this.repositoryNameHasExpectedSuffix(event.repositoryName)) { return false } - if (!this.isAllowedRepositoryName(event.repositoryName)) { + if (!this.isAllowedRepositoryName(fullRepositoryName)) { return false } - if (this.isDisallowedRepositoryName(event.repositoryName)) { + if (this.isDisallowedRepositoryName(fullRepositoryName)) { return false } return true From c455ed786f1f8b426ba9eb5026292f0ab55f623a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:27:05 +0200 Subject: [PATCH 155/191] Adds RepositoryNameEventFilter tests --- .../hooks/RepositoryNameEventFilter.test.ts | 94 +++++++++++++++++++ src/app/api/hooks/github/route.ts | 8 +- .../hooks/domain/RepositoryNameEventFilter.ts | 18 ++-- 3 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 __test__/hooks/RepositoryNameEventFilter.test.ts diff --git a/__test__/hooks/RepositoryNameEventFilter.test.ts b/__test__/hooks/RepositoryNameEventFilter.test.ts new file mode 100644 index 00000000..473ce34b --- /dev/null +++ b/__test__/hooks/RepositoryNameEventFilter.test.ts @@ -0,0 +1,94 @@ +import { RepositoryNameEventFilter } from "../../src/features/hooks/domain" + +test("It does not include repositories that do not have the \"-openapi\" suffix", async () => { + const sut = new RepositoryNameEventFilter({ + repositoryNameSuffix: "-openapi", + allowlist: [], + disallowlist: [] + }) + const result = sut.includeEvent({ + repositoryOwner: "acme", + repositoryName: "foo" + }) + expect(result).toBeFalsy() +}) + +test("It includes repository when both allowlist and disallowlist are empty", async () => { + const sut = new RepositoryNameEventFilter({ + repositoryNameSuffix: "-openapi", + allowlist: [], + disallowlist: [] + }) + const result = sut.includeEvent({ + repositoryOwner: "acme", + repositoryName: "foo-openapi" + }) + expect(result).toBeTruthy() +}) + +test("It does not include repository when it is not on the allowlist", async () => { + const sut = new RepositoryNameEventFilter({ + repositoryNameSuffix: "-openapi", + allowlist: ["acme/example-openapi"], + disallowlist: [] + }) + const result = sut.includeEvent({ + repositoryOwner: "acme", + repositoryName: "foo" + }) + expect(result).toBeFalsy() +}) + +test("It include repository when it is on the disallowlist", async () => { + const sut = new RepositoryNameEventFilter({ + repositoryNameSuffix: "-openapi", + allowlist: [], + disallowlist: ["acme/example-openapi"] + }) + const result = sut.includeEvent({ + repositoryOwner: "acme", + repositoryName: "example-openapi" + }) + expect(result).toBeFalsy() +}) + +test("It ensures that the disallowlist takes precedence over the allowlist", async () => { + const sut = new RepositoryNameEventFilter({ + repositoryNameSuffix: "-openapi", + allowlist: ["acme/example-openapi"], + disallowlist: ["acme/example-openapi"] + }) + const result = sut.includeEvent({ + repositoryOwner: "acme", + repositoryName: "example-openapi" + }) + expect(result).toBeFalsy() +}) + +test("It requires owner to be included when matching against the allowlist", async () => { + const sut = new RepositoryNameEventFilter({ + repositoryNameSuffix: "-openapi", + allowlist: ["example-openapi"], + disallowlist: [] + }) + const result = sut.includeEvent({ + repositoryOwner: "acme", + repositoryName: "example-openapi" + }) + // The repository is not allowed because the repository in the allowlist does not include the owner. + expect(result).toBeFalsy() +}) + +test("It requires owner to be included when matching against the disallowlist", async () => { + const sut = new RepositoryNameEventFilter({ + repositoryNameSuffix: "-openapi", + allowlist: [], + disallowlist: ["example-openapi"] + }) + const result = sut.includeEvent({ + repositoryOwner: "acme", + repositoryName: "example-openapi" + }) + // The repository is allowed because the repository in the disallowlist does not include the owner. + expect(result).toBeTruthy() +}) \ No newline at end of file diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index c2b59517..4038770d 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -27,16 +27,16 @@ const listFromCommaSeparatedString = (str?: string) => { return str.split(",").map(e => e.trim()) } -const allowedRepositoryNames = listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_ALLOWLIST) -const disallowedRepositoryNames = listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST) +const allowlist = listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_ALLOWLIST) +const disallowlist = listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST) const hookHandler = new GitHubHookHandler({ secret: GITHUB_WEBHOOK_SECRET, pullRequestEventHandler: new FilteringPullRequestEventHandler({ filter: new RepositoryNameEventFilter({ repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - allowedRepositoryNames, - disallowedRepositoryNames + allowlist, + disallowlist }), eventHandler: new PostCommentPullRequestEventHandler({ siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, diff --git a/src/features/hooks/domain/RepositoryNameEventFilter.ts b/src/features/hooks/domain/RepositoryNameEventFilter.ts index aace2631..aba2ea46 100644 --- a/src/features/hooks/domain/RepositoryNameEventFilter.ts +++ b/src/features/hooks/domain/RepositoryNameEventFilter.ts @@ -1,16 +1,16 @@ export default class RepositoryNameEventFilter { private readonly repositoryNameSuffix: string - private readonly allowedRepositoryNames: string[] - private readonly disallowedRepositoryNames: string[] + private readonly allowlist: string[] + private readonly disallowlist: string[] constructor(config: { repositoryNameSuffix: string - allowedRepositoryNames: string[] - disallowedRepositoryNames: string[] + allowlist: string[] + disallowlist: string[] }) { this.repositoryNameSuffix = config.repositoryNameSuffix - this.allowedRepositoryNames = config.allowedRepositoryNames - this.disallowedRepositoryNames = config.disallowedRepositoryNames + this.allowlist = config.allowlist + this.disallowlist = config.disallowlist } includeEvent(event: { repositoryOwner: string, repositoryName: string }) { @@ -32,13 +32,13 @@ export default class RepositoryNameEventFilter { } private isAllowedRepositoryName(repositoryName: string) { - if (this.allowedRepositoryNames.length == 0) { + if (this.allowlist.length == 0) { return true } - return this.allowedRepositoryNames.includes(repositoryName) + return this.allowlist.includes(repositoryName) } private isDisallowedRepositoryName(repositoryName: string) { - return this.disallowedRepositoryNames.includes(repositoryName) + return this.disallowlist.includes(repositoryName) } } \ No newline at end of file From a4dc02cae9bbb77276291af6a50d5db801f8eee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:27:26 +0200 Subject: [PATCH 156/191] Removes ExistingCommentCheckingPullRequestEventHandler tests --- ...entCheckingPullRequestEventHandler.test.ts | 145 ------------------ 1 file changed, 145 deletions(-) delete mode 100644 __test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts diff --git a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts b/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts deleted file mode 100644 index 0939af29..00000000 --- a/__test__/hooks/ExistingCommentCheckingPullRequestEventHandler.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { ExistingCommentCheckingPullRequestEventHandler } from "../../src/features/hooks/domain" - -test("It fetches comments from the repository", async () => { - let didFetchComments = false - const sut = new ExistingCommentCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() {} - }, - commentRepository: { - async getComments() { - didFetchComments = true - return [] - }, - async addComment() {} - }, - gitHubAppId: "appid1234" - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didFetchComments).toBeTruthy() -}) - -test("It calls decorated event handler if a comment does not exist in the repository", async () => { - let didCallEventHandler = false - const sut = new ExistingCommentCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - commentRepository: { - async getComments() { - return [] - }, - async addComment() {} - }, - gitHubAppId: "appid1234" - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeTruthy() -}) - -test("It does not call the event handler if a comment already exists in the repository", async () => { - let didCallEventHandler = false - const sut = new ExistingCommentCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - commentRepository: { - async getComments() { - return [{ - isFromBot: true, - gitHubApp: { - id: "appid1234" - } - }] - }, - async addComment() {} - }, - gitHubAppId: "appid1234" - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeFalsy() -}) - -test("It calls the event handler if a comment exists with our GitHub app ID but that comment is not from a bot", async () => { - let didCallEventHandler = false - const sut = new ExistingCommentCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - commentRepository: { - async getComments() { - return [{ - isFromBot: false, - gitHubApp: { - id: "appid1234" - } - }] - }, - async addComment() {} - }, - gitHubAppId: "appid1234" - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeTruthy() -}) - -test("It calls the event handler if the repository contains a comment from a bot but that comment is not from our GitHub app", async () => { - let didCallEventHandler = false - const sut = new ExistingCommentCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - commentRepository: { - async getComments() { - return [{ - isFromBot: true, - gitHubApp: { - id: "someotherapp" - } - }] - }, - async addComment() {} - }, - gitHubAppId: "appid1234" - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeTruthy() -}) From b35a2b7b955f8c28410273054547b357706959ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:27:41 +0200 Subject: [PATCH 157/191] Removes RepositoryNameCheckingPullRequestEventHandler tests --- ...ameCheckingPullRequestEventHandler.test.ts | 133 ------------------ 1 file changed, 133 deletions(-) delete mode 100644 __test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts diff --git a/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts b/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts deleted file mode 100644 index a71a6b6d..00000000 --- a/__test__/hooks/RepositoryNameCheckingPullRequestEventHandler.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { RepositoryNameCheckingPullRequestEventHandler } from "../../src/features/hooks/domain" - -test("It does not call event handler when repository name does not have \"-openapi\" suffix", async () => { - let didCallEventHandler = false - const sut = new RepositoryNameCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - repositoryNameSuffix: "-openapi", - allowedRepositoryNames: [], - disallowedRepositoryNames: [] - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeFalsy() -}) - -test("It does not call event handler when repository name contains \"-openapi\" but it is not the last part of the repository name", async () => { - let didCallEventHandler = false - const sut = new RepositoryNameCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - repositoryNameSuffix: "-openapi", - allowedRepositoryNames: [], - disallowedRepositoryNames: [] - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo-openapi-bar", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeFalsy() -}) - -test("It calls event handler when no repositories have been allowed or disallowed", async () => { - let didCallEventHandler = false - const sut = new RepositoryNameCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - repositoryNameSuffix: "-openapi", - allowedRepositoryNames: [], - disallowedRepositoryNames: [] - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo-openapi", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeTruthy() -}) - -test("It does not call event handler for repository that is not on the allowlist", async () => { - let didCallEventHandler = false - const sut = new RepositoryNameCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - repositoryNameSuffix: "-openapi", - allowedRepositoryNames: ["example-openapi"], - disallowedRepositoryNames: [] - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeFalsy() -}) - -test("It does not call event handler for repository that is on the disallowlist", async () => { - let didCallEventHandler = false - const sut = new RepositoryNameCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - repositoryNameSuffix: "-openapi", - allowedRepositoryNames: [], - disallowedRepositoryNames: ["example-openapi"] - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "example-openapi", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeFalsy() -}) - -test("It lets the disallowlist takes precedence over the allowlist", async () => { - let didCallEventHandler = false - const sut = new RepositoryNameCheckingPullRequestEventHandler({ - eventHandler: { - async pullRequestOpened() { - didCallEventHandler = true - } - }, - repositoryNameSuffix: "-openapi", - allowedRepositoryNames: ["example-openapi"], - disallowedRepositoryNames: ["example-openapi"] - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "example-openapi", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didCallEventHandler).toBeFalsy() -}) From 527d9529a59ac4936c0149d83eb6e83e2efa8c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:31:35 +0200 Subject: [PATCH 158/191] Adds FilteringPullRequestEventHandler tests --- .../FilteringPullRequestEventHandler.test.ts | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 __test__/hooks/FilteringPullRequestEventHandler.test.ts diff --git a/__test__/hooks/FilteringPullRequestEventHandler.test.ts b/__test__/hooks/FilteringPullRequestEventHandler.test.ts new file mode 100644 index 00000000..2c1e8045 --- /dev/null +++ b/__test__/hooks/FilteringPullRequestEventHandler.test.ts @@ -0,0 +1,157 @@ +import { FilteringPullRequestEventHandler } from "../../src/features/hooks/domain" + +test("It calls pullRequestOpened(_:) when event is included", async () => { + let didCall = false + const sut = new FilteringPullRequestEventHandler({ + filter: { + includeEvent(_event) { + return true + } + }, + eventHandler: { + async pullRequestOpened(_event) { + didCall = true + }, + async pullRequestReopened(_event) {}, + async pullRequestSynchronized(_event) {} + } + }) + await sut.pullRequestOpened({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryName: "demo-openapi", + repositoryOwner: "acme", + ref: "main" + }) + expect(didCall).toBeTruthy() +}) + +test("It calls pullRequestReopened(_:) when event is included", async () => { + let didCall = false + const sut = new FilteringPullRequestEventHandler({ + filter: { + includeEvent(_event) { + return true + } + }, + eventHandler: { + async pullRequestOpened(_event) {}, + async pullRequestReopened(_event) { + didCall = true + }, + async pullRequestSynchronized(_event) {} + } + }) + await sut.pullRequestReopened({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryName: "demo-openapi", + repositoryOwner: "acme", + ref: "main" + }) + expect(didCall).toBeTruthy() +}) + +test("It calls pullRequestSynchronized(_:) when event is included", async () => { + let didCall = false + const sut = new FilteringPullRequestEventHandler({ + filter: { + includeEvent(_event) { + return true + } + }, + eventHandler: { + async pullRequestOpened(_event) {}, + async pullRequestReopened(_event) {}, + async pullRequestSynchronized(_event) { + didCall = true + } + } + }) + await sut.pullRequestSynchronized({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryName: "demo-openapi", + repositoryOwner: "acme", + ref: "main" + }) + expect(didCall).toBeTruthy() +}) + +test("It skips calling pullRequestOpened(_:) when event is not included", async () => { + let didCall = false + const sut = new FilteringPullRequestEventHandler({ + filter: { + includeEvent(_event) { + return false + } + }, + eventHandler: { + async pullRequestOpened(_event) { + didCall = true + }, + async pullRequestReopened(_event) {}, + async pullRequestSynchronized(_event) {} + } + }) + await sut.pullRequestOpened({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryName: "demo-openapi", + repositoryOwner: "acme", + ref: "main" + }) + expect(didCall).toBeFalsy() +}) + +test("It skips calling pullRequestReopened(_:) when event is not included", async () => { + let didCall = false + const sut = new FilteringPullRequestEventHandler({ + filter: { + includeEvent(_event) { + return false + } + }, + eventHandler: { + async pullRequestOpened(_event) {}, + async pullRequestReopened(_event) { + didCall = true + }, + async pullRequestSynchronized(_event) {} + } + }) + await sut.pullRequestReopened({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryName: "demo-openapi", + repositoryOwner: "acme", + ref: "main" + }) + expect(didCall).toBeFalsy() +}) + +test("It skips calling pullRequestSynchronized(_:) when event is not included", async () => { + let didCall = false + const sut = new FilteringPullRequestEventHandler({ + filter: { + includeEvent(_event) { + return false + } + }, + eventHandler: { + async pullRequestOpened(_event) {}, + async pullRequestReopened(_event) {}, + async pullRequestSynchronized(_event) { + didCall = true + } + } + }) + await sut.pullRequestSynchronized({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryName: "demo-openapi", + repositoryOwner: "acme", + ref: "main" + }) + expect(didCall).toBeFalsy() +}) \ No newline at end of file From e1f6cbcc1f35d1b2c14e693f949d02a3f5e2e55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:31:55 +0200 Subject: [PATCH 159/191] Removes PostCommentPullRequestEventHandler tests --- ...PostCommentPullRequestEventHandler.test.ts | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 __test__/hooks/PostCommentPullRequestEventHandler.test.ts diff --git a/__test__/hooks/PostCommentPullRequestEventHandler.test.ts b/__test__/hooks/PostCommentPullRequestEventHandler.test.ts deleted file mode 100644 index 60f88adc..00000000 --- a/__test__/hooks/PostCommentPullRequestEventHandler.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PostCommentPullRequestEventHandler } from "../../src/features/hooks/domain" - -test("It adds a comment to the repository", async () => { - let didAddComment = false - const sut = new PostCommentPullRequestEventHandler({ - commentRepository: { - async getComments() { - return [] - }, - async addComment() { - didAddComment = true - } - }, - commentFactory: { - makeDocumentationPreviewReadyComment() { - return "This is the comment" - } - } - }) - await sut.pullRequestOpened({ - appInstallationId: 42, - repositoryOwner: "acme", - repositoryName: "foo", - ref: "bar", - pullRequestNumber: 1337 - }) - expect(didAddComment).toBeTruthy() -}) From d5edf504208dd4a53ec0dc048d522887b93f567b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:32:30 +0200 Subject: [PATCH 160/191] Removes GitHubCommentFactory tests --- __test__/hooks/GitHubCommentFactory.test.ts | 27 --------------------- 1 file changed, 27 deletions(-) delete mode 100644 __test__/hooks/GitHubCommentFactory.test.ts diff --git a/__test__/hooks/GitHubCommentFactory.test.ts b/__test__/hooks/GitHubCommentFactory.test.ts deleted file mode 100644 index 1d47c7db..00000000 --- a/__test__/hooks/GitHubCommentFactory.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { GitHubCommentFactory } from "../../src/features/hooks/domain" - -test("It includes a link to the documentation", async () => { - const sut = new GitHubCommentFactory({ - repositoryNameSuffix: "-openapi", - siteName: "Demo Docs", - domain: "https://example.com" - }) - const text = sut.makeDocumentationPreviewReadyComment({ - repositoryName: "foo", - ref: "bar" - }) - expect(text).toContain("https://example.com/foo/bar") -}) - -test("It removes the \"openapi\" suffix of the repository name", async () => { - const sut = new GitHubCommentFactory({ - repositoryNameSuffix: "-openapi", - siteName: "Demo Docs", - domain: "https://example.com" - }) - const text = sut.makeDocumentationPreviewReadyComment({ - repositoryName: "foo-openapi", - ref: "bar" - }) - expect(text).toContain("https://example.com/foo/bar") -}) \ No newline at end of file From 3cab528b5208a0775501b9a12c5954157807c93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:34:48 +0200 Subject: [PATCH 161/191] Fixes OAuthTokenRefreshingGitHubClient tests --- .../OAuthTokenRefreshingGitHubClient.test.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts b/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts index c2fdf710..8610daa0 100644 --- a/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts +++ b/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts @@ -30,9 +30,13 @@ test("It forwards a GraphQL request", async () => { async getRepositoryContent() { return { downloadURL: "https://example.com" } }, + async getPullRequestFiles() { + return [] + }, async getPullRequestComments() { return [] }, + async updatePullRequestComment() {}, async addCommentToPullRequest() {}, async getOrganizationMembershipStatus() { return { state: "active" } @@ -71,10 +75,14 @@ test("It forwards a request to get the repository content", async () => { forwardedRequest = request return { downloadURL: "https://example.com" } }, + async getPullRequestFiles() { + return [] + }, async getPullRequestComments() { return [] }, async addCommentToPullRequest() {}, + async updatePullRequestComment() {}, async getOrganizationMembershipStatus() { return { state: "active" } } @@ -113,11 +121,15 @@ test("It forwards a request to get comments to a pull request", async () => { async getRepositoryContent() { return { downloadURL: "https://example.com" } }, + async getPullRequestFiles() { + return [] + }, async getPullRequestComments(request: GetPullRequestCommentsRequest) { forwardedRequest = request return [] }, async addCommentToPullRequest() {}, + async updatePullRequestComment() {}, async getOrganizationMembershipStatus() { return { state: "active" } } @@ -156,12 +168,16 @@ test("It forwards a request to add a comment to a pull request", async () => { async getRepositoryContent() { return { downloadURL: "https://example.com" } }, + async getPullRequestFiles() { + return [] + }, async getPullRequestComments() { return [] }, async addCommentToPullRequest(request: AddCommentToPullRequestRequest) { forwardedRequest = request }, + async updatePullRequestComment() {}, async getOrganizationMembershipStatus() { return { state: "active" } } @@ -207,9 +223,13 @@ test("It retries with a refreshed OAuth token when receiving HTTP 401", async () async getRepositoryContent() { return { downloadURL: "https://example.com" } }, + async getPullRequestFiles() { + return [] + }, async getPullRequestComments() { return [] }, + async updatePullRequestComment() {}, async addCommentToPullRequest() {}, async getOrganizationMembershipStatus() { return { state: "active" } @@ -248,9 +268,13 @@ test("It only retries a request once when receiving HTTP 401", async () => { async getRepositoryContent() { return { downloadURL: "https://example.com" } }, + async getPullRequestFiles() { + return [] + }, async getPullRequestComments() { return [] }, + async updatePullRequestComment() {}, async addCommentToPullRequest() {}, async getOrganizationMembershipStatus() { return { state: "active" } @@ -292,10 +316,14 @@ test("It does not refresh an OAuth token when the initial request was successful async getRepositoryContent() { return { downloadURL: "https://example.com" } }, + async getPullRequestFiles() { + return [] + }, async getPullRequestComments() { return [] }, async addCommentToPullRequest() {}, + async updatePullRequestComment() {}, async getOrganizationMembershipStatus() { return { state: "active" } } From 0870f455ac1e06570365390a91216bb2e44bfd14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 13:59:52 +0200 Subject: [PATCH 162/191] Adds tests to PostCommentPullRequestEventHandler --- ...PostCommentPullRequestEventHandler.test.ts | 58 +++++++ src/app/api/hooks/github/route.ts | 17 +- .../PostCommentPullRequestEventHandler.ts | 157 ++---------------- .../hooks/domain/PullRequestCommenter.ts | 141 ++++++++++++++++ src/features/hooks/domain/index.ts | 1 + 5 files changed, 227 insertions(+), 147 deletions(-) create mode 100644 __test__/hooks/PostCommentPullRequestEventHandler.test.ts create mode 100644 src/features/hooks/domain/PullRequestCommenter.ts diff --git a/__test__/hooks/PostCommentPullRequestEventHandler.test.ts b/__test__/hooks/PostCommentPullRequestEventHandler.test.ts new file mode 100644 index 00000000..46f7ee55 --- /dev/null +++ b/__test__/hooks/PostCommentPullRequestEventHandler.test.ts @@ -0,0 +1,58 @@ +import { PostCommentPullRequestEventHandler } from "../../src/features/hooks/domain" + +test("It comments when opening a pull request", async () => { + let didComment = false + const sut = new PostCommentPullRequestEventHandler({ + pullRequestCommenter: { + async commentPullRequest(_request) { + didComment = true + } + } + }) + await sut.pullRequestOpened({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didComment).toBeTruthy() +}) + +test("It comments when reopening a pull request", async () => { + let didComment = false + const sut = new PostCommentPullRequestEventHandler({ + pullRequestCommenter: { + async commentPullRequest(_request) { + didComment = true + } + } + }) + await sut.pullRequestReopened({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didComment).toBeTruthy() +}) + +test("It comments when synchronizing a pull request", async () => { + let didComment = false + const sut = new PostCommentPullRequestEventHandler({ + pullRequestCommenter: { + async commentPullRequest(_request) { + didComment = true + } + } + }) + await sut.pullRequestSynchronized({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didComment).toBeTruthy() +}) diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index 4038770d..bad9989d 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -5,7 +5,8 @@ import { import { PostCommentPullRequestEventHandler, FilteringPullRequestEventHandler, - RepositoryNameEventFilter + RepositoryNameEventFilter, + PullRequestCommenter } from "@/features/hooks/domain" import { gitHubClient } from "@/composition" @@ -39,12 +40,14 @@ const hookHandler = new GitHubHookHandler({ disallowlist }), eventHandler: new PostCommentPullRequestEventHandler({ - siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, - domain: SHAPE_DOCS_BASE_URL, - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, - gitHubAppId: GITHUB_APP_ID, - gitHubClient: gitHubClient + pullRequestCommenter: new PullRequestCommenter({ + siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, + domain: SHAPE_DOCS_BASE_URL, + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, + projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, + gitHubAppId: GITHUB_APP_ID, + gitHubClient: gitHubClient + }) }) }) }) diff --git a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts index 5c93cc09..839d6760 100644 --- a/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts +++ b/src/features/hooks/domain/PostCommentPullRequestEventHandler.ts @@ -3,156 +3,33 @@ import IPullRequestEventHandler, { IPullRequestReopenedEvent, IPullRequestSynchronizedEvent } from "./IPullRequestEventHandler" -import { IGitHubClient, PullRequestFile } from "@/common" -export default class PostCommentPullRequestEventHandler implements IPullRequestEventHandler { - private readonly domain: string - private readonly siteName: string - private readonly repositoryNameSuffix: string - private readonly projectConfigurationFilename: string - private readonly gitHubAppId: string - private readonly gitHubClient: IGitHubClient - private readonly fileExtensionRegex = /\.ya?ml$/ - - constructor(config: { - domain: string - siteName: string - repositoryNameSuffix: string - projectConfigurationFilename: string - gitHubAppId: string - gitHubClient: IGitHubClient - }) { - this.domain = config.domain - this.siteName = config.siteName - this.repositoryNameSuffix = config.repositoryNameSuffix - this.projectConfigurationFilename = config.projectConfigurationFilename - this.gitHubAppId = config.gitHubAppId - this.gitHubClient = config.gitHubClient - } - - async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { - await this.processEvent(event) - } - - async pullRequestReopened(event: IPullRequestReopenedEvent) { - await this.processEvent(event) - } - - async pullRequestSynchronized(event: IPullRequestSynchronizedEvent) { - await this.processEvent(event) - } - - private async processEvent(event: { +interface IPullRequestCommenter { + commentPullRequest(request: { appInstallationId: number repositoryOwner: string repositoryName: string ref: string pullRequestNumber: number - }) { - const files = await this.getChangedYamlFiles(event) - if (files.length == 0) { - // Do nothing if no OpenAPI files were updated. - return - } - const commentBody = this.makeCommentBody({ - files, - owner: event.repositoryOwner, - repositoryName: event.repositoryName, - ref: event.ref - }) - const existingComment = await this.getExistingComment(event) - if (existingComment && existingComment.body !== commentBody) { - await this.gitHubClient.updatePullRequestComment({ - appInstallationId: event.appInstallationId, - commentId: existingComment.id, - repositoryOwner: event.repositoryOwner, - repositoryName: event.repositoryName, - body: commentBody - }) - } else if (!existingComment) { - await this.gitHubClient.addCommentToPullRequest({ - appInstallationId: event.appInstallationId, - repositoryOwner: event.repositoryOwner, - repositoryName: event.repositoryName, - pullRequestNumber: event.pullRequestNumber, - body: commentBody - }) - } + }): Promise +} + +export default class PostCommentPullRequestEventHandler implements IPullRequestEventHandler { + private readonly pullRequestCommenter: IPullRequestCommenter + + constructor(config: { pullRequestCommenter: IPullRequestCommenter }) { + this.pullRequestCommenter = config.pullRequestCommenter } - private async getChangedYamlFiles(request: { - appInstallationId: number, - repositoryOwner: string - repositoryName: string - pullRequestNumber: number - }) { - const files = await this.gitHubClient.getPullRequestFiles({ - appInstallationId: request.appInstallationId, - repositoryOwner: request.repositoryOwner, - repositoryName: request.repositoryName, - pullRequestNumber: request.pullRequestNumber - }) - return files - .filter(file => file.filename.match(this.fileExtensionRegex)) - .filter(file => file.status != "unchanged") + async pullRequestOpened(event: IPullRequestOpenedEvent): Promise { + await this.pullRequestCommenter.commentPullRequest(event) } - private async getExistingComment(request: { - appInstallationId: number, - repositoryOwner: string - repositoryName: string - pullRequestNumber: number - }) { - const comments = await this.gitHubClient.getPullRequestComments({ - appInstallationId: request.appInstallationId, - repositoryOwner: request.repositoryOwner, - repositoryName: request.repositoryName, - pullRequestNumber: request.pullRequestNumber, - }) - return comments.find(comment => { - return comment.isFromBot && comment.gitHubApp?.id == this.gitHubAppId - }) + async pullRequestReopened(event: IPullRequestReopenedEvent) { + await this.pullRequestCommenter.commentPullRequest(event) } - private makeCommentBody(params: { - files: PullRequestFile[] - owner: string - repositoryName: string - ref: string - }): string { - const { files, owner, repositoryName, ref } = params - const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") - let rows: { title: string, status: string, link: string }[] = [] - // Make sure we don't include the project configuration file. - const baseConfigFilename = this.projectConfigurationFilename.replace(this.fileExtensionRegex, "") - const changedFiles = files.filter(file => file.filename.replace(this.fileExtensionRegex, "") != baseConfigFilename) - for (const file of changedFiles) { - let status = "" - if (file.status == "added") { - status += "Added" - } else if (file.status == "removed") { - status += "Remvoed" - } else if (file.status == "renamed") { - status += "Renamed" - } else if (file.status == "changed" || file.status == "modified") { - status += "Changed" - } - let link = "" - if (file.status != "removed") { - const url = `${this.domain}/${owner}/${projectId}/${ref}/${file.filename}` - link += ` ${url}` - } - rows.push({ title: file.filename, status, link }) - } - let result = `### 📖 Documentation Preview` - result += `\n\n` - result += `The changes are now ready to previewed on ${this.siteName} 🚀` - if (rows.length > 0) { - const rowsHTML = rows - .map(row => `${row.title}${row.link}${row.status}`) - .join("\n") - result = `${result}\n\n${rowsHTML}
` - } - return result + async pullRequestSynchronized(event: IPullRequestSynchronizedEvent) { + await this.pullRequestCommenter.commentPullRequest(event) } -} +} \ No newline at end of file diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts new file mode 100644 index 00000000..0b706411 --- /dev/null +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -0,0 +1,141 @@ +import { IGitHubClient, PullRequestFile } from "@/common" + +export default class PullRequestCommenter { + private readonly domain: string + private readonly siteName: string + private readonly repositoryNameSuffix: string + private readonly projectConfigurationFilename: string + private readonly gitHubAppId: string + private readonly gitHubClient: IGitHubClient + private readonly fileExtensionRegex = /\.ya?ml$/ + + constructor(config: { + domain: string + siteName: string + repositoryNameSuffix: string + projectConfigurationFilename: string + gitHubAppId: string + gitHubClient: IGitHubClient + }) { + this.domain = config.domain + this.siteName = config.siteName + this.repositoryNameSuffix = config.repositoryNameSuffix + this.projectConfigurationFilename = config.projectConfigurationFilename + this.gitHubAppId = config.gitHubAppId + this.gitHubClient = config.gitHubClient + } + + async commentPullRequest(request: { + appInstallationId: number + repositoryOwner: string + repositoryName: string + ref: string + pullRequestNumber: number + }) { + const files = await this.getChangedYamlFiles(request) + if (files.length == 0) { + // Do nothing if no OpenAPI files were updated. + return + } + const commentBody = this.makeCommentBody({ + files, + owner: request.repositoryOwner, + repositoryName: request.repositoryName, + ref: request.ref + }) + const existingComment = await this.getExistingComment(request) + if (existingComment && existingComment.body !== commentBody) { + await this.gitHubClient.updatePullRequestComment({ + appInstallationId: request.appInstallationId, + commentId: existingComment.id, + repositoryOwner: request.repositoryOwner, + repositoryName: request.repositoryName, + body: commentBody + }) + } else if (!existingComment) { + await this.gitHubClient.addCommentToPullRequest({ + appInstallationId: request.appInstallationId, + repositoryOwner: request.repositoryOwner, + repositoryName: request.repositoryName, + pullRequestNumber: request.pullRequestNumber, + body: commentBody + }) + } + } + + private async getChangedYamlFiles(request: { + appInstallationId: number, + repositoryOwner: string + repositoryName: string + pullRequestNumber: number + }) { + const files = await this.gitHubClient.getPullRequestFiles({ + appInstallationId: request.appInstallationId, + repositoryOwner: request.repositoryOwner, + repositoryName: request.repositoryName, + pullRequestNumber: request.pullRequestNumber + }) + return files + .filter(file => file.filename.match(this.fileExtensionRegex)) + .filter(file => file.status != "unchanged") + } + + private async getExistingComment(request: { + appInstallationId: number, + repositoryOwner: string + repositoryName: string + pullRequestNumber: number + }) { + const comments = await this.gitHubClient.getPullRequestComments({ + appInstallationId: request.appInstallationId, + repositoryOwner: request.repositoryOwner, + repositoryName: request.repositoryName, + pullRequestNumber: request.pullRequestNumber, + }) + return comments.find(comment => { + return comment.isFromBot && comment.gitHubApp?.id == this.gitHubAppId + }) + } + + private makeCommentBody(params: { + files: PullRequestFile[] + owner: string + repositoryName: string + ref: string + }): string { + const { files, owner, repositoryName, ref } = params + const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") + let rows: { title: string, status: string, link: string }[] = [] + // Make sure we don't include the project configuration file. + const baseConfigFilename = this.projectConfigurationFilename.replace(this.fileExtensionRegex, "") + const changedFiles = files.filter(file => file.filename.replace(this.fileExtensionRegex, "") != baseConfigFilename) + for (const file of changedFiles) { + let status = "" + if (file.status == "added") { + status += "Added" + } else if (file.status == "removed") { + status += "Remvoed" + } else if (file.status == "renamed") { + status += "Renamed" + } else if (file.status == "changed" || file.status == "modified") { + status += "Changed" + } + let link = "" + if (file.status != "removed") { + const url = `${this.domain}/${owner}/${projectId}/${ref}/${file.filename}` + link += ` ${url}` + } + rows.push({ title: file.filename, status, link }) + } + let result = `### 📖 Documentation Preview` + result += `\n\n` + result += `The changes are now ready to previewed on ${this.siteName} 🚀` + if (rows.length > 0) { + const rowsHTML = rows + .map(row => `${row.title}${row.link}${row.status}`) + .join("\n") + result = `${result}\n\n${rowsHTML}
` + } + return result + } +} diff --git a/src/features/hooks/domain/index.ts b/src/features/hooks/domain/index.ts index bfd3bc5c..2e271bbd 100644 --- a/src/features/hooks/domain/index.ts +++ b/src/features/hooks/domain/index.ts @@ -2,3 +2,4 @@ export type { default as IPullRequestEventHandler } from "./IPullRequestEventHan export { default as PostCommentPullRequestEventHandler } from "./PostCommentPullRequestEventHandler" export { default as FilteringPullRequestEventHandler } from "./FilteringPullRequestEventHandler" export { default as RepositoryNameEventFilter } from "./RepositoryNameEventFilter" +export { default as PullRequestCommenter } from "./PullRequestCommenter" From 27531c3981c4b28c4be6176b8adc7d07c378ac05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 14:22:40 +0200 Subject: [PATCH 163/191] Improves formatting --- src/features/hooks/domain/PullRequestCommenter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts index 0b706411..2dbdedb5 100644 --- a/src/features/hooks/domain/PullRequestCommenter.ts +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -132,8 +132,8 @@ export default class PullRequestCommenter { result += `The changes are now ready to previewed on ${this.siteName} 🚀` if (rows.length > 0) { const rowsHTML = rows - .map(row => `${row.title}${row.link}${row.status}`) - .join("\n") + .map(row => `${row.title}${row.link}${row.status}`) + .join("\n") result = `${result}\n\n${rowsHTML}
` } return result From c9f6af49dae4e152e480c660f684588230125715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 14:23:24 +0200 Subject: [PATCH 164/191] Updates comment when removing relevant file changes --- src/features/hooks/domain/PullRequestCommenter.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts index 2dbdedb5..df03f5ea 100644 --- a/src/features/hooks/domain/PullRequestCommenter.ts +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -33,10 +33,6 @@ export default class PullRequestCommenter { pullRequestNumber: number }) { const files = await this.getChangedYamlFiles(request) - if (files.length == 0) { - // Do nothing if no OpenAPI files were updated. - return - } const commentBody = this.makeCommentBody({ files, owner: request.repositoryOwner, @@ -52,7 +48,7 @@ export default class PullRequestCommenter { repositoryName: request.repositoryName, body: commentBody }) - } else if (!existingComment) { + } else if (!existingComment && files.length > 0) { await this.gitHubClient.addCommentToPullRequest({ appInstallationId: request.appInstallationId, repositoryOwner: request.repositoryOwner, From 42af1c0a7ec4920f7fae83a2972a2ea8dcf265f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 14:23:42 +0200 Subject: [PATCH 165/191] Adds tests to PullRequestCommenter --- __test__/hooks/PullRequestCommenter.test.ts | 390 ++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 __test__/hooks/PullRequestCommenter.test.ts diff --git a/__test__/hooks/PullRequestCommenter.test.ts b/__test__/hooks/PullRequestCommenter.test.ts new file mode 100644 index 00000000..8b96586f --- /dev/null +++ b/__test__/hooks/PullRequestCommenter.test.ts @@ -0,0 +1,390 @@ +import { PullRequestCommenter } from "../../src/features/hooks/domain" + +test("It adds comment when none exist", async () => { + let didAddComment = false + const sut = new PullRequestCommenter({ + domain: "https://example.com", + siteName: "Demo Docs", + repositoryNameSuffix: "-openapi", + projectConfigurationFilename: ".demo-docs.yml", + gitHubAppId: "appid1234", + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestFiles() { + return [{ + filename: "openapi.yml", + status: "changed" + }] + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest() { + didAddComment = true + }, + async updatePullRequestComment() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } + } + }) + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didAddComment).toBeTruthy() +}) + +test("It adds comment containing list of changed files", async () => { + let didAddComment = false + let commentBody: string | undefined + const sut = new PullRequestCommenter({ + domain: "https://example.com", + siteName: "Demo Docs", + repositoryNameSuffix: "-openapi", + projectConfigurationFilename: ".demo-docs.yml", + gitHubAppId: "appid1234", + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestFiles() { + return [{ + filename: "foo.yml", + status: "changed" + }, { + filename: "bar.yml", + status: "changed" + }] + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest(request) { + didAddComment = true + commentBody = request.body + }, + async updatePullRequestComment() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } + } + }) + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didAddComment).toBeTruthy() + expect(commentBody).toContain("") + expect(commentBody).toContain("foo.yml") + expect(commentBody).toContain("bar.yml") +}) + +test("It skips adding comment when no YAML files were found in the PR", async () => { + let didAddComment = false + const sut = new PullRequestCommenter({ + domain: "https://example.com", + siteName: "Demo Docs", + repositoryNameSuffix: "-openapi", + projectConfigurationFilename: ".demo-docs.yml", + gitHubAppId: "appid1234", + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestFiles() { + return [{ + filename: "dummy.swift", + status: "changed" + }] + }, + async getPullRequestComments() { + return [] + }, + async addCommentToPullRequest() { + didAddComment = true + }, + async updatePullRequestComment() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } + } + }) + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didAddComment).toBeFalsy() +}) + +test("It updates comment when one already exists", async () => { + let didUpdateComment = false + const sut = new PullRequestCommenter({ + domain: "https://example.com", + siteName: "Demo Docs", + repositoryNameSuffix: "-openapi", + projectConfigurationFilename: ".demo-docs.yml", + gitHubAppId: "appid1234", + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestFiles() { + return [{ + filename: "openapi.yml", + status: "changed" + }] + }, + async getPullRequestComments() { + return [{ + id: 1, + body: "Hello world!", + isFromBot: true, + gitHubApp: { + id: "appid1234" + } + }] + }, + async addCommentToPullRequest() {}, + async updatePullRequestComment() { + didUpdateComment = true + }, + async getOrganizationMembershipStatus() { + return { state: "active" } + } + } + }) + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didUpdateComment).toBeTruthy() +}) + +test("It skips updating comment when the body has not changed", async () => { + let addedCommentBody: string | undefined + let didUpdateComment = false + const sut = new PullRequestCommenter({ + domain: "https://example.com", + siteName: "Demo Docs", + repositoryNameSuffix: "-openapi", + projectConfigurationFilename: ".demo-docs.yml", + gitHubAppId: "appid1234", + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestFiles() { + return [{ + filename: "openapi.yml", + status: "changed" + }] + }, + async getPullRequestComments() { + if (!addedCommentBody) { + return [] + } + return [{ + id: 1, + body: addedCommentBody, + isFromBot: true, + gitHubApp: { + id: "appid1234" + } + }] + }, + async addCommentToPullRequest(request) { + addedCommentBody = request.body + }, + async updatePullRequestComment() { + didUpdateComment = true + }, + async getOrganizationMembershipStatus() { + return { state: "active" } + } + } + }) + // Add a comment. + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + // Attempt to update a comment. This is skipped because the body has not changed. + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(addedCommentBody).not.toBeUndefined() + expect(didUpdateComment).toBeFalsy() +}) + +test("It updates comment to remove file list when all relevant file changes were removed from the PR", async () => { + let didAddComment = false + let didUpdateComment = false + let addedCommentBody: string | undefined + let updatedCommentBody: string | undefined + const sut = new PullRequestCommenter({ + domain: "https://example.com", + siteName: "Demo Docs", + repositoryNameSuffix: "-openapi", + projectConfigurationFilename: ".demo-docs.yml", + gitHubAppId: "appid1234", + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestFiles() { + if (didAddComment) { + // Simulate relevant files removed. + return [] + } else { + return [{ + filename: "openapi.yml", + status: "changed" + }] + } + }, + async getPullRequestComments() { + if (!didAddComment) { + return [] + } + return [{ + id: 1, + body: "Hello world", + isFromBot: true, + gitHubApp: { + id: "appid1234" + } + }] + }, + async addCommentToPullRequest(request) { + didAddComment = true + addedCommentBody = request.body + }, + async updatePullRequestComment(request) { + didUpdateComment = true + updatedCommentBody = request.body + }, + async getOrganizationMembershipStatus() { + return { state: "active" } + } + } + }) + // Add a comment. + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + // Attempt to update a comment. This is skipped because the body has not changed. + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didAddComment).toBeTruthy() + expect(didUpdateComment).toBeTruthy() + expect(addedCommentBody).toContain("
") + expect(addedCommentBody).toContain("openapi.yml") + expect(updatedCommentBody).not.toContain("
") + expect(updatedCommentBody).not.toContain("openapi.yml") +}) + +test("It adds comment without file table if only project configuration was edited", async () => { + let didAddComment = false + let commentBody: string | undefined + const sut = new PullRequestCommenter({ + domain: "https://example.com", + siteName: "Demo Docs", + repositoryNameSuffix: "-openapi", + projectConfigurationFilename: ".demo-docs.yml", + gitHubAppId: "appid1234", + gitHubClient: { + async graphql() { + return {} + }, + async getRepositoryContent() { + return { downloadURL: "https://example.com" } + }, + async getPullRequestFiles() { + return [{ + filename: ".demo-docs.yml", + status: "changed" + }] + }, + async getPullRequestComments() { + if (!didAddComment) { + return [] + } + return [{ + id: 1, + body: "Hello world", + isFromBot: true, + gitHubApp: { + id: "appid1234" + } + }] + }, + async addCommentToPullRequest(request) { + didAddComment = true + commentBody = request.body + }, + async updatePullRequestComment() {}, + async getOrganizationMembershipStatus() { + return { state: "active" } + } + } + }) + await sut.commentPullRequest({ + appInstallationId: 1234, + pullRequestNumber: 42, + repositoryOwner: "acme", + repositoryName: "demo-openapi", + ref: "main" + }) + expect(didAddComment).toBeTruthy() + expect(commentBody).not.toContain("
") + expect(commentBody).not.toContain(".demo-docs.yml") +}) From b0bb4a22f9059a67e2c5446da318277deeff6ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 14:24:36 +0200 Subject: [PATCH 166/191] Fixes typo --- src/features/hooks/domain/PullRequestCommenter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts index df03f5ea..9d6690e5 100644 --- a/src/features/hooks/domain/PullRequestCommenter.ts +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -110,7 +110,7 @@ export default class PullRequestCommenter { if (file.status == "added") { status += "Added" } else if (file.status == "removed") { - status += "Remvoed" + status += "Removed" } else if (file.status == "renamed") { status += "Renamed" } else if (file.status == "changed" || file.status == "modified") { From da79b2401e5e029f2de9b86f7808fc980716e36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 14:25:48 +0200 Subject: [PATCH 167/191] Adds getStatusText --- .../hooks/domain/PullRequestCommenter.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts index 9d6690e5..76f4bf82 100644 --- a/src/features/hooks/domain/PullRequestCommenter.ts +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -106,16 +106,7 @@ export default class PullRequestCommenter { const baseConfigFilename = this.projectConfigurationFilename.replace(this.fileExtensionRegex, "") const changedFiles = files.filter(file => file.filename.replace(this.fileExtensionRegex, "") != baseConfigFilename) for (const file of changedFiles) { - let status = "" - if (file.status == "added") { - status += "Added" - } else if (file.status == "removed") { - status += "Removed" - } else if (file.status == "renamed") { - status += "Renamed" - } else if (file.status == "changed" || file.status == "modified") { - status += "Changed" - } + const status = this.getStatusText(file) let link = "" if (file.status != "removed") { const url = `${this.domain}/${owner}/${projectId}/${ref}/${file.filename}` @@ -134,4 +125,18 @@ export default class PullRequestCommenter { } return result } + + private getStatusText(file: PullRequestFile) { + if (file.status == "added") { + return "Added" + } else if (file.status == "removed") { + return "Removed" + } else if (file.status == "renamed") { + return "Renamed" + } else if (file.status == "changed" || file.status == "modified") { + return "Changed" + } else { + return "" + } + } } From b7e4eede1aa2636279c3d3cc1137883793d39929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 14:31:52 +0200 Subject: [PATCH 168/191] Refactor PullRequestCommenter --- .../hooks/domain/PullRequestCommenter.ts | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts index 76f4bf82..4b4551f9 100644 --- a/src/features/hooks/domain/PullRequestCommenter.ts +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -99,12 +99,31 @@ export default class PullRequestCommenter { repositoryName: string ref: string }): string { + const { owner, repositoryName, ref } = params + const projectId = this.getProjectId({ repositoryName }) + const tableHTML = this.makeFileTableHTML(params) + let result = "### 📖 Documentation Preview" + result += "\n\n" + result += `The changes are now ready to previewed on ${this.siteName} 🚀` + if (tableHTML) { + result += "\n\n" + tableHTML + } + return result + } + + private makeFileTableHTML(params: { + files: PullRequestFile[] + owner: string + repositoryName: string + ref: string + }) { const { files, owner, repositoryName, ref } = params - const projectId = repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") - let rows: { title: string, status: string, link: string }[] = [] + let rows: { filename: string, link: string, status: string }[] = [] + const projectId = this.getProjectId({ repositoryName }) // Make sure we don't include the project configuration file. const baseConfigFilename = this.projectConfigurationFilename.replace(this.fileExtensionRegex, "") const changedFiles = files.filter(file => file.filename.replace(this.fileExtensionRegex, "") != baseConfigFilename) + // Create rows for each file for (const file of changedFiles) { const status = this.getStatusText(file) let link = "" @@ -112,18 +131,15 @@ export default class PullRequestCommenter { const url = `${this.domain}/${owner}/${projectId}/${ref}/${file.filename}` link += ` ${url}` } - rows.push({ title: file.filename, status, link }) + rows.push({ filename: file.filename, status, link }) } - let result = `### 📖 Documentation Preview` - result += `\n\n` - result += `The changes are now ready to previewed on ${this.siteName} 🚀` - if (rows.length > 0) { - const rowsHTML = rows - .map(row => ``) - .join("\n") - result = `${result}\n\n
${row.title}${row.link}${row.status}
${rowsHTML}
` + if (rows.length == 0) { + return undefined } - return result + const rowsHTML = rows + .map(row => `${row.filename}${row.link}${row.status}`) + .join("\n") + return `${rowsHTML}
` } private getStatusText(file: PullRequestFile) { @@ -139,4 +155,8 @@ export default class PullRequestCommenter { return "" } } + + private getProjectId({ repositoryName }: { repositoryName: string }): string { + return repositoryName.replace(new RegExp(this.repositoryNameSuffix + "$"), "") + } } From 3fa1274d1a4934d4d36fabdc2e5a80fe246bcf21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 14:33:32 +0200 Subject: [PATCH 169/191] Removes getOrganizationMembershipStatus() from IGitHubClient --- .../OAuthTokenRefreshingGitHubClient.test.ts | 35 ++++--------------- __test__/hooks/PullRequestCommenter.test.ts | 29 +++------------ src/common/github/GitHubClient.ts | 13 ------- src/common/github/IGitHubClient.ts | 9 ----- .../OAuthTokenRefreshingGitHubClient.ts | 10 ------ 5 files changed, 11 insertions(+), 85 deletions(-) diff --git a/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts b/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts index 8610daa0..7e353354 100644 --- a/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts +++ b/__test__/common/github/OAuthTokenRefreshingGitHubClient.test.ts @@ -37,10 +37,7 @@ test("It forwards a GraphQL request", async () => { return [] }, async updatePullRequestComment() {}, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async addCommentToPullRequest() {} } }) const request: GraphQLQueryRequest = { @@ -82,10 +79,7 @@ test("It forwards a request to get the repository content", async () => { return [] }, async addCommentToPullRequest() {}, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) const request: GetRepositoryContentRequest = { @@ -129,10 +123,7 @@ test("It forwards a request to get comments to a pull request", async () => { return [] }, async addCommentToPullRequest() {}, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) const request: GetPullRequestCommentsRequest = { @@ -177,10 +168,7 @@ test("It forwards a request to add a comment to a pull request", async () => { async addCommentToPullRequest(request: AddCommentToPullRequestRequest) { forwardedRequest = request }, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) const request: AddCommentToPullRequestRequest = { @@ -230,10 +218,7 @@ test("It retries with a refreshed OAuth token when receiving HTTP 401", async () return [] }, async updatePullRequestComment() {}, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async addCommentToPullRequest() {} } }) const request: GraphQLQueryRequest = { @@ -275,10 +260,7 @@ test("It only retries a request once when receiving HTTP 401", async () => { return [] }, async updatePullRequestComment() {}, - async addCommentToPullRequest() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async addCommentToPullRequest() {} } }) const request: GraphQLQueryRequest = { @@ -323,10 +305,7 @@ test("It does not refresh an OAuth token when the initial request was successful return [] }, async addCommentToPullRequest() {}, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) const request: GraphQLQueryRequest = { diff --git a/__test__/hooks/PullRequestCommenter.test.ts b/__test__/hooks/PullRequestCommenter.test.ts index 8b96586f..0091b8d4 100644 --- a/__test__/hooks/PullRequestCommenter.test.ts +++ b/__test__/hooks/PullRequestCommenter.test.ts @@ -27,10 +27,7 @@ test("It adds comment when none exist", async () => { async addCommentToPullRequest() { didAddComment = true }, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) await sut.commentPullRequest({ @@ -75,10 +72,7 @@ test("It adds comment containing list of changed files", async () => { didAddComment = true commentBody = request.body }, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) await sut.commentPullRequest({ @@ -121,10 +115,7 @@ test("It skips adding comment when no YAML files were found in the PR", async () async addCommentToPullRequest() { didAddComment = true }, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) await sut.commentPullRequest({ @@ -171,9 +162,6 @@ test("It updates comment when one already exists", async () => { async addCommentToPullRequest() {}, async updatePullRequestComment() { didUpdateComment = true - }, - async getOrganizationMembershipStatus() { - return { state: "active" } } } }) @@ -227,9 +215,6 @@ test("It skips updating comment when the body has not changed", async () => { }, async updatePullRequestComment() { didUpdateComment = true - }, - async getOrganizationMembershipStatus() { - return { state: "active" } } } }) @@ -302,9 +287,6 @@ test("It updates comment to remove file list when all relevant file changes wer async updatePullRequestComment(request) { didUpdateComment = true updatedCommentBody = request.body - }, - async getOrganizationMembershipStatus() { - return { state: "active" } } } }) @@ -371,10 +353,7 @@ test("It adds comment without file table if only project configuration was edite didAddComment = true commentBody = request.body }, - async updatePullRequestComment() {}, - async getOrganizationMembershipStatus() { - return { state: "active" } - } + async updatePullRequestComment() {} } }) await sut.commentPullRequest({ diff --git a/src/common/github/GitHubClient.ts b/src/common/github/GitHubClient.ts index 18f74b28..3ecf10e1 100644 --- a/src/common/github/GitHubClient.ts +++ b/src/common/github/GitHubClient.ts @@ -8,8 +8,6 @@ import IGitHubClient, { GetPullRequestFilesRequest, AddCommentToPullRequestRequest, UpdatePullRequestCommentRequest, - GetOrganizationMembershipStatusRequest, - GetOrganizationMembershipStatusRequestResponse, RepositoryContent, PullRequestComment, PullRequestFile @@ -121,15 +119,4 @@ export default class GitHubClient implements IGitHubClient { body: request.body }) } - - async getOrganizationMembershipStatus( - request: GetOrganizationMembershipStatusRequest - ): Promise { - const oauthToken = await this.oauthTokenDataSource.getOAuthToken() - const octokit = new Octokit({ auth: oauthToken.accessToken }) - const response = await octokit.rest.orgs.getMembershipForAuthenticatedUser({ - org: request.organizationName - }) - return { state: response.data.state } - } } diff --git a/src/common/github/IGitHubClient.ts b/src/common/github/IGitHubClient.ts index 6cace33a..bf2c4a75 100644 --- a/src/common/github/IGitHubClient.ts +++ b/src/common/github/IGitHubClient.ts @@ -70,14 +70,6 @@ export type UpdatePullRequestCommentRequest = { readonly body: string } -export type GetOrganizationMembershipStatusRequest = { - readonly organizationName: string -} - -export type GetOrganizationMembershipStatusRequestResponse = { - readonly state: "active" | "pending" -} - export default interface IGitHubClient { graphql(request: GraphQLQueryRequest): Promise getRepositoryContent(request: GetRepositoryContentRequest): Promise @@ -85,5 +77,4 @@ export default interface IGitHubClient { getPullRequestComments(request: GetPullRequestCommentsRequest): Promise addCommentToPullRequest(request: AddCommentToPullRequestRequest): Promise updatePullRequestComment(request: UpdatePullRequestCommentRequest): Promise - getOrganizationMembershipStatus(request: GetOrganizationMembershipStatusRequest): Promise } diff --git a/src/common/github/OAuthTokenRefreshingGitHubClient.ts b/src/common/github/OAuthTokenRefreshingGitHubClient.ts index 59222897..48b6373e 100644 --- a/src/common/github/OAuthTokenRefreshingGitHubClient.ts +++ b/src/common/github/OAuthTokenRefreshingGitHubClient.ts @@ -4,8 +4,6 @@ import IGitHubClient, { GraphQlQueryResponse, GetRepositoryContentRequest, GetPullRequestCommentsRequest, - GetOrganizationMembershipStatusRequest, - GetOrganizationMembershipStatusRequestResponse, AddCommentToPullRequestRequest, UpdatePullRequestCommentRequest, GetPullRequestFilesRequest, @@ -79,14 +77,6 @@ export default class OAuthTokenRefreshingGitHubClient implements IGitHubClient { }) } - async getOrganizationMembershipStatus( - request: GetOrganizationMembershipStatusRequest - ): Promise { - return await this.send(async () => { - return await this.gitHubClient.getOrganizationMembershipStatus(request) - }) - } - private async send(fn: () => Promise): Promise { const oauthToken = await this.oauthTokenDataSource.getOAuthToken() try { From ed4459016d0d393da9f8c88e6e13667c73ea49a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 15:58:41 +0200 Subject: [PATCH 170/191] Fixes building workflow --- src/features/auth/view/SessionBarrier.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/features/auth/view/SessionBarrier.tsx b/src/features/auth/view/SessionBarrier.tsx index 68e4c3c8..8f1fdf0d 100644 --- a/src/features/auth/view/SessionBarrier.tsx +++ b/src/features/auth/view/SessionBarrier.tsx @@ -1,5 +1,5 @@ import { ReactNode } from "react" -import { session, blockingSessionValidator } from "@/composition" +import { blockingSessionValidator } from "@/composition" import ClientSessionBarrier from "./client/SessionBarrier" export default async function SessionBarrier({ @@ -9,10 +9,7 @@ export default async function SessionBarrier({ }) { const sessionValidity = await blockingSessionValidator.validateSession() return ( - + {children} ) From b9b3eafdafc000b394f9346cd171f73418f1819d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:10:48 +0200 Subject: [PATCH 171/191] Fixes attempting to connect to Redis during build --- src/common/key-value-store/RedisKeyValueStore.ts | 14 ++++++++++++-- src/common/mutex/RedisKeyedMutexFactory.ts | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/common/key-value-store/RedisKeyValueStore.ts b/src/common/key-value-store/RedisKeyValueStore.ts index 8c4c5401..0dd56cd5 100644 --- a/src/common/key-value-store/RedisKeyValueStore.ts +++ b/src/common/key-value-store/RedisKeyValueStore.ts @@ -2,10 +2,11 @@ import IKeyValueStore from "./IKeyValueStore" import Redis from "ioredis" export default class RedisKeyValueStore implements IKeyValueStore { - private readonly redis: Redis + private readonly url: string + private _redis: Redis | undefined constructor(url: string) { - this.redis = new Redis(url) + this.url = url } async get(key: string): Promise { @@ -27,4 +28,13 @@ export default class RedisKeyValueStore implements IKeyValueStore { async delete(key: string): Promise { await this.redis.del(key) } + + private get redis(): Redis { + if (this._redis) { + return this._redis + } + const redis = new Redis(this.url) + this._redis = redis + return redis + } } diff --git a/src/common/mutex/RedisKeyedMutexFactory.ts b/src/common/mutex/RedisKeyedMutexFactory.ts index f4fae94f..5605126e 100644 --- a/src/common/mutex/RedisKeyedMutexFactory.ts +++ b/src/common/mutex/RedisKeyedMutexFactory.ts @@ -20,13 +20,23 @@ class RedisMutex implements IMutex { } export default class RedisKeyedMutexFactory implements IKeyedMutexFactory { - private readonly redis: Redis + private readonly url: string + private _redis: Redis | undefined constructor(url: string) { - this.redis = new Redis(url) + this.url = url } makeMutex(key: string): IMutex { return new RedisMutex(this.redis, key) } + + private get redis(): Redis { + if (this._redis) { + return this._redis + } + const redis = new Redis(this.url) + this._redis = redis + return redis + } } From d7b67a364d868deb0b494940d111c689f608a797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:16:33 +0200 Subject: [PATCH 172/191] Updates dependencies --- package-lock.json | 1178 +++++++++++++++++++++++++-------------------- 1 file changed, 650 insertions(+), 528 deletions(-) diff --git a/package-lock.json b/package-lock.json index 216af57d..7065d875 100644 --- a/package-lock.json +++ b/package-lock.json @@ -148,9 +148,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", + "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", "devOptional": true, "peer": true, "engines": { @@ -158,22 +158,22 @@ } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", + "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", "devOptional": true, "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", + "@babel/generator": "^7.24.9", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-module-transforms": "^7.24.9", + "@babel/helpers": "^7.24.8", + "@babel/parser": "^7.24.8", "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -206,11 +206,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.9.tgz", + "integrity": "sha512-G8v3jRg+z8IwY1jHFxvCNhOPYPterE4XljNgdGTYfSTtzzwjIswIzIaSPSLs3R7yFuqnqNeay5rjICfqVr+/6A==", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.24.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -220,15 +220,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", + "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", "devOptional": true, "peer": true, "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -303,9 +303,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", + "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", "devOptional": true, "peer": true, "dependencies": { @@ -323,9 +323,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "peer": true, "engines": { @@ -358,9 +358,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "engines": { "node": ">=6.9.0" } @@ -374,9 +374,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "devOptional": true, "peer": true, "engines": { @@ -384,14 +384,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", + "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", "devOptional": true, "peer": true, "dependencies": { "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -412,9 +412,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", "bin": { "parser": "bin/babel-parser.js" }, @@ -614,9 +614,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -625,9 +625,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.7.tgz", - "integrity": "sha512-eytSX6JLBY6PVAeQa2bFlDx/7Mmln/gaEpsit5a3WEvjGfiIytEsgAwuIXCPM0xvw0v0cJn3ilq0/TvXrW0kgA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.8.tgz", + "integrity": "sha512-DXG/BhegtMHhnN7YPIvxWd303/9aXvYFD1TjNL3CD6tUrhI2LVsg3Lck0aql5TRH29n4sj3emcROypkZVUfSuA==", "dependencies": { "core-js-pure": "^3.30.2", "regenerator-runtime": "^0.14.0" @@ -650,18 +650,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", + "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", + "@babel/generator": "^7.24.8", "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-function-name": "^7.24.7", "@babel/helper-hoist-variables": "^7.24.7", "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/parser": "^7.24.8", + "@babel/types": "^7.24.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -670,11 +670,11 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", + "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -860,9 +860,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -960,20 +960,20 @@ "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==" }, "node_modules/@floating-ui/core": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.3.tgz", - "integrity": "sha512-1ZpCvYf788/ZXOhRQGFxnYQOVgeU+pi0i+d0Ow34La7qjIXETi6RNswGVKkA6KcDO8/+Ysu2E/CeUmmeEBDvTg==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", + "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", "dependencies": { - "@floating-ui/utils": "^0.2.3" + "@floating-ui/utils": "^0.2.4" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.6.tgz", - "integrity": "sha512-qiTYajAnh3P+38kECeffMSQgbvXty2VB6rS+42iWR4FPIlZjLK84E9qtLnMTLIpPz2znD/TaFqaiavMUrS+Hcw==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", + "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.3" + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.4" } }, "node_modules/@floating-ui/react-dom": { @@ -989,9 +989,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.3.tgz", - "integrity": "sha512-XGndio0l5/Gvd6CLIABvsav9HHezgDFFhDfHk1bvLfr9ni8dojqLSvBbotJEjmIwNHL7vK4QzBJTdBRoB+c1ww==" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", + "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.5.2", @@ -2004,9 +2004,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -2059,18 +2059,18 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.15.20", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.20.tgz", - "integrity": "sha512-DoL2ppgldL16utL8nNyj/P12f8mCNdx/Hb/AJnX9rLY4b52hCMIx1kH83pbXQ6uMy6n54M3StmEbvSGoj2OFuA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.1.tgz", + "integrity": "sha512-62Jq7ACYi/55Kjkh/nVfEL3F3ytTYTsdB8MGJ9iI+eRQv+Aoem5CPUAzQihUo25qqh1VkVu9/jQn3dFbyrXHgw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.15.20", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.20.tgz", - "integrity": "sha512-oGcKmCuHaYbAAoLN67WKSXtHmEgyWcJToT1uRtmPyxMj9N5uqwc/mRtEnst4Wj/eGr+zYH2FiZQ79v9k7kSk1Q==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.1.tgz", + "integrity": "sha512-ogQPweYba4+5XZykilwxn2/oS78uwoQ0BVBpOhhCJo0ooZsqTTsalhzP2qD/RdGqMQ8xyXPz1sYM2djTruVVVA==", "dependencies": { "@babel/runtime": "^7.23.9" }, @@ -2093,21 +2093,21 @@ } }, "node_modules/@mui/material": { - "version": "5.15.20", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.20.tgz", - "integrity": "sha512-tVq3l4qoXx/NxUgIx/x3lZiPn/5xDbdTE8VrLczNpfblLYZzlrbxA7kb9mI8NoBF6+w9WE9IrxWnKK5KlPI2bg==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.1.tgz", + "integrity": "sha512-BGTgJRb0d/hX9tus5CEb6N/Fo8pE4tYA+s9r4/S0PCrtZ3urCLXlTH4qrAvggQbiF1cYRAbHCkVHoQ+4Pdxl+w==", "dependencies": { "@babel/runtime": "^7.23.9", "@mui/base": "5.0.0-beta.40", - "@mui/core-downloads-tracker": "^5.15.20", - "@mui/system": "^5.15.20", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.20", + "@mui/core-downloads-tracker": "^5.16.1", + "@mui/system": "^5.16.1", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.1", "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1", - "react-is": "^18.2.0", + "react-is": "^18.3.1", "react-transition-group": "^4.4.5" }, "engines": { @@ -2137,12 +2137,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.15.20", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.20.tgz", - "integrity": "sha512-BK8F94AIqSrnaPYXf2KAOjGZJgWfvqAVQ2gVR3EryvQFtuBnG6RwodxrCvd3B48VuMy6Wsk897+lQMUxJyk+6g==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.1.tgz", + "integrity": "sha512-2EGCKnAlq9vRIFj61jNWNXlKAxXp56577OVvsts7fAqRx+G1y6F+N7Q198SBaz8jYQeGKSz8ZMXK/M3FqjdEyw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.20", + "@mui/utils": "^5.16.1", "prop-types": "^15.8.1" }, "engines": { @@ -2163,9 +2163,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", - "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.1.tgz", + "integrity": "sha512-JwWUBaYR8HHCFefSeos0z6JoTbu0MnjAuNHu4QoDgPxl2EE70XH38CsKay66Iy0QkNWmGTRXVU2sVFgUOPL/Dw==", "dependencies": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", @@ -2194,15 +2194,15 @@ } }, "node_modules/@mui/system": { - "version": "5.15.20", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.20.tgz", - "integrity": "sha512-LoMq4IlAAhxzL2VNUDBTQxAb4chnBe8JvRINVNDiMtHE2PiPOoHlhOPutSxEbaL5mkECPVWSv6p8JEV+uykwIA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.1.tgz", + "integrity": "sha512-VaFcClC+uhvIEzhzcNmh9FRBvrG9IPjsOokhj6U1HPZsFnLzHV7AD7dJcT6LxWoiIZj9Ej0GK+MGh/b8+BtSlQ==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.20", - "@mui/styled-engine": "^5.15.14", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.20", + "@mui/private-theming": "^5.16.1", + "@mui/styled-engine": "^5.16.1", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.1", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -2233,9 +2233,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.14", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", - "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", + "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -2246,14 +2246,14 @@ } }, "node_modules/@mui/utils": { - "version": "5.15.20", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.20.tgz", - "integrity": "sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.1.tgz", + "integrity": "sha512-4UQzK46tAEYs2xZv79hRiIc3GxZScd00kGPDadNrGztAEZlmSaUY8cb9ITd2xCiTfzsx5AN6DH8aaQ8QEKJQeQ==", "dependencies": { "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.11", + "@types/prop-types": "^15.7.12", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^18.3.1" }, "engines": { "node": ">=12.0.0" @@ -2273,23 +2273,23 @@ } }, "node_modules/@next/env": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.4.tgz", - "integrity": "sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==" + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.5.tgz", + "integrity": "sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==" }, "node_modules/@next/eslint-plugin-next": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.4.tgz", - "integrity": "sha512-svSFxW9f3xDaZA3idQmlFw7SusOuWTpDTAeBlO3AEPDltrraV+lqs7mAc6A27YdnpQVVIA3sODqUAAHdWhVWsA==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.5.tgz", + "integrity": "sha512-LY3btOpPh+OTIpviNojDpUdIbHW9j0JBYBjsIp8IxtDFfYFyORvw3yNq6N231FVqQA7n7lwaf7xHbVJlA1ED7g==", "dev": true, "dependencies": { "glob": "10.3.10" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.4.tgz", - "integrity": "sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz", + "integrity": "sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==", "cpu": [ "arm64" ], @@ -2302,9 +2302,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz", - "integrity": "sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz", + "integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==", "cpu": [ "x64" ], @@ -2317,9 +2317,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz", - "integrity": "sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz", + "integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==", "cpu": [ "arm64" ], @@ -2332,9 +2332,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz", - "integrity": "sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz", + "integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==", "cpu": [ "arm64" ], @@ -2347,9 +2347,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz", - "integrity": "sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz", + "integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==", "cpu": [ "x64" ], @@ -2362,9 +2362,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz", - "integrity": "sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz", + "integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==", "cpu": [ "x64" ], @@ -2377,9 +2377,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz", - "integrity": "sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz", + "integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==", "cpu": [ "arm64" ], @@ -2392,9 +2392,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz", - "integrity": "sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz", + "integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==", "cpu": [ "ia32" ], @@ -2407,9 +2407,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz", - "integrity": "sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz", + "integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==", "cpu": [ "x64" ], @@ -2846,9 +2846,9 @@ "integrity": "sha512-FE2V+QZ2UYlh+9wWd5BPLNXG+J/XUD/PPq0ovS+nCcGX4+3qVbi3jYOmCTW48hg9SBBLtInx9+o7fFt4H5iP0Q==" }, "node_modules/@panva/hkdf": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.0.tgz", - "integrity": "sha512-97ZQvZJ4gJhi24Io6zI+W7B67I82q1I8i3BSzQ4OyZj1z4OW87/ruF26lrMES58inTKLy2KgVIDcx8PU4AaANQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -2995,17 +2995,17 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/@redocly/config": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.6.1.tgz", - "integrity": "sha512-p4mlj+CD3Byec3wOxDlDln0B0gOcNvEkpl4jn3/e9y8h11ogzXnWxnlJAtE5Kcr1ByujS/7Mbt01df9z1xfMbg==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.6.3.tgz", + "integrity": "sha512-hGWJgCsXRw0Ow4rplqRlUQifZvoSwZipkYnt11e3SeH1Eb23VUIDBcRuaQOUqy1wn0eevXkU2GzzQ8fbKdQ7Mg==" }, "node_modules/@redocly/openapi-core": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.16.0.tgz", - "integrity": "sha512-z06h+svyqbUcdAaePq8LPSwTPlm6Ig7j2VlL8skPBYnJvyaQ2IN7x/JkOvRL4ta+wcOCBdAex5JWnZbKaNktJg==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.18.0.tgz", + "integrity": "sha512-kcbt7w23pcVYGLnJkh2LZpXF1OX5RDM4DLOtwPug2HvRE8ow/YfY8ZEM1YCFlA41D8rBPBVP918cYeIx4BVUbw==", "dependencies": { "@redocly/ajv": "^8.11.0", - "@redocly/config": "^0.6.0", + "@redocly/config": "^0.6.2", "colorette": "^1.2.0", "https-proxy-agent": "^7.0.4", "js-levenshtein": "^1.1.6", @@ -3948,12 +3948,12 @@ "integrity": "sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==" }, "node_modules/@swagger-api/apidom-ast": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-alpha.5.tgz", - "integrity": "sha512-ZH3xryzmwd8OvUdOJH4ujNAyQMXN6NCrRT0HGR8z9TnA0nFPFoOAswq7317mCn77VJmViu/tpCuvmRS0a9BROg==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-alpha.6.tgz", + "integrity": "sha512-uzDNIeTLFeITzK7yX9PSsu3dl92rHP/gKMNAlJhmDRr7r+OLr5dCpAzyZ0WvONRxjxR8Otj5LX4AD12+EX32fg==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -3961,13 +3961,13 @@ } }, "node_modules/@swagger-api/apidom-core": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-alpha.5.tgz", - "integrity": "sha512-iArtPxwcQ/EpQU/VqwBDrD+F0lngyUyLVCa8zR4gT+7mP6fpiU7jcerizw0hDpFmvieXddx5UdfO28Pxuq204g==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-alpha.6.tgz", + "integrity": "sha512-5u7dK3+8cF2h5bHEI/qrA6JrfXX+HIHSmUgPGbeMAqSCEfpsjnsngXK6gHtd4ktLlPz3TplNZAQl88wIp+39nw==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ast": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "minim": "~0.23.8", "ramda": "~0.30.0", @@ -3977,36 +3977,36 @@ } }, "node_modules/@swagger-api/apidom-error": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-alpha.5.tgz", - "integrity": "sha512-5UEgSZuQPdkqKSKDtRXQ0cm7x1o4EPyusLBVsCG4l8QtJvAhG1OOpEzJbTZ48/nRt7VkbK7MTj/up+oEILzVvw==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-alpha.6.tgz", + "integrity": "sha512-eOcqaXwLitjp5CIGYR0W2oM6p4UiTL7EjNdkCcfrELKHdgNS6U7kZdl3KCBlOuMb14CijwtZNEJbIGhhGZUYHg==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7" } }, "node_modules/@swagger-api/apidom-json-pointer": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-alpha.5.tgz", - "integrity": "sha512-eDAz7/UaGpGCvB0y1GoRjFwxFWseCsF/0ZYIQvvq9PS025inc/I6M+XX8dWMmkpNpbbf+KfD7WlwfqnUZLv/MQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-alpha.6.tgz", + "integrity": "sha512-8ULBcQRQ1UPgqJ+ZuuKjmeKeuxqbuIUHwWHRRA848jK5+IHmNw/Cp68MhNiwYXLmTLkTIGaDubcOplMeHCxSyA==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-ns-api-design-systems": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.0.0-alpha.5.tgz", - "integrity": "sha512-aq9Ix2Wo2TMfYW3HmheTO3qVd2MYrdinjLFHn9uozzC2x+CSzALhvKkwOc29HiGOn4QQ6QHHPRojNgD86WkwUg==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.0.0-alpha.6.tgz", + "integrity": "sha512-JRiImw3XKrfm22pzlx7uM6XYJtWM71QkCLy86gOTBFybWgTOCECnN4c8jFBnYl6KYuIb2VV9kXZs38xjK4NfBQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4014,14 +4014,14 @@ } }, "node_modules/@swagger-api/apidom-ns-asyncapi-2": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-alpha.5.tgz", - "integrity": "sha512-JFtQBhCOkYuyNVcYGMFd9+U0UO6lEj9kO5qCgUjPOTgkOpZOZQslVEtg3TDmRlBATwVdmRv39xy3ZLK8O/JdmQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-alpha.6.tgz", + "integrity": "sha512-I8Yq+AmJPUJihGneBv1/m/ly+2dp4FJiCxW/auRQSicvYIV7hoBO5qGZqcEEoVt7OpuhFbFqI2pwnambz90Uvg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4029,13 +4029,13 @@ } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-alpha.5.tgz", - "integrity": "sha512-aDmcpGikL5JZmDTg7J6EJfLFjtUmX/MfduS4hQeopFCkw91dZsqxO10j7KEiRVVuJBuGStbYoHI5aIsQTlebzA==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-alpha.6.tgz", + "integrity": "sha512-E8JjqdDgopnLd4HXEXGSrc6rkbDyB8Qk6sYgmyT6lB8caFUMRdJ5Rp57fPePETnVpegb8cAuKjBdjTImX1gQ3Q==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-alpha.5", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-ast": "^1.0.0-alpha.6", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4043,15 +4043,15 @@ } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-6": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-alpha.5.tgz", - "integrity": "sha512-ylh96E59aaV1VDv9sDrNwpTmjVT6vmOSncpmytlc0ynb374dwZkLZ63Hd30rcMFAhKmg5aYOG+i5O1QXKFYz8A==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-alpha.6.tgz", + "integrity": "sha512-uzYmV65nn7i6nlp7Kp7ldGfAoXWPPquIocoHLWDBTx5sPdS+ALu2T2yvytav0z6StKeV+gU2HZjMLVRWdLzLZA==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4059,15 +4059,15 @@ } }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-7": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-alpha.5.tgz", - "integrity": "sha512-Mks9gabJvz4atkjzLDwjWbo12xirul7a9ifHYZQJc/jfVKfVNy1e3QgFG1+EbSWWG5Yfbr3WKyxUDJLgr75qKg==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-alpha.6.tgz", + "integrity": "sha512-dWEVUVMByOs5JIMsgcceETOYH3nTiAHoIIjXbYeHP6m6HaNP8IE5ex0ZgfmQc29uH0E6H+6aYAv1flfvy56rVQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4075,15 +4075,15 @@ } }, "node_modules/@swagger-api/apidom-ns-openapi-2": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-alpha.5.tgz", - "integrity": "sha512-uY+1G4oRf9UT/6sGuatvWKstmlRnEiN9XqaVvV8euXESxI4jtwcPbRwoEX31vEYXoTqq2ZScFy8UQJ2CJ2ZADw==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-alpha.6.tgz", + "integrity": "sha512-sPwvOY+FGd5yEAijYLupmIYwf4HIpW6yegzrz6uUvGmONZpiCNIidCu+2m6GyYCoZ/lQZdPMw29DuU2O4iiDKw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4091,14 +4091,14 @@ } }, "node_modules/@swagger-api/apidom-ns-openapi-3-0": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-alpha.5.tgz", - "integrity": "sha512-UAOGZaGMDVRQ10l8OgXCAfxS9PxGoCW66o/vFmhPfrK8NwU1GEo6sYHYoo1mflNMHCN2eVYyM5LxA+qYm0SJgQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-alpha.6.tgz", + "integrity": "sha512-kE4s17j69DDvXrf7xeRTunmSQJLiX52fCX1YnfC81e1IPm3q/mdpkZiysM87FuJQQj522fX2G+QUIJlDkD5U9w==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4106,15 +4106,15 @@ } }, "node_modules/@swagger-api/apidom-ns-openapi-3-1": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-alpha.5.tgz", - "integrity": "sha512-8VkdZ2MfxXIdmzQZrV0qGk18MG7XNJKIL3GT9lad9NyXyiKSvBVFJDmS4S43qcQTL0rjHXF6ds25yErDSTprjg==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-alpha.6.tgz", + "integrity": "sha512-2QA2z9beyaVyZDOXbLg4Nu8o8xKWo9L0WHWOP+hg/haGRyyPHXgyg2XdwRuFBozBI9wBaIfEg1lvNC+J0taDjg==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-alpha.5", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-json-pointer": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.5", + "@swagger-api/apidom-ast": "^1.0.0-alpha.6", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-json-pointer": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4122,14 +4122,14 @@ } }, "node_modules/@swagger-api/apidom-ns-workflows-1": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-workflows-1/-/apidom-ns-workflows-1-1.0.0-alpha.5.tgz", - "integrity": "sha512-6cMv37y4kftJySoMAeubz5yhHaRKnSK0YglvCv8v7rE2OBduR/yEITDOej2/KFAnt29LxkhotSbNsmHx0weICQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-workflows-1/-/apidom-ns-workflows-1-1.0.0-alpha.6.tgz", + "integrity": "sha512-9kXU7hUdz25TTGF8b1pmKGugkET4gkW7ING+qSUjU5nWdrkdUIVuq1o8qjaZwRDRvkNynnlRbWHqXeWgRWyi/w==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4137,75 +4137,75 @@ } }, "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-alpha.5.tgz", - "integrity": "sha512-QVWS2sPKA1sG52UIJut/St6+j7zO8QxzPlL5akR/8QPX2FWKqmw808Ewvjq9WLtqlPhVY2G33tv90d4/FJUNwQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-alpha.6.tgz", + "integrity": "sha512-7THBmhvwTmsb1eFXvj/tbIK91g5tzkvhxGSUVbpGt1zApEFmKvjZbDhGnMx15CImIUURW1QZ6TQ/cZ7jRWk8kQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-yaml": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.0.0-alpha.5.tgz", - "integrity": "sha512-T7UD/SWd5u2zlPyswDdtfAStm6Qt5hQWAWvCmQKxy37qJA9QGXcQKNavaSMPGvN660hufNaJEBxgJ/B0Zd5iaw==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.0.0-alpha.6.tgz", + "integrity": "sha512-xD0aRgRGPaM1J+H3nRg8qP6bQ4fNtsUopoc6JEKzi7NJxd+r/mZV4pVa+Gr6CS+xv4d6TJ53UCJmGsjgmR1bQw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-2": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-alpha.5.tgz", - "integrity": "sha512-UfCS9DFIURTUfaHfmEn8omHaevIV2i24Ncp46M/Pnk6JwZHjAEMxmPxsgMl4TTGbzqvySUQsJka8Qz1ziYZ1og==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-alpha.6.tgz", + "integrity": "sha512-BVouq+7XiS2/HmNHd/CHHieyRT5mTN+kqYACnKV/TAzC5+fK3t2mcdng4I81m3Mzb9OJ/VpHiEVlSZiWZoPU/Q==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.0.0-alpha.5.tgz", - "integrity": "sha512-X5avFyLnlu6Zjyul35f8Ff0DRE70aNc+Bk7il+eV8g+FR/qgrmuNziQEBOhCrIUnYB1kFbTty6BZRsNLdjW9XQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.0.0-alpha.6.tgz", + "integrity": "sha512-CsUu5t6ijflz0DDjdoxE/OUbSjBAeh5v2zfMRVOfGdnNDhDhrE/3P0VTpdKdVmbWQ1ueIbq2QaC8thQ+Jcxwyg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-json": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-alpha.5.tgz", - "integrity": "sha512-NdVjlRrtr1EvrBsk6DHSkjI8zdnSve/bjeGgo0NR2IRmA/8BRcY6rffM1BR76Ku+CjxhCB2mfQxotilD71dL+g==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-alpha.6.tgz", + "integrity": "sha512-ruEkgvJSmBUUsGZdYiHeczekTWCJSWHrNvhAczY6c1ZFhpCukZg9tCqdVhnni/LPp4r4h7BdNldjY7dtrUkCiQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-alpha.5", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ast": "^1.0.0-alpha.6", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4215,135 +4215,135 @@ } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-2": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-alpha.5.tgz", - "integrity": "sha512-qOwQl2WezfdDVmtf9ZlOiqT1hcDS52j7ZbBdH9MqMGJ+/mo6sv0qEY2ZXS104lWeRamgi4o/4o4jGqjZS1YrMg==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-alpha.6.tgz", + "integrity": "sha512-RLvjHvjURnqfWxEdLFOW6agBS8CzVyV9++Vg4TSB9gPCNsTlz5w9iy82NYvApExHJIlN55Ga92OZ6CuWXJ8iKw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-2": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-0": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.0.0-alpha.5.tgz", - "integrity": "sha512-t5oj7XteTu2Yh8uNkzXAcKU81CQky+q6Qt/ImQ/S6MGxpXJnWwgVfm/j/dH2wnHFKghNS3vgm6IewpojSbUw4w==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.0.0-alpha.6.tgz", + "integrity": "sha512-cf9+M9vySTrUZW+m2SR04IMnl+5QX6P7S2xgFF705ySOMkPiA9GTgAZJFqwzncAEPovkp7/A24adxyhFz52iZg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-1": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.0.0-alpha.5.tgz", - "integrity": "sha512-w0G53HXYdzcespfa3atN90jVLDRoH9FU7XEWG4DvFWM90WGwuNscojcaB28r8pZMhSQAKMPxggh6PnmvK3gdEQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.0.0-alpha.6.tgz", + "integrity": "sha512-Z7TCUWB/VotmHU5kjUcAlu3qMHCVr1pOpnsuI01I6vCHGJOqUZPelnNqVyw5tjiVbgwDCKzXiPSQo9gGG1HoGA==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-2": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.0.0-alpha.5.tgz", - "integrity": "sha512-nfeYRL0o6QwtKsyF30d2JmtW7fzoI/EYKSFgzaDm7IFlrQWMpB6BidpZKdk5MtYN4zvmfAM+lOhrqR7a5BvHMg==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.0.0-alpha.6.tgz", + "integrity": "sha512-XI0qlTjL2Q1TMvzxjjEki2iuJqt43C0mwGHremjcpbNHpJejnkEGFDPJqs1rp3RobwRl1ftHVFJi7JVPiA8Zvw==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-2": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.0.0-alpha.5.tgz", - "integrity": "sha512-HRziGD/YUcO21hmDIYNzwYivp/faeZRxcq8Gex7RLLhJZ60fGTJJ1k1yhWFPNSe9DEJUNBN949SDxMdZnGT9PQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.0.0-alpha.6.tgz", + "integrity": "sha512-yGd5dP52BrBMO4/nCJdcvotxCbmbXYOi/nQrj7rL4/7VFdKbC4ngT0ggprvKE8CVQC99qPz4qR1y728QdioPAg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.0.0-alpha.5.tgz", - "integrity": "sha512-aul2wSOvkdp9jQjSv1pvEGllVaDUnTKmRbCy7M/dFQyIhJQBvwW+/Cu//PprzAODtFNraOBjIXiJ5tVdv6NuIQ==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.0.0-alpha.6.tgz", + "integrity": "sha512-4F/rWh7bi97y20SRskrqz9UdO+YwHOn+vcOvNs5/arI5niSmTeAN3dgH9emTx1LJi8d7woUAct+TEqshwoh/zQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-workflows-json-1": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-json-1/-/apidom-parser-adapter-workflows-json-1-1.0.0-alpha.5.tgz", - "integrity": "sha512-R1LVe/gx7fRSCuDmmN3qScWonz6Xlaw11J+NAfiJzrNXBy1Qa1mCxgGs47w0slQN+FjYkVj5Y/q29jJgpUbLHA==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-json-1/-/apidom-parser-adapter-workflows-json-1-1.0.0-alpha.6.tgz", + "integrity": "sha512-K2gZFUHtp+Vw9rcizanIJLxSsaYQWNh1QtsEVGtAkn3RBVa130i3umcgQBKuvaBzfhi+Zr21sR4LSrs4XiRHiQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-workflows-1": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-workflows-1": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-workflows-yaml-1": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-yaml-1/-/apidom-parser-adapter-workflows-yaml-1-1.0.0-alpha.5.tgz", - "integrity": "sha512-W5wD+TdGNdW4aP9uqkxFbVmjWvLOXyV02VvyStyTlzxdUaPzKY3FGaxjxk8TGVRqwe2yEQVUc2zfGalrScA/Sg==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-yaml-1/-/apidom-parser-adapter-workflows-yaml-1-1.0.0-alpha.6.tgz", + "integrity": "sha512-yaJ9Iir43odK/zTB0tVL43RBC4ktQvNRfuT21vedqNaxO9J2pjTPy9NkIXJuOrcizinAASDLLUYX/b0UONhVxg==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-ns-workflows-1": "^1.0.0-alpha.5", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-ns-workflows-1": "^1.0.0-alpha.6", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0" } }, "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-alpha.5.tgz", - "integrity": "sha512-21TIQPkB+Z4ekNj5dh1uN0dhOBBCPeK572YpooA/pBTFLeH6Wtildx7ZZYfpJEejHaQKaqoRx3hp0G42GDOb7g==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-alpha.6.tgz", + "integrity": "sha512-qevJf2IRvskyvgeGnkJXCGKFnmrcnuMoFHoboI3nJFqdesN74g1hGm1VIVAOOkM4AcdG1w7BviCHEt4YEYGPcQ==", "optional": true, "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-alpha.5", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", - "@swagger-api/apidom-error": "^1.0.0-alpha.5", + "@swagger-api/apidom-ast": "^1.0.0-alpha.6", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", + "@swagger-api/apidom-error": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "ramda": "~0.30.0", "ramda-adjunct": "^5.0.0", @@ -4353,12 +4353,12 @@ } }, "node_modules/@swagger-api/apidom-reference": { - "version": "1.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-alpha.5.tgz", - "integrity": "sha512-zPMTScWI8oVUAT//RdAhl9GJuwtQLibP8iCrqFQDGjBzKQS5Uxz4hSXr/jqKPdkCJXbEoP94yYjvQjtI5yrv1A==", + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-alpha.6.tgz", + "integrity": "sha512-DbsxxgQCVd8ZTJag3EOtzJ2rtsaq4z5z/A4nEgzVQhStuHdRwrbQfxem1g7p6dOK2VrGEGf73UllGJvGV+uPpg==", "dependencies": { "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-alpha.5", + "@swagger-api/apidom-core": "^1.0.0-alpha.6", "@types/ramda": "~0.30.0", "axios": "^1.4.0", "minimatch": "^7.4.3", @@ -4419,9 +4419,9 @@ } }, "node_modules/@types/aws-lambda": { - "version": "8.10.140", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.140.tgz", - "integrity": "sha512-4Dh3dk2TUcbdfHrX0Al90mNGJDvA9NBiTQPzbrjGi/dLxzKCGOYgT8YQ47jUKNFALkAJAadifq0pzyjIUlhVhg==" + "version": "8.10.141", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.141.tgz", + "integrity": "sha512-SMWlRBukG9KV8ZNjwemp2AzDibp/czIAeKKTw09nCPbWxVskIxactCJCGOp4y6I1hCMY7T7UGfySvBLXNeUbEw==" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -4573,9 +4573,9 @@ "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==" }, "node_modules/@types/node": { - "version": "20.14.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", - "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", "dependencies": { "undici-types": "~5.26.4" } @@ -4616,11 +4616,11 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/ramda": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.0.tgz", - "integrity": "sha512-DQtfqUbSB18iM9NHbQ++kVUDuBWHMr6T2FpW1XTiksYRGjq4WnNPZLt712OEHEBJs7aMyJ68Mf2kGMOP1srVVw==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.1.tgz", + "integrity": "sha512-aoyF/ADPL6N+/NXXfhPWF+Qj6w1Cql59m9wX0Gi15uyF+bpzXeLd63HPdiTDE2bmLXfNcVufsDPKmbfOrOzTBA==", "dependencies": { - "types-ramda": "^0.30.0" + "types-ramda": "^0.30.1" } }, "node_modules/@types/react": { @@ -4926,9 +4926,9 @@ "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" }, "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5040,9 +5040,9 @@ } }, "node_modules/apg-lite": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/apg-lite/-/apg-lite-1.0.3.tgz", - "integrity": "sha512-lOoNkL7vN7PGdyQMFPey1aok2oVVqvs3n7UMFBRvQ9FoELSbKhgPc3rd7JptaGwCmo4125gLX9Cqb8ElvLCFaQ==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/apg-lite/-/apg-lite-1.0.4.tgz", + "integrity": "sha512-B32zCN3IdHIc99Vy7V9BaYTUzLeRA8YXYY1aQD1/5I2aqIrO0coi4t6hJPqMisidlBxhyME8UexkHt31SlR6Og==" }, "node_modules/arg": { "version": "5.0.2", @@ -5258,6 +5258,12 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5683,9 +5689,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", + "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", "devOptional": true, "funding": [ { @@ -5702,10 +5708,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", + "caniuse-lite": "^1.0.30001640", + "electron-to-chromium": "^1.4.820", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -5847,9 +5853,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001637", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001637.tgz", - "integrity": "sha512-1x0qRI1mD1o9e+7mBI7XtzFAP4XszbHaVWsMiGbSPLYekKTJF7K+FNk6AsXH4sUpc+qrsI3pVgf1Jdl/uGkuSQ==", + "version": "1.0.30001642", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", + "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", "funding": [ { "type": "opencollective", @@ -6873,9 +6879,9 @@ } }, "node_modules/dompurify": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", - "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" }, "node_modules/domutils": { "version": "3.1.0", @@ -6912,10 +6918,25 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.4.812", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.812.tgz", - "integrity": "sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg==", + "version": "1.4.827", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz", + "integrity": "sha512-VY+J0e4SFcNfQy19MEoMdaIcZLmDCprqvBtkii1WTCTQHpRvf5N8+3kTYCgL/PcntvwQvmMJWTuDPsq+IlhWKQ==", "devOptional": true }, "node_modules/emittery": { @@ -7296,12 +7317,12 @@ } }, "node_modules/eslint-config-next": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.4.tgz", - "integrity": "sha512-Qr0wMgG9m6m4uYy2jrYJmyuNlYZzPRQq5Kvb9IDlYwn+7yq6W6sfMNFgb+9guM1KYwuIo6TIaiFhZJ6SnQ/Efw==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.5.tgz", + "integrity": "sha512-zogs9zlOiZ7ka+wgUnmcM0KBEDjo4Jis7kxN1jvC0N4wynQ2MIx/KBkg4mVF63J5EK4W0QMCn7xO3vNisjaAoA==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "14.2.4", + "@next/eslint-plugin-next": "14.2.5", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", "eslint-import-resolver-node": "^0.3.6", @@ -7528,9 +7549,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", - "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", + "version": "7.34.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.4.tgz", + "integrity": "sha512-Np+jo9bUwJNxCsT12pXtrGhJgT3T44T1sHhn1Ssr42XFn8TES0267wPGo5nNrMHi8qkyimDAX2BUmkf9pSaVzA==", "dev": true, "dependencies": { "array-includes": "^3.1.8", @@ -7541,16 +7562,17 @@ "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" @@ -7810,9 +7832,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -7977,11 +7999,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-loops": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz", - "integrity": "sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==" - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -8063,6 +8080,27 @@ "node": ">=0.10.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -8812,9 +8850,9 @@ "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -8975,12 +9013,11 @@ "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, "node_modules/inline-style-prefixer": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.0.tgz", - "integrity": "sha512-I7GEdScunP1dQ6IM2mQWh6v0mOYdYmH3Bp31UecKdrcUgcURTcctSe1IECdUznSHKSmsHtjrT3CwCPI1pyxfUQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", + "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==", "dependencies": { - "css-in-js-utils": "^3.1.0", - "fast-loops": "^1.1.3" + "css-in-js-utils": "^3.1.0" } }, "node_modules/install": { @@ -9553,9 +9590,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "peer": true, "dependencies": { @@ -9676,6 +9713,116 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", + "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -11406,9 +11553,9 @@ } }, "node_modules/jose": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.4.1.tgz", - "integrity": "sha512-U6QajmpV/nhL9SyfAewo000fkiRQ+Yd2H0lBxJJ9apjpOgkOcBQJWOrMo917lxLptdS/n/o/xPzMkXhF46K8hQ==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.6.3.tgz", + "integrity": "sha512-1Jh//hEEwMhNYPDDLwXHa2ePWgWiFNNUadVmguAAw2IJ6sj9mNxV5tGXJNqlMkJAybF6Lgw1mISDxTePP/187g==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -11871,12 +12018,9 @@ } }, "node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/lunr": { "version": "2.3.9", @@ -12499,9 +12643,9 @@ "optional": true }, "node_modules/mobx": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.12.4.tgz", - "integrity": "sha512-uIymg89x+HmItX1p3MG+d09irn2k63J6biftZ5Ok+UpNojS1I3NJPLfcmJT9ANnUltNlHi+HQqrVyxiAN8ISYg==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.0.tgz", + "integrity": "sha512-1laWODrBWmB7mDJ8EClCjUQTyLwJ0ydJgE4FtK7t9r3JnjXgc9OhmYs2P4RtHrY1co5+4T6cKP2UswX2SU29mA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -12672,11 +12816,11 @@ } }, "node_modules/next": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.4.tgz", - "integrity": "sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.5.tgz", + "integrity": "sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==", "dependencies": { - "@next/env": "14.2.4", + "@next/env": "14.2.5", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -12691,15 +12835,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.4", - "@next/swc-darwin-x64": "14.2.4", - "@next/swc-linux-arm64-gnu": "14.2.4", - "@next/swc-linux-arm64-musl": "14.2.4", - "@next/swc-linux-x64-gnu": "14.2.4", - "@next/swc-linux-x64-musl": "14.2.4", - "@next/swc-win32-arm64-msvc": "14.2.4", - "@next/swc-win32-ia32-msvc": "14.2.4", - "@next/swc-win32-x64-msvc": "14.2.4" + "@next/swc-darwin-arm64": "14.2.5", + "@next/swc-darwin-x64": "14.2.5", + "@next/swc-linux-arm64-gnu": "14.2.5", + "@next/swc-linux-arm64-musl": "14.2.5", + "@next/swc-linux-x64-gnu": "14.2.5", + "@next/swc-linux-x64-musl": "14.2.5", + "@next/swc-win32-arm64-msvc": "14.2.5", + "@next/swc-win32-ia32-msvc": "14.2.5", + "@next/swc-win32-x64-msvc": "14.2.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -12940,9 +13084,9 @@ } }, "node_modules/npm": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.1.tgz", - "integrity": "sha512-Dp1C6SvSMYQI7YHq/y2l94uvI+59Eqbu1EpuKQHQ8p16txXRuRit5gH3Lnaagk2aXDIjg/Iru9pd05bnneKgdw==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.2.tgz", + "integrity": "sha512-x/AIjFIKRllrhcb48dqUNAAZl0ig9+qMuN91RpZo3Cb2+zuibfh+KISl6+kVVyktDz230JKc208UkQwwMqyB+w==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -13015,13 +13159,13 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^7.5.3", - "@npmcli/config": "^8.3.3", + "@npmcli/arborist": "^7.5.4", + "@npmcli/config": "^8.3.4", "@npmcli/fs": "^3.1.1", "@npmcli/map-workspaces": "^3.0.6", - "@npmcli/package-json": "^5.1.1", + "@npmcli/package-json": "^5.2.0", "@npmcli/promise-spawn": "^7.0.2", - "@npmcli/redact": "^2.0.0", + "@npmcli/redact": "^2.0.1", "@npmcli/run-script": "^8.1.0", "@sigstore/tuf": "^2.3.4", "abbrev": "^2.0.0", @@ -13032,7 +13176,7 @@ "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", - "glob": "^10.4.1", + "glob": "^10.4.2", "graceful-fs": "^4.2.11", "hosted-git-info": "^7.0.2", "ini": "^4.1.3", @@ -13040,30 +13184,30 @@ "is-cidr": "^5.1.0", "json-parse-even-better-errors": "^3.0.2", "libnpmaccess": "^8.0.6", - "libnpmdiff": "^6.1.3", - "libnpmexec": "^8.1.2", - "libnpmfund": "^5.0.11", + "libnpmdiff": "^6.1.4", + "libnpmexec": "^8.1.3", + "libnpmfund": "^5.0.12", "libnpmhook": "^10.0.5", "libnpmorg": "^6.0.6", - "libnpmpack": "^7.0.3", + "libnpmpack": "^7.0.4", "libnpmpublish": "^9.0.9", "libnpmsearch": "^7.0.6", "libnpmteam": "^6.0.5", "libnpmversion": "^6.0.3", "make-fetch-happen": "^13.0.1", - "minimatch": "^9.0.4", + "minimatch": "^9.0.5", "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^10.1.0", "nopt": "^7.2.1", - "normalize-package-data": "^6.0.1", + "normalize-package-data": "^6.0.2", "npm-audit-report": "^5.0.0", "npm-install-checks": "^6.3.0", "npm-package-arg": "^11.0.2", - "npm-pick-manifest": "^9.0.1", + "npm-pick-manifest": "^9.1.0", "npm-profile": "^10.0.0", - "npm-registry-fetch": "^17.0.1", + "npm-registry-fetch": "^17.1.0", "npm-user-validate": "^2.0.1", "p-map": "^4.0.0", "pacote": "^18.0.6", @@ -13187,7 +13331,7 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "7.5.3", + "version": "7.5.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -13235,16 +13379,16 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "8.3.3", + "version": "8.3.4", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/package-json": "^5.1.1", "ci-info": "^4.0.0", "ini": "^4.1.2", "nopt": "^7.2.1", "proc-log": "^4.2.0", - "read-package-json-fast": "^3.0.2", "semver": "^7.3.5", "walk-up-path": "^3.0.1" }, @@ -13264,11 +13408,12 @@ } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "5.0.7", + "version": "5.0.8", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", "proc-log": "^4.0.0", @@ -13342,7 +13487,7 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "5.1.1", + "version": "5.2.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -13381,7 +13526,7 @@ } }, "node_modules/npm/node_modules/@npmcli/redact": { - "version": "2.0.0", + "version": "2.0.1", "inBundle": true, "license": "ISC", "engines": { @@ -13753,7 +13898,7 @@ } }, "node_modules/npm/node_modules/debug": { - "version": "4.3.4", + "version": "4.3.5", "inBundle": true, "license": "MIT", "dependencies": { @@ -13827,7 +13972,7 @@ } }, "node_modules/npm/node_modules/foreground-child": { - "version": "3.1.1", + "version": "3.2.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -13852,16 +13997,8 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.2", - "inBundle": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/npm/node_modules/glob": { - "version": "10.4.1", + "version": "10.4.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -13869,6 +14006,7 @@ "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { @@ -13886,17 +14024,6 @@ "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/hasown": { - "version": "2.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/npm/node_modules/hosted-git-info": { "version": "7.0.2", "inBundle": true, @@ -13926,7 +14053,7 @@ } }, "node_modules/npm/node_modules/https-proxy-agent": { - "version": "7.0.4", + "version": "7.0.5", "inBundle": true, "license": "MIT", "dependencies": { @@ -14035,17 +14162,6 @@ "node": ">=14" } }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.13.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/npm/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "inBundle": true, @@ -14065,7 +14181,7 @@ "license": "ISC" }, "node_modules/npm/node_modules/jackspeak": { - "version": "3.1.2", + "version": "3.4.0", "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -14133,11 +14249,11 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "6.1.3", + "version": "6.1.4", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.5.3", + "@npmcli/arborist": "^7.5.4", "@npmcli/installed-package-contents": "^2.1.0", "binary-extensions": "^2.3.0", "diff": "^5.1.0", @@ -14151,11 +14267,11 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "8.1.2", + "version": "8.1.3", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.5.3", + "@npmcli/arborist": "^7.5.4", "@npmcli/run-script": "^8.1.0", "ci-info": "^4.0.0", "npm-package-arg": "^11.0.2", @@ -14171,11 +14287,11 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "5.0.11", + "version": "5.0.12", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.5.3" + "@npmcli/arborist": "^7.5.4" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -14206,11 +14322,11 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "7.0.3", + "version": "7.0.4", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^7.5.3", + "@npmcli/arborist": "^7.5.4", "@npmcli/run-script": "^8.1.0", "npm-package-arg": "^11.0.2", "pacote": "^18.0.6" @@ -14306,7 +14422,7 @@ } }, "node_modules/npm/node_modules/minimatch": { - "version": "9.0.4", + "version": "9.0.5", "inBundle": true, "license": "ISC", "dependencies": { @@ -14376,26 +14492,6 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { - "version": "3.3.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/npm/node_modules/minipass-pipeline": { "version": "1.2.4", "inBundle": true, @@ -14541,12 +14637,11 @@ } }, "node_modules/npm/node_modules/normalize-package-data": { - "version": "6.0.1", + "version": "6.0.2", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, @@ -14618,7 +14713,7 @@ } }, "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "9.0.1", + "version": "9.1.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -14644,15 +14739,15 @@ } }, "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "17.0.1", + "version": "17.1.0", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", "npm-package-arg": "^11.0.0", "proc-log": "^4.0.0" @@ -14683,6 +14778,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, "node_modules/npm/node_modules/pacote": { "version": "18.0.6", "inBundle": true, @@ -14953,13 +15053,13 @@ } }, "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "8.0.3", + "version": "8.0.4", "inBundle": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.1", "debug": "^4.3.4", - "socks": "^2.7.1" + "socks": "^2.8.3" }, "engines": { "node": ">= 14" @@ -15567,23 +15667,6 @@ "node": ">= 0.4" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", @@ -16203,9 +16286,10 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -16222,7 +16306,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -16332,9 +16416,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", + "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -16653,9 +16737,9 @@ "peer": true }, "node_modules/qs": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", - "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.3.tgz", + "integrity": "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==", "dependencies": { "side-channel": "^1.0.6" }, @@ -18244,6 +18328,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -18393,6 +18487,33 @@ "react-dom": ">= 16.8.0" } }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/styled-components/node_modules/stylis": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", @@ -18913,12 +19034,13 @@ "dev": true }, "node_modules/ts-jest": { - "version": "29.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.5.tgz", - "integrity": "sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg==", + "version": "29.2.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.2.tgz", + "integrity": "sha512-sSW7OooaKT34AAngP6k1VS669a0HdLxkQZnlC7T76sckGCokXFnvJ3yRlQZGRTAoV5K19HfSgCiSwWOSIfcYlg==", "dev": true, "dependencies": { "bs-logger": "0.x", + "ejs": "^3.0.0", "fast-json-stable-stringify": "2.x", "jest-util": "^29.0.0", "json5": "^2.2.3", @@ -19141,17 +19263,17 @@ } }, "node_modules/types-ramda": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.30.0.tgz", - "integrity": "sha512-oVPw/KHB5M0Du0txTEKKM8xZOG9cZBRdCVXvwHYuNJUVkAiJ9oWyqkA+9Bj2gjMsHgkkhsYevobQBWs8I2/Xvw==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.30.1.tgz", + "integrity": "sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==", "dependencies": { "ts-toolbelt": "^9.6.0" } }, "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -19347,9 +19469,9 @@ "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "devOptional": true, "funding": [ { From e754d221f0e105b2e25ef8d6437bed6e2a8c5d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:18:36 +0200 Subject: [PATCH 173/191] Updates Stoplight --- package-lock.json | 177 ++++++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 95 insertions(+), 84 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7065d875..05a07dbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@octokit/auth-app": "^6.0.1", "@octokit/core": "^5.1.0", "@octokit/webhooks": "^12.0.3", - "@stoplight/elements": "^7.15.3", + "@stoplight/elements": "^8.3.3", "core-js": "^3.33.3", "encoding": "^0.1.13", "figma-squircle": "^0.3.1", @@ -1104,6 +1104,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1120,6 +1121,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -1131,6 +1133,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -2857,6 +2860,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "optional": true, "engines": { "node": ">=14" @@ -3197,19 +3201,19 @@ } }, "node_modules/@stoplight/elements": { - "version": "7.16.6", - "resolved": "https://registry.npmjs.org/@stoplight/elements/-/elements-7.16.6.tgz", - "integrity": "sha512-E9sM60guNcNOLOjLM3MCHkjLu3vNa8jut2Dz/Ita8/gzLu5g5OjgMkfH34EtlKe7BX+ihEmkRJveII+OmK5Gpg==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@stoplight/elements/-/elements-8.3.3.tgz", + "integrity": "sha512-n0V2Clh+B4VZUZlIObQJAIPBq/GgmKYIpd2fyk6cMDmKh3IZh5Y230mvmrD/LuG9xfoIeqoSG0p6pn62JdYl9g==", "dependencies": { - "@stoplight/elements-core": "~7.16.6", - "@stoplight/http-spec": "^6.0.0", + "@stoplight/elements-core": "~8.3.3", + "@stoplight/http-spec": "^7.1.0", "@stoplight/json": "^3.18.1", - "@stoplight/mosaic": "^1.46.1", - "@stoplight/types": "^14.0.0", - "@stoplight/yaml": "^4.2.3", + "@stoplight/mosaic": "^1.53.1", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml": "^4.3.0", "classnames": "^2.2.6", "file-saver": "^2.0.5", - "lodash": "^4.17.19", + "lodash": "^4.17.21", "react-query": "^3.34.19", "react-router-dom": "^5.2.0" }, @@ -3222,29 +3226,29 @@ } }, "node_modules/@stoplight/elements-core": { - "version": "7.16.6", - "resolved": "https://registry.npmjs.org/@stoplight/elements-core/-/elements-core-7.16.6.tgz", - "integrity": "sha512-Mip2FRflP49iDFdE6dWiOAHx3ne70uZ7Z9rDZ72yDOV/2IAF31GGEKvytkzmS0xr4L5OHgLiY5UAjzAmEYuDxQ==", - "dependencies": { - "@stoplight/http-spec": "^6.0.0", - "@stoplight/json": "^3.18.1", - "@stoplight/json-schema-ref-parser": "^9.0.5", - "@stoplight/json-schema-sampler": "0.2.3", - "@stoplight/json-schema-tree": "^3.0.0", - "@stoplight/json-schema-viewer": "^4.14.2", - "@stoplight/markdown-viewer": "^5.6.0", - "@stoplight/mosaic": "^1.46.1", - "@stoplight/mosaic-code-editor": "^1.46.1", - "@stoplight/mosaic-code-viewer": "^1.46.1", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@stoplight/elements-core/-/elements-core-8.3.3.tgz", + "integrity": "sha512-DoqQ02Iu1kFSeeLQ5eQrzkIV9BpA+Zc7gTrktyTFu1Ec1idmtY+PvIc/O7lXAub3f8XMkzY4h8b2U2R2X1XcNA==", + "dependencies": { + "@stoplight/http-spec": "^7.1.0", + "@stoplight/json": "^3.21.0", + "@stoplight/json-schema-ref-parser": "^9.2.7", + "@stoplight/json-schema-sampler": "0.3.0", + "@stoplight/json-schema-tree": "^4.0.0", + "@stoplight/json-schema-viewer": "4.16.1", + "@stoplight/markdown-viewer": "^5.7.0", + "@stoplight/mosaic": "^1.53.1", + "@stoplight/mosaic-code-editor": "^1.53.1", + "@stoplight/mosaic-code-viewer": "^1.53.1", "@stoplight/path": "^1.3.2", "@stoplight/react-error-boundary": "^3.0.0", - "@stoplight/types": "^14.0.0", - "@stoplight/yaml": "^4.2.3", + "@stoplight/types": "^14.1.1", + "@stoplight/yaml": "^4.3.0", "classnames": "^2.2.6", "httpsnippet-lite": "^3.0.5", "jotai": "1.3.9", "json-schema": "^0.4.0", - "lodash": "^4.17.19", + "lodash": "^4.17.21", "nanoid": "^3.1.32", "prop-types": "^15.7.2", "react-query": "^3.34.19", @@ -3264,22 +3268,20 @@ } }, "node_modules/@stoplight/http-spec": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@stoplight/http-spec/-/http-spec-6.0.3.tgz", - "integrity": "sha512-ED5kELP/5QH/SdKsFVx4EYUcE8FRjnDeiAx7ipHOk97t1pKgPhxH1wsvrQG3wL9TlNm0YQLLniE9Y3I6ZARJzA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@stoplight/http-spec/-/http-spec-7.1.0.tgz", + "integrity": "sha512-Z2XqKX2SV8a1rrgSzFqccX2TolfcblT+l4pNvUU+THaLl50tKDoeidwWWZTzYUzqU0+UV97ponvqEbWWN3PaXg==", "dependencies": { "@stoplight/json": "^3.18.1", "@stoplight/json-schema-generator": "1.0.2", - "@stoplight/types": "14.0.0", + "@stoplight/types": "14.1.0", "@types/json-schema": "7.0.11", "@types/swagger-schema-official": "~2.0.22", "@types/type-is": "^1.6.3", "fnv-plus": "^1.3.1", - "lodash.isequalwith": "^4.4.0", - "lodash.pick": "^4.4.0", - "lodash.pickby": "^4.6.0", + "lodash": "^4.17.21", "openapi3-ts": "^2.0.2", - "postman-collection": "^4.1.2", + "postman-collection": "^4.1.3", "tslib": "^2.6.2", "type-is": "^1.6.18" }, @@ -3288,9 +3290,9 @@ } }, "node_modules/@stoplight/http-spec/node_modules/@stoplight/types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.0.0.tgz", - "integrity": "sha512-w7Ejau6TaB7RqR0vWzGJSdmgLEYD2frjgbHPZoxgGQwAq/R8Qh/D9p9Bl9JFdii+YTL5xoDjyX0c1WDRlbMV8g==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.0.tgz", + "integrity": "sha512-fL8Nzw03+diALw91xHEHA5Q0WCGeW9WpPgZQjodNUWogAgJ56aJs03P9YzsQ1J6fT7/XjDqHMgn7/RlsBzB/SQ==", "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" @@ -3355,18 +3357,18 @@ } }, "node_modules/@stoplight/json-schema-sampler": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@stoplight/json-schema-sampler/-/json-schema-sampler-0.2.3.tgz", - "integrity": "sha512-57PqNll9y/Rkfp4/t1AkVfz5C0PIrDd8i2AW/N0XU5wVJ50kIrmJg3BD+PzmVcrF3lXFH7/LojoOUkzLZXMJpg==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@stoplight/json-schema-sampler/-/json-schema-sampler-0.3.0.tgz", + "integrity": "sha512-G7QImi2xr9+8iPEg0D9YUi1BWhIiiEm19aMb91oWBSdxuhezOAqqRP3XNY6wczHV9jLWW18f+KkghTy9AG0BQA==", "dependencies": { "@types/json-schema": "^7.0.7", "json-pointer": "^0.6.1" } }, "node_modules/@stoplight/json-schema-tree": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@stoplight/json-schema-tree/-/json-schema-tree-3.0.0.tgz", - "integrity": "sha512-lxALWJtl7ev+iTNbW+QHDr66+nmspwrDyVEhNSIjWjp8Vd6+qYxqk3r9zr8Ktfrx4pREfG7iTq6rwNSxYdP+Nw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@stoplight/json-schema-tree/-/json-schema-tree-4.0.0.tgz", + "integrity": "sha512-SAGtof+ihIdPqETR+7XXOaqZJcrbSih/xEahaw5t1nXk5sVW6ss2l5A1WCIuvtvnQiUKnBfanmZU4eoM1ZvItg==", "dependencies": { "@stoplight/json": "^3.12.0", "@stoplight/json-schema-merge-allof": "^0.8.0", @@ -3403,21 +3405,6 @@ "react-dom": ">=16.8" } }, - "node_modules/@stoplight/json-schema-viewer/node_modules/@stoplight/json-schema-tree": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@stoplight/json-schema-tree/-/json-schema-tree-4.0.0.tgz", - "integrity": "sha512-SAGtof+ihIdPqETR+7XXOaqZJcrbSih/xEahaw5t1nXk5sVW6ss2l5A1WCIuvtvnQiUKnBfanmZU4eoM1ZvItg==", - "dependencies": { - "@stoplight/json": "^3.12.0", - "@stoplight/json-schema-merge-allof": "^0.8.0", - "@stoplight/lifecycle": "^2.3.2", - "@types/json-schema": "^7.0.7", - "magic-error": "0.0.1" - }, - "engines": { - "node": ">=10.18" - } - }, "node_modules/@stoplight/json-schema-viewer/node_modules/@stoplight/react-error-boundary": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@stoplight/react-error-boundary/-/react-error-boundary-2.0.0.tgz", @@ -6042,6 +6029,7 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, "funding": [ { "type": "github", @@ -6432,6 +6420,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6908,7 +6897,8 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -6955,7 +6945,8 @@ "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/encoding": { "version": "0.1.13", @@ -8195,6 +8186,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -8417,6 +8409,7 @@ "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", @@ -8975,6 +8968,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { "node": ">=0.8.19" } @@ -9005,7 +8999,8 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true }, "node_modules/inline-style-parser": { "version": "0.1.1", @@ -9568,7 +9563,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isomorphic-fetch": { "version": "3.0.0", @@ -9700,6 +9696,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -11932,11 +11929,6 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, - "node_modules/lodash.isequalwith": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz", - "integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ==" - }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -11974,16 +11966,6 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" - }, - "node_modules/lodash.pickby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==" - }, "node_modules/longest-streak": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", @@ -12602,6 +12584,7 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -12621,6 +12604,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -15943,6 +15927,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -15956,6 +15941,7 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -17889,6 +17875,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -17900,6 +17887,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -17982,6 +17970,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { "node": ">=14" }, @@ -18236,6 +18225,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -18253,6 +18243,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -18265,12 +18256,14 @@ "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -18282,6 +18275,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -18413,6 +18407,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -18881,7 +18876,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/thenify": { "version": "3.3.1", @@ -19789,6 +19785,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -19894,6 +19891,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -19911,6 +19909,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -19927,6 +19926,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -19941,6 +19941,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -19951,17 +19952,20 @@ "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -19975,6 +19979,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -19986,6 +19991,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { "node": ">=12" }, @@ -19997,6 +20003,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -20016,6 +20023,8 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -20027,7 +20036,9 @@ "node_modules/write-file-atomic/node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "peer": true }, "node_modules/xml": { "version": "1.0.1", diff --git a/package.json b/package.json index 4ca11e47..47fa8311 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@octokit/auth-app": "^6.0.1", "@octokit/core": "^5.1.0", "@octokit/webhooks": "^12.0.3", - "@stoplight/elements": "^7.15.3", + "@stoplight/elements": "^8.3.3", "core-js": "^3.33.3", "encoding": "^0.1.13", "figma-squircle": "^0.3.1", From 9aed5c3cf4881cb3c72889146eff4db0c24d26a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:22:03 +0200 Subject: [PATCH 174/191] Fixes linting errors --- src/app/api/user/projects/route.ts | 3 +-- src/common/db/IDB.ts | 5 ++++- src/common/db/PostgreSQLDB.ts | 2 ++ src/common/github/IGitHubClient.ts | 4 ++-- src/common/utils/ZodJSONCoder.ts | 2 +- src/common/utils/fetcher.ts | 2 +- src/common/utils/saneParseInt.ts | 6 ++++-- src/features/auth/domain/log-in/LogInHandler.ts | 2 +- .../repository/AuthjsAccountsOAuthTokenRepository.ts | 2 +- src/features/hooks/domain/PullRequestCommenter.ts | 2 +- src/features/projects/data/IGitHubGraphQLClient.ts | 4 ++-- src/features/projects/view/ProjectsPage.tsx | 1 - 12 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/app/api/user/projects/route.ts b/src/app/api/user/projects/route.ts index fb54ee97..53b0876d 100644 --- a/src/app/api/user/projects/route.ts +++ b/src/app/api/user/projects/route.ts @@ -4,8 +4,7 @@ import { UnauthorizedError, makeUnauthenticatedAPIErrorResponse } from "@/common" -import { projectDataSource } from "@/composition" -import { session } from "@/composition" +import { session, projectDataSource } from "@/composition" export async function GET() { const isAuthenticated = await session.getIsAuthenticated() diff --git a/src/common/db/IDB.ts b/src/common/db/IDB.ts index 7602b175..f2697a91 100644 --- a/src/common/db/IDB.ts +++ b/src/common/db/IDB.ts @@ -1,5 +1,6 @@ export interface IDBRow { - readonly [column: string]: any; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + readonly [column: string]: any } export interface IDBQueryResult { @@ -7,6 +8,7 @@ export interface IDBQueryResult { } export interface IDBConnection { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ query(query: string, values: any[]): Promise> query(query: string): Promise> disconnect(): Promise @@ -14,6 +16,7 @@ export interface IDBConnection { export default interface IDB { connect(): Promise + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ query(query: string, values: any[]): Promise> query(query: string): Promise> } diff --git a/src/common/db/PostgreSQLDB.ts b/src/common/db/PostgreSQLDB.ts index 8e197a33..02c2da92 100644 --- a/src/common/db/PostgreSQLDB.ts +++ b/src/common/db/PostgreSQLDB.ts @@ -12,6 +12,7 @@ export class PostgreSQLDBConnection implements IDBConnection { this.client.release() } + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ async query(query: string, values: any[] = []): Promise> { const result: QueryResult = await this.client.query(query, values) return { rows: result.rows } @@ -34,6 +35,7 @@ export default class PostgreSQLDB implements IDB { return new PostgreSQLDBConnection({ client }) } + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ async query(query: string, values: any[] = []): Promise> { const connection = await this.connect() const result = await connection.query(query, values) diff --git a/src/common/github/IGitHubClient.ts b/src/common/github/IGitHubClient.ts index bf2c4a75..a739f69c 100644 --- a/src/common/github/IGitHubClient.ts +++ b/src/common/github/IGitHubClient.ts @@ -1,11 +1,11 @@ export type GraphQLQueryRequest = { readonly query: string - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ readonly variables?: {[key: string]: any} } export type GraphQlQueryResponse = { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ readonly [key: string]: any } diff --git a/src/common/utils/ZodJSONCoder.ts b/src/common/utils/ZodJSONCoder.ts index bb9a1e5f..588ae58f 100644 --- a/src/common/utils/ZodJSONCoder.ts +++ b/src/common/utils/ZodJSONCoder.ts @@ -7,7 +7,7 @@ export default class ZodJSONCoder { } static decode(schema: Schema, string: string): T { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ let obj: any | undefined try { obj = JSON.parse(string) diff --git a/src/common/utils/fetcher.ts b/src/common/utils/fetcher.ts index 4d296810..70229a78 100644 --- a/src/common/utils/fetcher.ts +++ b/src/common/utils/fetcher.ts @@ -7,7 +7,7 @@ } } - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ export default async function fetcher( input: RequestInfo, init?: RequestInit diff --git a/src/common/utils/saneParseInt.ts b/src/common/utils/saneParseInt.ts index 79806e19..42f58f27 100644 --- a/src/common/utils/saneParseInt.ts +++ b/src/common/utils/saneParseInt.ts @@ -1,8 +1,10 @@ -export default (str: string) => { - let forcedString = `${str}` +const saneParseInt = (str: string) => { + const forcedString = `${str}` const num = parseInt(forcedString, 10) if (isNaN(num) || forcedString.trim() !== num.toString()) { return undefined } return num } + +export default saneParseInt diff --git a/src/features/auth/domain/log-in/LogInHandler.ts b/src/features/auth/domain/log-in/LogInHandler.ts index 04023277..32b26a5f 100644 --- a/src/features/auth/domain/log-in/LogInHandler.ts +++ b/src/features/auth/domain/log-in/LogInHandler.ts @@ -33,7 +33,7 @@ export default class LogInHandler implements ILogInHandler { if (!refreshToken) { return false } - let userId = saneParseInt(user.id) + const userId = saneParseInt(user.id) if (!userId) { // We do not have a valid user ID, meaning this is the first time the user logs in. // When logging in for the first time, the user has a temporary ID that we cannot diff --git a/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts b/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts index 9d851802..2e00b4b9 100644 --- a/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts +++ b/src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts @@ -18,7 +18,7 @@ export default class AuthjsAccountsOAuthTokenRepository implements IOAuthTokenRe FROM accounts WHERE - provider = $1 AND \"userId\" = $2; + provider = $1 AND "userId" = $2; ` const result = await this.db.query(query, [this.provider, userId]) if (result.rows.length == 0) { diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts index 4b4551f9..cb4783a7 100644 --- a/src/features/hooks/domain/PullRequestCommenter.ts +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -118,7 +118,7 @@ export default class PullRequestCommenter { ref: string }) { const { files, owner, repositoryName, ref } = params - let rows: { filename: string, link: string, status: string }[] = [] + const rows: { filename: string, link: string, status: string }[] = [] const projectId = this.getProjectId({ repositoryName }) // Make sure we don't include the project configuration file. const baseConfigFilename = this.projectConfigurationFilename.replace(this.fileExtensionRegex, "") diff --git a/src/features/projects/data/IGitHubGraphQLClient.ts b/src/features/projects/data/IGitHubGraphQLClient.ts index 47345fd0..6e86d444 100644 --- a/src/features/projects/data/IGitHubGraphQLClient.ts +++ b/src/features/projects/data/IGitHubGraphQLClient.ts @@ -1,11 +1,11 @@ export type GitHubGraphQLClientRequest = { readonly query: string - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ readonly variables?: {[key: string]: any} } export type GitHubGraphQLClientResponse = { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ readonly [key: string]: any } diff --git a/src/features/projects/view/ProjectsPage.tsx b/src/features/projects/view/ProjectsPage.tsx index ebcceaa4..298920de 100644 --- a/src/features/projects/view/ProjectsPage.tsx +++ b/src/features/projects/view/ProjectsPage.tsx @@ -1,4 +1,3 @@ -import { session } from "@/composition" import { ProjectRepository } from "../domain" import ClientProjectsPage from "./client/ProjectsPage" From 02ce0c62d77742f2f3a22a134e4df6c667c06a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:27:44 +0200 Subject: [PATCH 175/191] Updates dependencies --- package-lock.json | 145 ++++++++++++++++------------------------------ package.json | 84 +++++++++++++-------------- 2 files changed, 93 insertions(+), 136 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05a07dbc..664ce2d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,58 +8,58 @@ "name": "shape-docs", "version": "0.1.0", "dependencies": { - "@emotion/react": "^11.11.1", - "@emotion/styled": "^11.11.0", - "@fortawesome/fontawesome-svg-core": "^6.4.2", - "@fortawesome/free-solid-svg-icons": "^6.4.2", - "@fortawesome/react-fontawesome": "^0.2.0", - "@mui/icons-material": "^5.14.18", - "@mui/material": "^5.14.18", - "@octokit/auth-app": "^6.0.1", - "@octokit/core": "^5.1.0", - "@octokit/webhooks": "^12.0.3", + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", + "@fortawesome/react-fontawesome": "^0.2.2", + "@mui/icons-material": "^5.16.1", + "@mui/material": "^5.16.1", + "@octokit/auth-app": "^6.1.1", + "@octokit/core": "^5.2.0", + "@octokit/webhooks": "^12.2.0", "@stoplight/elements": "^8.3.3", - "core-js": "^3.33.3", + "core-js": "^3.37.1", "encoding": "^0.1.13", "figma-squircle": "^0.3.1", "install": "^0.13.0", - "ioredis": "^5.3.2", - "mobx": "^6.12.0", - "next": "^14.2.3", + "ioredis": "^5.4.1", + "mobx": "^6.13.0", + "next": "^14.2.5", "next-auth": "^5.0.0-beta.19", - "nodemailer": "^6.9.9", - "npm": "^10.2.4", - "octokit": "^3.1.2", + "nodemailer": "^6.9.14", + "npm": "^10.8.2", + "octokit": "^3.2.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "redis-semaphore": "^5.5.0", - "redoc": "^2.1.3", - "styled-components": "^6.1.1", - "swagger-ui-react": "^5.9.1", - "swr": "^2.2.4", - "usehooks-ts": "^2.9.1", - "yaml": "^2.3.4", - "zod": "^3.22.4" + "redis-semaphore": "^5.6.0", + "redoc": "^2.1.5", + "styled-components": "^6.1.11", + "swagger-ui-react": "^5.17.14", + "swr": "^2.2.5", + "usehooks-ts": "^2.16.0", + "yaml": "^2.4.5", + "zod": "^3.23.8" }, "devDependencies": { "@auth/pg-adapter": "^0.5.3", - "@types/jest": "^29.5.8", - "@types/node": "^20.9.2", - "@types/nodemailer": "^6.4.14", - "@types/pg": "^8.11.0", - "@types/react": "^18", - "@types/react-dom": "^18", - "@types/swagger-ui-react": "^4.18.2", - "@typescript-eslint/eslint-plugin": "^6.9.0", - "@typescript-eslint/parser": "^6.9.1", - "autoprefixer": "^10", - "eslint": "^8.56.0", - "eslint-config-next": "^14.2.3", - "pg": "^8.11.5", - "postcss": "^8", - "tailwindcss": "^3", - "ts-jest": "^29.1.1", - "typescript": "^5.3.2" + "@types/jest": "^29.5.12", + "@types/node": "^20.14.10", + "@types/nodemailer": "^6.4.15", + "@types/pg": "^8.11.6", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@types/swagger-ui-react": "^4.18.3", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "^14.2.5", + "pg": "^8.12.0", + "postcss": "^8.4.39", + "tailwindcss": "^3.4.4", + "ts-jest": "^29.2.2", + "typescript": "^5.5.3" }, "engines": { "node": "20.8.1", @@ -1104,7 +1104,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1121,7 +1120,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1133,7 +1131,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -2860,7 +2857,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -6029,7 +6025,6 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, "funding": [ { "type": "github", @@ -6420,7 +6415,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6897,8 +6891,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -6945,8 +6938,7 @@ "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/encoding": { "version": "0.1.13", @@ -8186,7 +8178,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -8409,7 +8400,6 @@ "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", @@ -8968,7 +8958,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "engines": { "node": ">=0.8.19" } @@ -8999,8 +8988,7 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inline-style-parser": { "version": "0.1.1", @@ -9563,8 +9551,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isomorphic-fetch": { "version": "3.0.0", @@ -9696,7 +9683,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -12584,7 +12570,6 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -12604,7 +12589,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -15927,7 +15911,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -15941,7 +15924,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -17875,7 +17857,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -17887,7 +17868,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -17970,7 +17950,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -18225,7 +18204,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -18243,7 +18221,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -18256,14 +18233,12 @@ "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -18275,7 +18250,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -18407,7 +18381,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -18876,8 +18849,7 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "node_modules/thenify": { "version": "3.3.1", @@ -19785,7 +19757,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -19891,7 +19862,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -19909,7 +19879,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -19926,7 +19895,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -19941,7 +19909,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -19952,20 +19919,17 @@ "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -19979,7 +19943,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -19991,7 +19954,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -20003,7 +19965,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -20023,8 +19984,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -20036,9 +19995,7 @@ "node_modules/write-file-atomic/node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "peer": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/xml": { "version": "1.0.1", diff --git a/package.json b/package.json index 47fa8311..61ae805a 100644 --- a/package.json +++ b/package.json @@ -15,57 +15,57 @@ "testwatch": "jest --watch" }, "dependencies": { - "@emotion/react": "^11.11.1", - "@emotion/styled": "^11.11.0", - "@fortawesome/fontawesome-svg-core": "^6.4.2", - "@fortawesome/free-solid-svg-icons": "^6.4.2", - "@fortawesome/react-fontawesome": "^0.2.0", - "@mui/icons-material": "^5.14.18", - "@mui/material": "^5.14.18", - "@octokit/auth-app": "^6.0.1", - "@octokit/core": "^5.1.0", - "@octokit/webhooks": "^12.0.3", + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", + "@fortawesome/react-fontawesome": "^0.2.2", + "@mui/icons-material": "^5.16.1", + "@mui/material": "^5.16.1", + "@octokit/auth-app": "^6.1.1", + "@octokit/core": "^5.2.0", + "@octokit/webhooks": "^12.2.0", "@stoplight/elements": "^8.3.3", - "core-js": "^3.33.3", + "core-js": "^3.37.1", "encoding": "^0.1.13", "figma-squircle": "^0.3.1", "install": "^0.13.0", - "ioredis": "^5.3.2", - "mobx": "^6.12.0", - "next": "^14.2.3", + "ioredis": "^5.4.1", + "mobx": "^6.13.0", + "next": "^14.2.5", "next-auth": "^5.0.0-beta.19", - "nodemailer": "^6.9.9", - "npm": "^10.2.4", - "octokit": "^3.1.2", + "nodemailer": "^6.9.14", + "npm": "^10.8.2", + "octokit": "^3.2.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "redis-semaphore": "^5.5.0", - "redoc": "^2.1.3", - "styled-components": "^6.1.1", - "swagger-ui-react": "^5.9.1", - "swr": "^2.2.4", - "usehooks-ts": "^2.9.1", - "yaml": "^2.3.4", - "zod": "^3.22.4" + "redis-semaphore": "^5.6.0", + "redoc": "^2.1.5", + "styled-components": "^6.1.11", + "swagger-ui-react": "^5.17.14", + "swr": "^2.2.5", + "usehooks-ts": "^2.16.0", + "yaml": "^2.4.5", + "zod": "^3.23.8" }, "devDependencies": { "@auth/pg-adapter": "^0.5.3", - "@types/jest": "^29.5.8", - "@types/node": "^20.9.2", - "@types/nodemailer": "^6.4.14", - "@types/pg": "^8.11.0", - "@types/react": "^18", - "@types/react-dom": "^18", - "@types/swagger-ui-react": "^4.18.2", - "@typescript-eslint/eslint-plugin": "^6.9.0", - "@typescript-eslint/parser": "^6.9.1", - "autoprefixer": "^10", - "eslint": "^8.56.0", - "eslint-config-next": "^14.2.3", - "pg": "^8.11.5", - "postcss": "^8", - "tailwindcss": "^3", - "ts-jest": "^29.1.1", - "typescript": "^5.3.2" + "@types/jest": "^29.5.12", + "@types/node": "^20.14.10", + "@types/nodemailer": "^6.4.15", + "@types/pg": "^8.11.6", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@types/swagger-ui-react": "^4.18.3", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "^14.2.5", + "pg": "^8.12.0", + "postcss": "^8.4.39", + "tailwindcss": "^3.4.4", + "ts-jest": "^29.2.2", + "typescript": "^5.5.3" } } From a992391b976ae7f6b90b56e12aae169e92ba1383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:31:27 +0200 Subject: [PATCH 176/191] Removes IS_BUILD_PROCESS --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e021646..40bd1a85 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,6 @@ env: GITHUB_APP_ID: 12345 GITHUB_PRIVATE_KEY_BASE_64: LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNCS2NaL3UyL3dubDA4R1BjeXdiSVc2QVdKdnhVL2o0V0g2UnkxODVPSHZDd0FBQUpBYW01MzdHcHVkCit3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQktjWi91Mi93bmwwOEdQY3l3YklXNkFXSnZ4VS9qNFdINlJ5MTg1T0h2Q3cKQUFBRUJ6T2htRGpuY3R0QSt3ai9USHRnWnN6MklRWjdLM2ovb0Z1OEZBNTMxTDhrcHhuKzdiL0NlWFR3WTl6TEJzaGJvQgpZbS9GVCtQaFlmcEhMWHprNGU4TEFBQUFDMlp2YjBCb1lYQmxMbVJyQVFJPQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K GITHUB_WEBHOOK_SECRET: super-duper-secret - IS_BUILD_PROCESS: true jobs: build: name: Build From 0518bbe19da978a121c52703f1599711cc425c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:33:35 +0200 Subject: [PATCH 177/191] Adds missing environment variables --- .github/workflows/build.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 40bd1a85..6b0ef7c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,13 +7,24 @@ concurrency: cancel-in-progress: true env: NEXT_TELEMETRY_DISABLED: 1 - NEXTAUTH_URL: http://dev.local:3000 + SHAPE_DOCS_BASE_URL: http://localhost:3000 + SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: .shape-docs.yml + NEXT_PUBLIC_SHAPE_DOCS_TITLE: Shape Docs + NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: Documentation for Shape's APIs + NEXTAUTH_URL_INTERNAL: http://localhost:3000 NEXTAUTH_SECRET: 336f7a926310cff425cea29556dce2a98859b8d234aa27968696c2e6f1cb7d34 + REDIS_URL: localhost + POSTGRESQL_HOST: localhost + POSTGRESQL_USER: dbuser + POSTGRESQL_DB: db + REPOSITORY_NAME_SUFFIX: -openapi + GITHUB_WEBHOOK_SECRET: this-is-our-webhook-secret + GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: + GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: GITHUB_CLIENT_ID: this-is-our-github-client-id GITHUB_CLIENT_SECRET: this-is-our-github-client-secret - GITHUB_APP_ID: 12345 + GITHUB_APP_ID: 123456 GITHUB_PRIVATE_KEY_BASE_64: LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNCS2NaL3UyL3dubDA4R1BjeXdiSVc2QVdKdnhVL2o0V0g2UnkxODVPSHZDd0FBQUpBYW01MzdHcHVkCit3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQktjWi91Mi93bmwwOEdQY3l3YklXNkFXSnZ4VS9qNFdINlJ5MTg1T0h2Q3cKQUFBRUJ6T2htRGpuY3R0QSt3ai9USHRnWnN6MklRWjdLM2ovb0Z1OEZBNTMxTDhrcHhuKzdiL0NlWFR3WTl6TEJzaGJvQgpZbS9GVCtQaFlmcEhMWHprNGU4TEFBQUFDMlp2YjBCb1lYQmxMbVJyQVFJPQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K - GITHUB_WEBHOOK_SECRET: super-duper-secret jobs: build: name: Build From 1f1c46deb197701fdadebc9270a280266d194249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Mon, 15 Jul 2024 16:40:16 +0200 Subject: [PATCH 178/191] Removes unused IS_BUILD_PROCESS --- .github/workflows/build-and-deploy-staging.yml | 1 - Dockerfile | 3 --- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/build-and-deploy-staging.yml b/.github/workflows/build-and-deploy-staging.yml index 3652eff1..6248cd3f 100644 --- a/.github/workflows/build-and-deploy-staging.yml +++ b/.github/workflows/build-and-deploy-staging.yml @@ -20,7 +20,6 @@ env: GITHUB_APP_ID: 12345 GITHUB_PRIVATE_KEY_BASE_64: LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNCS2NaL3UyL3dubDA4R1BjeXdiSVc2QVdKdnhVL2o0V0g2UnkxODVPSHZDd0FBQUpBYW01MzdHcHVkCit3QUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQktjWi91Mi93bmwwOEdQY3l3YklXNkFXSnZ4VS9qNFdINlJ5MTg1T0h2Q3cKQUFBRUJ6T2htRGpuY3R0QSt3ai9USHRnWnN6MklRWjdLM2ovb0Z1OEZBNTMxTDhrcHhuKzdiL0NlWFR3WTl6TEJzaGJvQgpZbS9GVCtQaFlmcEhMWHprNGU4TEFBQUFDMlp2YjBCb1lYQmxMbVJyQVFJPQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K GITHUB_WEBHOOK_SECRET: super-duper-secret - IS_BUILD_PROCESS: true # used to signal to the code that it is being built in a CI/CD environment and does not have access to e.g. DB # /end: needed for the build to succeed jobs: diff --git a/Dockerfile b/Dockerfile index a6573935..f05ffa4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,9 +32,6 @@ ENV NEXT_TELEMETRY_DISABLED 1 # Copy example env vars (needed for build process - picked up by Next) COPY .env.example .env -# Signal to code that we are in the build process (e.g. to disable database access) -ENV IS_BUILD_PROCESS true - # If using npm comment out above and use below instead RUN npm run build From c2ce00cbcebe883a5bdceb2a771b4d66b8b11f16 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 16 Jul 2024 08:56:56 +0200 Subject: [PATCH 179/191] Infrastructure updates --- infrastructure/aws/lib/app-stack.ts | 30 ++++++++++++++---------- infrastructure/aws/lib/postgres-stack.ts | 11 +++++++++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/infrastructure/aws/lib/app-stack.ts b/infrastructure/aws/lib/app-stack.ts index b92a44a4..c8b31c81 100644 --- a/infrastructure/aws/lib/app-stack.ts +++ b/infrastructure/aws/lib/app-stack.ts @@ -5,6 +5,7 @@ import { EcrImage, FargateService, Secret } from 'aws-cdk-lib/aws-ecs'; import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns'; import { Construct } from 'constructs'; import { Certificate } from 'aws-cdk-lib/aws-certificatemanager'; +import { ListenerAction } from 'aws-cdk-lib/aws-elasticloadbalancingv2'; interface AppStackProps extends cdk.StackProps { vpc: Vpc; @@ -34,19 +35,11 @@ export class AppStack extends cdk.Stack { "GITHUB_WEBHOK_REPOSITORY_ALLOWLIST", "GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST", "GITHUB_WEBHOOK_SECRET", - // Auth0 - "AUTH0_BASE_URL", // TODO: Remove once we have transitioned to NEXTAUTH - "AUTH0_CLIENT_ID", // TODO: Remove once we have transitioned to NEXTAUTH - "AUTH0_CLIENT_SECRET", // TODO: Remove once we have transitioned to NEXTAUTH - "AUTH0_ISSUER_BASE_URL", // TODO: Remove once we have transitioned to NEXTAUTH - "AUTH0_MANAGEMENT_CLIENT_ID", // TODO: Remove once we have transitioned to NEXTAUTH - "AUTH0_MANAGEMENT_CLIENT_SECRET", // TODO: Remove once we have transitioned to NEXTAUTH - "AUTH0_MANAGEMENT_DOMAIN", // TODO: Remove once we have transitioned to NEXTAUTH - "AUTH0_SECRET", // TODO: Remove once we have transitioned to NEXTAUTH + // NextAuth "NEXTAUTH_SECRET", - "NEXTAUTH_URL", // TODO: Could be part of config along with certificate issuing + "NEXTAUTH_URL", // Other - "SHAPE_DOCS_BASE_URL", // TODO: Could be part of config along with certificate issuing + "SHAPE_DOCS_BASE_URL", ] // create the env vars as secrets in Secrets Manager @@ -77,7 +70,11 @@ export class AppStack extends cdk.Stack { POSTGRESQL_HOST: props.postgresHostname, POSTGRESQL_USER: props.postgresUser, POSTGRESQL_DB: props.postgresDb, - NEXT_PUBLIC_SHAPE_DOCS_TITLE: 'Shape Docs', + NEXT_PUBLIC_SHAPE_DOCS_TITLE: "Shape Docs", + NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: "Documentation for Shape's APIs", + SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: ".shape-docs.yml", + REPOSITORY_NAME_SUFFIX: "-openapi", + AUTH_TRUST_HOST: "true", }, secrets: { ...envVars.reduce((acc, curr) => { // get each env var from Secrets Manager @@ -101,6 +98,15 @@ export class AppStack extends cdk.Stack { path: "/api/health", }); + app.loadBalancer.addListener('HTTP', { + port: 80, + defaultAction: ListenerAction.redirect({ + protocol: 'HTTPS', + port: '443', + permanent: true, + }), + }); + this.service = app.service; this.loadBalancer = app.loadBalancer; } diff --git a/infrastructure/aws/lib/postgres-stack.ts b/infrastructure/aws/lib/postgres-stack.ts index c76c31f1..161c2c4c 100644 --- a/infrastructure/aws/lib/postgres-stack.ts +++ b/infrastructure/aws/lib/postgres-stack.ts @@ -24,6 +24,16 @@ export class PostgresStack extends Stack { } }) + const parameterGroup = new rds.ParameterGroup(this, 'PostgresParameterGroup', { + engine: rds.DatabaseInstanceEngine.postgres({ + version: rds.PostgresEngineVersion.VER_16, + }), + description: 'Parameter group for postgres16', + parameters: { + 'rds.force_ssl': '0', // allow non-ssl connections + }, + }); + this.dbInstance = new rds.DatabaseInstance(this, `DbInstance`, { engine: rds.DatabaseInstanceEngine.postgres({ version: rds.PostgresEngineVersion.VER_16, @@ -45,6 +55,7 @@ export class PostgresStack extends Stack { enablePerformanceInsights: true, multiAz: false, storageEncrypted: true, + parameterGroup: parameterGroup, }) } } From ea35fe9c719b434351af1c6eeba059c3d5d4972c Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 16 Jul 2024 09:23:10 +0200 Subject: [PATCH 180/191] Update staging certificate --- infrastructure/aws/bin/infrastructure.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/aws/bin/infrastructure.ts b/infrastructure/aws/bin/infrastructure.ts index 34e2bdca..00c41876 100644 --- a/infrastructure/aws/bin/infrastructure.ts +++ b/infrastructure/aws/bin/infrastructure.ts @@ -21,7 +21,7 @@ const prodAccount: cdk.Environment = { new DocsDeployment(app, 'Staging', { env: nonProdAccount, - publicCertificateArn: 'arn:aws:acm:eu-central-1:841481405304:certificate/6d513b25-bbca-49ec-9de0-377e303c313f', + publicCertificateArn: 'arn:aws:acm:eu-central-1:841481405304:certificate/6a081533-5176-40d1-b1b8-27a88abb8dbe', }) new DocsDeployment(app, 'Prod', { From 0818c52b6fa326ba0f0c1571a0ded22952c4d569 Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 16 Jul 2024 10:24:50 +0200 Subject: [PATCH 181/191] Specify domain name in code --- infrastructure/aws/bin/infrastructure.ts | 2 ++ infrastructure/aws/lib/app-stack.ts | 8 ++++---- infrastructure/aws/lib/docs-deployment.ts | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/infrastructure/aws/bin/infrastructure.ts b/infrastructure/aws/bin/infrastructure.ts index 00c41876..17900059 100644 --- a/infrastructure/aws/bin/infrastructure.ts +++ b/infrastructure/aws/bin/infrastructure.ts @@ -21,10 +21,12 @@ const prodAccount: cdk.Environment = { new DocsDeployment(app, 'Staging', { env: nonProdAccount, + domainName: 'staging.docs.shapetools.io', publicCertificateArn: 'arn:aws:acm:eu-central-1:841481405304:certificate/6a081533-5176-40d1-b1b8-27a88abb8dbe', }) new DocsDeployment(app, 'Prod', { env: prodAccount, + domainName: 'docs.shapetools.io', publicCertificateArn: 'arn:aws:acm:eu-central-1:841481405304:certificate/6d513b25-bbca-49ec-9de0-377e303c313f', // TODO: Replace with prod cert }) diff --git a/infrastructure/aws/lib/app-stack.ts b/infrastructure/aws/lib/app-stack.ts index c8b31c81..a94c312e 100644 --- a/infrastructure/aws/lib/app-stack.ts +++ b/infrastructure/aws/lib/app-stack.ts @@ -15,6 +15,7 @@ interface AppStackProps extends cdk.StackProps { postgresUser: string, postgresDb: string, postgresPassword: sm.ISecret, + domainName: string, publicCertificateArn: string, } @@ -37,9 +38,6 @@ export class AppStack extends cdk.Stack { "GITHUB_WEBHOOK_SECRET", // NextAuth "NEXTAUTH_SECRET", - "NEXTAUTH_URL", - // Other - "SHAPE_DOCS_BASE_URL", ] // create the env vars as secrets in Secrets Manager @@ -74,7 +72,9 @@ export class AppStack extends cdk.Stack { NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: "Documentation for Shape's APIs", SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: ".shape-docs.yml", REPOSITORY_NAME_SUFFIX: "-openapi", - AUTH_TRUST_HOST: "true", + AUTH_TRUST_HOST: "true", // https://authjs.dev/getting-started/deployment#docker + SHAPE_DOCS_BASE_URL: `https://${props.domainName}`, + NEXTAUTH_URL: `https://${props.domainName}`, }, secrets: { ...envVars.reduce((acc, curr) => { // get each env var from Secrets Manager diff --git a/infrastructure/aws/lib/docs-deployment.ts b/infrastructure/aws/lib/docs-deployment.ts index 55427f91..7ff09a9a 100644 --- a/infrastructure/aws/lib/docs-deployment.ts +++ b/infrastructure/aws/lib/docs-deployment.ts @@ -8,6 +8,7 @@ import { ContainerImage } from 'aws-cdk-lib/aws-ecs'; interface DocsDeploymentProps { env: Environment, + domainName: string, publicCertificateArn: string, } @@ -43,6 +44,7 @@ export default class DocsDeployment extends Construct { postgresDb: this.postgres.database, postgresPassword: this.postgres.password, redisHostname: this.redis.cluster.attrRedisEndpointAddress, + domainName: props.domainName, publicCertificateArn: props.publicCertificateArn, }); From dbcb67325c1c133d7a5b58d9754b46297899674c Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 16 Jul 2024 10:29:40 +0200 Subject: [PATCH 182/191] Move AWS infrastructure to separate repository --- infrastructure/aws/.gitignore | 8 - infrastructure/aws/.npmignore | 6 - infrastructure/aws/README.md | 12 - infrastructure/aws/bin/infrastructure.ts | 32 - infrastructure/aws/cdk.context.json | 7 - infrastructure/aws/cdk.json | 64 - infrastructure/aws/lib/app-stack.ts | 113 - infrastructure/aws/lib/docs-deployment.ts | 54 - .../aws/lib/infrastructure-stack.ts | 59 - infrastructure/aws/lib/postgres-stack.ts | 61 - infrastructure/aws/lib/redis-stack.ts | 44 - infrastructure/aws/package-lock.json | 4287 ----------------- infrastructure/aws/package.json | 23 - infrastructure/aws/tsconfig.json | 31 - 14 files changed, 4801 deletions(-) delete mode 100644 infrastructure/aws/.gitignore delete mode 100644 infrastructure/aws/.npmignore delete mode 100644 infrastructure/aws/README.md delete mode 100644 infrastructure/aws/bin/infrastructure.ts delete mode 100644 infrastructure/aws/cdk.context.json delete mode 100644 infrastructure/aws/cdk.json delete mode 100644 infrastructure/aws/lib/app-stack.ts delete mode 100644 infrastructure/aws/lib/docs-deployment.ts delete mode 100644 infrastructure/aws/lib/infrastructure-stack.ts delete mode 100644 infrastructure/aws/lib/postgres-stack.ts delete mode 100644 infrastructure/aws/lib/redis-stack.ts delete mode 100644 infrastructure/aws/package-lock.json delete mode 100644 infrastructure/aws/package.json delete mode 100644 infrastructure/aws/tsconfig.json diff --git a/infrastructure/aws/.gitignore b/infrastructure/aws/.gitignore deleted file mode 100644 index f60797b6..00000000 --- a/infrastructure/aws/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.js -!jest.config.js -*.d.ts -node_modules - -# CDK asset staging directory -.cdk.staging -cdk.out diff --git a/infrastructure/aws/.npmignore b/infrastructure/aws/.npmignore deleted file mode 100644 index c1d6d45d..00000000 --- a/infrastructure/aws/.npmignore +++ /dev/null @@ -1,6 +0,0 @@ -*.ts -!*.d.ts - -# CDK asset staging directory -.cdk.staging -cdk.out diff --git a/infrastructure/aws/README.md b/infrastructure/aws/README.md deleted file mode 100644 index 317604aa..00000000 --- a/infrastructure/aws/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Shape Docs AWS Infrastructure - -Infrastructure as code for deploying Shape Docs to AWS. - -## Useful commands - -* `npm run build` compile typescript to js -* `npm run watch` watch for changes and compile -* `npm run test` perform the jest unit tests -* `npx cdk deploy` deploy this stack to your default AWS account/region -* `npx cdk diff` compare deployed stack with current state -* `npx cdk synth` emits the synthesized CloudFormation template diff --git a/infrastructure/aws/bin/infrastructure.ts b/infrastructure/aws/bin/infrastructure.ts deleted file mode 100644 index 17900059..00000000 --- a/infrastructure/aws/bin/infrastructure.ts +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env node -import 'source-map-support/register'; -import * as cdk from 'aws-cdk-lib'; -import DocsDeployment from '../lib/docs-deployment'; - -const app = new cdk.App(); - -/** ACCOUNTS */ - -const nonProdAccount: cdk.Environment = { - account: "841481405304", - region: "eu-central-1", -}; - -const prodAccount: cdk.Environment = { - account: "721428964064", - region: "eu-central-1", -}; - -/** DEPLOYMENTS */ - -new DocsDeployment(app, 'Staging', { - env: nonProdAccount, - domainName: 'staging.docs.shapetools.io', - publicCertificateArn: 'arn:aws:acm:eu-central-1:841481405304:certificate/6a081533-5176-40d1-b1b8-27a88abb8dbe', -}) - -new DocsDeployment(app, 'Prod', { - env: prodAccount, - domainName: 'docs.shapetools.io', - publicCertificateArn: 'arn:aws:acm:eu-central-1:841481405304:certificate/6d513b25-bbca-49ec-9de0-377e303c313f', // TODO: Replace with prod cert -}) diff --git a/infrastructure/aws/cdk.context.json b/infrastructure/aws/cdk.context.json deleted file mode 100644 index 2d7f926b..00000000 --- a/infrastructure/aws/cdk.context.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "availability-zones:account=841481405304:region=eu-central-1": [ - "eu-central-1a", - "eu-central-1b", - "eu-central-1c" - ] -} diff --git a/infrastructure/aws/cdk.json b/infrastructure/aws/cdk.json deleted file mode 100644 index d5298049..00000000 --- a/infrastructure/aws/cdk.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "app": "npx ts-node --prefer-ts-exts bin/infrastructure.ts", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "**/*.d.ts", - "**/*.js", - "tsconfig.json", - "package*.json", - "yarn.lock", - "node_modules", - "test" - ] - }, - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-iam:standardizedServicePrincipals": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-efs:denyAnonymousAccess": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, - "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, - "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, - "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, - "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, - "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, - "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true - } -} diff --git a/infrastructure/aws/lib/app-stack.ts b/infrastructure/aws/lib/app-stack.ts deleted file mode 100644 index a94c312e..00000000 --- a/infrastructure/aws/lib/app-stack.ts +++ /dev/null @@ -1,113 +0,0 @@ -import * as cdk from 'aws-cdk-lib'; -import * as sm from 'aws-cdk-lib/aws-secretsmanager'; -import { Vpc } from 'aws-cdk-lib/aws-ec2'; -import { EcrImage, FargateService, Secret } from 'aws-cdk-lib/aws-ecs'; -import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns'; -import { Construct } from 'constructs'; -import { Certificate } from 'aws-cdk-lib/aws-certificatemanager'; -import { ListenerAction } from 'aws-cdk-lib/aws-elasticloadbalancingv2'; - -interface AppStackProps extends cdk.StackProps { - vpc: Vpc; - image: EcrImage; - redisHostname: string, - postgresHostname: string, - postgresUser: string, - postgresDb: string, - postgresPassword: sm.ISecret, - domainName: string, - publicCertificateArn: string, -} - -export class AppStack extends cdk.Stack { - readonly service: FargateService; - readonly loadBalancer: cdk.aws_elasticloadbalancingv2.ApplicationLoadBalancer; - - constructor(scope: Construct, id: string, props: AppStackProps) { - super(scope, id, props); - - // list of all env vars to be stored in Secrets Manager - const envVars = [ - // GitHub - "GITHUB_APP_ID", - "GITHUB_CLIENT_ID", - "GITHUB_CLIENT_SECRET", - "GITHUB_PRIVATE_KEY_BASE_64", - "GITHUB_WEBHOK_REPOSITORY_ALLOWLIST", - "GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST", - "GITHUB_WEBHOOK_SECRET", - // NextAuth - "NEXTAUTH_SECRET", - ] - - // create the env vars as secrets in Secrets Manager - // Note: secrets are created with an initial value which should be replaced via the AWS SecretsManager Console - // https://eu-central-1.console.aws.amazon.com/secretsmanager/listsecrets?region=eu-central-1 - const secrets = envVars.reduce((acc, curr) => { - acc[curr] = new sm.Secret(this, `${id}Secret${curr}`, { - secretName: `${id}/${curr}`, - }); - return acc; - }, {} as { [key: string]: sm.Secret }); - - // must be created & validated in the AWS Console - // https://eu-central-1.console.aws.amazon.com/acm/home?region=eu-central-1 - const certificate = Certificate.fromCertificateArn(this, `${id}Certificate`, props.publicCertificateArn) - - const app = new ApplicationLoadBalancedFargateService(this, "AppService", { - vpc: props.vpc, - assignPublicIp: false, // run in private network - desiredCount: 1, - cpu: 256, - memoryLimitMiB: 512, - publicLoadBalancer: true, - taskImageOptions: { - image: props.image, - environment: { - REDIS_URL: props.redisHostname, - POSTGRESQL_HOST: props.postgresHostname, - POSTGRESQL_USER: props.postgresUser, - POSTGRESQL_DB: props.postgresDb, - NEXT_PUBLIC_SHAPE_DOCS_TITLE: "Shape Docs", - NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: "Documentation for Shape's APIs", - SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: ".shape-docs.yml", - REPOSITORY_NAME_SUFFIX: "-openapi", - AUTH_TRUST_HOST: "true", // https://authjs.dev/getting-started/deployment#docker - SHAPE_DOCS_BASE_URL: `https://${props.domainName}`, - NEXTAUTH_URL: `https://${props.domainName}`, - }, - secrets: { - ...envVars.reduce((acc, curr) => { // get each env var from Secrets Manager - acc[curr] = Secret.fromSecretsManager(secrets[curr]); - return acc; - }, {} as { [key: string]: Secret }), - POSTGRESQL_PASSWORD: Secret.fromSecretsManager(props.postgresPassword), - }, - containerPort: 3000, - }, - circuitBreaker: { - rollback: true, - }, - healthCheckGracePeriod: cdk.Duration.seconds(60), - certificate: certificate, - }); - - app.targetGroup.setAttribute('deregistration_delay.timeout_seconds', '15'); - - app.targetGroup.configureHealthCheck({ - path: "/api/health", - }); - - app.loadBalancer.addListener('HTTP', { - port: 80, - defaultAction: ListenerAction.redirect({ - protocol: 'HTTPS', - port: '443', - permanent: true, - }), - }); - - this.service = app.service; - this.loadBalancer = app.loadBalancer; - } -} diff --git a/infrastructure/aws/lib/docs-deployment.ts b/infrastructure/aws/lib/docs-deployment.ts deleted file mode 100644 index 7ff09a9a..00000000 --- a/infrastructure/aws/lib/docs-deployment.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Construct } from 'constructs' -import { InfrastructureStack } from './infrastructure-stack'; -import { Environment } from 'aws-cdk-lib'; -import { PostgresStack } from './postgres-stack'; -import { RedisStack } from './redis-stack'; -import { AppStack } from './app-stack'; -import { ContainerImage } from 'aws-cdk-lib/aws-ecs'; - -interface DocsDeploymentProps { - env: Environment, - domainName: string, - publicCertificateArn: string, -} - -export default class DocsDeployment extends Construct { - readonly infrastructure: InfrastructureStack - readonly postgres: PostgresStack - readonly redis: RedisStack - readonly app: AppStack - - constructor(scope: Construct, id: string, props: DocsDeploymentProps) { - super(scope, id) - - this.infrastructure = new InfrastructureStack(scope, `${id}Infrastructure`, { - env: props.env, - }); - - this.postgres = new PostgresStack(scope, `${id}Postgres`, { - env: props.env, - vpc: this.infrastructure.vpc, - }); - - this.redis = new RedisStack(scope, `${id}Redis`, { - env: props.env, - vpc: this.infrastructure.vpc, - }); - - this.app = new AppStack(scope, `${id}App`, { - env: props.env, - vpc: this.infrastructure.vpc, - image: ContainerImage.fromEcrRepository(this.infrastructure.dockerRepository, 'latest'), - postgresHostname: this.postgres.dbInstance.instanceEndpoint.hostname, - postgresUser: this.postgres.username, - postgresDb: this.postgres.database, - postgresPassword: this.postgres.password, - redisHostname: this.redis.cluster.attrRedisEndpointAddress, - domainName: props.domainName, - publicCertificateArn: props.publicCertificateArn, - }); - - this.app.service.connections.allowToDefaultPort(this.redis); - this.app.service.connections.allowToDefaultPort(this.postgres.dbInstance); - } -} diff --git a/infrastructure/aws/lib/infrastructure-stack.ts b/infrastructure/aws/lib/infrastructure-stack.ts deleted file mode 100644 index 06afdf84..00000000 --- a/infrastructure/aws/lib/infrastructure-stack.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as cdk from 'aws-cdk-lib'; -import { IpAddresses, Vpc } from 'aws-cdk-lib/aws-ec2'; -import { Repository } from 'aws-cdk-lib/aws-ecr'; -import { Effect, Policy, PolicyStatement, User } from 'aws-cdk-lib/aws-iam'; -import { Construct } from 'constructs'; - -export class InfrastructureStack extends cdk.Stack { - readonly vpc: Vpc; - readonly dockerRepository: Repository; - - constructor(scope: Construct, id: string, props?: cdk.StackProps) { - super(scope, id, props); - - this.vpc = new Vpc(this, 'VPC', { - ipAddresses: IpAddresses.cidr("10.0.0.0/16"), - maxAzs: 2, - }); - - this.dockerRepository = new Repository(this, 'Repository', { - repositoryName: 'shapedocs', - removalPolicy: cdk.RemovalPolicy.DESTROY, - }); - - const deploymentPolicy = new Policy(this, 'DeploymentPolicy', { - policyName: 'DeploymentPolicy', - statements: [ - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - // ECR - "ecr:GetAuthorizationToken", - "ecr:GetDownloadUrlForLayer", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload", - // ECS - "ecs:DescribeServices", - "ecs:UpdateService", - "ecs:RegisterTaskDefinition", - "ecs:DeregisterTaskDefinition", - "ecs:DescribeTaskDefinition", - "iam:PassRole" - ], - resources: [ - "*" - ], - }), - ], - }); - - const deploymentUser = new User(this, 'GitHubActionsUser'); - - deploymentPolicy.attachToUser(deploymentUser); - - deploymentUser.attachInlinePolicy(deploymentPolicy); - } -} diff --git a/infrastructure/aws/lib/postgres-stack.ts b/infrastructure/aws/lib/postgres-stack.ts deleted file mode 100644 index 161c2c4c..00000000 --- a/infrastructure/aws/lib/postgres-stack.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Construct } from 'constructs' -import { Duration, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib' -import { Vpc, InstanceClass, InstanceType, InstanceSize } from 'aws-cdk-lib/aws-ec2' -import * as rds from 'aws-cdk-lib/aws-rds' -import * as sm from 'aws-cdk-lib/aws-secretsmanager' - -interface PostgresStackProps extends StackProps { - vpc: Vpc; -} - -export class PostgresStack extends Stack { - readonly dbInstance: rds.DatabaseInstance; - readonly database: string = 'docs'; - readonly username: string = 'docs'; - readonly password: sm.Secret; - - constructor(scope: Construct, id: string, props: PostgresStackProps) { - super(scope, id, props); - - this.password = new sm.Secret(this, `DbPasswordSecret`, { - secretName: `${id}DbPassword`, - generateSecretString: { - excludePunctuation: true - } - }) - - const parameterGroup = new rds.ParameterGroup(this, 'PostgresParameterGroup', { - engine: rds.DatabaseInstanceEngine.postgres({ - version: rds.PostgresEngineVersion.VER_16, - }), - description: 'Parameter group for postgres16', - parameters: { - 'rds.force_ssl': '0', // allow non-ssl connections - }, - }); - - this.dbInstance = new rds.DatabaseInstance(this, `DbInstance`, { - engine: rds.DatabaseInstanceEngine.postgres({ - version: rds.PostgresEngineVersion.VER_16, - }), - instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MICRO), - credentials: { - username: this.username, - password: this.password.secretValue - }, - vpc: props.vpc, - allocatedStorage: 5, - maxAllocatedStorage: 50, - allowMajorVersionUpgrade: false, - autoMinorVersionUpgrade: true, - backupRetention: Duration.days(7), - deletionProtection: true, - databaseName: this.database, - removalPolicy: RemovalPolicy.RETAIN, - enablePerformanceInsights: true, - multiAz: false, - storageEncrypted: true, - parameterGroup: parameterGroup, - }) - } -} diff --git a/infrastructure/aws/lib/redis-stack.ts b/infrastructure/aws/lib/redis-stack.ts deleted file mode 100644 index 40d3497a..00000000 --- a/infrastructure/aws/lib/redis-stack.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Stack, StackProps } from "aws-cdk-lib" -import { CfnCacheCluster, CfnSubnetGroup } from "aws-cdk-lib/aws-elasticache" -import { Connections, Port, SecurityGroup, Vpc } from "aws-cdk-lib/aws-ec2" -import { Construct } from "constructs" - -interface RedisStackProps extends StackProps { - vpc: Vpc; -} - -export class RedisStack extends Stack { - readonly cluster: CfnCacheCluster; - readonly connections: Connections; - - constructor(scope: Construct, id: string, props: RedisStackProps) { - super(scope, id, props); - - const subnetGroup = new CfnSubnetGroup(this, "SubnetGroup", { - cacheSubnetGroupName: `${id}SubnetGroup`, - description: `List of subnets used for redis cache ${id}`, - subnetIds: props.vpc.privateSubnets.map(subnet => subnet.subnetId) - }); - - const securityGroup = new SecurityGroup(this, "SecurityGroup", { - securityGroupName: `${id}SecurityGroup`, - vpc: props.vpc - }); - - this.connections = new Connections({ - securityGroups: [securityGroup], - defaultPort: Port.tcp(6379) - }); - - this.cluster = new CfnCacheCluster(this, `${id}Cluster`, { - cacheNodeType: 'cache.t2.micro', - engine: 'redis', - numCacheNodes: 1, - autoMinorVersionUpgrade: true, - cacheSubnetGroupName: subnetGroup.cacheSubnetGroupName, - vpcSecurityGroupIds: [ - securityGroup.securityGroupId, - ] - }); - } -} diff --git a/infrastructure/aws/package-lock.json b/infrastructure/aws/package-lock.json deleted file mode 100644 index 327c87a3..00000000 --- a/infrastructure/aws/package-lock.json +++ /dev/null @@ -1,4287 +0,0 @@ -{ - "name": "infrastructure", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "infrastructure", - "version": "0.1.0", - "dependencies": { - "aws-cdk-lib": "2.113.0", - "constructs": "^10.0.0", - "source-map-support": "^0.5.21" - }, - "bin": { - "infrastructure": "bin/infrastructure.js" - }, - "devDependencies": { - "@types/jest": "^29.5.8", - "@types/node": "20.9.1", - "aws-cdk": "2.139.0", - "jest": "^29.7.0", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.1", - "typescript": "~5.2.2" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.201", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.201.tgz", - "integrity": "sha512-INZqcwDinNaIdb5CtW3ez5s943nX5stGBQS6VOP2JDlOFP81hM3fds/9NDknipqfUkZM43dx+HgVvkXYXXARCQ==" - }, - "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", - "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==" - }, - "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.1.tgz", - "integrity": "sha512-DDt4SLdLOwWCjGtltH4VCST7hpOI5DzieuhGZsBpZ+AgJdSI2GCjklCXm0GCTwJG/SolkL5dtQXyUKgg9luBDg==" - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "dependencies": { - "@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.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@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.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.10", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.10.tgz", - "integrity": "sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/node": { - "version": "20.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.1.tgz", - "integrity": "sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aws-cdk": { - "version": "2.139.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.139.0.tgz", - "integrity": "sha512-MjMsySQbhR5yTWCphnuVQuS15UdGMV6v4XIM+C8SN7/eUOfv7BFr7QgYMUm5WXCG/f66RnY0zjJbOLRxvcjCrQ==", - "dev": true, - "bin": { - "cdk": "bin/cdk" - }, - "engines": { - "node": ">= 14.15.0" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/aws-cdk-lib": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.113.0.tgz", - "integrity": "sha512-wZYooUd8nb3ADFtg1dSnsRt1MJmeqEK8cKOnPfCGCfm852YnVdb0qIEclURCQ4ygDJSuqiw9CE+xWVeTQTG6Dw==", - "bundleDependencies": [ - "@balena/dockerignore", - "case", - "fs-extra", - "ignore", - "jsonschema", - "minimatch", - "punycode", - "semver", - "table", - "yaml" - ], - "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.201", - "@aws-cdk/asset-kubectl-v20": "^2.1.2", - "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.1", - "@balena/dockerignore": "^1.0.2", - "case": "1.6.3", - "fs-extra": "^11.1.1", - "ignore": "^5.3.0", - "jsonschema": "^1.4.1", - "minimatch": "^3.1.2", - "punycode": "^2.3.1", - "semver": "^7.5.4", - "table": "^6.8.1", - "yaml": "1.10.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "constructs": "^10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { - "version": "1.0.2", - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/aws-cdk-lib/node_modules/ajv": { - "version": "8.12.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/aws-cdk-lib/node_modules/ansi-regex": { - "version": "5.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/ansi-styles": { - "version": "4.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aws-cdk-lib/node_modules/astral-regex": { - "version": "2.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/aws-cdk-lib/node_modules/case": { - "version": "1.6.3", - "inBundle": true, - "license": "(MIT OR GPL-3.0-or-later)", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/color-convert": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/color-name": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/emoji-regex": { - "version": "8.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { - "version": "3.1.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "11.1.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/aws-cdk-lib/node_modules/graceful-fs": { - "version": "4.2.11", - "inBundle": true, - "license": "ISC" - }, - "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.3.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/jsonfile": { - "version": "6.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/aws-cdk-lib/node_modules/jsonschema": { - "version": "1.4.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { - "version": "4.4.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/aws-cdk-lib/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aws-cdk-lib/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/aws-cdk-lib/node_modules/punycode": { - "version": "2.3.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/aws-cdk-lib/node_modules/require-from-string": { - "version": "2.0.2", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.5.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aws-cdk-lib/node_modules/slice-ansi": { - "version": "4.0.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/aws-cdk-lib/node_modules/string-width": { - "version": "4.2.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/strip-ansi": { - "version": "6.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aws-cdk-lib/node_modules/table": { - "version": "6.8.1", - "inBundle": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/universalify": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/uri-js": { - "version": "4.4.1", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/aws-cdk-lib/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/aws-cdk-lib/node_modules/yaml": { - "version": "1.10.2", - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001566", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", - "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/constructs": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", - "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", - "engines": { - "node": ">= 16.14.0" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.601", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.601.tgz", - "integrity": "sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/infrastructure/aws/package.json b/infrastructure/aws/package.json deleted file mode 100644 index 8571ce85..00000000 --- a/infrastructure/aws/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "infrastructure", - "version": "0.1.0", - "bin": { - "infrastructure": "bin/infrastructure.js" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "cdk": "cdk" - }, - "devDependencies": { - "@types/node": "20.9.1", - "aws-cdk": "2.139.0", - "ts-node": "^10.9.1", - "typescript": "~5.2.2" - }, - "dependencies": { - "aws-cdk-lib": "2.113.0", - "constructs": "^10.0.0", - "source-map-support": "^0.5.21" - } -} diff --git a/infrastructure/aws/tsconfig.json b/infrastructure/aws/tsconfig.json deleted file mode 100644 index aaa7dc51..00000000 --- a/infrastructure/aws/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": [ - "es2020", - "dom" - ], - "declaration": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "noImplicitThis": true, - "alwaysStrict": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": false, - "inlineSourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictPropertyInitialization": false, - "typeRoots": [ - "./node_modules/@types" - ] - }, - "exclude": [ - "node_modules", - "cdk.out" - ] -} From 264c76f15f11301b706eaa97875d46df31ceba39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Tue, 16 Jul 2024 15:22:53 +0200 Subject: [PATCH 183/191] Updates PR comments --- src/features/hooks/domain/PullRequestCommenter.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/features/hooks/domain/PullRequestCommenter.ts b/src/features/hooks/domain/PullRequestCommenter.ts index cb4783a7..d1d0ca90 100644 --- a/src/features/hooks/domain/PullRequestCommenter.ts +++ b/src/features/hooks/domain/PullRequestCommenter.ts @@ -118,7 +118,7 @@ export default class PullRequestCommenter { ref: string }) { const { files, owner, repositoryName, ref } = params - const rows: { filename: string, link: string, status: string }[] = [] + const rows: { filename: string, status: string, button: string }[] = [] const projectId = this.getProjectId({ repositoryName }) // Make sure we don't include the project configuration file. const baseConfigFilename = this.projectConfigurationFilename.replace(this.fileExtensionRegex, "") @@ -126,18 +126,18 @@ export default class PullRequestCommenter { // Create rows for each file for (const file of changedFiles) { const status = this.getStatusText(file) - let link = "" + let button = "" if (file.status != "removed") { - const url = `${this.domain}/${owner}/${projectId}/${ref}/${file.filename}` - link += ` ${url}` + const link = `${this.domain}/${owner}/${projectId}/${ref}/${file.filename}` + button = ` Preview` } - rows.push({ filename: file.filename, status, link }) + rows.push({ filename: file.filename, status, button }) } if (rows.length == 0) { return undefined } const rowsHTML = rows - .map(row => `${row.filename}${row.link}${row.status}`) + .map(row => `${row.filename}${row.status}${row.button}`) .join("\n") return `${rowsHTML}
` } From 54de62bb9d728ec228951bee6c847e36c0ede173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=2E=20St=C3=B8vring?= Date: Tue, 16 Jul 2024 17:32:21 +0200 Subject: [PATCH 184/191] Update README.md --- README.md | 95 ++++++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 2cb27f30..eaeefee6 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,66 @@ -

- -

+
+Shape Docs logo +
-# shape-docs +
+

👋 Welcome to Shape Docs

+

Self-hosted web portal that collects OpenAPI documentation and facilitates spec-driven development, built with GitHub-based authorization.

+
-Portal displaying our projects that are documented with OpenAPI. Hosted on [docs.shapetools.io](https://docs.shapetools.io) and [staging.docs.shapetools.io](https://staging.docs.shapetools.io). +
+ + + +
-[![Build](https://github.com/shapehq/shape-docs/actions/workflows/build.yml/badge.svg)](https://github.com/shapehq/shape-docs/actions/workflows/build.yml) -[![Test](https://github.com/shapehq/shape-docs/actions/workflows/test.yml/badge.svg)](https://github.com/shapehq/shape-docs/actions/workflows/test.yml) -[![Lint](https://github.com/shapehq/shape-docs/actions/workflows/lint.yml/badge.svg)](https://github.com/shapehq/shape-docs/actions/workflows/lint.yml) +--- -## 💻 Running the App Locally + -Copy `.env.example` to `.env.local` in the root of the project. Make sure to replace any placeholders and generate a random secret using OpenSSL. +
-The table below contains explanations for environment variables. The list is not preemptive and `.env.example` contains the full list. +Shape Docs makes managing and previewing OpenAPI documentation a breeze, streamlining spec-driven development. With GitHub-based authorization, you can easily control who accesses your docs. Shape Docs comments on pull requests that tweak your OpenAPI specs, giving you preview URLs to ensure every update is well-reviewed -|Environment Variable|Description| -|-|-| -|NEXT_PUBLIC_SHAPE_DOCS_TITLE|Title of the portal. Displayed to the user in the browser.| -|SHAPE_DOCS_BASE_URL|The URL where Shape Docs is hosted.| -|NEXTAUTH_URL|The URL where Shape Docs is hosted.| -|NEXTAUTH_SECRET|A long secret value used to encrypt the session cookie. Generate it using `openssl rand -base64 32`.| -|GITHUB_CLIENT_ID|The client ID of your GitHub app.| -|GITHUB_CLIENT_SECRET|The client secret of your GitHub app.| -|GITHUB_APP_ID|The ID of your GitHub app.| -|GITHUB_PRIVATE_KEY_BASE_64|Your GitHub app's private key encoded to base 64. Can be created using `cat my-key.pem | base64 | pbcopy`.| -|GITHUB_WEBHOOK_SECRET|Secret shared with the GitHub app to validate a webhook call.| -|GITHUB_WEBHOK_REPOSITORY_ALLOWLIST|Comma-separated list of repositories from which webhook calls should be accepted. Leave empty to accept calls from all repositories.| -|GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST|Comma-separated list of repositories from which webhook calls should be ignored. The list of disallowed repositories takes precedence over the list of allowed repositories.| -|REDIS_URL|The URL to the Redis store.| +
+Screenshot of Shape Docs +
-Be aware that the GitHub private key must be PKCS8. GitHub creates PKCS1 keys, so we must manually convert the key from PKCS1 to PKCS8 before base64 encoding it. This can be done as follows: +## 🚀 Getting Started -```bash -openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt \ - -in private-key-pkcs1.pem \ - -out private-key-pkcs8.pem -``` +Please refer to the following articles in [the wiki](https://github.com/shapehq/tartelet/wiki) to get started with Tartelet. -The key can then be base 64 encoded and assigned to the GITHUB_PRIVATE_KEY_BASE_64 environment variable as follows: +- [Adding Documentation to Shape Docs](https://github.com/shapehq/shape-docs/wiki/Adding-Documentation-to-Shape-Docs) +- [Browsing Documentation](https://github.com/shapehq/shape-docs/wiki/Browsing-Documentation) +- [Updating Documentation](https://github.com/shapehq/shape-docs/wiki/Updating-Documentation) +- [Deploying Shape Docs](https://github.com/shapehq/shape-docs/wiki/Deploying-Shape-Docs) -```bash -base64 -i ~/Downloads/private-key-pkcs8.pem | pbcopy -``` +## 👨‍🔧 How does it work? -Run the app using the following command: +Shape Docs uses [OpenAPI specifications](https://swagger.io) from GitHub repositories. Users log in with their GitHub account to access documentation for projects they have access to. A repository only needs an OpenAPI spec to be recognized by Shape Docs, but customization is possible with a .shape-docs.yml file. Here's an example: -``` -npm run dev -``` + -Finally, open the application on https://dev.local:3000. +Shape Docs supports spec-driven development by requiring OpenAPI specs in GitHub repos, ensuring version control and peer review. When a pull request is opened, Shape Docs comments with links to preview the documentation: -## Database Schemas + -See `create-tables.sql` +Learn more from the [Adding Documentation](https://github.com/shapehq/shape-docs/wiki/Adding-Documentation-to-Shape-Docs), [Browsing Documentation](https://github.com/shapehq/shape-docs/wiki/Browsing-Documentation), and [Updating Documentation](https://github.com/shapehq/shape-docs/wiki/Updating-Documentation) articles in the wiki. -## 🚀 Deploying the App +## 👩‍💻 How can I contribute? -The app is hosted on Heroku in two different environments. +Pull requests with bugfixes and new features are much appreciated. We are happy to review PRs and merge them once they are ready, as long as they contain changes that fit within the vision of Shape Docs. -|Environment|URL|Branch| -|-|-|-| -|Staging|[staging.docs.shapetools.io](https://staging.docs.shapetools.io)|develop| -|Production|[docs.shapetools.io](https://docs.shapetools.io)|main| +Clone the repository and consult [the article on contributing](https://github.com/shapehq/shape-docs/wiki/Contributing) to get started working on the project. -Each environment is deployed by merging changes into their respective branch. Heroku is responsible for observing changes to the repository and schedule a deployment when changes are observed. +```bash +git clone git@github.com:shapehq/shape-docs.git +``` -## 📖 Getting Started with Shape Docs +--- -Details on getting started showing documentation on Shape Docs can be [found on our Confluence](https://shapedk.atlassian.net/wiki/spaces/DEVELOPERS/pages/3795615745/Shape+Docs). +Tartelet is built with ❤️ by [Shape](https://shape.dk) in Denmark. Oh, and [we are hiring](https://careers.shape.dk) 🤗 From 96c80082ee30ce6345992f152158c62a6b33a77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=2E=20St=C3=B8vring?= Date: Tue, 16 Jul 2024 17:42:48 +0200 Subject: [PATCH 185/191] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index eaeefee6..2a6e621c 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ Clone the repository and consult [the article on contributing](https://github.co git clone git@github.com:shapehq/shape-docs.git ``` +## ❤️ The Product of a Shape Weekend + +Every year we go on Shape Weekend, three days where all employees in Shape get together for a hackathon to build amazing products. In 2023, a team of Shape developers with a passion for documentation and spec-driven development built Shape Docs and we've used it daily ever since! + --- Tartelet is built with ❤️ by [Shape](https://shape.dk) in Denmark. Oh, and [we are hiring](https://careers.shape.dk) 🤗 From 87049ca0d7bd24e8d8e561108f8ad9bdc70df0cb Mon Sep 17 00:00:00 2001 From: Ulrik Andersen Date: Tue, 16 Jul 2024 18:28:30 +0200 Subject: [PATCH 186/191] Minor fixes to README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2a6e621c..694b6dc8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Shape Docs makes managing and previewing OpenAPI documentation a breeze, streaml ## 🚀 Getting Started -Please refer to the following articles in [the wiki](https://github.com/shapehq/tartelet/wiki) to get started with Tartelet. +Please refer to the following articles in [the wiki](https://github.com/shapehq/shape-docs/wiki) to get started with Shape Docs. - [Adding Documentation to Shape Docs](https://github.com/shapehq/shape-docs/wiki/Adding-Documentation-to-Shape-Docs) - [Browsing Documentation](https://github.com/shapehq/shape-docs/wiki/Browsing-Documentation) @@ -67,4 +67,4 @@ Every year we go on Shape Weekend, three days where all employees in Shape get t --- -Tartelet is built with ❤️ by [Shape](https://shape.dk) in Denmark. Oh, and [we are hiring](https://careers.shape.dk) 🤗 +Shape Docks is built with ❤️ by [Shape](https://shape.dk) in Denmark. Oh, and [we are hiring](https://careers.shape.dk) 🤗 From c30e6370e645329b18bcc337317665d0d1580c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 17 Jul 2024 09:45:08 +0200 Subject: [PATCH 187/191] Configures webhooks in composition --- src/app/api/hooks/github/route.ts | 55 +------------------ src/common/utils/index.ts | 1 + .../utils/listFromCommaSeparatedString.ts | 8 +++ src/composition.ts | 44 ++++++++++++++- 4 files changed, 52 insertions(+), 56 deletions(-) create mode 100644 src/common/utils/listFromCommaSeparatedString.ts diff --git a/src/app/api/hooks/github/route.ts b/src/app/api/hooks/github/route.ts index bad9989d..6f359950 100644 --- a/src/app/api/hooks/github/route.ts +++ b/src/app/api/hooks/github/route.ts @@ -1,58 +1,7 @@ import { NextRequest, NextResponse } from "next/server" -import { - GitHubHookHandler -} from "@/features/hooks/data" -import { - PostCommentPullRequestEventHandler, - FilteringPullRequestEventHandler, - RepositoryNameEventFilter, - PullRequestCommenter -} from "@/features/hooks/domain" -import { gitHubClient } from "@/composition" - -const { - NEXT_PUBLIC_SHAPE_DOCS_TITLE, - SHAPE_DOCS_BASE_URL, - SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, - REPOSITORY_NAME_SUFFIX, - GITHUB_APP_ID, - GITHUB_WEBHOOK_SECRET, - GITHUB_WEBHOK_REPOSITORY_ALLOWLIST, - GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST -} = process.env - -const listFromCommaSeparatedString = (str?: string) => { - if (!str) { - return [] - } - return str.split(",").map(e => e.trim()) -} - -const allowlist = listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_ALLOWLIST) -const disallowlist = listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST) - -const hookHandler = new GitHubHookHandler({ - secret: GITHUB_WEBHOOK_SECRET, - pullRequestEventHandler: new FilteringPullRequestEventHandler({ - filter: new RepositoryNameEventFilter({ - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - allowlist, - disallowlist - }), - eventHandler: new PostCommentPullRequestEventHandler({ - pullRequestCommenter: new PullRequestCommenter({ - siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, - domain: SHAPE_DOCS_BASE_URL, - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, - gitHubAppId: GITHUB_APP_ID, - gitHubClient: gitHubClient - }) - }) - }) -}) +import { gitHubHookHandler } from "@/composition" export const POST = async (req: NextRequest): Promise => { - await hookHandler.handle(req) + await gitHubHookHandler.handle(req) return NextResponse.json({ status: "OK" }) } diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 0b808be9..cd7547e6 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -1,3 +1,4 @@ export * from "./fetcher" export { default as fetcher } from "./fetcher" export { default as ZodJSONCoder } from "./ZodJSONCoder" +export { default as listFromCommaSeparatedString } from "./listFromCommaSeparatedString" diff --git a/src/common/utils/listFromCommaSeparatedString.ts b/src/common/utils/listFromCommaSeparatedString.ts new file mode 100644 index 00000000..18e692a0 --- /dev/null +++ b/src/common/utils/listFromCommaSeparatedString.ts @@ -0,0 +1,8 @@ +const listFromCommaSeparatedString = (str?: string) => { + if (!str) { + return [] + } + return str.split(",").map(e => e.trim()) +} + +export default listFromCommaSeparatedString diff --git a/src/composition.ts b/src/composition.ts index f4f2c407..da0a49aa 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -11,7 +11,8 @@ import { KeyValueUserDataRepository, OAuthTokenRefreshingGitHubClient, PostgreSQLDB, - SessionMutexFactory + SessionMutexFactory, + listFromCommaSeparatedString } from "@/common" import { GitHubLoginDataSource, @@ -37,9 +38,20 @@ import { PersistingOAuthTokenRefresher, UserDataCleanUpLogOutHandler } from "@/features/auth/domain" +import { + GitHubHookHandler +} from "@/features/hooks/data" +import { + PostCommentPullRequestEventHandler, + FilteringPullRequestEventHandler, + RepositoryNameEventFilter, + PullRequestCommenter +} from "@/features/hooks/domain" const { + SHAPE_DOCS_BASE_URL, SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, + NEXT_PUBLIC_SHAPE_DOCS_TITLE, REPOSITORY_NAME_SUFFIX, GITHUB_APP_ID, GITHUB_CLIENT_ID, @@ -49,9 +61,14 @@ const { POSTGRESQL_HOST, POSTGRESQL_USER, POSTGRESQL_PASSWORD, - POSTGRESQL_DB + POSTGRESQL_DB, + GITHUB_WEBHOOK_SECRET, + GITHUB_WEBHOK_REPOSITORY_ALLOWLIST, + GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST } = process.env +const projectConfigurationFilename = SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME || ".shape-docs.yml" + const gitHubAppCredentials = { appId: GITHUB_APP_ID, clientId: GITHUB_CLIENT_ID, @@ -165,7 +182,7 @@ export const projectDataSource = new CachingProjectDataSource({ }), graphQlClient: userGitHubClient, repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - projectConfigurationFilename: SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME + projectConfigurationFilename }), repository: projectRepository }) @@ -175,3 +192,24 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( new UserDataCleanUpLogOutHandler(session, projectUserDataRepository) ]) ) + +export const gitHubHookHandler = new GitHubHookHandler({ + secret: GITHUB_WEBHOOK_SECRET, + pullRequestEventHandler: new FilteringPullRequestEventHandler({ + filter: new RepositoryNameEventFilter({ + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, + allowlist: listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_ALLOWLIST), + disallowlist: listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST) + }), + eventHandler: new PostCommentPullRequestEventHandler({ + pullRequestCommenter: new PullRequestCommenter({ + siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, + domain: SHAPE_DOCS_BASE_URL, + repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, + projectConfigurationFilename, + gitHubAppId: GITHUB_APP_ID, + gitHubClient: gitHubClient + }) + }) + }) +}) \ No newline at end of file From 3937634c5ca8808e5a5644f2f6c3c88929490555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 17 Jul 2024 10:13:36 +0200 Subject: [PATCH 188/191] Removes separate export --- src/features/hooks/data/GitHubHookHandler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/features/hooks/data/GitHubHookHandler.ts b/src/features/hooks/data/GitHubHookHandler.ts index f548f330..22ee0005 100644 --- a/src/features/hooks/data/GitHubHookHandler.ts +++ b/src/features/hooks/data/GitHubHookHandler.ts @@ -8,7 +8,7 @@ interface GitHubHookHandlerConfig { readonly pullRequestEventHandler: IPullRequestEventHandler } -class GitHubHookHandler { +export default class GitHubHookHandler { private readonly webhooks: Webhooks private readonly pullRequestEventHandler: IPullRequestEventHandler @@ -69,5 +69,3 @@ class GitHubHookHandler { }) } } - -export default GitHubHookHandler From 13c7ba7a0b42953754c1d0e690166fd07384afe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 17 Jul 2024 12:45:28 +0200 Subject: [PATCH 189/191] Adds POSTGRESQL_PASSWORD --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 26fdbfbb..93fe7359 100644 --- a/.env.example +++ b/.env.example @@ -7,6 +7,7 @@ NEXTAUTH_SECRET=use [openssl rand -base64 32] to generate a 32 bytes value REDIS_URL=localhost POSTGRESQL_HOST=localhost POSTGRESQL_USER=dbuser +POSTGRESQL_PASSWORD= POSTGRESQL_DB=db REPOSITORY_NAME_SUFFIX=-openapi GITHUB_WEBHOOK_SECRET=preshared secret also put in app configuration in GitHub From 7f954b2b574d0515875932e8754613608102e24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 17 Jul 2024 12:54:30 +0200 Subject: [PATCH 190/191] Ensures environment variables are set --- src/app/layout.tsx | 5 +- src/common/utils/env.ts | 33 +++++++++ src/common/utils/index.ts | 1 + src/composition.ts | 70 +++++++------------ .../projects/view/client/ProjectsPage.tsx | 3 +- src/features/sidebar/view/SidebarHeader.tsx | 5 +- types/env.d.ts | 22 ------ 7 files changed, 67 insertions(+), 72 deletions(-) create mode 100644 src/common/utils/env.ts delete mode 100644 types/env.d.ts diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f221724d..a22f3c64 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,12 +4,13 @@ import { config as fontAwesomeConfig } from "@fortawesome/fontawesome-svg-core" import { CssBaseline } from "@mui/material" import ThemeRegistry from "@/common/theme/ThemeRegistry" import "@fortawesome/fontawesome-svg-core/styles.css" +import { env } from "@/common" fontAwesomeConfig.autoAddCss = false export const metadata: Metadata = { - title: process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE, - description: process.env.NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION + title: env.getOrThrow("NEXT_PUBLIC_SHAPE_DOCS_TITLE"), + description: env.getOrThrow("NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION") } export default function RootLayout({ children }: { children: React.ReactNode }) { diff --git a/src/common/utils/env.ts b/src/common/utils/env.ts new file mode 100644 index 00000000..da89b3f9 --- /dev/null +++ b/src/common/utils/env.ts @@ -0,0 +1,33 @@ +type EnvMethods = { + get(key: string): string | undefined, + getOrThrow(key: string): string +} + +type EnvObject = EnvMethods & { + [key: string]: string | undefined +} + +const base: EnvMethods = { + get(key: string): string | undefined { + return process.env[key] + }, + getOrThrow(key: string): string { + const value = process.env[key] + if (!value || value.length === 0) { + throw new Error(`Environment variable "${key}" is not set`) + } + console.log(`${key}=${value}`) + return value + } +} + +const env = new Proxy(base, { + get(target, prop: string) { + if (prop in target) { + return (target as any)[prop] + } + return target.get(prop) + } +}) as EnvObject + +export default env diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index cd7547e6..4b6ec76d 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -2,3 +2,4 @@ export * from "./fetcher" export { default as fetcher } from "./fetcher" export { default as ZodJSONCoder } from "./ZodJSONCoder" export { default as listFromCommaSeparatedString } from "./listFromCommaSeparatedString" +export { default as env } from "./env" diff --git a/src/composition.ts b/src/composition.ts index da0a49aa..0fdeb7e0 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -6,6 +6,7 @@ import RedisKeyedMutexFactory from "@/common/mutex/RedisKeyedMutexFactory" import RedisKeyValueStore from "@/common/key-value-store/RedisKeyValueStore" import { AuthjsSession, + env, GitHubClient, ISession, KeyValueUserDataRepository, @@ -48,41 +49,20 @@ import { PullRequestCommenter } from "@/features/hooks/domain" -const { - SHAPE_DOCS_BASE_URL, - SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME, - NEXT_PUBLIC_SHAPE_DOCS_TITLE, - REPOSITORY_NAME_SUFFIX, - GITHUB_APP_ID, - GITHUB_CLIENT_ID, - GITHUB_CLIENT_SECRET, - GITHUB_PRIVATE_KEY_BASE_64, - REDIS_URL, - POSTGRESQL_HOST, - POSTGRESQL_USER, - POSTGRESQL_PASSWORD, - POSTGRESQL_DB, - GITHUB_WEBHOOK_SECRET, - GITHUB_WEBHOK_REPOSITORY_ALLOWLIST, - GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST -} = process.env - -const projectConfigurationFilename = SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME || ".shape-docs.yml" - const gitHubAppCredentials = { - appId: GITHUB_APP_ID, - clientId: GITHUB_CLIENT_ID, - clientSecret: GITHUB_CLIENT_SECRET, + appId: env.getOrThrow("GITHUB_APP_ID"), + clientId: env.getOrThrow("GITHUB_CLIENT_ID"), + clientSecret: env.getOrThrow("GITHUB_CLIENT_SECRET"), privateKey: Buffer - .from(GITHUB_PRIVATE_KEY_BASE_64, "base64") + .from(env.getOrThrow("GITHUB_PRIVATE_KEY_BASE_64"), "base64") .toString("utf-8") } const pool = new Pool({ - host: POSTGRESQL_HOST, - user: POSTGRESQL_USER, - password: POSTGRESQL_PASSWORD, - database: POSTGRESQL_DB, + host: env.getOrThrow("POSTGRESQL_HOST"), + user: env.getOrThrow("POSTGRESQL_USER"), + password: env.get("POSTGRESQL_PASSWORD"), + database: env.getOrThrow("POSTGRESQL_DB"), max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000 @@ -99,7 +79,7 @@ const logInHandler = new LogInHandler({ oauthTokenRepository }) export const auth = NextAuth({ adapter: PostgresAdapter(pool), - secret: process.env.NEXTAUTH_SECRET, + secret: env.getOrThrow("NEXTAUTH_SECRET"), theme: { logo: "/images/logo.png", colorScheme: "light", @@ -107,8 +87,8 @@ export const auth = NextAuth({ }, providers: [ GithubProvider({ - clientId: process.env.GITHUB_CLIENT_ID, - clientSecret: process.env.GITHUB_CLIENT_SECRET, + clientId: env.getOrThrow("GITHUB_CLIENT_ID"), + clientSecret: env.getOrThrow("GITHUB_CLIENT_SECRET"), authorization: { params: { scope: "repo" @@ -140,7 +120,7 @@ const oauthTokenDataSource = new OAuthTokenDataSource({ const oauthTokenRefresher = new LockingOAuthTokenRefresher({ mutexFactory: new SessionMutexFactory({ baseKey: "mutexLockingOAuthTokenRefresher", - mutexFactory: new RedisKeyedMutexFactory(REDIS_URL), + mutexFactory: new RedisKeyedMutexFactory(env.getOrThrow("REDIS_URL")), userIdReader: session }), oauthTokenRefresher: new PersistingOAuthTokenRefresher({ @@ -166,7 +146,7 @@ export const blockingSessionValidator = new OAuthTokenSessionValidator({ }) const projectUserDataRepository = new KeyValueUserDataRepository({ - store: new RedisKeyValueStore(REDIS_URL), + store: new RedisKeyValueStore(env.getOrThrow("REDIS_URL")), baseKey: "projects" }) @@ -181,8 +161,8 @@ export const projectDataSource = new CachingProjectDataSource({ graphQlClient: userGitHubClient }), graphQlClient: userGitHubClient, - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - projectConfigurationFilename + repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"), + projectConfigurationFilename: env.getOrThrow("SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME") }), repository: projectRepository }) @@ -194,20 +174,20 @@ export const logOutHandler = new ErrorIgnoringLogOutHandler( ) export const gitHubHookHandler = new GitHubHookHandler({ - secret: GITHUB_WEBHOOK_SECRET, + secret: env.getOrThrow("GITHUB_WEBHOOK_SECRET"), pullRequestEventHandler: new FilteringPullRequestEventHandler({ filter: new RepositoryNameEventFilter({ - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - allowlist: listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_ALLOWLIST), - disallowlist: listFromCommaSeparatedString(GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST) + repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"), + allowlist: listFromCommaSeparatedString(env.get("GITHUB_WEBHOK_REPOSITORY_ALLOWLIST")), + disallowlist: listFromCommaSeparatedString(env.get("GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST")) }), eventHandler: new PostCommentPullRequestEventHandler({ pullRequestCommenter: new PullRequestCommenter({ - siteName: NEXT_PUBLIC_SHAPE_DOCS_TITLE, - domain: SHAPE_DOCS_BASE_URL, - repositoryNameSuffix: REPOSITORY_NAME_SUFFIX, - projectConfigurationFilename, - gitHubAppId: GITHUB_APP_ID, + siteName: env.getOrThrow("NEXT_PUBLIC_SHAPE_DOCS_TITLE"), + domain: env.getOrThrow("SHAPE_DOCS_BASE_URL"), + repositoryNameSuffix: env.getOrThrow("REPOSITORY_NAME_SUFFIX"), + projectConfigurationFilename: env.getOrThrow("SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME"), + gitHubAppId: env.getOrThrow("GITHUB_APP_ID"), gitHubClient: gitHubClient }) }) diff --git a/src/features/projects/view/client/ProjectsPage.tsx b/src/features/projects/view/client/ProjectsPage.tsx index 951d4df6..3546df7e 100644 --- a/src/features/projects/view/client/ProjectsPage.tsx +++ b/src/features/projects/view/client/ProjectsPage.tsx @@ -31,10 +31,11 @@ export default function ProjectsPage({ const { projects: clientProjects, error, isLoading: isClientLoading } = useProjects() const projects = isClientLoading ? (serverProjects || []) : clientProjects const { project, version, specification } = getSelection({ projects, path }) + const siteName = process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE || "" useEffect(() => { updateWindowTitle({ storage: document, - defaultTitle: process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE, + defaultTitle: siteName, project, version, specification diff --git a/src/features/sidebar/view/SidebarHeader.tsx b/src/features/sidebar/view/SidebarHeader.tsx index 1703a18e..5bc88a4d 100644 --- a/src/features/sidebar/view/SidebarHeader.tsx +++ b/src/features/sidebar/view/SidebarHeader.tsx @@ -3,6 +3,7 @@ import { Box, Typography } from "@mui/material" import Link from "next/link" export default function SidebarHeader() { + const siteName = process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE return ( {`${process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE} - {process.env.NEXT_PUBLIC_SHAPE_DOCS_TITLE} + {siteName} diff --git a/types/env.d.ts b/types/env.d.ts deleted file mode 100644 index c21d62ae..00000000 --- a/types/env.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -namespace NodeJS { - interface ProcessEnv { - readonly SHAPE_DOCS_BASE_URL: string - readonly SHAPE_DOCS_PROJECT_CONFIGURATION_FILENAME: string - readonly NEXT_PUBLIC_SHAPE_DOCS_TITLE: string - readonly NEXT_PUBLIC_SHAPE_DOCS_DESCRIPTION: string - readonly NEXTAUTH_URL_INTERNAL: string - readonly NEXTAUTH_SECRET: string - readonly REDIS_URL: string - readonly POSTGRESQL_HOST: string - readonly POSTGRESQL_USER: string - readonly POSTGRESQL_DB: string - readonly REPOSITORY_NAME_SUFFIX: string - readonly GITHUB_WEBHOOK_SECRET: string - readonly GITHUB_WEBHOK_REPOSITORY_ALLOWLIST: string - readonly GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST: string - readonly GITHUB_CLIENT_ID: string - readonly GITHUB_CLIENT_SECRET: string - readonly GITHUB_APP_ID: string - readonly GITHUB_PRIVATE_KEY_BASE_64: string - } -} From 070273dc5f5321deaa876993222452fd9953be8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 17 Jul 2024 12:58:34 +0200 Subject: [PATCH 191/191] Fixes linting warnings --- src/common/utils/env.ts | 1 + src/features/projects/view/client/ProjectsPage.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/utils/env.ts b/src/common/utils/env.ts index da89b3f9..f864ebaf 100644 --- a/src/common/utils/env.ts +++ b/src/common/utils/env.ts @@ -24,6 +24,7 @@ const base: EnvMethods = { const env = new Proxy(base, { get(target, prop: string) { if (prop in target) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ return (target as any)[prop] } return target.get(prop) diff --git a/src/features/projects/view/client/ProjectsPage.tsx b/src/features/projects/view/client/ProjectsPage.tsx index 3546df7e..efd27800 100644 --- a/src/features/projects/view/client/ProjectsPage.tsx +++ b/src/features/projects/view/client/ProjectsPage.tsx @@ -40,7 +40,7 @@ export default function ProjectsPage({ version, specification }) - }, [project, version, specification]) + }, [project, version, specification, siteName]) useEffect(() => { // Ensure the URL reflects the current selection of project, version, and specification. projectNavigator.navigateIfNeeded({