diff --git a/.github/actions/pr-stack-ci-gate/.gitignore b/.github/actions/pr-stack-ci-gate/.gitignore new file mode 100644 index 000000000000..c2658d7d1b31 --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/.github/actions/pr-stack-ci-gate/README.md b/.github/actions/pr-stack-ci-gate/README.md new file mode 100644 index 000000000000..63278025802b --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/README.md @@ -0,0 +1,52 @@ +# PR Stack CI Gate + +The `build_and_test` workflow calls this read-only action for same-repository PRs. +It infers branch-based stacks from open PR head/base branch names; no GitHub +stack API or Graphite integration is required. Fork PRs bypass the checkout and +this action entirely and run full CI immediately. + +The first three PRs and every leaf run immediately. An internal PR polls the +closest three predecessors every five minutes: **any** current-head/current-base +`thank you, next` success releases full CI, while three terminal failures fail +the required gate. Unresolved waits open after five hours (six-hour job timeout). +Transient GitHub 5xx/429 errors retry every five minutes; other errors start +full CI so they cannot accidentally mark the aggregate green. + +## Development + +```bash +pnpm --dir .github/actions/pr-stack-ci-gate install --frozen-lockfile --ignore-scripts +pnpm --dir .github/actions/pr-stack-ci-gate types +pnpm --dir .github/actions/pr-stack-ci-gate build +pnpm --dir .github/actions/pr-stack-ci-gate test +``` + +Commit changes to `src/` **and** the generated `dist/index.js` and +`dist/licenses.txt`. Tests use an isolated Jest config because the repository's +main Jest config searches only the `test/` and package trees. A captured shape +from #99095's actual required-check response lives in `fixtures/`. The workflow +sparse-checks out only `action.yml` and `dist/index.js` from `${{ github.sha }}`; +that is the test-merge commit on PR runs. The caller and action job grant only +`checks: read`, `contents: read` and `pull-requests: read`. No secrets are +inherited, and `persist-credentials` is false. + +## API budget and correctness + +A stable middle-PR poll rechecks its own PR, finds successors, walks three +predecessor links and checks all three required results: **8 REST calls** versus +11 before this action. The `pulls.list` response already contains head/base +SHAs, so the three extra predecessor `pulls.get` calls are unnecessary while +waiting. Before opening or failing, the action re-reads current PR metadata and +the decisive predecessor(s) and check(s). We cannot stop at the first pending +predecessor: an older PR can already have passed; nor can we permanently cache +a terminal failure because that PR can succeed on rerun. Rechecking live branch +topology on *every* five-minute poll catches retargeted, closed, or leaf PRs at +the original cadence. + +GitHub's `GITHUB_TOKEN` primary budget is typically **1,000/hour/repository** +for both REST requests and GraphQL points. Most REST GETs cost one point. +GraphQL may batch data into a one-point query, but the check-run-to-PR base-SHA +association and nested pagination would need independent correctness testing; +this implementation instead takes a measured REST reduction without weakening +the already verified current-base guard. An `ubuntu-slim` runner is unsuitable: +its 15-minute job limit is shorter than the gate's multi-hour wait. diff --git a/.github/actions/pr-stack-ci-gate/action.yml b/.github/actions/pr-stack-ci-gate/action.yml new file mode 100644 index 000000000000..e5ba1d6ebafd --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/action.yml @@ -0,0 +1,5 @@ +name: 'PR Stack CI Gate' +description: 'Delay expensive CI for branch-based stacked pull requests until prior CI succeeds.' +runs: + using: 'node24' + main: 'dist/index.js' diff --git a/.github/actions/pr-stack-ci-gate/dist/index.js b/.github/actions/pr-stack-ci-gate/dist/index.js new file mode 100644 index 000000000000..8fffb172ecdc --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/dist/index.js @@ -0,0 +1,3 @@ +(()=>{var e={4156:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};Object.defineProperty(A,"__esModule",{value:true});A.issue=A.issueCommand=void 0;const n=o(t(857));const i=t(6816);function issueCommand(e,A,t){const r=new Command(e,A,t);process.stdout.write(r.toString()+n.EOL)}A.issueCommand=issueCommand;function issue(e,A=""){issueCommand(e,{},A)}A.issue=issue;const a="::";class Command{constructor(e,A,t){if(!e){e="missing.command"}this.command=e;this.properties=A;this.message=t}toString(){let e=a+this.command;if(this.properties&&Object.keys(this.properties).length>0){e+=" ";let A=true;for(const t in this.properties){if(this.properties.hasOwnProperty(t)){const r=this.properties[t];if(r){if(A){A=false}else{e+=","}e+=`${t}=${escapeProperty(r)}`}}}}e+=`${a}${escapeData(this.message)}`;return e}}function escapeData(e){return(0,i.toCommandValue)(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A")}function escapeProperty(e){return(0,i.toCommandValue)(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A").replace(/:/g,"%3A").replace(/,/g,"%2C")}},4442:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.platform=A.toPlatformPath=A.toWin32Path=A.toPosixPath=A.markdownSummary=A.summary=A.getIDToken=A.getState=A.saveState=A.group=A.endGroup=A.startGroup=A.info=A.notice=A.warning=A.error=A.debug=A.isDebug=A.setFailed=A.setCommandEcho=A.setOutput=A.getBooleanInput=A.getMultilineInput=A.getInput=A.addPath=A.setSecret=A.exportVariable=A.ExitCode=void 0;const i=t(4156);const a=t(9883);const c=t(6816);const g=o(t(857));const E=o(t(6928));const l=t(8224);var u;(function(e){e[e["Success"]=0]="Success";e[e["Failure"]=1]="Failure"})(u||(A.ExitCode=u={}));function exportVariable(e,A){const t=(0,c.toCommandValue)(A);process.env[e]=t;const r=process.env["GITHUB_ENV"]||"";if(r){return(0,a.issueFileCommand)("ENV",(0,a.prepareKeyValueMessage)(e,A))}(0,i.issueCommand)("set-env",{name:e},t)}A.exportVariable=exportVariable;function setSecret(e){(0,i.issueCommand)("add-mask",{},e)}A.setSecret=setSecret;function addPath(e){const A=process.env["GITHUB_PATH"]||"";if(A){(0,a.issueFileCommand)("PATH",e)}else{(0,i.issueCommand)("add-path",{},e)}process.env["PATH"]=`${e}${E.delimiter}${process.env["PATH"]}`}A.addPath=addPath;function getInput(e,A){const t=process.env[`INPUT_${e.replace(/ /g,"_").toUpperCase()}`]||"";if(A&&A.required&&!t){throw new Error(`Input required and not supplied: ${e}`)}if(A&&A.trimWhitespace===false){return t}return t.trim()}A.getInput=getInput;function getMultilineInput(e,A){const t=getInput(e,A).split("\n").filter((e=>e!==""));if(A&&A.trimWhitespace===false){return t}return t.map((e=>e.trim()))}A.getMultilineInput=getMultilineInput;function getBooleanInput(e,A){const t=["true","True","TRUE"];const r=["false","False","FALSE"];const s=getInput(e,A);if(t.includes(s))return true;if(r.includes(s))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\``)}A.getBooleanInput=getBooleanInput;function setOutput(e,A){const t=process.env["GITHUB_OUTPUT"]||"";if(t){return(0,a.issueFileCommand)("OUTPUT",(0,a.prepareKeyValueMessage)(e,A))}process.stdout.write(g.EOL);(0,i.issueCommand)("set-output",{name:e},(0,c.toCommandValue)(A))}A.setOutput=setOutput;function setCommandEcho(e){(0,i.issue)("echo",e?"on":"off")}A.setCommandEcho=setCommandEcho;function setFailed(e){process.exitCode=u.Failure;error(e)}A.setFailed=setFailed;function isDebug(){return process.env["RUNNER_DEBUG"]==="1"}A.isDebug=isDebug;function debug(e){(0,i.issueCommand)("debug",{},e)}A.debug=debug;function error(e,A={}){(0,i.issueCommand)("error",(0,c.toCommandProperties)(A),e instanceof Error?e.toString():e)}A.error=error;function warning(e,A={}){(0,i.issueCommand)("warning",(0,c.toCommandProperties)(A),e instanceof Error?e.toString():e)}A.warning=warning;function notice(e,A={}){(0,i.issueCommand)("notice",(0,c.toCommandProperties)(A),e instanceof Error?e.toString():e)}A.notice=notice;function info(e){process.stdout.write(e+g.EOL)}A.info=info;function startGroup(e){(0,i.issue)("group",e)}A.startGroup=startGroup;function endGroup(){(0,i.issue)("endgroup")}A.endGroup=endGroup;function group(e,A){return n(this,void 0,void 0,(function*(){startGroup(e);let t;try{t=yield A()}finally{endGroup()}return t}))}A.group=group;function saveState(e,A){const t=process.env["GITHUB_STATE"]||"";if(t){return(0,a.issueFileCommand)("STATE",(0,a.prepareKeyValueMessage)(e,A))}(0,i.issueCommand)("save-state",{name:e},(0,c.toCommandValue)(A))}A.saveState=saveState;function getState(e){return process.env[`STATE_${e}`]||""}A.getState=getState;function getIDToken(e){return n(this,void 0,void 0,(function*(){return yield l.OidcClient.getIDToken(e)}))}A.getIDToken=getIDToken;var Q=t(669);Object.defineProperty(A,"summary",{enumerable:true,get:function(){return Q.summary}});var C=t(669);Object.defineProperty(A,"markdownSummary",{enumerable:true,get:function(){return C.markdownSummary}});var h=t(2078);Object.defineProperty(A,"toPosixPath",{enumerable:true,get:function(){return h.toPosixPath}});Object.defineProperty(A,"toWin32Path",{enumerable:true,get:function(){return h.toWin32Path}});Object.defineProperty(A,"toPlatformPath",{enumerable:true,get:function(){return h.toPlatformPath}});A.platform=o(t(4182))},9883:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};Object.defineProperty(A,"__esModule",{value:true});A.prepareKeyValueMessage=A.issueFileCommand=void 0;const n=o(t(6982));const i=o(t(9896));const a=o(t(857));const c=t(6816);function issueFileCommand(e,A){const t=process.env[`GITHUB_${e}`];if(!t){throw new Error(`Unable to find environment variable for file command ${e}`)}if(!i.existsSync(t)){throw new Error(`Missing file at path: ${t}`)}i.appendFileSync(t,`${(0,c.toCommandValue)(A)}${a.EOL}`,{encoding:"utf8"})}A.issueFileCommand=issueFileCommand;function prepareKeyValueMessage(e,A){const t=`ghadelimiter_${n.randomUUID()}`;const r=(0,c.toCommandValue)(A);if(e.includes(t)){throw new Error(`Unexpected input: name should not contain the delimiter "${t}"`)}if(r.includes(t)){throw new Error(`Unexpected input: value should not contain the delimiter "${t}"`)}return`${e}<<${t}${a.EOL}${r}${a.EOL}${t}`}A.prepareKeyValueMessage=prepareKeyValueMessage},8224:function(e,A,t){"use strict";var r=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.OidcClient=void 0;const s=t(5290);const o=t(8358);const n=t(4442);class OidcClient{static createHttpClient(e=true,A=10){const t={allowRetries:e,maxRetries:A};return new s.HttpClient("actions/oidc-client",[new o.BearerCredentialHandler(OidcClient.getRequestToken())],t)}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 A;return r(this,void 0,void 0,(function*(){const t=OidcClient.createHttpClient();const r=yield t.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 s=(A=r.result)===null||A===void 0?void 0:A.value;if(!s){throw new Error("Response json body do not have ID Token field")}return s}))}static getIDToken(e){return r(this,void 0,void 0,(function*(){try{let A=OidcClient.getIDTokenUrl();if(e){const t=encodeURIComponent(e);A=`${A}&audience=${t}`}(0,n.debug)(`ID token url is ${A}`);const t=yield OidcClient.getCall(A);(0,n.setSecret)(t);return t}catch(e){throw new Error(`Error message: ${e.message}`)}}))}}A.OidcClient=OidcClient},2078:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};Object.defineProperty(A,"__esModule",{value:true});A.toPlatformPath=A.toWin32Path=A.toPosixPath=void 0;const n=o(t(6928));function toPosixPath(e){return e.replace(/[\\]/g,"/")}A.toPosixPath=toPosixPath;function toWin32Path(e){return e.replace(/[/]/g,"\\")}A.toWin32Path=toWin32Path;function toPlatformPath(e){return e.replace(/[/\\]/g,n.sep)}A.toPlatformPath=toPlatformPath},4182:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};var i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(A,"__esModule",{value:true});A.getDetails=A.isLinux=A.isMacOS=A.isWindows=A.arch=A.platform=void 0;const a=i(t(857));const c=o(t(7167));const getWindowsInfo=()=>n(void 0,void 0,void 0,(function*(){const{stdout:e}=yield c.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"',undefined,{silent:true});const{stdout:A}=yield c.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"',undefined,{silent:true});return{name:A.trim(),version:e.trim()}}));const getMacOsInfo=()=>n(void 0,void 0,void 0,(function*(){var e,A,t,r;const{stdout:s}=yield c.getExecOutput("sw_vers",undefined,{silent:true});const o=(A=(e=s.match(/ProductVersion:\s*(.+)/))===null||e===void 0?void 0:e[1])!==null&&A!==void 0?A:"";const n=(r=(t=s.match(/ProductName:\s*(.+)/))===null||t===void 0?void 0:t[1])!==null&&r!==void 0?r:"";return{name:n,version:o}}));const getLinuxInfo=()=>n(void 0,void 0,void 0,(function*(){const{stdout:e}=yield c.getExecOutput("lsb_release",["-i","-r","-s"],{silent:true});const[A,t]=e.trim().split("\n");return{name:A,version:t}}));A.platform=a.default.platform();A.arch=a.default.arch();A.isWindows=A.platform==="win32";A.isMacOS=A.platform==="darwin";A.isLinux=A.platform==="linux";function getDetails(){return n(this,void 0,void 0,(function*(){return Object.assign(Object.assign({},yield A.isWindows?getWindowsInfo():A.isMacOS?getMacOsInfo():getLinuxInfo()),{platform:A.platform,arch:A.arch,isWindows:A.isWindows,isMacOS:A.isMacOS,isLinux:A.isLinux})}))}A.getDetails=getDetails},669:function(e,A,t){"use strict";var r=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.summary=A.markdownSummary=A.SUMMARY_DOCS_URL=A.SUMMARY_ENV_VAR=void 0;const s=t(857);const o=t(9896);const{access:n,appendFile:i,writeFile:a}=o.promises;A.SUMMARY_ENV_VAR="GITHUB_STEP_SUMMARY";A.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 r(this,void 0,void 0,(function*(){if(this._filePath){return this._filePath}const e=process.env[A.SUMMARY_ENV_VAR];if(!e){throw new Error(`Unable to find environment variable for $${A.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`)}try{yield n(e,o.constants.R_OK|o.constants.W_OK)}catch(A){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,A,t={}){const r=Object.entries(t).map((([e,A])=>` ${e}="${A}"`)).join("");if(!A){return`<${e}${r}>`}return`<${e}${r}>${A}`}write(e){return r(this,void 0,void 0,(function*(){const A=!!(e===null||e===void 0?void 0:e.overwrite);const t=yield this.filePath();const r=A?a:i;yield r(t,this._buffer,{encoding:"utf8"});return this.emptyBuffer()}))}clear(){return r(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,A=false){this._buffer+=e;return A?this.addEOL():this}addEOL(){return this.addRaw(s.EOL)}addCodeBlock(e,A){const t=Object.assign({},A&&{lang:A});const r=this.wrap("pre",this.wrap("code",e),t);return this.addRaw(r).addEOL()}addList(e,A=false){const t=A?"ol":"ul";const r=e.map((e=>this.wrap("li",e))).join("");const s=this.wrap(t,r);return this.addRaw(s).addEOL()}addTable(e){const A=e.map((e=>{const A=e.map((e=>{if(typeof e==="string"){return this.wrap("td",e)}const{header:A,data:t,colspan:r,rowspan:s}=e;const o=A?"th":"td";const n=Object.assign(Object.assign({},r&&{colspan:r}),s&&{rowspan:s});return this.wrap(o,t,n)})).join("");return this.wrap("tr",A)})).join("");const t=this.wrap("table",A);return this.addRaw(t).addEOL()}addDetails(e,A){const t=this.wrap("details",this.wrap("summary",e)+A);return this.addRaw(t).addEOL()}addImage(e,A,t){const{width:r,height:s}=t||{};const o=Object.assign(Object.assign({},r&&{width:r}),s&&{height:s});const n=this.wrap("img",null,Object.assign({src:e,alt:A},o));return this.addRaw(n).addEOL()}addHeading(e,A){const t=`h${A}`;const r=["h1","h2","h3","h4","h5","h6"].includes(t)?t:"h1";const s=this.wrap(r,e);return this.addRaw(s).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,A){const t=Object.assign({},A&&{cite:A});const r=this.wrap("blockquote",e,t);return this.addRaw(r).addEOL()}addLink(e,A){const t=this.wrap("a",e,{href:A});return this.addRaw(t).addEOL()}}const c=new Summary;A.markdownSummary=c;A.summary=c},6816:(e,A)=>{"use strict";Object.defineProperty(A,"__esModule",{value:true});A.toCommandProperties=A.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)}A.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}}A.toCommandProperties=toCommandProperties},7167:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;Object.defineProperty(e,r,{enumerable:true,get:function(){return A[t]}})}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.getExecOutput=A.exec=void 0;const i=t(3193);const a=o(t(7594));function exec(e,A,t){return n(this,void 0,void 0,(function*(){const r=a.argStringToArray(e);if(r.length===0){throw new Error(`Parameter 'commandLine' cannot be null or empty.`)}const s=r[0];A=r.slice(1).concat(A||[]);const o=new a.ToolRunner(s,A,t);return o.exec()}))}A.exec=exec;function getExecOutput(e,A,t){var r,s;return n(this,void 0,void 0,(function*(){let o="";let n="";const a=new i.StringDecoder("utf8");const c=new i.StringDecoder("utf8");const g=(r=t===null||t===void 0?void 0:t.listeners)===null||r===void 0?void 0:r.stdout;const E=(s=t===null||t===void 0?void 0:t.listeners)===null||s===void 0?void 0:s.stderr;const stdErrListener=e=>{n+=c.write(e);if(E){E(e)}};const stdOutListener=e=>{o+=a.write(e);if(g){g(e)}};const l=Object.assign(Object.assign({},t===null||t===void 0?void 0:t.listeners),{stdout:stdOutListener,stderr:stdErrListener});const u=yield exec(e,A,Object.assign(Object.assign({},t),{listeners:l}));o+=a.end();n+=c.end();return{exitCode:u,stdout:o,stderr:n}}))}A.getExecOutput=getExecOutput},7594:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;Object.defineProperty(e,r,{enumerable:true,get:function(){return A[t]}})}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.argStringToArray=A.ToolRunner=void 0;const i=o(t(857));const a=o(t(4434));const c=o(t(5317));const g=o(t(6928));const E=o(t(4398));const l=o(t(2667));const u=t(3557);const Q=process.platform==="win32";class ToolRunner extends a.EventEmitter{constructor(e,A,t){super();if(!e){throw new Error("Parameter 'toolPath' cannot be null or empty.")}this.toolPath=e;this.args=A||[];this.options=t||{}}_debug(e){if(this.options.listeners&&this.options.listeners.debug){this.options.listeners.debug(e)}}_getCommandString(e,A){const t=this._getSpawnFileName();const r=this._getSpawnArgs(e);let s=A?"":"[command]";if(Q){if(this._isCmdFile()){s+=t;for(const e of r){s+=` ${e}`}}else if(e.windowsVerbatimArguments){s+=`"${t}"`;for(const e of r){s+=` ${e}`}}else{s+=this._windowsQuoteCmdArg(t);for(const e of r){s+=` ${this._windowsQuoteCmdArg(e)}`}}}else{s+=t;for(const e of r){s+=` ${e}`}}return s}_processLineBuffer(e,A,t){try{let r=A+e.toString();let s=r.indexOf(i.EOL);while(s>-1){const e=r.substring(0,s);t(e);r=r.substring(s+i.EOL.length);s=r.indexOf(i.EOL)}return r}catch(e){this._debug(`error processing line. Failed with error ${e}`);return""}}_getSpawnFileName(){if(Q){if(this._isCmdFile()){return process.env["COMSPEC"]||"cmd.exe"}}return this.toolPath}_getSpawnArgs(e){if(Q){if(this._isCmdFile()){let A=`/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`;for(const t of this.args){A+=" ";A+=e.windowsVerbatimArguments?t:this._windowsQuoteCmdArg(t)}A+='"';return[A]}}return this.args}_endsWith(e,A){return e.endsWith(A)}_isCmdFile(){const e=this.toolPath.toUpperCase();return this._endsWith(e,".CMD")||this._endsWith(e,".BAT")}_windowsQuoteCmdArg(e){if(!this._isCmdFile()){return this._uvQuoteCmdArg(e)}if(!e){return'""'}const A=[" ","\t","&","(",")","[","]","{","}","^","=",";","!","'","+",",","`","~","|","<",">",'"'];let t=false;for(const r of e){if(A.some((e=>e===r))){t=true;break}}if(!t){return e}let r='"';let s=true;for(let A=e.length;A>0;A--){r+=e[A-1];if(s&&e[A-1]==="\\"){r+="\\"}else if(e[A-1]==='"'){s=true;r+='"'}else{s=false}}r+='"';return r.split("").reverse().join("")}_uvQuoteCmdArg(e){if(!e){return'""'}if(!e.includes(" ")&&!e.includes("\t")&&!e.includes('"')){return e}if(!e.includes('"')&&!e.includes("\\")){return`"${e}"`}let A='"';let t=true;for(let r=e.length;r>0;r--){A+=e[r-1];if(t&&e[r-1]==="\\"){A+="\\"}else if(e[r-1]==='"'){t=true;A+="\\"}else{t=false}}A+='"';return A.split("").reverse().join("")}_cloneExecOptions(e){e=e||{};const A={cwd:e.cwd||process.cwd(),env:e.env||process.env,silent:e.silent||false,windowsVerbatimArguments:e.windowsVerbatimArguments||false,failOnStdErr:e.failOnStdErr||false,ignoreReturnCode:e.ignoreReturnCode||false,delay:e.delay||1e4};A.outStream=e.outStream||process.stdout;A.errStream=e.errStream||process.stderr;return A}_getSpawnOptions(e,A){e=e||{};const t={};t.cwd=e.cwd;t.env=e.env;t["windowsVerbatimArguments"]=e.windowsVerbatimArguments||this._isCmdFile();if(e.windowsVerbatimArguments){t.argv0=`"${A}"`}return t}exec(){return n(this,void 0,void 0,(function*(){if(!l.isRooted(this.toolPath)&&(this.toolPath.includes("/")||Q&&this.toolPath.includes("\\"))){this.toolPath=g.resolve(process.cwd(),this.options.cwd||process.cwd(),this.toolPath)}this.toolPath=yield E.which(this.toolPath,true);return new Promise(((e,A)=>n(this,void 0,void 0,(function*(){this._debug(`exec tool: ${this.toolPath}`);this._debug("arguments:");for(const e of this.args){this._debug(` ${e}`)}const t=this._cloneExecOptions(this.options);if(!t.silent&&t.outStream){t.outStream.write(this._getCommandString(t)+i.EOL)}const r=new ExecState(t,this.toolPath);r.on("debug",(e=>{this._debug(e)}));if(this.options.cwd&&!(yield l.exists(this.options.cwd))){return A(new Error(`The cwd: ${this.options.cwd} does not exist!`))}const s=this._getSpawnFileName();const o=c.spawn(s,this._getSpawnArgs(t),this._getSpawnOptions(this.options,s));let n="";if(o.stdout){o.stdout.on("data",(e=>{if(this.options.listeners&&this.options.listeners.stdout){this.options.listeners.stdout(e)}if(!t.silent&&t.outStream){t.outStream.write(e)}n=this._processLineBuffer(e,n,(e=>{if(this.options.listeners&&this.options.listeners.stdline){this.options.listeners.stdline(e)}}))}))}let a="";if(o.stderr){o.stderr.on("data",(e=>{r.processStderr=true;if(this.options.listeners&&this.options.listeners.stderr){this.options.listeners.stderr(e)}if(!t.silent&&t.errStream&&t.outStream){const A=t.failOnStdErr?t.errStream:t.outStream;A.write(e)}a=this._processLineBuffer(e,a,(e=>{if(this.options.listeners&&this.options.listeners.errline){this.options.listeners.errline(e)}}))}))}o.on("error",(e=>{r.processError=e.message;r.processExited=true;r.processClosed=true;r.CheckComplete()}));o.on("exit",(e=>{r.processExitCode=e;r.processExited=true;this._debug(`Exit code ${e} received from tool '${this.toolPath}'`);r.CheckComplete()}));o.on("close",(e=>{r.processExitCode=e;r.processExited=true;r.processClosed=true;this._debug(`STDIO streams have closed for tool '${this.toolPath}'`);r.CheckComplete()}));r.on("done",((t,r)=>{if(n.length>0){this.emit("stdline",n)}if(a.length>0){this.emit("errline",a)}o.removeAllListeners();if(t){A(t)}else{e(r)}}));if(this.options.input){if(!o.stdin){throw new Error("child process missing stdin")}o.stdin.end(this.options.input)}}))))}))}}A.ToolRunner=ToolRunner;function argStringToArray(e){const A=[];let t=false;let r=false;let s="";function append(e){if(r&&e!=='"'){s+="\\"}s+=e;r=false}for(let o=0;o0){A.push(s);s=""}continue}append(n)}if(s.length>0){A.push(s.trim())}return A}A.argStringToArray=argStringToArray;class ExecState extends a.EventEmitter{constructor(e,A){super();this.processClosed=false;this.processError="";this.processExitCode=0;this.processExited=false;this.processStderr=false;this.delay=1e4;this.done=false;this.timeout=null;if(!A){throw new Error("toolPath must not be empty")}this.options=e;this.toolPath=A;if(e.delay){this.delay=e.delay}}CheckComplete(){if(this.done){return}if(this.processClosed){this._setResult()}else if(this.processExited){this.timeout=u.setTimeout(ExecState.HandleTimeout,this.delay,this)}}_debug(e){this.emit("debug",e)}_setResult(){let e;if(this.processExited){if(this.processError){e=new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`)}else if(this.processExitCode!==0&&!this.options.ignoreReturnCode){e=new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`)}else if(this.processStderr&&this.options.failOnStdErr){e=new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`)}}if(this.timeout){clearTimeout(this.timeout);this.timeout=null}this.done=true;this.emit("done",e,this.processExitCode)}static HandleTimeout(e){if(e.done){return}if(!e.processClosed&&e.processExited){const A=`The STDIO streams did not close within ${e.delay/1e3} seconds of the exit event from process '${e.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`;e._debug(A)}e._setResult()}}},88:(e,A,t)=>{"use strict";Object.defineProperty(A,"__esModule",{value:true});A.Context=void 0;const r=t(9896);const s=t(857);class Context{constructor(){var e,A,t;this.payload={};if(process.env.GITHUB_EVENT_PATH){if((0,r.existsSync)(process.env.GITHUB_EVENT_PATH)){this.payload=JSON.parse((0,r.readFileSync)(process.env.GITHUB_EVENT_PATH,{encoding:"utf8"}))}else{const e=process.env.GITHUB_EVENT_PATH;process.stdout.write(`GITHUB_EVENT_PATH ${e} does not exist${s.EOL}`)}}this.eventName=process.env.GITHUB_EVENT_NAME;this.sha=process.env.GITHUB_SHA;this.ref=process.env.GITHUB_REF;this.workflow=process.env.GITHUB_WORKFLOW;this.action=process.env.GITHUB_ACTION;this.actor=process.env.GITHUB_ACTOR;this.job=process.env.GITHUB_JOB;this.runNumber=parseInt(process.env.GITHUB_RUN_NUMBER,10);this.runId=parseInt(process.env.GITHUB_RUN_ID,10);this.apiUrl=(e=process.env.GITHUB_API_URL)!==null&&e!==void 0?e:`https://api.github.com`;this.serverUrl=(A=process.env.GITHUB_SERVER_URL)!==null&&A!==void 0?A:`https://github.com`;this.graphqlUrl=(t=process.env.GITHUB_GRAPHQL_URL)!==null&&t!==void 0?t:`https://api.github.com/graphql`}get issue(){const e=this.payload;return Object.assign(Object.assign({},this.repo),{number:(e.issue||e.pull_request||e).number})}get repo(){if(process.env.GITHUB_REPOSITORY){const[e,A]=process.env.GITHUB_REPOSITORY.split("/");return{owner:e,repo:A}}if(this.payload.repository){return{owner:this.payload.repository.owner.login,repo:this.payload.repository.name}}throw new Error("context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'")}}A.Context=Context},9156:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};Object.defineProperty(A,"__esModule",{value:true});A.getOctokit=A.context=void 0;const n=o(t(88));const i=t(2942);A.context=new n.Context;function getOctokit(e,A,...t){const r=i.GitHub.plugin(...t);return new r((0,i.getOctokitOptions)(e,A))}A.getOctokit=getOctokit},9644:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.getApiBaseUrl=A.getProxyFetch=A.getProxyAgentDispatcher=A.getProxyAgent=A.getAuthString=void 0;const i=o(t(5290));const a=t(4770);function getAuthString(e,A){if(!e&&!A.auth){throw new Error("Parameter token or opts.auth is required")}else if(e&&A.auth){throw new Error("Parameters token and opts.auth may not both be specified")}return typeof A.auth==="string"?A.auth:`token ${e}`}A.getAuthString=getAuthString;function getProxyAgent(e){const A=new i.HttpClient;return A.getAgent(e)}A.getProxyAgent=getProxyAgent;function getProxyAgentDispatcher(e){const A=new i.HttpClient;return A.getAgentDispatcher(e)}A.getProxyAgentDispatcher=getProxyAgentDispatcher;function getProxyFetch(e){const A=getProxyAgentDispatcher(e);const proxyFetch=(e,t)=>n(this,void 0,void 0,(function*(){return(0,a.fetch)(e,Object.assign(Object.assign({},t),{dispatcher:A}))}));return proxyFetch}A.getProxyFetch=getProxyFetch;function getApiBaseUrl(){return process.env["GITHUB_API_URL"]||"https://api.github.com"}A.getApiBaseUrl=getApiBaseUrl},2942:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};Object.defineProperty(A,"__esModule",{value:true});A.getOctokitOptions=A.GitHub=A.defaults=A.context=void 0;const n=o(t(88));const i=o(t(9644));const a=t(8850);const c=t(9389);const g=t(3895);A.context=new n.Context;const E=i.getApiBaseUrl();A.defaults={baseUrl:E,request:{agent:i.getProxyAgent(E),fetch:i.getProxyFetch(E)}};A.GitHub=a.Octokit.plugin(c.restEndpointMethods,g.paginateRest).defaults(A.defaults);function getOctokitOptions(e,A){const t=Object.assign({},A||{});const r=i.getAuthString(e,t);if(r){t.auth=r}return t}A.getOctokitOptions=getOctokitOptions},8358:function(e,A){"use strict";var t=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.PersonalAccessTokenCredentialHandler=A.BearerCredentialHandler=A.BasicCredentialHandler=void 0;class BasicCredentialHandler{constructor(e,A){this.username=e;this.password=A}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 t(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}A.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 t(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}A.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 t(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}A.PersonalAccessTokenCredentialHandler=PersonalAccessTokenCredentialHandler},5290:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.prototype.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.HttpClient=A.isHttps=A.HttpClientResponse=A.HttpClientError=A.getProxyUrl=A.MediaTypes=A.Headers=A.HttpCodes=void 0;const i=o(t(8611));const a=o(t(5692));const c=o(t(8758));const g=o(t(7285));const E=t(4770);var l;(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"})(l||(A.HttpCodes=l={}));var u;(function(e){e["Accept"]="accept";e["ContentType"]="content-type"})(u||(A.Headers=u={}));var Q;(function(e){e["ApplicationJson"]="application/json"})(Q||(A.MediaTypes=Q={}));function getProxyUrl(e){const A=c.getProxyUrl(new URL(e));return A?A.href:""}A.getProxyUrl=getProxyUrl;const C=[l.MovedPermanently,l.ResourceMoved,l.SeeOther,l.TemporaryRedirect,l.PermanentRedirect];const h=[l.BadGateway,l.ServiceUnavailable,l.GatewayTimeout];const B=["OPTIONS","GET","DELETE","HEAD"];const I=10;const d=5;class HttpClientError extends Error{constructor(e,A){super(e);this.name="HttpClientError";this.statusCode=A;Object.setPrototypeOf(this,HttpClientError.prototype)}}A.HttpClientError=HttpClientError;class HttpClientResponse{constructor(e){this.message=e}readBody(){return n(this,void 0,void 0,(function*(){return new Promise((e=>n(this,void 0,void 0,(function*(){let A=Buffer.alloc(0);this.message.on("data",(e=>{A=Buffer.concat([A,e])}));this.message.on("end",(()=>{e(A.toString())}))}))))}))}readBodyBuffer(){return n(this,void 0,void 0,(function*(){return new Promise((e=>n(this,void 0,void 0,(function*(){const A=[];this.message.on("data",(e=>{A.push(e)}));this.message.on("end",(()=>{e(Buffer.concat(A))}))}))))}))}}A.HttpClientResponse=HttpClientResponse;function isHttps(e){const A=new URL(e);return A.protocol==="https:"}A.isHttps=isHttps;class HttpClient{constructor(e,A,t){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=A||[];this.requestOptions=t;if(t){if(t.ignoreSslError!=null){this._ignoreSslError=t.ignoreSslError}this._socketTimeout=t.socketTimeout;if(t.allowRedirects!=null){this._allowRedirects=t.allowRedirects}if(t.allowRedirectDowngrade!=null){this._allowRedirectDowngrade=t.allowRedirectDowngrade}if(t.maxRedirects!=null){this._maxRedirects=Math.max(t.maxRedirects,0)}if(t.keepAlive!=null){this._keepAlive=t.keepAlive}if(t.allowRetries!=null){this._allowRetries=t.allowRetries}if(t.maxRetries!=null){this._maxRetries=t.maxRetries}}}options(e,A){return n(this,void 0,void 0,(function*(){return this.request("OPTIONS",e,null,A||{})}))}get(e,A){return n(this,void 0,void 0,(function*(){return this.request("GET",e,null,A||{})}))}del(e,A){return n(this,void 0,void 0,(function*(){return this.request("DELETE",e,null,A||{})}))}post(e,A,t){return n(this,void 0,void 0,(function*(){return this.request("POST",e,A,t||{})}))}patch(e,A,t){return n(this,void 0,void 0,(function*(){return this.request("PATCH",e,A,t||{})}))}put(e,A,t){return n(this,void 0,void 0,(function*(){return this.request("PUT",e,A,t||{})}))}head(e,A){return n(this,void 0,void 0,(function*(){return this.request("HEAD",e,null,A||{})}))}sendStream(e,A,t,r){return n(this,void 0,void 0,(function*(){return this.request(e,A,t,r)}))}getJson(e,A={}){return n(this,void 0,void 0,(function*(){A[u.Accept]=this._getExistingOrDefaultHeader(A,u.Accept,Q.ApplicationJson);const t=yield this.get(e,A);return this._processResponse(t,this.requestOptions)}))}postJson(e,A,t={}){return n(this,void 0,void 0,(function*(){const r=JSON.stringify(A,null,2);t[u.Accept]=this._getExistingOrDefaultHeader(t,u.Accept,Q.ApplicationJson);t[u.ContentType]=this._getExistingOrDefaultHeader(t,u.ContentType,Q.ApplicationJson);const s=yield this.post(e,r,t);return this._processResponse(s,this.requestOptions)}))}putJson(e,A,t={}){return n(this,void 0,void 0,(function*(){const r=JSON.stringify(A,null,2);t[u.Accept]=this._getExistingOrDefaultHeader(t,u.Accept,Q.ApplicationJson);t[u.ContentType]=this._getExistingOrDefaultHeader(t,u.ContentType,Q.ApplicationJson);const s=yield this.put(e,r,t);return this._processResponse(s,this.requestOptions)}))}patchJson(e,A,t={}){return n(this,void 0,void 0,(function*(){const r=JSON.stringify(A,null,2);t[u.Accept]=this._getExistingOrDefaultHeader(t,u.Accept,Q.ApplicationJson);t[u.ContentType]=this._getExistingOrDefaultHeader(t,u.ContentType,Q.ApplicationJson);const s=yield this.patch(e,r,t);return this._processResponse(s,this.requestOptions)}))}request(e,A,t,r){return n(this,void 0,void 0,(function*(){if(this._disposed){throw new Error("Client has already been disposed.")}const s=new URL(A);let o=this._prepareRequest(e,s,r);const n=this._allowRetries&&B.includes(e)?this._maxRetries+1:1;let i=0;let a;do{a=yield this.requestRaw(o,t);if(a&&a.message&&a.message.statusCode===l.Unauthorized){let e;for(const A of this.handlers){if(A.canHandleAuthentication(a)){e=A;break}}if(e){return e.handleAuthentication(this,o,t)}else{return a}}let A=this._maxRedirects;while(a.message.statusCode&&C.includes(a.message.statusCode)&&this._allowRedirects&&A>0){const n=a.message.headers["location"];if(!n){break}const i=new URL(n);if(s.protocol==="https:"&&s.protocol!==i.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(i.hostname!==s.hostname){for(const e in r){if(e.toLowerCase()==="authorization"){delete r[e]}}}o=this._prepareRequest(e,i,r);a=yield this.requestRaw(o,t);A--}if(!a.message.statusCode||!h.includes(a.message.statusCode)){return a}i+=1;if(i{function callbackForResult(e,A){if(e){r(e)}else if(!A){r(new Error("Unknown error"))}else{t(A)}}this.requestRawWithCallback(e,A,callbackForResult)}))}))}requestRawWithCallback(e,A,t){if(typeof A==="string"){if(!e.options.headers){e.options.headers={}}e.options.headers["Content-Length"]=Buffer.byteLength(A,"utf8")}let r=false;function handleResult(e,A){if(!r){r=true;t(e,A)}}const s=e.httpModule.request(e.options,(e=>{const A=new HttpClientResponse(e);handleResult(undefined,A)}));let o;s.on("socket",(e=>{o=e}));s.setTimeout(this._socketTimeout||3*6e4,(()=>{if(o){o.end()}handleResult(new Error(`Request timeout: ${e.options.path}`))}));s.on("error",(function(e){handleResult(e)}));if(A&&typeof A==="string"){s.write(A,"utf8")}if(A&&typeof A!=="string"){A.on("close",(function(){s.end()}));A.pipe(s)}else{s.end()}}getAgent(e){const A=new URL(e);return this._getAgent(A)}getAgentDispatcher(e){const A=new URL(e);const t=c.getProxyUrl(A);const r=t&&t.hostname;if(!r){return}return this._getProxyAgentDispatcher(A,t)}_prepareRequest(e,A,t){const r={};r.parsedUrl=A;const s=r.parsedUrl.protocol==="https:";r.httpModule=s?a:i;const o=s?443:80;r.options={};r.options.host=r.parsedUrl.hostname;r.options.port=r.parsedUrl.port?parseInt(r.parsedUrl.port):o;r.options.path=(r.parsedUrl.pathname||"")+(r.parsedUrl.search||"");r.options.method=e;r.options.headers=this._mergeHeaders(t);if(this.userAgent!=null){r.options.headers["user-agent"]=this.userAgent}r.options.agent=this._getAgent(r.parsedUrl);if(this.handlers){for(const e of this.handlers){e.prepareRequest(r.options)}}return r}_mergeHeaders(e){if(this.requestOptions&&this.requestOptions.headers){return Object.assign({},lowercaseKeys(this.requestOptions.headers),lowercaseKeys(e||{}))}return lowercaseKeys(e||{})}_getExistingOrDefaultHeader(e,A,t){let r;if(this.requestOptions&&this.requestOptions.headers){r=lowercaseKeys(this.requestOptions.headers)[A]}return e[A]||r||t}_getAgent(e){let A;const t=c.getProxyUrl(e);const r=t&&t.hostname;if(this._keepAlive&&r){A=this._proxyAgent}if(!r){A=this._agent}if(A){return A}const s=e.protocol==="https:";let o=100;if(this.requestOptions){o=this.requestOptions.maxSockets||i.globalAgent.maxSockets}if(t&&t.hostname){const e={maxSockets:o,keepAlive:this._keepAlive,proxy:Object.assign(Object.assign({},(t.username||t.password)&&{proxyAuth:`${t.username}:${t.password}`}),{host:t.hostname,port:t.port})};let r;const n=t.protocol==="https:";if(s){r=n?g.httpsOverHttps:g.httpsOverHttp}else{r=n?g.httpOverHttps:g.httpOverHttp}A=r(e);this._proxyAgent=A}if(!A){const e={keepAlive:this._keepAlive,maxSockets:o};A=s?new a.Agent(e):new i.Agent(e);this._agent=A}if(s&&this._ignoreSslError){A.options=Object.assign(A.options||{},{rejectUnauthorized:false})}return A}_getProxyAgentDispatcher(e,A){let t;if(this._keepAlive){t=this._proxyAgentDispatcher}if(t){return t}const r=e.protocol==="https:";t=new E.ProxyAgent(Object.assign({uri:A.href,pipelining:!this._keepAlive?0:1},(A.username||A.password)&&{token:`Basic ${Buffer.from(`${A.username}:${A.password}`).toString("base64")}`}));this._proxyAgentDispatcher=t;if(r&&this._ignoreSslError){t.options=Object.assign(t.options.requestTls||{},{rejectUnauthorized:false})}return t}_performExponentialBackoff(e){return n(this,void 0,void 0,(function*(){e=Math.min(I,e);const A=d*Math.pow(2,e);return new Promise((e=>setTimeout((()=>e()),A)))}))}_processResponse(e,A){return n(this,void 0,void 0,(function*(){return new Promise(((t,r)=>n(this,void 0,void 0,(function*(){const s=e.message.statusCode||0;const o={statusCode:s,result:null,headers:{}};if(s===l.NotFound){t(o)}function dateTimeDeserializer(e,A){if(typeof A==="string"){const e=new Date(A);if(!isNaN(e.valueOf())){return e}}return A}let n;let i;try{i=yield e.readBody();if(i&&i.length>0){if(A&&A.deserializeDates){n=JSON.parse(i,dateTimeDeserializer)}else{n=JSON.parse(i)}o.result=n}o.headers=e.message.headers}catch(e){}if(s>299){let e;if(n&&n.message){e=n.message}else if(i&&i.length>0){e=i}else{e=`Failed request: (${s})`}const A=new HttpClientError(e,s);A.result=o.result;r(A)}else{t(o)}}))))}))}}A.HttpClient=HttpClient;const lowercaseKeys=e=>Object.keys(e).reduce(((A,t)=>(A[t.toLowerCase()]=e[t],A)),{})},8758:(e,A)=>{"use strict";Object.defineProperty(A,"__esModule",{value:true});A.checkBypass=A.getProxyUrl=void 0;function getProxyUrl(e){const A=e.protocol==="https:";if(checkBypass(e)){return undefined}const t=(()=>{if(A){return process.env["https_proxy"]||process.env["HTTPS_PROXY"]}else{return process.env["http_proxy"]||process.env["HTTP_PROXY"]}})();if(t){try{return new DecodedURL(t)}catch(e){if(!t.startsWith("http://")&&!t.startsWith("https://"))return new DecodedURL(`http://${t}`)}}else{return undefined}}A.getProxyUrl=getProxyUrl;function checkBypass(e){if(!e.hostname){return false}const A=e.hostname;if(isLoopbackAddress(A)){return true}const t=process.env["no_proxy"]||process.env["NO_PROXY"]||"";if(!t){return false}let r;if(e.port){r=Number(e.port)}else if(e.protocol==="http:"){r=80}else if(e.protocol==="https:"){r=443}const s=[e.hostname.toUpperCase()];if(typeof r==="number"){s.push(`${s[0]}:${r}`)}for(const e of t.split(",").map((e=>e.trim().toUpperCase())).filter((e=>e))){if(e==="*"||s.some((A=>A===e||A.endsWith(`.${e}`)||e.startsWith(".")&&A.endsWith(`${e}`)))){return true}}return false}A.checkBypass=checkBypass;function isLoopbackAddress(e){const A=e.toLowerCase();return A==="localhost"||A.startsWith("127.")||A.startsWith("[::1]")||A.startsWith("[0:0:0:0:0:0:0:1]")}class DecodedURL extends URL{constructor(e,A){super(e,A);this._decodedUsername=decodeURIComponent(super.username);this._decodedPassword=decodeURIComponent(super.password)}get username(){return this._decodedUsername}get password(){return this._decodedPassword}}},2667:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;Object.defineProperty(e,r,{enumerable:true,get:function(){return A[t]}})}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};var i;Object.defineProperty(A,"__esModule",{value:true});A.getCmdPath=A.tryGetExecutablePath=A.isRooted=A.isDirectory=A.exists=A.READONLY=A.UV_FS_O_EXLOCK=A.IS_WINDOWS=A.unlink=A.symlink=A.stat=A.rmdir=A.rm=A.rename=A.readlink=A.readdir=A.open=A.mkdir=A.lstat=A.copyFile=A.chmod=void 0;const a=o(t(9896));const c=o(t(6928));i=a.promises,A.chmod=i.chmod,A.copyFile=i.copyFile,A.lstat=i.lstat,A.mkdir=i.mkdir,A.open=i.open,A.readdir=i.readdir,A.readlink=i.readlink,A.rename=i.rename,A.rm=i.rm,A.rmdir=i.rmdir,A.stat=i.stat,A.symlink=i.symlink,A.unlink=i.unlink;A.IS_WINDOWS=process.platform==="win32";A.UV_FS_O_EXLOCK=268435456;A.READONLY=a.constants.O_RDONLY;function exists(e){return n(this,void 0,void 0,(function*(){try{yield A.stat(e)}catch(e){if(e.code==="ENOENT"){return false}throw e}return true}))}A.exists=exists;function isDirectory(e,t=false){return n(this,void 0,void 0,(function*(){const r=t?yield A.stat(e):yield A.lstat(e);return r.isDirectory()}))}A.isDirectory=isDirectory;function isRooted(e){e=normalizeSeparators(e);if(!e){throw new Error('isRooted() parameter "p" cannot be empty')}if(A.IS_WINDOWS){return e.startsWith("\\")||/^[A-Z]:/i.test(e)}return e.startsWith("/")}A.isRooted=isRooted;function tryGetExecutablePath(e,t){return n(this,void 0,void 0,(function*(){let r=undefined;try{r=yield A.stat(e)}catch(A){if(A.code!=="ENOENT"){console.log(`Unexpected error attempting to determine if executable file exists '${e}': ${A}`)}}if(r&&r.isFile()){if(A.IS_WINDOWS){const A=c.extname(e).toUpperCase();if(t.some((e=>e.toUpperCase()===A))){return e}}else{if(isUnixExecutable(r)){return e}}}const s=e;for(const o of t){e=s+o;r=undefined;try{r=yield A.stat(e)}catch(A){if(A.code!=="ENOENT"){console.log(`Unexpected error attempting to determine if executable file exists '${e}': ${A}`)}}if(r&&r.isFile()){if(A.IS_WINDOWS){try{const t=c.dirname(e);const r=c.basename(e).toUpperCase();for(const s of yield A.readdir(t)){if(r===s.toUpperCase()){e=c.join(t,s);break}}}catch(A){console.log(`Unexpected error attempting to determine the actual case of the file '${e}': ${A}`)}return e}else{if(isUnixExecutable(r)){return e}}}}return""}))}A.tryGetExecutablePath=tryGetExecutablePath;function normalizeSeparators(e){e=e||"";if(A.IS_WINDOWS){e=e.replace(/\//g,"\\");return e.replace(/\\\\+/g,"\\")}return e.replace(/\/\/+/g,"/")}function isUnixExecutable(e){return(e.mode&1)>0||(e.mode&8)>0&&e.gid===process.getgid()||(e.mode&64)>0&&e.uid===process.getuid()}function getCmdPath(){var e;return(e=process.env["COMSPEC"])!==null&&e!==void 0?e:`cmd.exe`}A.getCmdPath=getCmdPath},4398:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;Object.defineProperty(e,r,{enumerable:true,get:function(){return A[t]}})}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t in e)if(t!=="default"&&Object.hasOwnProperty.call(e,t))r(A,e,t);s(A,e);return A};var n=this&&this.__awaiter||function(e,A,t,r){function adopt(e){return e instanceof t?e:new t((function(A){A(e)}))}return new(t||(t=Promise))((function(t,s){function fulfilled(e){try{step(r.next(e))}catch(e){s(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){s(e)}}function step(e){e.done?t(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,A||[])).next())}))};Object.defineProperty(A,"__esModule",{value:true});A.findInPath=A.which=A.mkdirP=A.rmRF=A.mv=A.cp=void 0;const i=t(2613);const a=o(t(6928));const c=o(t(2667));function cp(e,A,t={}){return n(this,void 0,void 0,(function*(){const{force:r,recursive:s,copySourceDirectory:o}=readCopyOptions(t);const n=(yield c.exists(A))?yield c.stat(A):null;if(n&&n.isFile()&&!r){return}const i=n&&n.isDirectory()&&o?a.join(A,a.basename(e)):A;if(!(yield c.exists(e))){throw new Error(`no such file or directory: ${e}`)}const g=yield c.stat(e);if(g.isDirectory()){if(!s){throw new Error(`Failed to copy. ${e} is a directory, but tried to copy without recursive flag.`)}else{yield cpDirRecursive(e,i,0,r)}}else{if(a.relative(e,i)===""){throw new Error(`'${i}' and '${e}' are the same file`)}yield copyFile(e,i,r)}}))}A.cp=cp;function mv(e,A,t={}){return n(this,void 0,void 0,(function*(){if(yield c.exists(A)){let r=true;if(yield c.isDirectory(A)){A=a.join(A,a.basename(e));r=yield c.exists(A)}if(r){if(t.force==null||t.force){yield rmRF(A)}else{throw new Error("Destination already exists")}}}yield mkdirP(a.dirname(A));yield c.rename(e,A)}))}A.mv=mv;function rmRF(e){return n(this,void 0,void 0,(function*(){if(c.IS_WINDOWS){if(/[*"<>|]/.test(e)){throw new Error('File path must not contain `*`, `"`, `<`, `>` or `|` on Windows')}}try{yield c.rm(e,{force:true,maxRetries:3,recursive:true,retryDelay:300})}catch(e){throw new Error(`File was unable to be removed ${e}`)}}))}A.rmRF=rmRF;function mkdirP(e){return n(this,void 0,void 0,(function*(){i.ok(e,"a path argument must be provided");yield c.mkdir(e,{recursive:true})}))}A.mkdirP=mkdirP;function which(e,A){return n(this,void 0,void 0,(function*(){if(!e){throw new Error("parameter 'tool' is required")}if(A){const A=yield which(e,false);if(!A){if(c.IS_WINDOWS){throw new Error(`Unable to locate executable file: ${e}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`)}else{throw new Error(`Unable to locate executable file: ${e}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`)}}return A}const t=yield findInPath(e);if(t&&t.length>0){return t[0]}return""}))}A.which=which;function findInPath(e){return n(this,void 0,void 0,(function*(){if(!e){throw new Error("parameter 'tool' is required")}const A=[];if(c.IS_WINDOWS&&process.env["PATHEXT"]){for(const e of process.env["PATHEXT"].split(a.delimiter)){if(e){A.push(e)}}}if(c.isRooted(e)){const t=yield c.tryGetExecutablePath(e,A);if(t){return[t]}return[]}if(e.includes(a.sep)){return[]}const t=[];if(process.env.PATH){for(const e of process.env.PATH.split(a.delimiter)){if(e){t.push(e)}}}const r=[];for(const s of t){const t=yield c.tryGetExecutablePath(a.join(s,e),A);if(t){r.push(t)}}return r}))}A.findInPath=findInPath;function readCopyOptions(e){const A=e.force==null?true:e.force;const t=Boolean(e.recursive);const r=e.copySourceDirectory==null?true:Boolean(e.copySourceDirectory);return{force:A,recursive:t,copySourceDirectory:r}}function cpDirRecursive(e,A,t,r){return n(this,void 0,void 0,(function*(){if(t>=255)return;t++;yield mkdirP(A);const s=yield c.readdir(e);for(const o of s){const s=`${e}/${o}`;const n=`${A}/${o}`;const i=yield c.lstat(s);if(i.isDirectory()){yield cpDirRecursive(s,n,t,r)}else{yield copyFile(s,n,r)}}yield c.chmod(A,(yield c.stat(e)).mode)}))}function copyFile(e,A,t){return n(this,void 0,void 0,(function*(){if((yield c.lstat(e)).isSymbolicLink()){try{yield c.lstat(A);yield c.unlink(A)}catch(e){if(e.code==="EPERM"){yield c.chmod(A,"0666");yield c.unlink(A)}}const t=yield c.readlink(e);yield c.symlink(t,A,c.IS_WINDOWS?"junction":null)}else if(!(yield c.exists(A))||t){yield c.copyFile(e,A)}}))}},3907:e=>{"use strict";var A=Object.defineProperty;var t=Object.getOwnPropertyDescriptor;var r=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var __export=(e,t)=>{for(var r in t)A(e,r,{get:t[r],enumerable:true})};var __copyProps=(e,o,n,i)=>{if(o&&typeof o==="object"||typeof o==="function"){for(let a of r(o))if(!s.call(e,a)&&a!==n)A(e,a,{get:()=>o[a],enumerable:!(i=t(o,a))||i.enumerable})}return e};var __toCommonJS=e=>__copyProps(A({},"__esModule",{value:true}),e);var o={};__export(o,{createTokenAuth:()=>c});e.exports=__toCommonJS(o);var n=/^v1\./;var i=/^ghs_/;var a=/^ghu_/;async function auth(e){const A=e.split(/\./).length===3;const t=n.test(e)||i.test(e);const r=a.test(e);const s=A?"app":t?"installation":r?"user-to-server":"oauth";return{type:"token",token:e,tokenType:s}}function withAuthorizationPrefix(e){if(e.split(/\./).length===3){return`bearer ${e}`}return`token ${e}`}async function hook(e,A,t,r){const s=A.endpoint.merge(t,r);s.headers.authorization=withAuthorizationPrefix(e);return A(s)}var c=function createTokenAuth2(e){if(!e){throw new Error("[@octokit/auth-token] No token passed to createTokenAuth")}if(typeof e!=="string"){throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string")}e=e.replace(/^(token|bearer) +/i,"");return Object.assign(auth.bind(null,e),{hook:hook.bind(null,e)})};0&&0},8850:(e,A,t)=>{"use strict";var r=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var o=Object.getOwnPropertyNames;var n=Object.prototype.hasOwnProperty;var __export=(e,A)=>{for(var t in A)r(e,t,{get:A[t],enumerable:true})};var __copyProps=(e,A,t,i)=>{if(A&&typeof A==="object"||typeof A==="function"){for(let a of o(A))if(!n.call(e,a)&&a!==t)r(e,a,{get:()=>A[a],enumerable:!(i=s(A,a))||i.enumerable})}return e};var __toCommonJS=e=>__copyProps(r({},"__esModule",{value:true}),e);var i={};__export(i,{Octokit:()=>B});e.exports=__toCommonJS(i);var a=t(9653);var c=t(7932);var g=t(8662);var E=t(6536);var l=t(3907);var u="5.0.2";var noop=()=>{};var Q=console.warn.bind(console);var C=console.error.bind(console);var h=`octokit-core.js/${u} ${(0,a.getUserAgent)()}`;var B=class{static{this.VERSION=u}static defaults(e){const A=class extends(this){constructor(...A){const t=A[0]||{};if(typeof e==="function"){super(e(t));return}super(Object.assign({},e,t,t.userAgent&&e.userAgent?{userAgent:`${t.userAgent} ${e.userAgent}`}:null))}};return A}static{this.plugins=[]}static plugin(...e){const A=this.plugins;const t=class extends(this){static{this.plugins=A.concat(e.filter((e=>!A.includes(e))))}};return t}constructor(e={}){const A=new c.Collection;const t={baseUrl:g.request.endpoint.DEFAULTS.baseUrl,headers:{},request:Object.assign({},e.request,{hook:A.bind(null,"request")}),mediaType:{previews:[],format:""}};t.headers["user-agent"]=e.userAgent?`${e.userAgent} ${h}`:h;if(e.baseUrl){t.baseUrl=e.baseUrl}if(e.previews){t.mediaType.previews=e.previews}if(e.timeZone){t.headers["time-zone"]=e.timeZone}this.request=g.request.defaults(t);this.graphql=(0,E.withCustomRequest)(this.request).defaults(t);this.log=Object.assign({debug:noop,info:noop,warn:Q,error:C},e.log);this.hook=A;if(!e.authStrategy){if(!e.auth){this.auth=async()=>({type:"unauthenticated"})}else{const t=(0,l.createTokenAuth)(e.auth);A.wrap("request",t.hook);this.auth=t}}else{const{authStrategy:t,...r}=e;const s=t(Object.assign({request:this.request,log:this.log,octokit:this,octokitOptions:r},e.auth));A.wrap("request",s.hook);this.auth=s}const r=this.constructor;for(let A=0;A{"use strict";var r=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var o=Object.getOwnPropertyNames;var n=Object.prototype.hasOwnProperty;var __export=(e,A)=>{for(var t in A)r(e,t,{get:A[t],enumerable:true})};var __copyProps=(e,A,t,i)=>{if(A&&typeof A==="object"||typeof A==="function"){for(let a of o(A))if(!n.call(e,a)&&a!==t)r(e,a,{get:()=>A[a],enumerable:!(i=s(A,a))||i.enumerable})}return e};var __toCommonJS=e=>__copyProps(r({},"__esModule",{value:true}),e);var i={};__export(i,{endpoint:()=>u});e.exports=__toCommonJS(i);var a=t(9653);var c="9.0.4";var g=`octokit-endpoint.js/${c} ${(0,a.getUserAgent)()}`;var E={method:"GET",baseUrl:"https://api.github.com",headers:{accept:"application/vnd.github.v3+json","user-agent":g},mediaType:{format:""}};function lowercaseKeys(e){if(!e){return{}}return Object.keys(e).reduce(((A,t)=>{A[t.toLowerCase()]=e[t];return A}),{})}function isPlainObject(e){if(typeof e!=="object"||e===null)return false;if(Object.prototype.toString.call(e)!=="[object Object]")return false;const A=Object.getPrototypeOf(e);if(A===null)return true;const t=Object.prototype.hasOwnProperty.call(A,"constructor")&&A.constructor;return typeof t==="function"&&t instanceof t&&Function.prototype.call(t)===Function.prototype.call(e)}function mergeDeep(e,A){const t=Object.assign({},e);Object.keys(A).forEach((r=>{if(isPlainObject(A[r])){if(!(r in e))Object.assign(t,{[r]:A[r]});else t[r]=mergeDeep(e[r],A[r])}else{Object.assign(t,{[r]:A[r]})}}));return t}function removeUndefinedProperties(e){for(const A in e){if(e[A]===void 0){delete e[A]}}return e}function merge(e,A,t){if(typeof A==="string"){let[e,r]=A.split(" ");t=Object.assign(r?{method:e,url:r}:{url:e},t)}else{t=Object.assign({},A)}t.headers=lowercaseKeys(t.headers);removeUndefinedProperties(t);removeUndefinedProperties(t.headers);const r=mergeDeep(e||{},t);if(t.url==="/graphql"){if(e&&e.mediaType.previews?.length){r.mediaType.previews=e.mediaType.previews.filter((e=>!r.mediaType.previews.includes(e))).concat(r.mediaType.previews)}r.mediaType.previews=(r.mediaType.previews||[]).map((e=>e.replace(/-preview/,"")))}return r}function addQueryParameters(e,A){const t=/\?/.test(e)?"&":"?";const r=Object.keys(A);if(r.length===0){return e}return e+t+r.map((e=>{if(e==="q"){return"q="+A.q.split("+").map(encodeURIComponent).join("+")}return`${e}=${encodeURIComponent(A[e])}`})).join("&")}var l=/\{[^}]+\}/g;function removeNonChars(e){return e.replace(/^\W+|\W+$/g,"").split(/,/)}function extractUrlVariableNames(e){const A=e.match(l);if(!A){return[]}return A.map(removeNonChars).reduce(((e,A)=>e.concat(A)),[])}function omit(e,A){const t={__proto__:null};for(const r of Object.keys(e)){if(A.indexOf(r)===-1){t[r]=e[r]}}return t}function encodeReserved(e){return e.split(/(%[0-9A-Fa-f]{2})/g).map((function(e){if(!/%[0-9A-Fa-f]/.test(e)){e=encodeURI(e).replace(/%5B/g,"[").replace(/%5D/g,"]")}return e})).join("")}function encodeUnreserved(e){return encodeURIComponent(e).replace(/[!'()*]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}))}function encodeValue(e,A,t){A=e==="+"||e==="#"?encodeReserved(A):encodeUnreserved(A);if(t){return encodeUnreserved(t)+"="+A}else{return A}}function isDefined(e){return e!==void 0&&e!==null}function isKeyOperator(e){return e===";"||e==="&"||e==="?"}function getValues(e,A,t,r){var s=e[t],o=[];if(isDefined(s)&&s!==""){if(typeof s==="string"||typeof s==="number"||typeof s==="boolean"){s=s.toString();if(r&&r!=="*"){s=s.substring(0,parseInt(r,10))}o.push(encodeValue(A,s,isKeyOperator(A)?t:""))}else{if(r==="*"){if(Array.isArray(s)){s.filter(isDefined).forEach((function(e){o.push(encodeValue(A,e,isKeyOperator(A)?t:""))}))}else{Object.keys(s).forEach((function(e){if(isDefined(s[e])){o.push(encodeValue(A,s[e],e))}}))}}else{const e=[];if(Array.isArray(s)){s.filter(isDefined).forEach((function(t){e.push(encodeValue(A,t))}))}else{Object.keys(s).forEach((function(t){if(isDefined(s[t])){e.push(encodeUnreserved(t));e.push(encodeValue(A,s[t].toString()))}}))}if(isKeyOperator(A)){o.push(encodeUnreserved(t)+"="+e.join(","))}else if(e.length!==0){o.push(e.join(","))}}}}else{if(A===";"){if(isDefined(s)){o.push(encodeUnreserved(t))}}else if(s===""&&(A==="&"||A==="?")){o.push(encodeUnreserved(t)+"=")}else if(s===""){o.push("")}}return o}function parseUrl(e){return{expand:expand.bind(null,e)}}function expand(e,A){var t=["+","#",".","/",";","?","&"];e=e.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g,(function(e,r,s){if(r){let e="";const s=[];if(t.indexOf(r.charAt(0))!==-1){e=r.charAt(0);r=r.substr(1)}r.split(/,/g).forEach((function(t){var r=/([^:\*]*)(?::(\d+)|(\*))?/.exec(t);s.push(getValues(A,e,r[1],r[2]||r[3]))}));if(e&&e!=="+"){var o=",";if(e==="?"){o="&"}else if(e!=="#"){o=e}return(s.length!==0?e:"")+s.join(o)}else{return s.join(",")}}else{return encodeReserved(s)}}));if(e==="/"){return e}else{return e.replace(/\/$/,"")}}function parse(e){let A=e.method.toUpperCase();let t=(e.url||"/").replace(/:([a-z]\w+)/g,"{$1}");let r=Object.assign({},e.headers);let s;let o=omit(e,["method","baseUrl","url","headers","request","mediaType"]);const n=extractUrlVariableNames(t);t=parseUrl(t).expand(o);if(!/^http/.test(t)){t=e.baseUrl+t}const i=Object.keys(e).filter((e=>n.includes(e))).concat("baseUrl");const a=omit(o,i);const c=/application\/octet-stream/i.test(r.accept);if(!c){if(e.mediaType.format){r.accept=r.accept.split(/,/).map((A=>A.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/,`application/vnd$1$2.${e.mediaType.format}`))).join(",")}if(t.endsWith("/graphql")){if(e.mediaType.previews?.length){const A=r.accept.match(/[\w-]+(?=-preview)/g)||[];r.accept=A.concat(e.mediaType.previews).map((A=>{const t=e.mediaType.format?`.${e.mediaType.format}`:"+json";return`application/vnd.github.${A}-preview${t}`})).join(",")}}}if(["GET","HEAD"].includes(A)){t=addQueryParameters(t,a)}else{if("data"in a){s=a.data}else{if(Object.keys(a).length){s=a}}}if(!r["content-type"]&&typeof s!=="undefined"){r["content-type"]="application/json; charset=utf-8"}if(["PATCH","PUT"].includes(A)&&typeof s==="undefined"){s=""}return Object.assign({method:A,url:t,headers:r},typeof s!=="undefined"?{body:s}:null,e.request?{request:e.request}:null)}function endpointWithDefaults(e,A,t){return parse(merge(e,A,t))}function withDefaults(e,A){const t=merge(e,A);const r=endpointWithDefaults.bind(null,t);return Object.assign(r,{DEFAULTS:t,defaults:withDefaults.bind(null,t),merge:merge.bind(null,t),parse:parse})}var u=withDefaults(null,E);0&&0},6536:(e,A,t)=>{"use strict";var r=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var o=Object.getOwnPropertyNames;var n=Object.prototype.hasOwnProperty;var __export=(e,A)=>{for(var t in A)r(e,t,{get:A[t],enumerable:true})};var __copyProps=(e,A,t,i)=>{if(A&&typeof A==="object"||typeof A==="function"){for(let a of o(A))if(!n.call(e,a)&&a!==t)r(e,a,{get:()=>A[a],enumerable:!(i=s(A,a))||i.enumerable})}return e};var __toCommonJS=e=>__copyProps(r({},"__esModule",{value:true}),e);var i={};__export(i,{GraphqlResponseError:()=>u,graphql:()=>B,withCustomRequest:()=>withCustomRequest});e.exports=__toCommonJS(i);var a=t(8662);var c=t(9653);var g="7.0.2";var E=t(8662);var l=t(8662);function _buildMessageForResponseErrors(e){return`Request failed due to following response errors:\n`+e.errors.map((e=>` - ${e.message}`)).join("\n")}var u=class extends Error{constructor(e,A,t){super(_buildMessageForResponseErrors(t));this.request=e;this.headers=A;this.response=t;this.name="GraphqlResponseError";this.errors=t.errors;this.data=t.data;if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}}};var Q=["method","baseUrl","url","headers","request","query","mediaType"];var C=["query","method","url"];var h=/\/api\/v3\/?$/;function graphql(e,A,t){if(t){if(typeof A==="string"&&"query"in t){return Promise.reject(new Error(`[@octokit/graphql] "query" cannot be used as variable name`))}for(const e in t){if(!C.includes(e))continue;return Promise.reject(new Error(`[@octokit/graphql] "${e}" cannot be used as variable name`))}}const r=typeof A==="string"?Object.assign({query:A},t):A;const s=Object.keys(r).reduce(((e,A)=>{if(Q.includes(A)){e[A]=r[A];return e}if(!e.variables){e.variables={}}e.variables[A]=r[A];return e}),{});const o=r.baseUrl||e.endpoint.DEFAULTS.baseUrl;if(h.test(o)){s.url=o.replace(h,"/api/graphql")}return e(s).then((e=>{if(e.data.errors){const A={};for(const t of Object.keys(e.headers)){A[t]=e.headers[t]}throw new u(s,A,e.data)}return e.data.data}))}function withDefaults(e,A){const t=e.defaults(A);const newApi=(e,A)=>graphql(t,e,A);return Object.assign(newApi,{defaults:withDefaults.bind(null,t),endpoint:t.endpoint})}var B=withDefaults(a.request,{headers:{"user-agent":`octokit-graphql.js/${g} ${(0,c.getUserAgent)()}`},method:"POST",url:"/graphql"});function withCustomRequest(e){return withDefaults(e,{method:"POST",url:"/graphql"})}0&&0},3895:e=>{"use strict";var A=Object.defineProperty;var t=Object.getOwnPropertyDescriptor;var r=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var __export=(e,t)=>{for(var r in t)A(e,r,{get:t[r],enumerable:true})};var __copyProps=(e,o,n,i)=>{if(o&&typeof o==="object"||typeof o==="function"){for(let a of r(o))if(!s.call(e,a)&&a!==n)A(e,a,{get:()=>o[a],enumerable:!(i=t(o,a))||i.enumerable})}return e};var __toCommonJS=e=>__copyProps(A({},"__esModule",{value:true}),e);var o={};__export(o,{composePaginateRest:()=>i,isPaginatingEndpoint:()=>isPaginatingEndpoint,paginateRest:()=>paginateRest,paginatingEndpoints:()=>a});e.exports=__toCommonJS(o);var n="9.1.5";function normalizePaginatedListResponse(e){if(!e.data){return{...e,data:[]}}const A="total_count"in e.data&&!("url"in e.data);if(!A)return e;const t=e.data.incomplete_results;const r=e.data.repository_selection;const s=e.data.total_count;delete e.data.incomplete_results;delete e.data.repository_selection;delete e.data.total_count;const o=Object.keys(e.data)[0];const n=e.data[o];e.data=n;if(typeof t!=="undefined"){e.data.incomplete_results=t}if(typeof r!=="undefined"){e.data.repository_selection=r}e.data.total_count=s;return e}function iterator(e,A,t){const r=typeof A==="function"?A.endpoint(t):e.request.endpoint(A,t);const s=typeof A==="function"?A:e.request;const o=r.method;const n=r.headers;let i=r.url;return{[Symbol.asyncIterator]:()=>({async next(){if(!i)return{done:true};try{const e=await s({method:o,url:i,headers:n});const A=normalizePaginatedListResponse(e);i=((A.headers.link||"").match(/<([^>]+)>;\s*rel="next"/)||[])[1];return{value:A}}catch(e){if(e.status!==409)throw e;i="";return{value:{status:200,headers:{},data:[]}}}}})}}function paginate(e,A,t,r){if(typeof t==="function"){r=t;t=void 0}return gather(e,[],iterator(e,A,t)[Symbol.asyncIterator](),r)}function gather(e,A,t,r){return t.next().then((s=>{if(s.done){return A}let o=false;function done(){o=true}A=A.concat(r?r(s.value,done):s.value.data);if(o){return A}return gather(e,A,t,r)}))}var i=Object.assign(paginate,{iterator:iterator});var a=["GET /advisories","GET /app/hook/deliveries","GET /app/installation-requests","GET /app/installations","GET /assignments/{assignment_id}/accepted_assignments","GET /classrooms","GET /classrooms/{classroom_id}/assignments","GET /enterprises/{enterprise}/dependabot/alerts","GET /enterprises/{enterprise}/secret-scanning/alerts","GET /events","GET /gists","GET /gists/public","GET /gists/starred","GET /gists/{gist_id}/comments","GET /gists/{gist_id}/commits","GET /gists/{gist_id}/forks","GET /installation/repositories","GET /issues","GET /licenses","GET /marketplace_listing/plans","GET /marketplace_listing/plans/{plan_id}/accounts","GET /marketplace_listing/stubbed/plans","GET /marketplace_listing/stubbed/plans/{plan_id}/accounts","GET /networks/{owner}/{repo}/events","GET /notifications","GET /organizations","GET /orgs/{org}/actions/cache/usage-by-repository","GET /orgs/{org}/actions/permissions/repositories","GET /orgs/{org}/actions/runners","GET /orgs/{org}/actions/secrets","GET /orgs/{org}/actions/secrets/{secret_name}/repositories","GET /orgs/{org}/actions/variables","GET /orgs/{org}/actions/variables/{name}/repositories","GET /orgs/{org}/blocks","GET /orgs/{org}/code-scanning/alerts","GET /orgs/{org}/codespaces","GET /orgs/{org}/codespaces/secrets","GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories","GET /orgs/{org}/copilot/billing/seats","GET /orgs/{org}/dependabot/alerts","GET /orgs/{org}/dependabot/secrets","GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories","GET /orgs/{org}/events","GET /orgs/{org}/failed_invitations","GET /orgs/{org}/hooks","GET /orgs/{org}/hooks/{hook_id}/deliveries","GET /orgs/{org}/installations","GET /orgs/{org}/invitations","GET /orgs/{org}/invitations/{invitation_id}/teams","GET /orgs/{org}/issues","GET /orgs/{org}/members","GET /orgs/{org}/members/{username}/codespaces","GET /orgs/{org}/migrations","GET /orgs/{org}/migrations/{migration_id}/repositories","GET /orgs/{org}/outside_collaborators","GET /orgs/{org}/packages","GET /orgs/{org}/packages/{package_type}/{package_name}/versions","GET /orgs/{org}/personal-access-token-requests","GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories","GET /orgs/{org}/personal-access-tokens","GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories","GET /orgs/{org}/projects","GET /orgs/{org}/properties/values","GET /orgs/{org}/public_members","GET /orgs/{org}/repos","GET /orgs/{org}/rulesets","GET /orgs/{org}/rulesets/rule-suites","GET /orgs/{org}/secret-scanning/alerts","GET /orgs/{org}/security-advisories","GET /orgs/{org}/teams","GET /orgs/{org}/teams/{team_slug}/discussions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions","GET /orgs/{org}/teams/{team_slug}/invitations","GET /orgs/{org}/teams/{team_slug}/members","GET /orgs/{org}/teams/{team_slug}/projects","GET /orgs/{org}/teams/{team_slug}/repos","GET /orgs/{org}/teams/{team_slug}/teams","GET /projects/columns/{column_id}/cards","GET /projects/{project_id}/collaborators","GET /projects/{project_id}/columns","GET /repos/{owner}/{repo}/actions/artifacts","GET /repos/{owner}/{repo}/actions/caches","GET /repos/{owner}/{repo}/actions/organization-secrets","GET /repos/{owner}/{repo}/actions/organization-variables","GET /repos/{owner}/{repo}/actions/runners","GET /repos/{owner}/{repo}/actions/runs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts","GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs","GET /repos/{owner}/{repo}/actions/secrets","GET /repos/{owner}/{repo}/actions/variables","GET /repos/{owner}/{repo}/actions/workflows","GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs","GET /repos/{owner}/{repo}/activity","GET /repos/{owner}/{repo}/assignees","GET /repos/{owner}/{repo}/branches","GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations","GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs","GET /repos/{owner}/{repo}/code-scanning/alerts","GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances","GET /repos/{owner}/{repo}/code-scanning/analyses","GET /repos/{owner}/{repo}/codespaces","GET /repos/{owner}/{repo}/codespaces/devcontainers","GET /repos/{owner}/{repo}/codespaces/secrets","GET /repos/{owner}/{repo}/collaborators","GET /repos/{owner}/{repo}/comments","GET /repos/{owner}/{repo}/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/commits","GET /repos/{owner}/{repo}/commits/{commit_sha}/comments","GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls","GET /repos/{owner}/{repo}/commits/{ref}/check-runs","GET /repos/{owner}/{repo}/commits/{ref}/check-suites","GET /repos/{owner}/{repo}/commits/{ref}/status","GET /repos/{owner}/{repo}/commits/{ref}/statuses","GET /repos/{owner}/{repo}/contributors","GET /repos/{owner}/{repo}/dependabot/alerts","GET /repos/{owner}/{repo}/dependabot/secrets","GET /repos/{owner}/{repo}/deployments","GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses","GET /repos/{owner}/{repo}/environments","GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies","GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps","GET /repos/{owner}/{repo}/events","GET /repos/{owner}/{repo}/forks","GET /repos/{owner}/{repo}/hooks","GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries","GET /repos/{owner}/{repo}/invitations","GET /repos/{owner}/{repo}/issues","GET /repos/{owner}/{repo}/issues/comments","GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/issues/events","GET /repos/{owner}/{repo}/issues/{issue_number}/comments","GET /repos/{owner}/{repo}/issues/{issue_number}/events","GET /repos/{owner}/{repo}/issues/{issue_number}/labels","GET /repos/{owner}/{repo}/issues/{issue_number}/reactions","GET /repos/{owner}/{repo}/issues/{issue_number}/timeline","GET /repos/{owner}/{repo}/keys","GET /repos/{owner}/{repo}/labels","GET /repos/{owner}/{repo}/milestones","GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels","GET /repos/{owner}/{repo}/notifications","GET /repos/{owner}/{repo}/pages/builds","GET /repos/{owner}/{repo}/projects","GET /repos/{owner}/{repo}/pulls","GET /repos/{owner}/{repo}/pulls/comments","GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/pulls/{pull_number}/comments","GET /repos/{owner}/{repo}/pulls/{pull_number}/commits","GET /repos/{owner}/{repo}/pulls/{pull_number}/files","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments","GET /repos/{owner}/{repo}/releases","GET /repos/{owner}/{repo}/releases/{release_id}/assets","GET /repos/{owner}/{repo}/releases/{release_id}/reactions","GET /repos/{owner}/{repo}/rules/branches/{branch}","GET /repos/{owner}/{repo}/rulesets","GET /repos/{owner}/{repo}/rulesets/rule-suites","GET /repos/{owner}/{repo}/secret-scanning/alerts","GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations","GET /repos/{owner}/{repo}/security-advisories","GET /repos/{owner}/{repo}/stargazers","GET /repos/{owner}/{repo}/subscribers","GET /repos/{owner}/{repo}/tags","GET /repos/{owner}/{repo}/teams","GET /repos/{owner}/{repo}/topics","GET /repositories","GET /repositories/{repository_id}/environments/{environment_name}/secrets","GET /repositories/{repository_id}/environments/{environment_name}/variables","GET /search/code","GET /search/commits","GET /search/issues","GET /search/labels","GET /search/repositories","GET /search/topics","GET /search/users","GET /teams/{team_id}/discussions","GET /teams/{team_id}/discussions/{discussion_number}/comments","GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /teams/{team_id}/discussions/{discussion_number}/reactions","GET /teams/{team_id}/invitations","GET /teams/{team_id}/members","GET /teams/{team_id}/projects","GET /teams/{team_id}/repos","GET /teams/{team_id}/teams","GET /user/blocks","GET /user/codespaces","GET /user/codespaces/secrets","GET /user/emails","GET /user/followers","GET /user/following","GET /user/gpg_keys","GET /user/installations","GET /user/installations/{installation_id}/repositories","GET /user/issues","GET /user/keys","GET /user/marketplace_purchases","GET /user/marketplace_purchases/stubbed","GET /user/memberships/orgs","GET /user/migrations","GET /user/migrations/{migration_id}/repositories","GET /user/orgs","GET /user/packages","GET /user/packages/{package_type}/{package_name}/versions","GET /user/public_emails","GET /user/repos","GET /user/repository_invitations","GET /user/social_accounts","GET /user/ssh_signing_keys","GET /user/starred","GET /user/subscriptions","GET /user/teams","GET /users","GET /users/{username}/events","GET /users/{username}/events/orgs/{org}","GET /users/{username}/events/public","GET /users/{username}/followers","GET /users/{username}/following","GET /users/{username}/gists","GET /users/{username}/gpg_keys","GET /users/{username}/keys","GET /users/{username}/orgs","GET /users/{username}/packages","GET /users/{username}/projects","GET /users/{username}/received_events","GET /users/{username}/received_events/public","GET /users/{username}/repos","GET /users/{username}/social_accounts","GET /users/{username}/ssh_signing_keys","GET /users/{username}/starred","GET /users/{username}/subscriptions"];function isPaginatingEndpoint(e){if(typeof e==="string"){return a.includes(e)}else{return false}}function paginateRest(e){return{paginate:Object.assign(paginate.bind(null,e),{iterator:iterator.bind(null,e)})}}paginateRest.VERSION=n;0&&0},9389:e=>{"use strict";var A=Object.defineProperty;var t=Object.getOwnPropertyDescriptor;var r=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var __export=(e,t)=>{for(var r in t)A(e,r,{get:t[r],enumerable:true})};var __copyProps=(e,o,n,i)=>{if(o&&typeof o==="object"||typeof o==="function"){for(let a of r(o))if(!s.call(e,a)&&a!==n)A(e,a,{get:()=>o[a],enumerable:!(i=t(o,a))||i.enumerable})}return e};var __toCommonJS=e=>__copyProps(A({},"__esModule",{value:true}),e);var o={};__export(o,{legacyRestEndpointMethods:()=>legacyRestEndpointMethods,restEndpointMethods:()=>restEndpointMethods});e.exports=__toCommonJS(o);var n="10.2.0";var i={actions:{addCustomLabelsToSelfHostedRunnerForOrg:["POST /orgs/{org}/actions/runners/{runner_id}/labels"],addCustomLabelsToSelfHostedRunnerForRepo:["POST /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],addSelectedRepoToOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"],addSelectedRepoToOrgVariable:["PUT /orgs/{org}/actions/variables/{name}/repositories/{repository_id}"],approveWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve"],cancelWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel"],createEnvironmentVariable:["POST /repositories/{repository_id}/environments/{environment_name}/variables"],createOrUpdateEnvironmentSecret:["PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"],createOrUpdateOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}"],createOrgVariable:["POST /orgs/{org}/actions/variables"],createRegistrationTokenForOrg:["POST /orgs/{org}/actions/runners/registration-token"],createRegistrationTokenForRepo:["POST /repos/{owner}/{repo}/actions/runners/registration-token"],createRemoveTokenForOrg:["POST /orgs/{org}/actions/runners/remove-token"],createRemoveTokenForRepo:["POST /repos/{owner}/{repo}/actions/runners/remove-token"],createRepoVariable:["POST /repos/{owner}/{repo}/actions/variables"],createWorkflowDispatch:["POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches"],deleteActionsCacheById:["DELETE /repos/{owner}/{repo}/actions/caches/{cache_id}"],deleteActionsCacheByKey:["DELETE /repos/{owner}/{repo}/actions/caches{?key,ref}"],deleteArtifact:["DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id}"],deleteEnvironmentSecret:["DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"],deleteEnvironmentVariable:["DELETE /repositories/{repository_id}/environments/{environment_name}/variables/{name}"],deleteOrgSecret:["DELETE /orgs/{org}/actions/secrets/{secret_name}"],deleteOrgVariable:["DELETE /orgs/{org}/actions/variables/{name}"],deleteRepoSecret:["DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}"],deleteRepoVariable:["DELETE /repos/{owner}/{repo}/actions/variables/{name}"],deleteSelfHostedRunnerFromOrg:["DELETE /orgs/{org}/actions/runners/{runner_id}"],deleteSelfHostedRunnerFromRepo:["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}"],deleteWorkflowRun:["DELETE /repos/{owner}/{repo}/actions/runs/{run_id}"],deleteWorkflowRunLogs:["DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs"],disableSelectedRepositoryGithubActionsOrganization:["DELETE /orgs/{org}/actions/permissions/repositories/{repository_id}"],disableWorkflow:["PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable"],downloadArtifact:["GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}"],downloadJobLogsForWorkflowRun:["GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs"],downloadWorkflowRunAttemptLogs:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/logs"],downloadWorkflowRunLogs:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs"],enableSelectedRepositoryGithubActionsOrganization:["PUT /orgs/{org}/actions/permissions/repositories/{repository_id}"],enableWorkflow:["PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable"],forceCancelWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/force-cancel"],generateRunnerJitconfigForOrg:["POST /orgs/{org}/actions/runners/generate-jitconfig"],generateRunnerJitconfigForRepo:["POST /repos/{owner}/{repo}/actions/runners/generate-jitconfig"],getActionsCacheList:["GET /repos/{owner}/{repo}/actions/caches"],getActionsCacheUsage:["GET /repos/{owner}/{repo}/actions/cache/usage"],getActionsCacheUsageByRepoForOrg:["GET /orgs/{org}/actions/cache/usage-by-repository"],getActionsCacheUsageForOrg:["GET /orgs/{org}/actions/cache/usage"],getAllowedActionsOrganization:["GET /orgs/{org}/actions/permissions/selected-actions"],getAllowedActionsRepository:["GET /repos/{owner}/{repo}/actions/permissions/selected-actions"],getArtifact:["GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}"],getEnvironmentPublicKey:["GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key"],getEnvironmentSecret:["GET /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"],getEnvironmentVariable:["GET /repositories/{repository_id}/environments/{environment_name}/variables/{name}"],getGithubActionsDefaultWorkflowPermissionsOrganization:["GET /orgs/{org}/actions/permissions/workflow"],getGithubActionsDefaultWorkflowPermissionsRepository:["GET /repos/{owner}/{repo}/actions/permissions/workflow"],getGithubActionsPermissionsOrganization:["GET /orgs/{org}/actions/permissions"],getGithubActionsPermissionsRepository:["GET /repos/{owner}/{repo}/actions/permissions"],getJobForWorkflowRun:["GET /repos/{owner}/{repo}/actions/jobs/{job_id}"],getOrgPublicKey:["GET /orgs/{org}/actions/secrets/public-key"],getOrgSecret:["GET /orgs/{org}/actions/secrets/{secret_name}"],getOrgVariable:["GET /orgs/{org}/actions/variables/{name}"],getPendingDeploymentsForRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments"],getRepoPermissions:["GET /repos/{owner}/{repo}/actions/permissions",{},{renamed:["actions","getGithubActionsPermissionsRepository"]}],getRepoPublicKey:["GET /repos/{owner}/{repo}/actions/secrets/public-key"],getRepoSecret:["GET /repos/{owner}/{repo}/actions/secrets/{secret_name}"],getRepoVariable:["GET /repos/{owner}/{repo}/actions/variables/{name}"],getReviewsForRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/approvals"],getSelfHostedRunnerForOrg:["GET /orgs/{org}/actions/runners/{runner_id}"],getSelfHostedRunnerForRepo:["GET /repos/{owner}/{repo}/actions/runners/{runner_id}"],getWorkflow:["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}"],getWorkflowAccessToRepository:["GET /repos/{owner}/{repo}/actions/permissions/access"],getWorkflowRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}"],getWorkflowRunAttempt:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}"],getWorkflowRunUsage:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/timing"],getWorkflowUsage:["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing"],listArtifactsForRepo:["GET /repos/{owner}/{repo}/actions/artifacts"],listEnvironmentSecrets:["GET /repositories/{repository_id}/environments/{environment_name}/secrets"],listEnvironmentVariables:["GET /repositories/{repository_id}/environments/{environment_name}/variables"],listJobsForWorkflowRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs"],listJobsForWorkflowRunAttempt:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs"],listLabelsForSelfHostedRunnerForOrg:["GET /orgs/{org}/actions/runners/{runner_id}/labels"],listLabelsForSelfHostedRunnerForRepo:["GET /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],listOrgSecrets:["GET /orgs/{org}/actions/secrets"],listOrgVariables:["GET /orgs/{org}/actions/variables"],listRepoOrganizationSecrets:["GET /repos/{owner}/{repo}/actions/organization-secrets"],listRepoOrganizationVariables:["GET /repos/{owner}/{repo}/actions/organization-variables"],listRepoSecrets:["GET /repos/{owner}/{repo}/actions/secrets"],listRepoVariables:["GET /repos/{owner}/{repo}/actions/variables"],listRepoWorkflows:["GET /repos/{owner}/{repo}/actions/workflows"],listRunnerApplicationsForOrg:["GET /orgs/{org}/actions/runners/downloads"],listRunnerApplicationsForRepo:["GET /repos/{owner}/{repo}/actions/runners/downloads"],listSelectedReposForOrgSecret:["GET /orgs/{org}/actions/secrets/{secret_name}/repositories"],listSelectedReposForOrgVariable:["GET /orgs/{org}/actions/variables/{name}/repositories"],listSelectedRepositoriesEnabledGithubActionsOrganization:["GET /orgs/{org}/actions/permissions/repositories"],listSelfHostedRunnersForOrg:["GET /orgs/{org}/actions/runners"],listSelfHostedRunnersForRepo:["GET /repos/{owner}/{repo}/actions/runners"],listWorkflowRunArtifacts:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts"],listWorkflowRuns:["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs"],listWorkflowRunsForRepo:["GET /repos/{owner}/{repo}/actions/runs"],reRunJobForWorkflowRun:["POST /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun"],reRunWorkflow:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun"],reRunWorkflowFailedJobs:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs"],removeAllCustomLabelsFromSelfHostedRunnerForOrg:["DELETE /orgs/{org}/actions/runners/{runner_id}/labels"],removeAllCustomLabelsFromSelfHostedRunnerForRepo:["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],removeCustomLabelFromSelfHostedRunnerForOrg:["DELETE /orgs/{org}/actions/runners/{runner_id}/labels/{name}"],removeCustomLabelFromSelfHostedRunnerForRepo:["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels/{name}"],removeSelectedRepoFromOrgSecret:["DELETE /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"],removeSelectedRepoFromOrgVariable:["DELETE /orgs/{org}/actions/variables/{name}/repositories/{repository_id}"],reviewCustomGatesForRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/deployment_protection_rule"],reviewPendingDeploymentsForRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments"],setAllowedActionsOrganization:["PUT /orgs/{org}/actions/permissions/selected-actions"],setAllowedActionsRepository:["PUT /repos/{owner}/{repo}/actions/permissions/selected-actions"],setCustomLabelsForSelfHostedRunnerForOrg:["PUT /orgs/{org}/actions/runners/{runner_id}/labels"],setCustomLabelsForSelfHostedRunnerForRepo:["PUT /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],setGithubActionsDefaultWorkflowPermissionsOrganization:["PUT /orgs/{org}/actions/permissions/workflow"],setGithubActionsDefaultWorkflowPermissionsRepository:["PUT /repos/{owner}/{repo}/actions/permissions/workflow"],setGithubActionsPermissionsOrganization:["PUT /orgs/{org}/actions/permissions"],setGithubActionsPermissionsRepository:["PUT /repos/{owner}/{repo}/actions/permissions"],setSelectedReposForOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories"],setSelectedReposForOrgVariable:["PUT /orgs/{org}/actions/variables/{name}/repositories"],setSelectedRepositoriesEnabledGithubActionsOrganization:["PUT /orgs/{org}/actions/permissions/repositories"],setWorkflowAccessToRepository:["PUT /repos/{owner}/{repo}/actions/permissions/access"],updateEnvironmentVariable:["PATCH /repositories/{repository_id}/environments/{environment_name}/variables/{name}"],updateOrgVariable:["PATCH /orgs/{org}/actions/variables/{name}"],updateRepoVariable:["PATCH /repos/{owner}/{repo}/actions/variables/{name}"]},activity:{checkRepoIsStarredByAuthenticatedUser:["GET /user/starred/{owner}/{repo}"],deleteRepoSubscription:["DELETE /repos/{owner}/{repo}/subscription"],deleteThreadSubscription:["DELETE /notifications/threads/{thread_id}/subscription"],getFeeds:["GET /feeds"],getRepoSubscription:["GET /repos/{owner}/{repo}/subscription"],getThread:["GET /notifications/threads/{thread_id}"],getThreadSubscriptionForAuthenticatedUser:["GET /notifications/threads/{thread_id}/subscription"],listEventsForAuthenticatedUser:["GET /users/{username}/events"],listNotificationsForAuthenticatedUser:["GET /notifications"],listOrgEventsForAuthenticatedUser:["GET /users/{username}/events/orgs/{org}"],listPublicEvents:["GET /events"],listPublicEventsForRepoNetwork:["GET /networks/{owner}/{repo}/events"],listPublicEventsForUser:["GET /users/{username}/events/public"],listPublicOrgEvents:["GET /orgs/{org}/events"],listReceivedEventsForUser:["GET /users/{username}/received_events"],listReceivedPublicEventsForUser:["GET /users/{username}/received_events/public"],listRepoEvents:["GET /repos/{owner}/{repo}/events"],listRepoNotificationsForAuthenticatedUser:["GET /repos/{owner}/{repo}/notifications"],listReposStarredByAuthenticatedUser:["GET /user/starred"],listReposStarredByUser:["GET /users/{username}/starred"],listReposWatchedByUser:["GET /users/{username}/subscriptions"],listStargazersForRepo:["GET /repos/{owner}/{repo}/stargazers"],listWatchedReposForAuthenticatedUser:["GET /user/subscriptions"],listWatchersForRepo:["GET /repos/{owner}/{repo}/subscribers"],markNotificationsAsRead:["PUT /notifications"],markRepoNotificationsAsRead:["PUT /repos/{owner}/{repo}/notifications"],markThreadAsRead:["PATCH /notifications/threads/{thread_id}"],setRepoSubscription:["PUT /repos/{owner}/{repo}/subscription"],setThreadSubscription:["PUT /notifications/threads/{thread_id}/subscription"],starRepoForAuthenticatedUser:["PUT /user/starred/{owner}/{repo}"],unstarRepoForAuthenticatedUser:["DELETE /user/starred/{owner}/{repo}"]},apps:{addRepoToInstallation:["PUT /user/installations/{installation_id}/repositories/{repository_id}",{},{renamed:["apps","addRepoToInstallationForAuthenticatedUser"]}],addRepoToInstallationForAuthenticatedUser:["PUT /user/installations/{installation_id}/repositories/{repository_id}"],checkToken:["POST /applications/{client_id}/token"],createFromManifest:["POST /app-manifests/{code}/conversions"],createInstallationAccessToken:["POST /app/installations/{installation_id}/access_tokens"],deleteAuthorization:["DELETE /applications/{client_id}/grant"],deleteInstallation:["DELETE /app/installations/{installation_id}"],deleteToken:["DELETE /applications/{client_id}/token"],getAuthenticated:["GET /app"],getBySlug:["GET /apps/{app_slug}"],getInstallation:["GET /app/installations/{installation_id}"],getOrgInstallation:["GET /orgs/{org}/installation"],getRepoInstallation:["GET /repos/{owner}/{repo}/installation"],getSubscriptionPlanForAccount:["GET /marketplace_listing/accounts/{account_id}"],getSubscriptionPlanForAccountStubbed:["GET /marketplace_listing/stubbed/accounts/{account_id}"],getUserInstallation:["GET /users/{username}/installation"],getWebhookConfigForApp:["GET /app/hook/config"],getWebhookDelivery:["GET /app/hook/deliveries/{delivery_id}"],listAccountsForPlan:["GET /marketplace_listing/plans/{plan_id}/accounts"],listAccountsForPlanStubbed:["GET /marketplace_listing/stubbed/plans/{plan_id}/accounts"],listInstallationReposForAuthenticatedUser:["GET /user/installations/{installation_id}/repositories"],listInstallationRequestsForAuthenticatedApp:["GET /app/installation-requests"],listInstallations:["GET /app/installations"],listInstallationsForAuthenticatedUser:["GET /user/installations"],listPlans:["GET /marketplace_listing/plans"],listPlansStubbed:["GET /marketplace_listing/stubbed/plans"],listReposAccessibleToInstallation:["GET /installation/repositories"],listSubscriptionsForAuthenticatedUser:["GET /user/marketplace_purchases"],listSubscriptionsForAuthenticatedUserStubbed:["GET /user/marketplace_purchases/stubbed"],listWebhookDeliveries:["GET /app/hook/deliveries"],redeliverWebhookDelivery:["POST /app/hook/deliveries/{delivery_id}/attempts"],removeRepoFromInstallation:["DELETE /user/installations/{installation_id}/repositories/{repository_id}",{},{renamed:["apps","removeRepoFromInstallationForAuthenticatedUser"]}],removeRepoFromInstallationForAuthenticatedUser:["DELETE /user/installations/{installation_id}/repositories/{repository_id}"],resetToken:["PATCH /applications/{client_id}/token"],revokeInstallationAccessToken:["DELETE /installation/token"],scopeToken:["POST /applications/{client_id}/token/scoped"],suspendInstallation:["PUT /app/installations/{installation_id}/suspended"],unsuspendInstallation:["DELETE /app/installations/{installation_id}/suspended"],updateWebhookConfigForApp:["PATCH /app/hook/config"]},billing:{getGithubActionsBillingOrg:["GET /orgs/{org}/settings/billing/actions"],getGithubActionsBillingUser:["GET /users/{username}/settings/billing/actions"],getGithubPackagesBillingOrg:["GET /orgs/{org}/settings/billing/packages"],getGithubPackagesBillingUser:["GET /users/{username}/settings/billing/packages"],getSharedStorageBillingOrg:["GET /orgs/{org}/settings/billing/shared-storage"],getSharedStorageBillingUser:["GET /users/{username}/settings/billing/shared-storage"]},checks:{create:["POST /repos/{owner}/{repo}/check-runs"],createSuite:["POST /repos/{owner}/{repo}/check-suites"],get:["GET /repos/{owner}/{repo}/check-runs/{check_run_id}"],getSuite:["GET /repos/{owner}/{repo}/check-suites/{check_suite_id}"],listAnnotations:["GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations"],listForRef:["GET /repos/{owner}/{repo}/commits/{ref}/check-runs"],listForSuite:["GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs"],listSuitesForRef:["GET /repos/{owner}/{repo}/commits/{ref}/check-suites"],rerequestRun:["POST /repos/{owner}/{repo}/check-runs/{check_run_id}/rerequest"],rerequestSuite:["POST /repos/{owner}/{repo}/check-suites/{check_suite_id}/rerequest"],setSuitesPreferences:["PATCH /repos/{owner}/{repo}/check-suites/preferences"],update:["PATCH /repos/{owner}/{repo}/check-runs/{check_run_id}"]},codeScanning:{deleteAnalysis:["DELETE /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}{?confirm_delete}"],getAlert:["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}",{},{renamedParameters:{alert_id:"alert_number"}}],getAnalysis:["GET /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}"],getCodeqlDatabase:["GET /repos/{owner}/{repo}/code-scanning/codeql/databases/{language}"],getDefaultSetup:["GET /repos/{owner}/{repo}/code-scanning/default-setup"],getSarif:["GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id}"],listAlertInstances:["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances"],listAlertsForOrg:["GET /orgs/{org}/code-scanning/alerts"],listAlertsForRepo:["GET /repos/{owner}/{repo}/code-scanning/alerts"],listAlertsInstances:["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances",{},{renamed:["codeScanning","listAlertInstances"]}],listCodeqlDatabases:["GET /repos/{owner}/{repo}/code-scanning/codeql/databases"],listRecentAnalyses:["GET /repos/{owner}/{repo}/code-scanning/analyses"],updateAlert:["PATCH /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}"],updateDefaultSetup:["PATCH /repos/{owner}/{repo}/code-scanning/default-setup"],uploadSarif:["POST /repos/{owner}/{repo}/code-scanning/sarifs"]},codesOfConduct:{getAllCodesOfConduct:["GET /codes_of_conduct"],getConductCode:["GET /codes_of_conduct/{key}"]},codespaces:{addRepositoryForSecretForAuthenticatedUser:["PUT /user/codespaces/secrets/{secret_name}/repositories/{repository_id}"],addSelectedRepoToOrgSecret:["PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id}"],checkPermissionsForDevcontainer:["GET /repos/{owner}/{repo}/codespaces/permissions_check"],codespaceMachinesForAuthenticatedUser:["GET /user/codespaces/{codespace_name}/machines"],createForAuthenticatedUser:["POST /user/codespaces"],createOrUpdateOrgSecret:["PUT /orgs/{org}/codespaces/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"],createOrUpdateSecretForAuthenticatedUser:["PUT /user/codespaces/secrets/{secret_name}"],createWithPrForAuthenticatedUser:["POST /repos/{owner}/{repo}/pulls/{pull_number}/codespaces"],createWithRepoForAuthenticatedUser:["POST /repos/{owner}/{repo}/codespaces"],deleteForAuthenticatedUser:["DELETE /user/codespaces/{codespace_name}"],deleteFromOrganization:["DELETE /orgs/{org}/members/{username}/codespaces/{codespace_name}"],deleteOrgSecret:["DELETE /orgs/{org}/codespaces/secrets/{secret_name}"],deleteRepoSecret:["DELETE /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"],deleteSecretForAuthenticatedUser:["DELETE /user/codespaces/secrets/{secret_name}"],exportForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/exports"],getCodespacesForUserInOrg:["GET /orgs/{org}/members/{username}/codespaces"],getExportDetailsForAuthenticatedUser:["GET /user/codespaces/{codespace_name}/exports/{export_id}"],getForAuthenticatedUser:["GET /user/codespaces/{codespace_name}"],getOrgPublicKey:["GET /orgs/{org}/codespaces/secrets/public-key"],getOrgSecret:["GET /orgs/{org}/codespaces/secrets/{secret_name}"],getPublicKeyForAuthenticatedUser:["GET /user/codespaces/secrets/public-key"],getRepoPublicKey:["GET /repos/{owner}/{repo}/codespaces/secrets/public-key"],getRepoSecret:["GET /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"],getSecretForAuthenticatedUser:["GET /user/codespaces/secrets/{secret_name}"],listDevcontainersInRepositoryForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces/devcontainers"],listForAuthenticatedUser:["GET /user/codespaces"],listInOrganization:["GET /orgs/{org}/codespaces",{},{renamedParameters:{org_id:"org"}}],listInRepositoryForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces"],listOrgSecrets:["GET /orgs/{org}/codespaces/secrets"],listRepoSecrets:["GET /repos/{owner}/{repo}/codespaces/secrets"],listRepositoriesForSecretForAuthenticatedUser:["GET /user/codespaces/secrets/{secret_name}/repositories"],listSecretsForAuthenticatedUser:["GET /user/codespaces/secrets"],listSelectedReposForOrgSecret:["GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories"],preFlightWithRepoForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces/new"],publishForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/publish"],removeRepositoryForSecretForAuthenticatedUser:["DELETE /user/codespaces/secrets/{secret_name}/repositories/{repository_id}"],removeSelectedRepoFromOrgSecret:["DELETE /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id}"],repoMachinesForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces/machines"],setRepositoriesForSecretForAuthenticatedUser:["PUT /user/codespaces/secrets/{secret_name}/repositories"],setSelectedReposForOrgSecret:["PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories"],startForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/start"],stopForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/stop"],stopInOrganization:["POST /orgs/{org}/members/{username}/codespaces/{codespace_name}/stop"],updateForAuthenticatedUser:["PATCH /user/codespaces/{codespace_name}"]},copilot:{addCopilotForBusinessSeatsForTeams:["POST /orgs/{org}/copilot/billing/selected_teams"],addCopilotForBusinessSeatsForUsers:["POST /orgs/{org}/copilot/billing/selected_users"],cancelCopilotSeatAssignmentForTeams:["DELETE /orgs/{org}/copilot/billing/selected_teams"],cancelCopilotSeatAssignmentForUsers:["DELETE /orgs/{org}/copilot/billing/selected_users"],getCopilotOrganizationDetails:["GET /orgs/{org}/copilot/billing"],getCopilotSeatDetailsForUser:["GET /orgs/{org}/members/{username}/copilot"],listCopilotSeats:["GET /orgs/{org}/copilot/billing/seats"]},dependabot:{addSelectedRepoToOrgSecret:["PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}"],createOrUpdateOrgSecret:["PUT /orgs/{org}/dependabot/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"],deleteOrgSecret:["DELETE /orgs/{org}/dependabot/secrets/{secret_name}"],deleteRepoSecret:["DELETE /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"],getAlert:["GET /repos/{owner}/{repo}/dependabot/alerts/{alert_number}"],getOrgPublicKey:["GET /orgs/{org}/dependabot/secrets/public-key"],getOrgSecret:["GET /orgs/{org}/dependabot/secrets/{secret_name}"],getRepoPublicKey:["GET /repos/{owner}/{repo}/dependabot/secrets/public-key"],getRepoSecret:["GET /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"],listAlertsForEnterprise:["GET /enterprises/{enterprise}/dependabot/alerts"],listAlertsForOrg:["GET /orgs/{org}/dependabot/alerts"],listAlertsForRepo:["GET /repos/{owner}/{repo}/dependabot/alerts"],listOrgSecrets:["GET /orgs/{org}/dependabot/secrets"],listRepoSecrets:["GET /repos/{owner}/{repo}/dependabot/secrets"],listSelectedReposForOrgSecret:["GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories"],removeSelectedRepoFromOrgSecret:["DELETE /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}"],setSelectedReposForOrgSecret:["PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories"],updateAlert:["PATCH /repos/{owner}/{repo}/dependabot/alerts/{alert_number}"]},dependencyGraph:{createRepositorySnapshot:["POST /repos/{owner}/{repo}/dependency-graph/snapshots"],diffRange:["GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead}"],exportSbom:["GET /repos/{owner}/{repo}/dependency-graph/sbom"]},emojis:{get:["GET /emojis"]},gists:{checkIsStarred:["GET /gists/{gist_id}/star"],create:["POST /gists"],createComment:["POST /gists/{gist_id}/comments"],delete:["DELETE /gists/{gist_id}"],deleteComment:["DELETE /gists/{gist_id}/comments/{comment_id}"],fork:["POST /gists/{gist_id}/forks"],get:["GET /gists/{gist_id}"],getComment:["GET /gists/{gist_id}/comments/{comment_id}"],getRevision:["GET /gists/{gist_id}/{sha}"],list:["GET /gists"],listComments:["GET /gists/{gist_id}/comments"],listCommits:["GET /gists/{gist_id}/commits"],listForUser:["GET /users/{username}/gists"],listForks:["GET /gists/{gist_id}/forks"],listPublic:["GET /gists/public"],listStarred:["GET /gists/starred"],star:["PUT /gists/{gist_id}/star"],unstar:["DELETE /gists/{gist_id}/star"],update:["PATCH /gists/{gist_id}"],updateComment:["PATCH /gists/{gist_id}/comments/{comment_id}"]},git:{createBlob:["POST /repos/{owner}/{repo}/git/blobs"],createCommit:["POST /repos/{owner}/{repo}/git/commits"],createRef:["POST /repos/{owner}/{repo}/git/refs"],createTag:["POST /repos/{owner}/{repo}/git/tags"],createTree:["POST /repos/{owner}/{repo}/git/trees"],deleteRef:["DELETE /repos/{owner}/{repo}/git/refs/{ref}"],getBlob:["GET /repos/{owner}/{repo}/git/blobs/{file_sha}"],getCommit:["GET /repos/{owner}/{repo}/git/commits/{commit_sha}"],getRef:["GET /repos/{owner}/{repo}/git/ref/{ref}"],getTag:["GET /repos/{owner}/{repo}/git/tags/{tag_sha}"],getTree:["GET /repos/{owner}/{repo}/git/trees/{tree_sha}"],listMatchingRefs:["GET /repos/{owner}/{repo}/git/matching-refs/{ref}"],updateRef:["PATCH /repos/{owner}/{repo}/git/refs/{ref}"]},gitignore:{getAllTemplates:["GET /gitignore/templates"],getTemplate:["GET /gitignore/templates/{name}"]},interactions:{getRestrictionsForAuthenticatedUser:["GET /user/interaction-limits"],getRestrictionsForOrg:["GET /orgs/{org}/interaction-limits"],getRestrictionsForRepo:["GET /repos/{owner}/{repo}/interaction-limits"],getRestrictionsForYourPublicRepos:["GET /user/interaction-limits",{},{renamed:["interactions","getRestrictionsForAuthenticatedUser"]}],removeRestrictionsForAuthenticatedUser:["DELETE /user/interaction-limits"],removeRestrictionsForOrg:["DELETE /orgs/{org}/interaction-limits"],removeRestrictionsForRepo:["DELETE /repos/{owner}/{repo}/interaction-limits"],removeRestrictionsForYourPublicRepos:["DELETE /user/interaction-limits",{},{renamed:["interactions","removeRestrictionsForAuthenticatedUser"]}],setRestrictionsForAuthenticatedUser:["PUT /user/interaction-limits"],setRestrictionsForOrg:["PUT /orgs/{org}/interaction-limits"],setRestrictionsForRepo:["PUT /repos/{owner}/{repo}/interaction-limits"],setRestrictionsForYourPublicRepos:["PUT /user/interaction-limits",{},{renamed:["interactions","setRestrictionsForAuthenticatedUser"]}]},issues:{addAssignees:["POST /repos/{owner}/{repo}/issues/{issue_number}/assignees"],addLabels:["POST /repos/{owner}/{repo}/issues/{issue_number}/labels"],checkUserCanBeAssigned:["GET /repos/{owner}/{repo}/assignees/{assignee}"],checkUserCanBeAssignedToIssue:["GET /repos/{owner}/{repo}/issues/{issue_number}/assignees/{assignee}"],create:["POST /repos/{owner}/{repo}/issues"],createComment:["POST /repos/{owner}/{repo}/issues/{issue_number}/comments"],createLabel:["POST /repos/{owner}/{repo}/labels"],createMilestone:["POST /repos/{owner}/{repo}/milestones"],deleteComment:["DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}"],deleteLabel:["DELETE /repos/{owner}/{repo}/labels/{name}"],deleteMilestone:["DELETE /repos/{owner}/{repo}/milestones/{milestone_number}"],get:["GET /repos/{owner}/{repo}/issues/{issue_number}"],getComment:["GET /repos/{owner}/{repo}/issues/comments/{comment_id}"],getEvent:["GET /repos/{owner}/{repo}/issues/events/{event_id}"],getLabel:["GET /repos/{owner}/{repo}/labels/{name}"],getMilestone:["GET /repos/{owner}/{repo}/milestones/{milestone_number}"],list:["GET /issues"],listAssignees:["GET /repos/{owner}/{repo}/assignees"],listComments:["GET /repos/{owner}/{repo}/issues/{issue_number}/comments"],listCommentsForRepo:["GET /repos/{owner}/{repo}/issues/comments"],listEvents:["GET /repos/{owner}/{repo}/issues/{issue_number}/events"],listEventsForRepo:["GET /repos/{owner}/{repo}/issues/events"],listEventsForTimeline:["GET /repos/{owner}/{repo}/issues/{issue_number}/timeline"],listForAuthenticatedUser:["GET /user/issues"],listForOrg:["GET /orgs/{org}/issues"],listForRepo:["GET /repos/{owner}/{repo}/issues"],listLabelsForMilestone:["GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels"],listLabelsForRepo:["GET /repos/{owner}/{repo}/labels"],listLabelsOnIssue:["GET /repos/{owner}/{repo}/issues/{issue_number}/labels"],listMilestones:["GET /repos/{owner}/{repo}/milestones"],lock:["PUT /repos/{owner}/{repo}/issues/{issue_number}/lock"],removeAllLabels:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels"],removeAssignees:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees"],removeLabel:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name}"],setLabels:["PUT /repos/{owner}/{repo}/issues/{issue_number}/labels"],unlock:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/lock"],update:["PATCH /repos/{owner}/{repo}/issues/{issue_number}"],updateComment:["PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}"],updateLabel:["PATCH /repos/{owner}/{repo}/labels/{name}"],updateMilestone:["PATCH /repos/{owner}/{repo}/milestones/{milestone_number}"]},licenses:{get:["GET /licenses/{license}"],getAllCommonlyUsed:["GET /licenses"],getForRepo:["GET /repos/{owner}/{repo}/license"]},markdown:{render:["POST /markdown"],renderRaw:["POST /markdown/raw",{headers:{"content-type":"text/plain; charset=utf-8"}}]},meta:{get:["GET /meta"],getAllVersions:["GET /versions"],getOctocat:["GET /octocat"],getZen:["GET /zen"],root:["GET /"]},migrations:{cancelImport:["DELETE /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.cancelImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#cancel-an-import"}],deleteArchiveForAuthenticatedUser:["DELETE /user/migrations/{migration_id}/archive"],deleteArchiveForOrg:["DELETE /orgs/{org}/migrations/{migration_id}/archive"],downloadArchiveForOrg:["GET /orgs/{org}/migrations/{migration_id}/archive"],getArchiveForAuthenticatedUser:["GET /user/migrations/{migration_id}/archive"],getCommitAuthors:["GET /repos/{owner}/{repo}/import/authors",{},{deprecated:"octokit.rest.migrations.getCommitAuthors() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-commit-authors"}],getImportStatus:["GET /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.getImportStatus() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-an-import-status"}],getLargeFiles:["GET /repos/{owner}/{repo}/import/large_files",{},{deprecated:"octokit.rest.migrations.getLargeFiles() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-large-files"}],getStatusForAuthenticatedUser:["GET /user/migrations/{migration_id}"],getStatusForOrg:["GET /orgs/{org}/migrations/{migration_id}"],listForAuthenticatedUser:["GET /user/migrations"],listForOrg:["GET /orgs/{org}/migrations"],listReposForAuthenticatedUser:["GET /user/migrations/{migration_id}/repositories"],listReposForOrg:["GET /orgs/{org}/migrations/{migration_id}/repositories"],listReposForUser:["GET /user/migrations/{migration_id}/repositories",{},{renamed:["migrations","listReposForAuthenticatedUser"]}],mapCommitAuthor:["PATCH /repos/{owner}/{repo}/import/authors/{author_id}",{},{deprecated:"octokit.rest.migrations.mapCommitAuthor() is deprecated, see https://docs.github.com/rest/migrations/source-imports#map-a-commit-author"}],setLfsPreference:["PATCH /repos/{owner}/{repo}/import/lfs",{},{deprecated:"octokit.rest.migrations.setLfsPreference() is deprecated, see https://docs.github.com/rest/migrations/source-imports#update-git-lfs-preference"}],startForAuthenticatedUser:["POST /user/migrations"],startForOrg:["POST /orgs/{org}/migrations"],startImport:["PUT /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.startImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#start-an-import"}],unlockRepoForAuthenticatedUser:["DELETE /user/migrations/{migration_id}/repos/{repo_name}/lock"],unlockRepoForOrg:["DELETE /orgs/{org}/migrations/{migration_id}/repos/{repo_name}/lock"],updateImport:["PATCH /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.updateImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#update-an-import"}]},orgs:{addSecurityManagerTeam:["PUT /orgs/{org}/security-managers/teams/{team_slug}"],blockUser:["PUT /orgs/{org}/blocks/{username}"],cancelInvitation:["DELETE /orgs/{org}/invitations/{invitation_id}"],checkBlockedUser:["GET /orgs/{org}/blocks/{username}"],checkMembershipForUser:["GET /orgs/{org}/members/{username}"],checkPublicMembershipForUser:["GET /orgs/{org}/public_members/{username}"],convertMemberToOutsideCollaborator:["PUT /orgs/{org}/outside_collaborators/{username}"],createInvitation:["POST /orgs/{org}/invitations"],createOrUpdateCustomProperties:["PATCH /orgs/{org}/properties/schema"],createOrUpdateCustomPropertiesValuesForRepos:["PATCH /orgs/{org}/properties/values"],createOrUpdateCustomProperty:["PUT /orgs/{org}/properties/schema/{custom_property_name}"],createWebhook:["POST /orgs/{org}/hooks"],delete:["DELETE /orgs/{org}"],deleteWebhook:["DELETE /orgs/{org}/hooks/{hook_id}"],enableOrDisableSecurityProductOnAllOrgRepos:["POST /orgs/{org}/{security_product}/{enablement}"],get:["GET /orgs/{org}"],getAllCustomProperties:["GET /orgs/{org}/properties/schema"],getCustomProperty:["GET /orgs/{org}/properties/schema/{custom_property_name}"],getMembershipForAuthenticatedUser:["GET /user/memberships/orgs/{org}"],getMembershipForUser:["GET /orgs/{org}/memberships/{username}"],getWebhook:["GET /orgs/{org}/hooks/{hook_id}"],getWebhookConfigForOrg:["GET /orgs/{org}/hooks/{hook_id}/config"],getWebhookDelivery:["GET /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}"],list:["GET /organizations"],listAppInstallations:["GET /orgs/{org}/installations"],listBlockedUsers:["GET /orgs/{org}/blocks"],listCustomPropertiesValuesForRepos:["GET /orgs/{org}/properties/values"],listFailedInvitations:["GET /orgs/{org}/failed_invitations"],listForAuthenticatedUser:["GET /user/orgs"],listForUser:["GET /users/{username}/orgs"],listInvitationTeams:["GET /orgs/{org}/invitations/{invitation_id}/teams"],listMembers:["GET /orgs/{org}/members"],listMembershipsForAuthenticatedUser:["GET /user/memberships/orgs"],listOutsideCollaborators:["GET /orgs/{org}/outside_collaborators"],listPatGrantRepositories:["GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories"],listPatGrantRequestRepositories:["GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories"],listPatGrantRequests:["GET /orgs/{org}/personal-access-token-requests"],listPatGrants:["GET /orgs/{org}/personal-access-tokens"],listPendingInvitations:["GET /orgs/{org}/invitations"],listPublicMembers:["GET /orgs/{org}/public_members"],listSecurityManagerTeams:["GET /orgs/{org}/security-managers"],listWebhookDeliveries:["GET /orgs/{org}/hooks/{hook_id}/deliveries"],listWebhooks:["GET /orgs/{org}/hooks"],pingWebhook:["POST /orgs/{org}/hooks/{hook_id}/pings"],redeliverWebhookDelivery:["POST /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts"],removeCustomProperty:["DELETE /orgs/{org}/properties/schema/{custom_property_name}"],removeMember:["DELETE /orgs/{org}/members/{username}"],removeMembershipForUser:["DELETE /orgs/{org}/memberships/{username}"],removeOutsideCollaborator:["DELETE /orgs/{org}/outside_collaborators/{username}"],removePublicMembershipForAuthenticatedUser:["DELETE /orgs/{org}/public_members/{username}"],removeSecurityManagerTeam:["DELETE /orgs/{org}/security-managers/teams/{team_slug}"],reviewPatGrantRequest:["POST /orgs/{org}/personal-access-token-requests/{pat_request_id}"],reviewPatGrantRequestsInBulk:["POST /orgs/{org}/personal-access-token-requests"],setMembershipForUser:["PUT /orgs/{org}/memberships/{username}"],setPublicMembershipForAuthenticatedUser:["PUT /orgs/{org}/public_members/{username}"],unblockUser:["DELETE /orgs/{org}/blocks/{username}"],update:["PATCH /orgs/{org}"],updateMembershipForAuthenticatedUser:["PATCH /user/memberships/orgs/{org}"],updatePatAccess:["POST /orgs/{org}/personal-access-tokens/{pat_id}"],updatePatAccesses:["POST /orgs/{org}/personal-access-tokens"],updateWebhook:["PATCH /orgs/{org}/hooks/{hook_id}"],updateWebhookConfigForOrg:["PATCH /orgs/{org}/hooks/{hook_id}/config"]},packages:{deletePackageForAuthenticatedUser:["DELETE /user/packages/{package_type}/{package_name}"],deletePackageForOrg:["DELETE /orgs/{org}/packages/{package_type}/{package_name}"],deletePackageForUser:["DELETE /users/{username}/packages/{package_type}/{package_name}"],deletePackageVersionForAuthenticatedUser:["DELETE /user/packages/{package_type}/{package_name}/versions/{package_version_id}"],deletePackageVersionForOrg:["DELETE /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}"],deletePackageVersionForUser:["DELETE /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}"],getAllPackageVersionsForAPackageOwnedByAnOrg:["GET /orgs/{org}/packages/{package_type}/{package_name}/versions",{},{renamed:["packages","getAllPackageVersionsForPackageOwnedByOrg"]}],getAllPackageVersionsForAPackageOwnedByTheAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}/versions",{},{renamed:["packages","getAllPackageVersionsForPackageOwnedByAuthenticatedUser"]}],getAllPackageVersionsForPackageOwnedByAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}/versions"],getAllPackageVersionsForPackageOwnedByOrg:["GET /orgs/{org}/packages/{package_type}/{package_name}/versions"],getAllPackageVersionsForPackageOwnedByUser:["GET /users/{username}/packages/{package_type}/{package_name}/versions"],getPackageForAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}"],getPackageForOrganization:["GET /orgs/{org}/packages/{package_type}/{package_name}"],getPackageForUser:["GET /users/{username}/packages/{package_type}/{package_name}"],getPackageVersionForAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}/versions/{package_version_id}"],getPackageVersionForOrganization:["GET /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}"],getPackageVersionForUser:["GET /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}"],listDockerMigrationConflictingPackagesForAuthenticatedUser:["GET /user/docker/conflicts"],listDockerMigrationConflictingPackagesForOrganization:["GET /orgs/{org}/docker/conflicts"],listDockerMigrationConflictingPackagesForUser:["GET /users/{username}/docker/conflicts"],listPackagesForAuthenticatedUser:["GET /user/packages"],listPackagesForOrganization:["GET /orgs/{org}/packages"],listPackagesForUser:["GET /users/{username}/packages"],restorePackageForAuthenticatedUser:["POST /user/packages/{package_type}/{package_name}/restore{?token}"],restorePackageForOrg:["POST /orgs/{org}/packages/{package_type}/{package_name}/restore{?token}"],restorePackageForUser:["POST /users/{username}/packages/{package_type}/{package_name}/restore{?token}"],restorePackageVersionForAuthenticatedUser:["POST /user/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"],restorePackageVersionForOrg:["POST /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"],restorePackageVersionForUser:["POST /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"]},projects:{addCollaborator:["PUT /projects/{project_id}/collaborators/{username}"],createCard:["POST /projects/columns/{column_id}/cards"],createColumn:["POST /projects/{project_id}/columns"],createForAuthenticatedUser:["POST /user/projects"],createForOrg:["POST /orgs/{org}/projects"],createForRepo:["POST /repos/{owner}/{repo}/projects"],delete:["DELETE /projects/{project_id}"],deleteCard:["DELETE /projects/columns/cards/{card_id}"],deleteColumn:["DELETE /projects/columns/{column_id}"],get:["GET /projects/{project_id}"],getCard:["GET /projects/columns/cards/{card_id}"],getColumn:["GET /projects/columns/{column_id}"],getPermissionForUser:["GET /projects/{project_id}/collaborators/{username}/permission"],listCards:["GET /projects/columns/{column_id}/cards"],listCollaborators:["GET /projects/{project_id}/collaborators"],listColumns:["GET /projects/{project_id}/columns"],listForOrg:["GET /orgs/{org}/projects"],listForRepo:["GET /repos/{owner}/{repo}/projects"],listForUser:["GET /users/{username}/projects"],moveCard:["POST /projects/columns/cards/{card_id}/moves"],moveColumn:["POST /projects/columns/{column_id}/moves"],removeCollaborator:["DELETE /projects/{project_id}/collaborators/{username}"],update:["PATCH /projects/{project_id}"],updateCard:["PATCH /projects/columns/cards/{card_id}"],updateColumn:["PATCH /projects/columns/{column_id}"]},pulls:{checkIfMerged:["GET /repos/{owner}/{repo}/pulls/{pull_number}/merge"],create:["POST /repos/{owner}/{repo}/pulls"],createReplyForReviewComment:["POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies"],createReview:["POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews"],createReviewComment:["POST /repos/{owner}/{repo}/pulls/{pull_number}/comments"],deletePendingReview:["DELETE /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"],deleteReviewComment:["DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}"],dismissReview:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals"],get:["GET /repos/{owner}/{repo}/pulls/{pull_number}"],getReview:["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"],getReviewComment:["GET /repos/{owner}/{repo}/pulls/comments/{comment_id}"],list:["GET /repos/{owner}/{repo}/pulls"],listCommentsForReview:["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments"],listCommits:["GET /repos/{owner}/{repo}/pulls/{pull_number}/commits"],listFiles:["GET /repos/{owner}/{repo}/pulls/{pull_number}/files"],listRequestedReviewers:["GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"],listReviewComments:["GET /repos/{owner}/{repo}/pulls/{pull_number}/comments"],listReviewCommentsForRepo:["GET /repos/{owner}/{repo}/pulls/comments"],listReviews:["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews"],merge:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge"],removeRequestedReviewers:["DELETE /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"],requestReviewers:["POST /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"],submitReview:["POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events"],update:["PATCH /repos/{owner}/{repo}/pulls/{pull_number}"],updateBranch:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch"],updateReview:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"],updateReviewComment:["PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id}"]},rateLimit:{get:["GET /rate_limit"]},reactions:{createForCommitComment:["POST /repos/{owner}/{repo}/comments/{comment_id}/reactions"],createForIssue:["POST /repos/{owner}/{repo}/issues/{issue_number}/reactions"],createForIssueComment:["POST /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions"],createForPullRequestReviewComment:["POST /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions"],createForRelease:["POST /repos/{owner}/{repo}/releases/{release_id}/reactions"],createForTeamDiscussionCommentInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions"],createForTeamDiscussionInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions"],deleteForCommitComment:["DELETE /repos/{owner}/{repo}/comments/{comment_id}/reactions/{reaction_id}"],deleteForIssue:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id}"],deleteForIssueComment:["DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id}"],deleteForPullRequestComment:["DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id}"],deleteForRelease:["DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id}"],deleteForTeamDiscussion:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions/{reaction_id}"],deleteForTeamDiscussionComment:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions/{reaction_id}"],listForCommitComment:["GET /repos/{owner}/{repo}/comments/{comment_id}/reactions"],listForIssue:["GET /repos/{owner}/{repo}/issues/{issue_number}/reactions"],listForIssueComment:["GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions"],listForPullRequestReviewComment:["GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions"],listForRelease:["GET /repos/{owner}/{repo}/releases/{release_id}/reactions"],listForTeamDiscussionCommentInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions"],listForTeamDiscussionInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions"]},repos:{acceptInvitation:["PATCH /user/repository_invitations/{invitation_id}",{},{renamed:["repos","acceptInvitationForAuthenticatedUser"]}],acceptInvitationForAuthenticatedUser:["PATCH /user/repository_invitations/{invitation_id}"],addAppAccessRestrictions:["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps",{},{mapToData:"apps"}],addCollaborator:["PUT /repos/{owner}/{repo}/collaborators/{username}"],addStatusCheckContexts:["POST /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts",{},{mapToData:"contexts"}],addTeamAccessRestrictions:["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams",{},{mapToData:"teams"}],addUserAccessRestrictions:["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users",{},{mapToData:"users"}],checkAutomatedSecurityFixes:["GET /repos/{owner}/{repo}/automated-security-fixes"],checkCollaborator:["GET /repos/{owner}/{repo}/collaborators/{username}"],checkVulnerabilityAlerts:["GET /repos/{owner}/{repo}/vulnerability-alerts"],codeownersErrors:["GET /repos/{owner}/{repo}/codeowners/errors"],compareCommits:["GET /repos/{owner}/{repo}/compare/{base}...{head}"],compareCommitsWithBasehead:["GET /repos/{owner}/{repo}/compare/{basehead}"],createAutolink:["POST /repos/{owner}/{repo}/autolinks"],createCommitComment:["POST /repos/{owner}/{repo}/commits/{commit_sha}/comments"],createCommitSignatureProtection:["POST /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"],createCommitStatus:["POST /repos/{owner}/{repo}/statuses/{sha}"],createDeployKey:["POST /repos/{owner}/{repo}/keys"],createDeployment:["POST /repos/{owner}/{repo}/deployments"],createDeploymentBranchPolicy:["POST /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies"],createDeploymentProtectionRule:["POST /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules"],createDeploymentStatus:["POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses"],createDispatchEvent:["POST /repos/{owner}/{repo}/dispatches"],createForAuthenticatedUser:["POST /user/repos"],createFork:["POST /repos/{owner}/{repo}/forks"],createInOrg:["POST /orgs/{org}/repos"],createOrUpdateEnvironment:["PUT /repos/{owner}/{repo}/environments/{environment_name}"],createOrUpdateFileContents:["PUT /repos/{owner}/{repo}/contents/{path}"],createOrgRuleset:["POST /orgs/{org}/rulesets"],createPagesDeployment:["POST /repos/{owner}/{repo}/pages/deployment"],createPagesSite:["POST /repos/{owner}/{repo}/pages"],createRelease:["POST /repos/{owner}/{repo}/releases"],createRepoRuleset:["POST /repos/{owner}/{repo}/rulesets"],createTagProtection:["POST /repos/{owner}/{repo}/tags/protection"],createUsingTemplate:["POST /repos/{template_owner}/{template_repo}/generate"],createWebhook:["POST /repos/{owner}/{repo}/hooks"],declineInvitation:["DELETE /user/repository_invitations/{invitation_id}",{},{renamed:["repos","declineInvitationForAuthenticatedUser"]}],declineInvitationForAuthenticatedUser:["DELETE /user/repository_invitations/{invitation_id}"],delete:["DELETE /repos/{owner}/{repo}"],deleteAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions"],deleteAdminBranchProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"],deleteAnEnvironment:["DELETE /repos/{owner}/{repo}/environments/{environment_name}"],deleteAutolink:["DELETE /repos/{owner}/{repo}/autolinks/{autolink_id}"],deleteBranchProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection"],deleteCommitComment:["DELETE /repos/{owner}/{repo}/comments/{comment_id}"],deleteCommitSignatureProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"],deleteDeployKey:["DELETE /repos/{owner}/{repo}/keys/{key_id}"],deleteDeployment:["DELETE /repos/{owner}/{repo}/deployments/{deployment_id}"],deleteDeploymentBranchPolicy:["DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}"],deleteFile:["DELETE /repos/{owner}/{repo}/contents/{path}"],deleteInvitation:["DELETE /repos/{owner}/{repo}/invitations/{invitation_id}"],deleteOrgRuleset:["DELETE /orgs/{org}/rulesets/{ruleset_id}"],deletePagesSite:["DELETE /repos/{owner}/{repo}/pages"],deletePullRequestReviewProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"],deleteRelease:["DELETE /repos/{owner}/{repo}/releases/{release_id}"],deleteReleaseAsset:["DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}"],deleteRepoRuleset:["DELETE /repos/{owner}/{repo}/rulesets/{ruleset_id}"],deleteTagProtection:["DELETE /repos/{owner}/{repo}/tags/protection/{tag_protection_id}"],deleteWebhook:["DELETE /repos/{owner}/{repo}/hooks/{hook_id}"],disableAutomatedSecurityFixes:["DELETE /repos/{owner}/{repo}/automated-security-fixes"],disableDeploymentProtectionRule:["DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}"],disablePrivateVulnerabilityReporting:["DELETE /repos/{owner}/{repo}/private-vulnerability-reporting"],disableVulnerabilityAlerts:["DELETE /repos/{owner}/{repo}/vulnerability-alerts"],downloadArchive:["GET /repos/{owner}/{repo}/zipball/{ref}",{},{renamed:["repos","downloadZipballArchive"]}],downloadTarballArchive:["GET /repos/{owner}/{repo}/tarball/{ref}"],downloadZipballArchive:["GET /repos/{owner}/{repo}/zipball/{ref}"],enableAutomatedSecurityFixes:["PUT /repos/{owner}/{repo}/automated-security-fixes"],enablePrivateVulnerabilityReporting:["PUT /repos/{owner}/{repo}/private-vulnerability-reporting"],enableVulnerabilityAlerts:["PUT /repos/{owner}/{repo}/vulnerability-alerts"],generateReleaseNotes:["POST /repos/{owner}/{repo}/releases/generate-notes"],get:["GET /repos/{owner}/{repo}"],getAccessRestrictions:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions"],getAdminBranchProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"],getAllDeploymentProtectionRules:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules"],getAllEnvironments:["GET /repos/{owner}/{repo}/environments"],getAllStatusCheckContexts:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts"],getAllTopics:["GET /repos/{owner}/{repo}/topics"],getAppsWithAccessToProtectedBranch:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps"],getAutolink:["GET /repos/{owner}/{repo}/autolinks/{autolink_id}"],getBranch:["GET /repos/{owner}/{repo}/branches/{branch}"],getBranchProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection"],getBranchRules:["GET /repos/{owner}/{repo}/rules/branches/{branch}"],getClones:["GET /repos/{owner}/{repo}/traffic/clones"],getCodeFrequencyStats:["GET /repos/{owner}/{repo}/stats/code_frequency"],getCollaboratorPermissionLevel:["GET /repos/{owner}/{repo}/collaborators/{username}/permission"],getCombinedStatusForRef:["GET /repos/{owner}/{repo}/commits/{ref}/status"],getCommit:["GET /repos/{owner}/{repo}/commits/{ref}"],getCommitActivityStats:["GET /repos/{owner}/{repo}/stats/commit_activity"],getCommitComment:["GET /repos/{owner}/{repo}/comments/{comment_id}"],getCommitSignatureProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"],getCommunityProfileMetrics:["GET /repos/{owner}/{repo}/community/profile"],getContent:["GET /repos/{owner}/{repo}/contents/{path}"],getContributorsStats:["GET /repos/{owner}/{repo}/stats/contributors"],getCustomDeploymentProtectionRule:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}"],getCustomPropertiesValues:["GET /repos/{owner}/{repo}/properties/values"],getDeployKey:["GET /repos/{owner}/{repo}/keys/{key_id}"],getDeployment:["GET /repos/{owner}/{repo}/deployments/{deployment_id}"],getDeploymentBranchPolicy:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}"],getDeploymentStatus:["GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses/{status_id}"],getEnvironment:["GET /repos/{owner}/{repo}/environments/{environment_name}"],getLatestPagesBuild:["GET /repos/{owner}/{repo}/pages/builds/latest"],getLatestRelease:["GET /repos/{owner}/{repo}/releases/latest"],getOrgRuleSuite:["GET /orgs/{org}/rulesets/rule-suites/{rule_suite_id}"],getOrgRuleSuites:["GET /orgs/{org}/rulesets/rule-suites"],getOrgRuleset:["GET /orgs/{org}/rulesets/{ruleset_id}"],getOrgRulesets:["GET /orgs/{org}/rulesets"],getPages:["GET /repos/{owner}/{repo}/pages"],getPagesBuild:["GET /repos/{owner}/{repo}/pages/builds/{build_id}"],getPagesHealthCheck:["GET /repos/{owner}/{repo}/pages/health"],getParticipationStats:["GET /repos/{owner}/{repo}/stats/participation"],getPullRequestReviewProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"],getPunchCardStats:["GET /repos/{owner}/{repo}/stats/punch_card"],getReadme:["GET /repos/{owner}/{repo}/readme"],getReadmeInDirectory:["GET /repos/{owner}/{repo}/readme/{dir}"],getRelease:["GET /repos/{owner}/{repo}/releases/{release_id}"],getReleaseAsset:["GET /repos/{owner}/{repo}/releases/assets/{asset_id}"],getReleaseByTag:["GET /repos/{owner}/{repo}/releases/tags/{tag}"],getRepoRuleSuite:["GET /repos/{owner}/{repo}/rulesets/rule-suites/{rule_suite_id}"],getRepoRuleSuites:["GET /repos/{owner}/{repo}/rulesets/rule-suites"],getRepoRuleset:["GET /repos/{owner}/{repo}/rulesets/{ruleset_id}"],getRepoRulesets:["GET /repos/{owner}/{repo}/rulesets"],getStatusChecksProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"],getTeamsWithAccessToProtectedBranch:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams"],getTopPaths:["GET /repos/{owner}/{repo}/traffic/popular/paths"],getTopReferrers:["GET /repos/{owner}/{repo}/traffic/popular/referrers"],getUsersWithAccessToProtectedBranch:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users"],getViews:["GET /repos/{owner}/{repo}/traffic/views"],getWebhook:["GET /repos/{owner}/{repo}/hooks/{hook_id}"],getWebhookConfigForRepo:["GET /repos/{owner}/{repo}/hooks/{hook_id}/config"],getWebhookDelivery:["GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}"],listActivities:["GET /repos/{owner}/{repo}/activity"],listAutolinks:["GET /repos/{owner}/{repo}/autolinks"],listBranches:["GET /repos/{owner}/{repo}/branches"],listBranchesForHeadCommit:["GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head"],listCollaborators:["GET /repos/{owner}/{repo}/collaborators"],listCommentsForCommit:["GET /repos/{owner}/{repo}/commits/{commit_sha}/comments"],listCommitCommentsForRepo:["GET /repos/{owner}/{repo}/comments"],listCommitStatusesForRef:["GET /repos/{owner}/{repo}/commits/{ref}/statuses"],listCommits:["GET /repos/{owner}/{repo}/commits"],listContributors:["GET /repos/{owner}/{repo}/contributors"],listCustomDeploymentRuleIntegrations:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps"],listDeployKeys:["GET /repos/{owner}/{repo}/keys"],listDeploymentBranchPolicies:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies"],listDeploymentStatuses:["GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses"],listDeployments:["GET /repos/{owner}/{repo}/deployments"],listForAuthenticatedUser:["GET /user/repos"],listForOrg:["GET /orgs/{org}/repos"],listForUser:["GET /users/{username}/repos"],listForks:["GET /repos/{owner}/{repo}/forks"],listInvitations:["GET /repos/{owner}/{repo}/invitations"],listInvitationsForAuthenticatedUser:["GET /user/repository_invitations"],listLanguages:["GET /repos/{owner}/{repo}/languages"],listPagesBuilds:["GET /repos/{owner}/{repo}/pages/builds"],listPublic:["GET /repositories"],listPullRequestsAssociatedWithCommit:["GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls"],listReleaseAssets:["GET /repos/{owner}/{repo}/releases/{release_id}/assets"],listReleases:["GET /repos/{owner}/{repo}/releases"],listTagProtection:["GET /repos/{owner}/{repo}/tags/protection"],listTags:["GET /repos/{owner}/{repo}/tags"],listTeams:["GET /repos/{owner}/{repo}/teams"],listWebhookDeliveries:["GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries"],listWebhooks:["GET /repos/{owner}/{repo}/hooks"],merge:["POST /repos/{owner}/{repo}/merges"],mergeUpstream:["POST /repos/{owner}/{repo}/merge-upstream"],pingWebhook:["POST /repos/{owner}/{repo}/hooks/{hook_id}/pings"],redeliverWebhookDelivery:["POST /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts"],removeAppAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps",{},{mapToData:"apps"}],removeCollaborator:["DELETE /repos/{owner}/{repo}/collaborators/{username}"],removeStatusCheckContexts:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts",{},{mapToData:"contexts"}],removeStatusCheckProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"],removeTeamAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams",{},{mapToData:"teams"}],removeUserAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users",{},{mapToData:"users"}],renameBranch:["POST /repos/{owner}/{repo}/branches/{branch}/rename"],replaceAllTopics:["PUT /repos/{owner}/{repo}/topics"],requestPagesBuild:["POST /repos/{owner}/{repo}/pages/builds"],setAdminBranchProtection:["POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"],setAppAccessRestrictions:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps",{},{mapToData:"apps"}],setStatusCheckContexts:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts",{},{mapToData:"contexts"}],setTeamAccessRestrictions:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams",{},{mapToData:"teams"}],setUserAccessRestrictions:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users",{},{mapToData:"users"}],testPushWebhook:["POST /repos/{owner}/{repo}/hooks/{hook_id}/tests"],transfer:["POST /repos/{owner}/{repo}/transfer"],update:["PATCH /repos/{owner}/{repo}"],updateBranchProtection:["PUT /repos/{owner}/{repo}/branches/{branch}/protection"],updateCommitComment:["PATCH /repos/{owner}/{repo}/comments/{comment_id}"],updateDeploymentBranchPolicy:["PUT /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}"],updateInformationAboutPagesSite:["PUT /repos/{owner}/{repo}/pages"],updateInvitation:["PATCH /repos/{owner}/{repo}/invitations/{invitation_id}"],updateOrgRuleset:["PUT /orgs/{org}/rulesets/{ruleset_id}"],updatePullRequestReviewProtection:["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"],updateRelease:["PATCH /repos/{owner}/{repo}/releases/{release_id}"],updateReleaseAsset:["PATCH /repos/{owner}/{repo}/releases/assets/{asset_id}"],updateRepoRuleset:["PUT /repos/{owner}/{repo}/rulesets/{ruleset_id}"],updateStatusCheckPotection:["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks",{},{renamed:["repos","updateStatusCheckProtection"]}],updateStatusCheckProtection:["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"],updateWebhook:["PATCH /repos/{owner}/{repo}/hooks/{hook_id}"],updateWebhookConfigForRepo:["PATCH /repos/{owner}/{repo}/hooks/{hook_id}/config"],uploadReleaseAsset:["POST /repos/{owner}/{repo}/releases/{release_id}/assets{?name,label}",{baseUrl:"https://uploads.github.com"}]},search:{code:["GET /search/code"],commits:["GET /search/commits"],issuesAndPullRequests:["GET /search/issues"],labels:["GET /search/labels"],repos:["GET /search/repositories"],topics:["GET /search/topics"],users:["GET /search/users"]},secretScanning:{getAlert:["GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}"],listAlertsForEnterprise:["GET /enterprises/{enterprise}/secret-scanning/alerts"],listAlertsForOrg:["GET /orgs/{org}/secret-scanning/alerts"],listAlertsForRepo:["GET /repos/{owner}/{repo}/secret-scanning/alerts"],listLocationsForAlert:["GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations"],updateAlert:["PATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}"]},securityAdvisories:{createPrivateVulnerabilityReport:["POST /repos/{owner}/{repo}/security-advisories/reports"],createRepositoryAdvisory:["POST /repos/{owner}/{repo}/security-advisories"],createRepositoryAdvisoryCveRequest:["POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/cve"],getGlobalAdvisory:["GET /advisories/{ghsa_id}"],getRepositoryAdvisory:["GET /repos/{owner}/{repo}/security-advisories/{ghsa_id}"],listGlobalAdvisories:["GET /advisories"],listOrgRepositoryAdvisories:["GET /orgs/{org}/security-advisories"],listRepositoryAdvisories:["GET /repos/{owner}/{repo}/security-advisories"],updateRepositoryAdvisory:["PATCH /repos/{owner}/{repo}/security-advisories/{ghsa_id}"]},teams:{addOrUpdateMembershipForUserInOrg:["PUT /orgs/{org}/teams/{team_slug}/memberships/{username}"],addOrUpdateProjectPermissionsInOrg:["PUT /orgs/{org}/teams/{team_slug}/projects/{project_id}"],addOrUpdateRepoPermissionsInOrg:["PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"],checkPermissionsForProjectInOrg:["GET /orgs/{org}/teams/{team_slug}/projects/{project_id}"],checkPermissionsForRepoInOrg:["GET /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"],create:["POST /orgs/{org}/teams"],createDiscussionCommentInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments"],createDiscussionInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions"],deleteDiscussionCommentInOrg:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"],deleteDiscussionInOrg:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"],deleteInOrg:["DELETE /orgs/{org}/teams/{team_slug}"],getByName:["GET /orgs/{org}/teams/{team_slug}"],getDiscussionCommentInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"],getDiscussionInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"],getMembershipForUserInOrg:["GET /orgs/{org}/teams/{team_slug}/memberships/{username}"],list:["GET /orgs/{org}/teams"],listChildInOrg:["GET /orgs/{org}/teams/{team_slug}/teams"],listDiscussionCommentsInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments"],listDiscussionsInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions"],listForAuthenticatedUser:["GET /user/teams"],listMembersInOrg:["GET /orgs/{org}/teams/{team_slug}/members"],listPendingInvitationsInOrg:["GET /orgs/{org}/teams/{team_slug}/invitations"],listProjectsInOrg:["GET /orgs/{org}/teams/{team_slug}/projects"],listReposInOrg:["GET /orgs/{org}/teams/{team_slug}/repos"],removeMembershipForUserInOrg:["DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}"],removeProjectInOrg:["DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id}"],removeRepoInOrg:["DELETE /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"],updateDiscussionCommentInOrg:["PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"],updateDiscussionInOrg:["PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"],updateInOrg:["PATCH /orgs/{org}/teams/{team_slug}"]},users:{addEmailForAuthenticated:["POST /user/emails",{},{renamed:["users","addEmailForAuthenticatedUser"]}],addEmailForAuthenticatedUser:["POST /user/emails"],addSocialAccountForAuthenticatedUser:["POST /user/social_accounts"],block:["PUT /user/blocks/{username}"],checkBlocked:["GET /user/blocks/{username}"],checkFollowingForUser:["GET /users/{username}/following/{target_user}"],checkPersonIsFollowedByAuthenticated:["GET /user/following/{username}"],createGpgKeyForAuthenticated:["POST /user/gpg_keys",{},{renamed:["users","createGpgKeyForAuthenticatedUser"]}],createGpgKeyForAuthenticatedUser:["POST /user/gpg_keys"],createPublicSshKeyForAuthenticated:["POST /user/keys",{},{renamed:["users","createPublicSshKeyForAuthenticatedUser"]}],createPublicSshKeyForAuthenticatedUser:["POST /user/keys"],createSshSigningKeyForAuthenticatedUser:["POST /user/ssh_signing_keys"],deleteEmailForAuthenticated:["DELETE /user/emails",{},{renamed:["users","deleteEmailForAuthenticatedUser"]}],deleteEmailForAuthenticatedUser:["DELETE /user/emails"],deleteGpgKeyForAuthenticated:["DELETE /user/gpg_keys/{gpg_key_id}",{},{renamed:["users","deleteGpgKeyForAuthenticatedUser"]}],deleteGpgKeyForAuthenticatedUser:["DELETE /user/gpg_keys/{gpg_key_id}"],deletePublicSshKeyForAuthenticated:["DELETE /user/keys/{key_id}",{},{renamed:["users","deletePublicSshKeyForAuthenticatedUser"]}],deletePublicSshKeyForAuthenticatedUser:["DELETE /user/keys/{key_id}"],deleteSocialAccountForAuthenticatedUser:["DELETE /user/social_accounts"],deleteSshSigningKeyForAuthenticatedUser:["DELETE /user/ssh_signing_keys/{ssh_signing_key_id}"],follow:["PUT /user/following/{username}"],getAuthenticated:["GET /user"],getByUsername:["GET /users/{username}"],getContextForUser:["GET /users/{username}/hovercard"],getGpgKeyForAuthenticated:["GET /user/gpg_keys/{gpg_key_id}",{},{renamed:["users","getGpgKeyForAuthenticatedUser"]}],getGpgKeyForAuthenticatedUser:["GET /user/gpg_keys/{gpg_key_id}"],getPublicSshKeyForAuthenticated:["GET /user/keys/{key_id}",{},{renamed:["users","getPublicSshKeyForAuthenticatedUser"]}],getPublicSshKeyForAuthenticatedUser:["GET /user/keys/{key_id}"],getSshSigningKeyForAuthenticatedUser:["GET /user/ssh_signing_keys/{ssh_signing_key_id}"],list:["GET /users"],listBlockedByAuthenticated:["GET /user/blocks",{},{renamed:["users","listBlockedByAuthenticatedUser"]}],listBlockedByAuthenticatedUser:["GET /user/blocks"],listEmailsForAuthenticated:["GET /user/emails",{},{renamed:["users","listEmailsForAuthenticatedUser"]}],listEmailsForAuthenticatedUser:["GET /user/emails"],listFollowedByAuthenticated:["GET /user/following",{},{renamed:["users","listFollowedByAuthenticatedUser"]}],listFollowedByAuthenticatedUser:["GET /user/following"],listFollowersForAuthenticatedUser:["GET /user/followers"],listFollowersForUser:["GET /users/{username}/followers"],listFollowingForUser:["GET /users/{username}/following"],listGpgKeysForAuthenticated:["GET /user/gpg_keys",{},{renamed:["users","listGpgKeysForAuthenticatedUser"]}],listGpgKeysForAuthenticatedUser:["GET /user/gpg_keys"],listGpgKeysForUser:["GET /users/{username}/gpg_keys"],listPublicEmailsForAuthenticated:["GET /user/public_emails",{},{renamed:["users","listPublicEmailsForAuthenticatedUser"]}],listPublicEmailsForAuthenticatedUser:["GET /user/public_emails"],listPublicKeysForUser:["GET /users/{username}/keys"],listPublicSshKeysForAuthenticated:["GET /user/keys",{},{renamed:["users","listPublicSshKeysForAuthenticatedUser"]}],listPublicSshKeysForAuthenticatedUser:["GET /user/keys"],listSocialAccountsForAuthenticatedUser:["GET /user/social_accounts"],listSocialAccountsForUser:["GET /users/{username}/social_accounts"],listSshSigningKeysForAuthenticatedUser:["GET /user/ssh_signing_keys"],listSshSigningKeysForUser:["GET /users/{username}/ssh_signing_keys"],setPrimaryEmailVisibilityForAuthenticated:["PATCH /user/email/visibility",{},{renamed:["users","setPrimaryEmailVisibilityForAuthenticatedUser"]}],setPrimaryEmailVisibilityForAuthenticatedUser:["PATCH /user/email/visibility"],unblock:["DELETE /user/blocks/{username}"],unfollow:["DELETE /user/following/{username}"],updateAuthenticated:["PATCH /user"]}};var a=i;var c=new Map;for(const[e,A]of Object.entries(a)){for(const[t,r]of Object.entries(A)){const[A,s,o]=r;const[n,i]=A.split(/ /);const a=Object.assign({method:n,url:i},s);if(!c.has(e)){c.set(e,new Map)}c.get(e).set(t,{scope:e,methodName:t,endpointDefaults:a,decorations:o})}}var g={has({scope:e},A){return c.get(e).has(A)},getOwnPropertyDescriptor(e,A){return{value:this.get(e,A),configurable:true,writable:true,enumerable:true}},defineProperty(e,A,t){Object.defineProperty(e.cache,A,t);return true},deleteProperty(e,A){delete e.cache[A];return true},ownKeys({scope:e}){return[...c.get(e).keys()]},set(e,A,t){return e.cache[A]=t},get({octokit:e,scope:A,cache:t},r){if(t[r]){return t[r]}const s=c.get(A).get(r);if(!s){return void 0}const{endpointDefaults:o,decorations:n}=s;if(n){t[r]=decorate(e,A,r,o,n)}else{t[r]=e.request.defaults(o)}return t[r]}};function endpointsToMethods(e){const A={};for(const t of c.keys()){A[t]=new Proxy({octokit:e,scope:t,cache:{}},g)}return A}function decorate(e,A,t,r,s){const o=e.request.defaults(r);function withDecorations(...r){let n=o.endpoint.merge(...r);if(s.mapToData){n=Object.assign({},n,{data:n[s.mapToData],[s.mapToData]:void 0});return o(n)}if(s.renamed){const[r,o]=s.renamed;e.log.warn(`octokit.${A}.${t}() has been renamed to octokit.${r}.${o}()`)}if(s.deprecated){e.log.warn(s.deprecated)}if(s.renamedParameters){const n=o.endpoint.merge(...r);for(const[r,o]of Object.entries(s.renamedParameters)){if(r in n){e.log.warn(`"${r}" parameter is deprecated for "octokit.${A}.${t}()". Use "${o}" instead`);if(!(o in n)){n[o]=n[r]}delete n[r]}}return o(n)}return o(...r)}return Object.assign(withDecorations,o)}function restEndpointMethods(e){const A=endpointsToMethods(e);return{rest:A}}restEndpointMethods.VERSION=n;function legacyRestEndpointMethods(e){const A=endpointsToMethods(e);return{...A,rest:A}}legacyRestEndpointMethods.VERSION=n;0&&0},7031:(e,A,t)=>{"use strict";var r=Object.create;var s=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var n=Object.getOwnPropertyNames;var i=Object.getPrototypeOf;var a=Object.prototype.hasOwnProperty;var __export=(e,A)=>{for(var t in A)s(e,t,{get:A[t],enumerable:true})};var __copyProps=(e,A,t,r)=>{if(A&&typeof A==="object"||typeof A==="function"){for(let i of n(A))if(!a.call(e,i)&&i!==t)s(e,i,{get:()=>A[i],enumerable:!(r=o(A,i))||r.enumerable})}return e};var __toESM=(e,A,t)=>(t=e!=null?r(i(e)):{},__copyProps(A||!e||!e.__esModule?s(t,"default",{value:e,enumerable:true}):t,e));var __toCommonJS=e=>__copyProps(s({},"__esModule",{value:true}),e);var c={};__export(c,{RequestError:()=>Q});e.exports=__toCommonJS(c);var g=t(3147);var E=__toESM(t(4947));var l=(0,E.default)((e=>console.warn(e)));var u=(0,E.default)((e=>console.warn(e)));var Q=class extends Error{constructor(e,A,t){super(e);if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}this.name="HttpError";this.status=A;let r;if("headers"in t&&typeof t.headers!=="undefined"){r=t.headers}if("response"in t){this.response=t.response;r=t.response.headers}const s=Object.assign({},t.request);if(t.request.headers.authorization){s.headers=Object.assign({},t.request.headers,{authorization:t.request.headers.authorization.replace(/ .*$/," [REDACTED]")})}s.url=s.url.replace(/\bclient_secret=\w+/g,"client_secret=[REDACTED]").replace(/\baccess_token=\w+/g,"access_token=[REDACTED]");this.request=s;Object.defineProperty(this,"code",{get(){l(new g.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`."));return A}});Object.defineProperty(this,"headers",{get(){u(new g.Deprecation("[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`."));return r||{}}})}};0&&0},8662:(e,A,t)=>{"use strict";var r=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var o=Object.getOwnPropertyNames;var n=Object.prototype.hasOwnProperty;var __export=(e,A)=>{for(var t in A)r(e,t,{get:A[t],enumerable:true})};var __copyProps=(e,A,t,i)=>{if(A&&typeof A==="object"||typeof A==="function"){for(let a of o(A))if(!n.call(e,a)&&a!==t)r(e,a,{get:()=>A[a],enumerable:!(i=s(A,a))||i.enumerable})}return e};var __toCommonJS=e=>__copyProps(r({},"__esModule",{value:true}),e);var i={};__export(i,{request:()=>l});e.exports=__toCommonJS(i);var a=t(292);var c=t(9653);var g="8.1.6";function isPlainObject(e){if(typeof e!=="object"||e===null)return false;if(Object.prototype.toString.call(e)!=="[object Object]")return false;const A=Object.getPrototypeOf(e);if(A===null)return true;const t=Object.prototype.hasOwnProperty.call(A,"constructor")&&A.constructor;return typeof t==="function"&&t instanceof t&&Function.prototype.call(t)===Function.prototype.call(e)}var E=t(7031);function getBufferResponse(e){return e.arrayBuffer()}function fetchWrapper(e){var A,t,r;const s=e.request&&e.request.log?e.request.log:console;const o=((A=e.request)==null?void 0:A.parseSuccessResponseBody)!==false;if(isPlainObject(e.body)||Array.isArray(e.body)){e.body=JSON.stringify(e.body)}let n={};let i;let a;let{fetch:c}=globalThis;if((t=e.request)==null?void 0:t.fetch){c=e.request.fetch}if(!c){throw new Error("fetch is not set. Please pass a fetch implementation as new Octokit({ request: { fetch }}). Learn more at https://github.com/octokit/octokit.js/#fetch-missing")}return c(e.url,{method:e.method,body:e.body,headers:e.headers,signal:(r=e.request)==null?void 0:r.signal,...e.body&&{duplex:"half"}}).then((async A=>{a=A.url;i=A.status;for(const e of A.headers){n[e[0]]=e[1]}if("deprecation"in n){const A=n.link&&n.link.match(/<([^>]+)>; rel="deprecation"/);const t=A&&A.pop();s.warn(`[@octokit/request] "${e.method} ${e.url}" is deprecated. It is scheduled to be removed on ${n.sunset}${t?`. See ${t}`:""}`)}if(i===204||i===205){return}if(e.method==="HEAD"){if(i<400){return}throw new E.RequestError(A.statusText,i,{response:{url:a,status:i,headers:n,data:void 0},request:e})}if(i===304){throw new E.RequestError("Not modified",i,{response:{url:a,status:i,headers:n,data:await getResponseData(A)},request:e})}if(i>=400){const t=await getResponseData(A);const r=new E.RequestError(toErrorMessage(t),i,{response:{url:a,status:i,headers:n,data:t},request:e});throw r}return o?await getResponseData(A):A.body})).then((e=>({status:i,url:a,headers:n,data:e}))).catch((A=>{if(A instanceof E.RequestError)throw A;else if(A.name==="AbortError")throw A;let t=A.message;if(A.name==="TypeError"&&"cause"in A){if(A.cause instanceof Error){t=A.cause.message}else if(typeof A.cause==="string"){t=A.cause}}throw new E.RequestError(t,500,{request:e})}))}async function getResponseData(e){const A=e.headers.get("content-type");if(/application\/json/.test(A)){return e.json().catch((()=>e.text())).catch((()=>""))}if(!A||/^text\/|charset=utf-8$/.test(A)){return e.text()}return getBufferResponse(e)}function toErrorMessage(e){if(typeof e==="string")return e;if("message"in e){if(Array.isArray(e.errors)){return`${e.message}: ${e.errors.map(JSON.stringify).join(", ")}`}return e.message}return`Unknown error: ${JSON.stringify(e)}`}function withDefaults(e,A){const t=e.defaults(A);const newApi=function(e,A){const r=t.merge(e,A);if(!r.request||!r.request.hook){return fetchWrapper(t.parse(r))}const request2=(e,A)=>fetchWrapper(t.parse(t.merge(e,A)));Object.assign(request2,{endpoint:t,defaults:withDefaults.bind(null,t)});return r.request.hook(request2,r)};return Object.assign(newApi,{endpoint:t,defaults:withDefaults.bind(null,t)})}var l=withDefaults(a.endpoint,{headers:{"user-agent":`octokit-request.js/${g} ${(0,c.getUserAgent)()}`}});0&&0},7932:(e,A,t)=>{var r=t(2951);var s=t(2027);var o=t(4110);var n=Function.bind;var i=n.bind(n);function bindApi(e,A,t){var r=i(o,null).apply(null,t?[A,t]:[A]);e.api={remove:r};e.remove=r;["before","error","after","wrap"].forEach((function(r){var o=t?[A,r,t]:[A,r];e[r]=e.api[r]=i(s,null).apply(null,o)}))}function HookSingular(){var e="h";var A={registry:{}};var t=r.bind(null,A,e);bindApi(t,A,e);return t}function HookCollection(){var e={registry:{}};var A=r.bind(null,e);bindApi(A,e);return A}var a=false;function Hook(){if(!a){console.warn('[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4');a=true}return HookCollection()}Hook.Singular=HookSingular.bind();Hook.Collection=HookCollection.bind();e.exports=Hook;e.exports.Hook=Hook;e.exports.Singular=Hook.Singular;e.exports.Collection=Hook.Collection},2027:e=>{e.exports=addHook;function addHook(e,A,t,r){var s=r;if(!e.registry[t]){e.registry[t]=[]}if(A==="before"){r=function(e,A){return Promise.resolve().then(s.bind(null,A)).then(e.bind(null,A))}}if(A==="after"){r=function(e,A){var t;return Promise.resolve().then(e.bind(null,A)).then((function(e){t=e;return s(t,A)})).then((function(){return t}))}}if(A==="error"){r=function(e,A){return Promise.resolve().then(e.bind(null,A)).catch((function(e){return s(e,A)}))}}e.registry[t].push({hook:r,orig:s})}},2951:e=>{e.exports=register;function register(e,A,t,r){if(typeof t!=="function"){throw new Error("method for before hook must be a function")}if(!r){r={}}if(Array.isArray(A)){return A.reverse().reduce((function(A,t){return register.bind(null,e,t,A,r)}),t)()}return Promise.resolve().then((function(){if(!e.registry[A]){return t(r)}return e.registry[A].reduce((function(e,A){return A.hook.bind(null,e,r)}),t)()}))}},4110:e=>{e.exports=removeHook;function removeHook(e,A,t){if(!e.registry[A]){return}var r=e.registry[A].map((function(e){return e.orig})).indexOf(t);if(r===-1){return}e.registry[A].splice(r,1)}},3147:(e,A)=>{"use strict";Object.defineProperty(A,"__esModule",{value:true});class Deprecation extends Error{constructor(e){super(e);if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}this.name="Deprecation"}}A.Deprecation=Deprecation},4947:(e,A,t)=>{var r=t(1459);e.exports=r(once);e.exports.strict=r(onceStrict);once.proto=once((function(){Object.defineProperty(Function.prototype,"once",{value:function(){return once(this)},configurable:true});Object.defineProperty(Function.prototype,"onceStrict",{value:function(){return onceStrict(this)},configurable:true})}));function once(e){var f=function(){if(f.called)return f.value;f.called=true;return f.value=e.apply(this,arguments)};f.called=false;return f}function onceStrict(e){var f=function(){if(f.called)throw new Error(f.onceError);f.called=true;return f.value=e.apply(this,arguments)};var A=e.name||"Function wrapped with `once`";f.onceError=A+" shouldn't be called more than once";f.called=false;return f}},7285:(e,A,t)=>{e.exports=t(8703)},8703:(e,A,t)=>{"use strict";var r=t(9278);var s=t(4756);var o=t(8611);var n=t(5692);var i=t(4434);var a=t(2613);var c=t(9023);A.httpOverHttp=httpOverHttp;A.httpsOverHttp=httpsOverHttp;A.httpOverHttps=httpOverHttps;A.httpsOverHttps=httpsOverHttps;function httpOverHttp(e){var A=new TunnelingAgent(e);A.request=o.request;return A}function httpsOverHttp(e){var A=new TunnelingAgent(e);A.request=o.request;A.createSocket=createSecureSocket;A.defaultPort=443;return A}function httpOverHttps(e){var A=new TunnelingAgent(e);A.request=n.request;return A}function httpsOverHttps(e){var A=new TunnelingAgent(e);A.request=n.request;A.createSocket=createSecureSocket;A.defaultPort=443;return A}function TunnelingAgent(e){var A=this;A.options=e||{};A.proxyOptions=A.options.proxy||{};A.maxSockets=A.options.maxSockets||o.Agent.defaultMaxSockets;A.requests=[];A.sockets=[];A.on("free",(function onFree(e,t,r,s){var o=toOptions(t,r,s);for(var n=0,i=A.requests.length;n=this.maxSockets){s.requests.push(o);return}s.createSocket(o,(function(A){A.on("free",onFree);A.on("close",onCloseOrRemove);A.on("agentRemove",onCloseOrRemove);e.onSocket(A);function onFree(){s.emit("free",A,o)}function onCloseOrRemove(e){s.removeSocket(A);A.removeListener("free",onFree);A.removeListener("close",onCloseOrRemove);A.removeListener("agentRemove",onCloseOrRemove)}}))};TunnelingAgent.prototype.createSocket=function createSocket(e,A){var t=this;var r={};t.sockets.push(r);var s=mergeOptions({},t.proxyOptions,{method:"CONNECT",path:e.host+":"+e.port,agent:false,headers:{host:e.host+":"+e.port}});if(e.localAddress){s.localAddress=e.localAddress}if(s.proxyAuth){s.headers=s.headers||{};s.headers["Proxy-Authorization"]="Basic "+new Buffer(s.proxyAuth).toString("base64")}g("making CONNECT request");var o=t.request(s);o.useChunkedEncodingByDefault=false;o.once("response",onResponse);o.once("upgrade",onUpgrade);o.once("connect",onConnect);o.once("error",onError);o.end();function onResponse(e){e.upgrade=true}function onUpgrade(e,A,t){process.nextTick((function(){onConnect(e,A,t)}))}function onConnect(s,n,i){o.removeAllListeners();n.removeAllListeners();if(s.statusCode!==200){g("tunneling socket could not be established, statusCode=%d",s.statusCode);n.destroy();var a=new Error("tunneling socket could not be established, "+"statusCode="+s.statusCode);a.code="ECONNRESET";e.request.emit("error",a);t.removeSocket(r);return}if(i.length>0){g("got illegal response body from proxy");n.destroy();var a=new Error("got illegal response body from proxy");a.code="ECONNRESET";e.request.emit("error",a);t.removeSocket(r);return}g("tunneling connection has established");t.sockets[t.sockets.indexOf(r)]=n;return A(n)}function onError(A){o.removeAllListeners();g("tunneling socket could not be established, cause=%s\n",A.message,A.stack);var s=new Error("tunneling socket could not be established, "+"cause="+A.message);s.code="ECONNRESET";e.request.emit("error",s);t.removeSocket(r)}};TunnelingAgent.prototype.removeSocket=function removeSocket(e){var A=this.sockets.indexOf(e);if(A===-1){return}this.sockets.splice(A,1);var t=this.requests.shift();if(t){this.createSocket(t,(function(e){t.request.onSocket(e)}))}};function createSecureSocket(e,A){var t=this;TunnelingAgent.prototype.createSocket.call(t,e,(function(r){var o=e.request.getHeader("host");var n=mergeOptions({},t.options,{socket:r,servername:o?o.replace(/:.*$/,""):e.host});var i=s.connect(0,n);t.sockets[t.sockets.indexOf(r)]=i;A(i)}))}function toOptions(e,A,t){if(typeof e==="string"){return{host:e,port:A,localAddress:t}}return e}function mergeOptions(e){for(var A=1,t=arguments.length;A{"use strict";const r=t(1247);const s=t(8841);const o=t(7221);const n=t(4094);const i=t(2263);const a=t(8787);const c=t(2806);const{InvalidArgumentError:g}=o;const E=t(2349);const l=t(4470);const u=t(7739);const Q=t(3703);const C=t(1986);const h=t(2567);const B=t(6014);const I=t(5723);const{getGlobalDispatcher:d,setGlobalDispatcher:p}=t(6875);const m=t(5658);const y=t(8977);const w=t(9421);let R;try{t(6982);R=true}catch{R=false}Object.assign(s.prototype,E);e.exports.Dispatcher=s;e.exports.Client=r;e.exports.Pool=n;e.exports.BalancedPool=i;e.exports.Agent=a;e.exports.ProxyAgent=B;e.exports.RetryHandler=I;e.exports.DecoratorHandler=m;e.exports.RedirectHandler=y;e.exports.createRedirectInterceptor=w;e.exports.buildConnector=l;e.exports.errors=o;function makeDispatcher(e){return(A,t,r)=>{if(typeof t==="function"){r=t;t=null}if(!A||typeof A!=="string"&&typeof A!=="object"&&!(A instanceof URL)){throw new g("invalid url")}if(t!=null&&typeof t!=="object"){throw new g("invalid opts")}if(t&&t.path!=null){if(typeof t.path!=="string"){throw new g("invalid opts.path")}let e=t.path;if(!t.path.startsWith("/")){e=`/${e}`}A=new URL(c.parseOrigin(A).origin+e)}else{if(!t){t=typeof A==="object"?A:{}}A=c.parseURL(A)}const{agent:s,dispatcher:o=d()}=t;if(s){throw new g("unsupported opts.agent. Did you mean opts.client?")}return e.call(o,{...t,origin:A.origin,path:A.search?`${A.pathname}${A.search}`:A.pathname,method:t.method||(t.body?"PUT":"GET")},r)}}e.exports.setGlobalDispatcher=p;e.exports.getGlobalDispatcher=d;if(c.nodeMajor>16||c.nodeMajor===16&&c.nodeMinor>=8){let A=null;e.exports.fetch=async function fetch(e){if(!A){A=t(5697).fetch}try{return await A(...arguments)}catch(e){if(typeof e==="object"){Error.captureStackTrace(e,this)}throw e}};e.exports.Headers=t(1815).Headers;e.exports.Response=t(7862).Response;e.exports.Request=t(1940).Request;e.exports.FormData=t(1499).FormData;e.exports.File=t(4019).File;e.exports.FileReader=t(2150).FileReader;const{setGlobalOrigin:r,getGlobalOrigin:s}=t(574);e.exports.setGlobalOrigin=r;e.exports.getGlobalOrigin=s;const{CacheStorage:o}=t(2472);const{kConstruct:n}=t(7202);e.exports.caches=new o(n)}if(c.nodeMajor>=16){const{deleteCookie:A,getCookies:r,getSetCookies:s,setCookie:o}=t(4346);e.exports.deleteCookie=A;e.exports.getCookies=r;e.exports.getSetCookies=s;e.exports.setCookie=o;const{parseMIMEType:n,serializeAMimeType:i}=t(7160);e.exports.parseMIMEType=n;e.exports.serializeAMimeType=i}if(c.nodeMajor>=18&&R){const{WebSocket:A}=t(7045);e.exports.WebSocket=A}e.exports.request=makeDispatcher(E.request);e.exports.stream=makeDispatcher(E.stream);e.exports.pipeline=makeDispatcher(E.pipeline);e.exports.connect=makeDispatcher(E.connect);e.exports.upgrade=makeDispatcher(E.upgrade);e.exports.MockClient=u;e.exports.MockPool=C;e.exports.MockAgent=Q;e.exports.mockErrors=h},8787:(e,A,t)=>{"use strict";const{InvalidArgumentError:r}=t(7221);const{kClients:s,kRunning:o,kClose:n,kDestroy:i,kDispatch:a,kInterceptors:c}=t(7781);const g=t(6915);const E=t(4094);const l=t(1247);const u=t(2806);const Q=t(9421);const{WeakRef:C,FinalizationRegistry:h}=t(4904)();const B=Symbol("onConnect");const I=Symbol("onDisconnect");const d=Symbol("onConnectionError");const p=Symbol("maxRedirections");const m=Symbol("onDrain");const y=Symbol("factory");const w=Symbol("finalizer");const R=Symbol("options");function defaultFactory(e,A){return A&&A.connections===1?new l(e,A):new E(e,A)}class Agent extends g{constructor({factory:e=defaultFactory,maxRedirections:A=0,connect:t,...o}={}){super();if(typeof e!=="function"){throw new r("factory must be a function.")}if(t!=null&&typeof t!=="function"&&typeof t!=="object"){throw new r("connect must be a function or an object")}if(!Number.isInteger(A)||A<0){throw new r("maxRedirections must be a positive number")}if(t&&typeof t!=="function"){t={...t}}this[c]=o.interceptors&&o.interceptors.Agent&&Array.isArray(o.interceptors.Agent)?o.interceptors.Agent:[Q({maxRedirections:A})];this[R]={...u.deepClone(o),connect:t};this[R].interceptors=o.interceptors?{...o.interceptors}:undefined;this[p]=A;this[y]=e;this[s]=new Map;this[w]=new h((e=>{const A=this[s].get(e);if(A!==undefined&&A.deref()===undefined){this[s].delete(e)}}));const n=this;this[m]=(e,A)=>{n.emit("drain",e,[n,...A])};this[B]=(e,A)=>{n.emit("connect",e,[n,...A])};this[I]=(e,A,t)=>{n.emit("disconnect",e,[n,...A],t)};this[d]=(e,A,t)=>{n.emit("connectionError",e,[n,...A],t)}}get[o](){let e=0;for(const A of this[s].values()){const t=A.deref();if(t){e+=t[o]}}return e}[a](e,A){let t;if(e.origin&&(typeof e.origin==="string"||e.origin instanceof URL)){t=String(e.origin)}else{throw new r("opts.origin must be a non-empty string or URL.")}const o=this[s].get(t);let n=o?o.deref():null;if(!n){n=this[y](e.origin,this[R]).on("drain",this[m]).on("connect",this[B]).on("disconnect",this[I]).on("connectionError",this[d]);this[s].set(t,new C(n));this[w].register(n,t)}return n.dispatch(e,A)}async[n](){const e=[];for(const A of this[s].values()){const t=A.deref();if(t){e.push(t.close())}}await Promise.all(e)}async[i](e){const A=[];for(const t of this[s].values()){const r=t.deref();if(r){A.push(r.destroy(e))}}await Promise.all(A)}}e.exports=Agent},4844:(e,A,t)=>{const{addAbortListener:r}=t(2806);const{RequestAbortedError:s}=t(7221);const o=Symbol("kListener");const n=Symbol("kSignal");function abort(e){if(e.abort){e.abort()}else{e.onError(new s)}}function addSignal(e,A){e[n]=null;e[o]=null;if(!A){return}if(A.aborted){abort(e);return}e[n]=A;e[o]=()=>{abort(e)};r(e[n],e[o])}function removeSignal(e){if(!e[n]){return}if("removeEventListener"in e[n]){e[n].removeEventListener("abort",e[o])}else{e[n].removeListener("abort",e[o])}e[n]=null;e[o]=null}e.exports={addSignal:addSignal,removeSignal:removeSignal}},1186:(e,A,t)=>{"use strict";const{AsyncResource:r}=t(290);const{InvalidArgumentError:s,RequestAbortedError:o,SocketError:n}=t(7221);const i=t(2806);const{addSignal:a,removeSignal:c}=t(4844);class ConnectHandler extends r{constructor(e,A){if(!e||typeof e!=="object"){throw new s("invalid opts")}if(typeof A!=="function"){throw new s("invalid callback")}const{signal:t,opaque:r,responseHeaders:o}=e;if(t&&typeof t.on!=="function"&&typeof t.addEventListener!=="function"){throw new s("signal must be an EventEmitter or EventTarget")}super("UNDICI_CONNECT");this.opaque=r||null;this.responseHeaders=o||null;this.callback=A;this.abort=null;a(this,t)}onConnect(e,A){if(!this.callback){throw new o}this.abort=e;this.context=A}onHeaders(){throw new n("bad connect",null)}onUpgrade(e,A,t){const{callback:r,opaque:s,context:o}=this;c(this);this.callback=null;let n=A;if(n!=null){n=this.responseHeaders==="raw"?i.parseRawHeaders(A):i.parseHeaders(A)}this.runInAsyncScope(r,null,null,{statusCode:e,headers:n,socket:t,opaque:s,context:o})}onError(e){const{callback:A,opaque:t}=this;c(this);if(A){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(A,null,e,{opaque:t})}))}}}function connect(e,A){if(A===undefined){return new Promise(((A,t)=>{connect.call(this,e,((e,r)=>e?t(e):A(r)))}))}try{const t=new ConnectHandler(e,A);this.dispatch({...e,method:"CONNECT"},t)}catch(t){if(typeof A!=="function"){throw t}const r=e&&e.opaque;queueMicrotask((()=>A(t,{opaque:r})))}}e.exports=connect},5144:(e,A,t)=>{"use strict";const{Readable:r,Duplex:s,PassThrough:o}=t(2203);const{InvalidArgumentError:n,InvalidReturnValueError:i,RequestAbortedError:a}=t(7221);const c=t(2806);const{AsyncResource:g}=t(290);const{addSignal:E,removeSignal:l}=t(4844);const u=t(2613);const Q=Symbol("resume");class PipelineRequest extends r{constructor(){super({autoDestroy:true});this[Q]=null}_read(){const{[Q]:e}=this;if(e){this[Q]=null;e()}}_destroy(e,A){this._read();A(e)}}class PipelineResponse extends r{constructor(e){super({autoDestroy:true});this[Q]=e}_read(){this[Q]()}_destroy(e,A){if(!e&&!this._readableState.endEmitted){e=new a}A(e)}}class PipelineHandler extends g{constructor(e,A){if(!e||typeof e!=="object"){throw new n("invalid opts")}if(typeof A!=="function"){throw new n("invalid handler")}const{signal:t,method:r,opaque:o,onInfo:i,responseHeaders:g}=e;if(t&&typeof t.on!=="function"&&typeof t.addEventListener!=="function"){throw new n("signal must be an EventEmitter or EventTarget")}if(r==="CONNECT"){throw new n("invalid method")}if(i&&typeof i!=="function"){throw new n("invalid onInfo callback")}super("UNDICI_PIPELINE");this.opaque=o||null;this.responseHeaders=g||null;this.handler=A;this.abort=null;this.context=null;this.onInfo=i||null;this.req=(new PipelineRequest).on("error",c.nop);this.ret=new s({readableObjectMode:e.objectMode,autoDestroy:true,read:()=>{const{body:e}=this;if(e&&e.resume){e.resume()}},write:(e,A,t)=>{const{req:r}=this;if(r.push(e,A)||r._readableState.destroyed){t()}else{r[Q]=t}},destroy:(e,A)=>{const{body:t,req:r,res:s,ret:o,abort:n}=this;if(!e&&!o._readableState.endEmitted){e=new a}if(n&&e){n()}c.destroy(t,e);c.destroy(r,e);c.destroy(s,e);l(this);A(e)}}).on("prefinish",(()=>{const{req:e}=this;e.push(null)}));this.res=null;E(this,t)}onConnect(e,A){const{ret:t,res:r}=this;u(!r,"pipeline cannot be retried");if(t.destroyed){throw new a}this.abort=e;this.context=A}onHeaders(e,A,t){const{opaque:r,handler:s,context:o}=this;if(e<200){if(this.onInfo){const t=this.responseHeaders==="raw"?c.parseRawHeaders(A):c.parseHeaders(A);this.onInfo({statusCode:e,headers:t})}return}this.res=new PipelineResponse(t);let n;try{this.handler=null;const t=this.responseHeaders==="raw"?c.parseRawHeaders(A):c.parseHeaders(A);n=this.runInAsyncScope(s,null,{statusCode:e,headers:t,opaque:r,body:this.res,context:o})}catch(e){this.res.on("error",c.nop);throw e}if(!n||typeof n.on!=="function"){throw new i("expected Readable")}n.on("data",(e=>{const{ret:A,body:t}=this;if(!A.push(e)&&t.pause){t.pause()}})).on("error",(e=>{const{ret:A}=this;c.destroy(A,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=n}onData(e){const{res:A}=this;return A.push(e)}onComplete(e){const{res:A}=this;A.push(null)}onError(e){const{ret:A}=this;this.handler=null;c.destroy(A,e)}}function pipeline(e,A){try{const t=new PipelineHandler(e,A);this.dispatch({...e,body:t.req},t);return t.ret}catch(e){return(new o).destroy(e)}}e.exports=pipeline},3933:(e,A,t)=>{"use strict";const r=t(5509);const{InvalidArgumentError:s,RequestAbortedError:o}=t(7221);const n=t(2806);const{getResolveErrorBodyCallback:i}=t(4061);const{AsyncResource:a}=t(290);const{addSignal:c,removeSignal:g}=t(4844);class RequestHandler extends a{constructor(e,A){if(!e||typeof e!=="object"){throw new s("invalid opts")}const{signal:t,method:r,opaque:o,body:i,onInfo:a,responseHeaders:g,throwOnError:E,highWaterMark:l}=e;try{if(typeof A!=="function"){throw new s("invalid callback")}if(l&&(typeof l!=="number"||l<0)){throw new s("invalid highWaterMark")}if(t&&typeof t.on!=="function"&&typeof t.addEventListener!=="function"){throw new s("signal must be an EventEmitter or EventTarget")}if(r==="CONNECT"){throw new s("invalid method")}if(a&&typeof a!=="function"){throw new s("invalid onInfo callback")}super("UNDICI_REQUEST")}catch(e){if(n.isStream(i)){n.destroy(i.on("error",n.nop),e)}throw e}this.responseHeaders=g||null;this.opaque=o||null;this.callback=A;this.res=null;this.abort=null;this.body=i;this.trailers={};this.context=null;this.onInfo=a||null;this.throwOnError=E;this.highWaterMark=l;if(n.isStream(i)){i.on("error",(e=>{this.onError(e)}))}c(this,t)}onConnect(e,A){if(!this.callback){throw new o}this.abort=e;this.context=A}onHeaders(e,A,t,s){const{callback:o,opaque:a,abort:c,context:g,responseHeaders:E,highWaterMark:l}=this;const u=E==="raw"?n.parseRawHeaders(A):n.parseHeaders(A);if(e<200){if(this.onInfo){this.onInfo({statusCode:e,headers:u})}return}const Q=E==="raw"?n.parseHeaders(A):u;const C=Q["content-type"];const h=new r({resume:t,abort:c,contentType:C,highWaterMark:l});this.callback=null;this.res=h;if(o!==null){if(this.throwOnError&&e>=400){this.runInAsyncScope(i,null,{callback:o,body:h,contentType:C,statusCode:e,statusMessage:s,headers:u})}else{this.runInAsyncScope(o,null,null,{statusCode:e,headers:u,trailers:this.trailers,opaque:a,body:h,context:g})}}}onData(e){const{res:A}=this;return A.push(e)}onComplete(e){const{res:A}=this;g(this);n.parseHeaders(e,this.trailers);A.push(null)}onError(e){const{res:A,callback:t,body:r,opaque:s}=this;g(this);if(t){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(t,null,e,{opaque:s})}))}if(A){this.res=null;queueMicrotask((()=>{n.destroy(A,e)}))}if(r){this.body=null;n.destroy(r,e)}}}function request(e,A){if(A===undefined){return new Promise(((A,t)=>{request.call(this,e,((e,r)=>e?t(e):A(r)))}))}try{this.dispatch(e,new RequestHandler(e,A))}catch(t){if(typeof A!=="function"){throw t}const r=e&&e.opaque;queueMicrotask((()=>A(t,{opaque:r})))}}e.exports=request;e.exports.RequestHandler=RequestHandler},9774:(e,A,t)=>{"use strict";const{finished:r,PassThrough:s}=t(2203);const{InvalidArgumentError:o,InvalidReturnValueError:n,RequestAbortedError:i}=t(7221);const a=t(2806);const{getResolveErrorBodyCallback:c}=t(4061);const{AsyncResource:g}=t(290);const{addSignal:E,removeSignal:l}=t(4844);class StreamHandler extends g{constructor(e,A,t){if(!e||typeof e!=="object"){throw new o("invalid opts")}const{signal:r,method:s,opaque:n,body:i,onInfo:c,responseHeaders:g,throwOnError:l}=e;try{if(typeof t!=="function"){throw new o("invalid callback")}if(typeof A!=="function"){throw new o("invalid factory")}if(r&&typeof r.on!=="function"&&typeof r.addEventListener!=="function"){throw new o("signal must be an EventEmitter or EventTarget")}if(s==="CONNECT"){throw new o("invalid method")}if(c&&typeof c!=="function"){throw new o("invalid onInfo callback")}super("UNDICI_STREAM")}catch(e){if(a.isStream(i)){a.destroy(i.on("error",a.nop),e)}throw e}this.responseHeaders=g||null;this.opaque=n||null;this.factory=A;this.callback=t;this.res=null;this.abort=null;this.context=null;this.trailers=null;this.body=i;this.onInfo=c||null;this.throwOnError=l||false;if(a.isStream(i)){i.on("error",(e=>{this.onError(e)}))}E(this,r)}onConnect(e,A){if(!this.callback){throw new i}this.abort=e;this.context=A}onHeaders(e,A,t,o){const{factory:i,opaque:g,context:E,callback:l,responseHeaders:u}=this;const Q=u==="raw"?a.parseRawHeaders(A):a.parseHeaders(A);if(e<200){if(this.onInfo){this.onInfo({statusCode:e,headers:Q})}return}this.factory=null;let C;if(this.throwOnError&&e>=400){const t=u==="raw"?a.parseHeaders(A):Q;const r=t["content-type"];C=new s;this.callback=null;this.runInAsyncScope(c,null,{callback:l,body:C,contentType:r,statusCode:e,statusMessage:o,headers:Q})}else{if(i===null){return}C=this.runInAsyncScope(i,null,{statusCode:e,headers:Q,opaque:g,context:E});if(!C||typeof C.write!=="function"||typeof C.end!=="function"||typeof C.on!=="function"){throw new n("expected Writable")}r(C,{readable:false},(e=>{const{callback:A,res:t,opaque:r,trailers:s,abort:o}=this;this.res=null;if(e||!t.readable){a.destroy(t,e)}this.callback=null;this.runInAsyncScope(A,null,e||null,{opaque:r,trailers:s});if(e){o()}}))}C.on("drain",t);this.res=C;const h=C.writableNeedDrain!==undefined?C.writableNeedDrain:C._writableState&&C._writableState.needDrain;return h!==true}onData(e){const{res:A}=this;return A?A.write(e):true}onComplete(e){const{res:A}=this;l(this);if(!A){return}this.trailers=a.parseHeaders(e);A.end()}onError(e){const{res:A,callback:t,opaque:r,body:s}=this;l(this);this.factory=null;if(A){this.res=null;a.destroy(A,e)}else if(t){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(t,null,e,{opaque:r})}))}if(s){this.body=null;a.destroy(s,e)}}}function stream(e,A,t){if(t===undefined){return new Promise(((t,r)=>{stream.call(this,e,A,((e,A)=>e?r(e):t(A)))}))}try{this.dispatch(e,new StreamHandler(e,A,t))}catch(A){if(typeof t!=="function"){throw A}const r=e&&e.opaque;queueMicrotask((()=>t(A,{opaque:r})))}}e.exports=stream},5836:(e,A,t)=>{"use strict";const{InvalidArgumentError:r,RequestAbortedError:s,SocketError:o}=t(7221);const{AsyncResource:n}=t(290);const i=t(2806);const{addSignal:a,removeSignal:c}=t(4844);const g=t(2613);class UpgradeHandler extends n{constructor(e,A){if(!e||typeof e!=="object"){throw new r("invalid opts")}if(typeof A!=="function"){throw new r("invalid callback")}const{signal:t,opaque:s,responseHeaders:o}=e;if(t&&typeof t.on!=="function"&&typeof t.addEventListener!=="function"){throw new r("signal must be an EventEmitter or EventTarget")}super("UNDICI_UPGRADE");this.responseHeaders=o||null;this.opaque=s||null;this.callback=A;this.abort=null;this.context=null;a(this,t)}onConnect(e,A){if(!this.callback){throw new s}this.abort=e;this.context=null}onHeaders(){throw new o("bad upgrade",null)}onUpgrade(e,A,t){const{callback:r,opaque:s,context:o}=this;g.strictEqual(e,101);c(this);this.callback=null;const n=this.responseHeaders==="raw"?i.parseRawHeaders(A):i.parseHeaders(A);this.runInAsyncScope(r,null,null,{headers:n,socket:t,opaque:s,context:o})}onError(e){const{callback:A,opaque:t}=this;c(this);if(A){this.callback=null;queueMicrotask((()=>{this.runInAsyncScope(A,null,e,{opaque:t})}))}}}function upgrade(e,A){if(A===undefined){return new Promise(((A,t)=>{upgrade.call(this,e,((e,r)=>e?t(e):A(r)))}))}try{const t=new UpgradeHandler(e,A);this.dispatch({...e,method:e.method||"GET",upgrade:e.protocol||"Websocket"},t)}catch(t){if(typeof A!=="function"){throw t}const r=e&&e.opaque;queueMicrotask((()=>A(t,{opaque:r})))}}e.exports=upgrade},2349:(e,A,t)=>{"use strict";e.exports.request=t(3933);e.exports.stream=t(9774);e.exports.pipeline=t(5144);e.exports.upgrade=t(5836);e.exports.connect=t(1186)},5509:(e,A,t)=>{"use strict";const r=t(2613);const{Readable:s}=t(2203);const{RequestAbortedError:o,NotSupportedError:n,InvalidArgumentError:i}=t(7221);const a=t(2806);const{ReadableStreamFrom:c,toUSVString:g}=t(2806);let E;const l=Symbol("kConsume");const u=Symbol("kReading");const Q=Symbol("kBody");const C=Symbol("abort");const h=Symbol("kContentType");const noop=()=>{};e.exports=class BodyReadable extends s{constructor({resume:e,abort:A,contentType:t="",highWaterMark:r=64*1024}){super({autoDestroy:true,read:e,highWaterMark:r});this._readableState.dataEmitted=false;this[C]=A;this[l]=null;this[Q]=null;this[h]=t;this[u]=false}destroy(e){if(this.destroyed){return this}if(!e&&!this._readableState.endEmitted){e=new o}if(e){this[C]()}return super.destroy(e)}emit(e,...A){if(e==="data"){this._readableState.dataEmitted=true}else if(e==="error"){this._readableState.errorEmitted=true}return super.emit(e,...A)}on(e,...A){if(e==="data"||e==="readable"){this[u]=true}return super.on(e,...A)}addListener(e,...A){return this.on(e,...A)}off(e,...A){const t=super.off(e,...A);if(e==="data"||e==="readable"){this[u]=this.listenerCount("data")>0||this.listenerCount("readable")>0}return t}removeListener(e,...A){return this.off(e,...A)}push(e){if(this[l]&&e!==null&&this.readableLength===0){consumePush(this[l],e);return this[u]?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 n}get bodyUsed(){return a.isDisturbed(this)}get body(){if(!this[Q]){this[Q]=c(this);if(this[l]){this[Q].getReader();r(this[Q].locked)}}return this[Q]}dump(e){let A=e&&Number.isFinite(e.limit)?e.limit:262144;const t=e&&e.signal;if(t){try{if(typeof t!=="object"||!("aborted"in t)){throw new i("signal must be an AbortSignal")}a.throwIfAborted(t)}catch(e){return Promise.reject(e)}}if(this.closed){return Promise.resolve(null)}return new Promise(((e,r)=>{const s=t?a.addAbortListener(t,(()=>{this.destroy()})):noop;this.on("close",(function(){s();if(t&&t.aborted){r(t.reason||Object.assign(new Error("The operation was aborted"),{name:"AbortError"}))}else{e(null)}})).on("error",noop).on("data",(function(e){A-=e.length;if(A<=0){this.destroy()}})).resume()}))}};function isLocked(e){return e[Q]&&e[Q].locked===true||e[l]}function isUnusable(e){return a.isDisturbed(e)||isLocked(e)}async function consume(e,A){if(isUnusable(e)){throw new TypeError("unusable")}r(!e[l]);return new Promise(((t,r)=>{e[l]={type:A,stream:e,resolve:t,reject:r,length:0,body:[]};e.on("error",(function(e){consumeFinish(this[l],e)})).on("close",(function(){if(this[l].body!==null){consumeFinish(this[l],new o)}}));process.nextTick(consumeStart,e[l])}))}function consumeStart(e){if(e.body===null){return}const{_readableState:A}=e.stream;for(const t of A.buffer){consumePush(e,t)}if(A.endEmitted){consumeEnd(this[l])}else{e.stream.on("end",(function(){consumeEnd(this[l])}))}e.stream.resume();while(e.stream.read()!=null){}}function consumeEnd(e){const{type:A,body:r,resolve:s,stream:o,length:n}=e;try{if(A==="text"){s(g(Buffer.concat(r)))}else if(A==="json"){s(JSON.parse(Buffer.concat(r)))}else if(A==="arrayBuffer"){const e=new Uint8Array(n);let A=0;for(const t of r){e.set(t,A);A+=t.byteLength}s(e.buffer)}else if(A==="blob"){if(!E){E=t(181).Blob}s(new E(r,{type:o[h]}))}consumeFinish(e)}catch(e){o.destroy(e)}}function consumePush(e,A){e.length+=A.length;e.body.push(A)}function consumeFinish(e,A){if(e.body===null){return}if(A){e.reject(A)}else{e.resolve()}e.type=null;e.stream=null;e.resolve=null;e.reject=null;e.length=0;e.body=null}},4061:(e,A,t)=>{const r=t(2613);const{ResponseStatusCodeError:s}=t(7221);const{toUSVString:o}=t(2806);async function getResolveErrorBodyCallback({callback:e,body:A,contentType:t,statusCode:n,statusMessage:i,headers:a}){r(A);let c=[];let g=0;for await(const e of A){c.push(e);g+=e.length;if(g>128*1024){c=null;break}}if(n===204||!t||!c){process.nextTick(e,new s(`Response status code ${n}${i?`: ${i}`:""}`,n,a));return}try{if(t.startsWith("application/json")){const A=JSON.parse(o(Buffer.concat(c)));process.nextTick(e,new s(`Response status code ${n}${i?`: ${i}`:""}`,n,a,A));return}if(t.startsWith("text/")){const A=o(Buffer.concat(c));process.nextTick(e,new s(`Response status code ${n}${i?`: ${i}`:""}`,n,a,A));return}}catch(e){}process.nextTick(e,new s(`Response status code ${n}${i?`: ${i}`:""}`,n,a))}e.exports={getResolveErrorBodyCallback:getResolveErrorBodyCallback}},2263:(e,A,t)=>{"use strict";const{BalancedPoolMissingUpstreamError:r,InvalidArgumentError:s}=t(7221);const{PoolBase:o,kClients:n,kNeedDrain:i,kAddClient:a,kRemoveClient:c,kGetDispatcher:g}=t(5934);const E=t(4094);const{kUrl:l,kInterceptors:u}=t(7781);const{parseOrigin:Q}=t(2806);const C=Symbol("factory");const h=Symbol("options");const B=Symbol("kGreatestCommonDivisor");const I=Symbol("kCurrentWeight");const d=Symbol("kIndex");const p=Symbol("kWeight");const m=Symbol("kMaxWeightPerServer");const y=Symbol("kErrorPenalty");function getGreatestCommonDivisor(e,A){if(A===0)return e;return getGreatestCommonDivisor(A,e%A)}function defaultFactory(e,A){return new E(e,A)}class BalancedPool extends o{constructor(e=[],{factory:A=defaultFactory,...t}={}){super();this[h]=t;this[d]=-1;this[I]=0;this[m]=this[h].maxWeightPerServer||100;this[y]=this[h].errorPenalty||15;if(!Array.isArray(e)){e=[e]}if(typeof A!=="function"){throw new s("factory must be a function.")}this[u]=t.interceptors&&t.interceptors.BalancedPool&&Array.isArray(t.interceptors.BalancedPool)?t.interceptors.BalancedPool:[];this[C]=A;for(const A of e){this.addUpstream(A)}this._updateBalancedPoolStats()}addUpstream(e){const A=Q(e).origin;if(this[n].find((e=>e[l].origin===A&&e.closed!==true&&e.destroyed!==true))){return this}const t=this[C](A,Object.assign({},this[h]));this[a](t);t.on("connect",(()=>{t[p]=Math.min(this[m],t[p]+this[y])}));t.on("connectionError",(()=>{t[p]=Math.max(1,t[p]-this[y]);this._updateBalancedPoolStats()}));t.on("disconnect",((...e)=>{const A=e[2];if(A&&A.code==="UND_ERR_SOCKET"){t[p]=Math.max(1,t[p]-this[y]);this._updateBalancedPoolStats()}}));for(const e of this[n]){e[p]=this[m]}this._updateBalancedPoolStats();return this}_updateBalancedPoolStats(){this[B]=this[n].map((e=>e[p])).reduce(getGreatestCommonDivisor,0)}removeUpstream(e){const A=Q(e).origin;const t=this[n].find((e=>e[l].origin===A&&e.closed!==true&&e.destroyed!==true));if(t){this[c](t)}return this}get upstreams(){return this[n].filter((e=>e.closed!==true&&e.destroyed!==true)).map((e=>e[l].origin))}[g](){if(this[n].length===0){throw new r}const e=this[n].find((e=>!e[i]&&e.closed!==true&&e.destroyed!==true));if(!e){return}const A=this[n].map((e=>e[i])).reduce(((e,A)=>e&&A),true);if(A){return}let t=0;let s=this[n].findIndex((e=>!e[i]));while(t++this[n][s][p]&&!e[i]){s=this[d]}if(this[d]===0){this[I]=this[I]-this[B];if(this[I]<=0){this[I]=this[m]}}if(e[p]>=this[I]&&!e[i]){return e}}this[I]=this[n][s][p];this[d]=s;return this[n][s]}}e.exports=BalancedPool},4937:(e,A,t)=>{"use strict";const{kConstruct:r}=t(7202);const{urlEquals:s,fieldValues:o}=t(4127);const{kEnumerableProperty:n,isDisturbed:i}=t(2806);const{kHeadersList:a}=t(7781);const{webidl:c}=t(6684);const{Response:g,cloneResponse:E}=t(7862);const{Request:l}=t(1940);const{kState:u,kHeaders:Q,kGuard:C,kRealm:h}=t(648);const{fetching:B}=t(5697);const{urlIsHttpHttpsScheme:I,createDeferredPromise:d,readAllBytes:p}=t(9913);const m=t(2613);const{getGlobalDispatcher:y}=t(6875);class Cache{#e;constructor(){if(arguments[0]!==r){c.illegalConstructor()}this.#e=arguments[1]}async match(e,A={}){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.match"});e=c.converters.RequestInfo(e);A=c.converters.CacheQueryOptions(A);const t=await this.matchAll(e,A);if(t.length===0){return}return t[0]}async matchAll(e=undefined,A={}){c.brandCheck(this,Cache);if(e!==undefined)e=c.converters.RequestInfo(e);A=c.converters.CacheQueryOptions(A);let t=null;if(e!==undefined){if(e instanceof l){t=e[u];if(t.method!=="GET"&&!A.ignoreMethod){return[]}}else if(typeof e==="string"){t=new l(e)[u]}}const r=[];if(e===undefined){for(const e of this.#e){r.push(e[1])}}else{const e=this.#A(t,A);for(const A of e){r.push(A[1])}}const s=[];for(const e of r){const A=new g(e.body?.source??null);const t=A[u].body;A[u]=e;A[u].body=t;A[Q][a]=e.headersList;A[Q][C]="immutable";s.push(A)}return Object.freeze(s)}async add(e){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.add"});e=c.converters.RequestInfo(e);const A=[e];const t=this.addAll(A);return await t}async addAll(e){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.addAll"});e=c.converters["sequence"](e);const A=[];const t=[];for(const A of e){if(typeof A==="string"){continue}const e=A[u];if(!I(e.url)||e.method!=="GET"){throw c.errors.exception({header:"Cache.addAll",message:"Expected http/s scheme when method is not GET."})}}const r=[];for(const s of e){const e=new l(s)[u];if(!I(e.url)){throw c.errors.exception({header:"Cache.addAll",message:"Expected http/s scheme."})}e.initiator="fetch";e.destination="subresource";t.push(e);const n=d();r.push(B({request:e,dispatcher:y(),processResponse(e){if(e.type==="error"||e.status===206||e.status<200||e.status>299){n.reject(c.errors.exception({header:"Cache.addAll",message:"Received an invalid status code or the request failed."}))}else if(e.headersList.contains("vary")){const A=o(e.headersList.get("vary"));for(const e of A){if(e==="*"){n.reject(c.errors.exception({header:"Cache.addAll",message:"invalid vary field value"}));for(const e of r){e.abort()}return}}}},processResponseEndOfBody(e){if(e.aborted){n.reject(new DOMException("aborted","AbortError"));return}n.resolve(e)}}));A.push(n.promise)}const s=Promise.all(A);const n=await s;const i=[];let a=0;for(const e of n){const A={type:"put",request:t[a],response:e};i.push(A);a++}const g=d();let E=null;try{this.#t(i)}catch(e){E=e}queueMicrotask((()=>{if(E===null){g.resolve(undefined)}else{g.reject(E)}}));return g.promise}async put(e,A){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,2,{header:"Cache.put"});e=c.converters.RequestInfo(e);A=c.converters.Response(A);let t=null;if(e instanceof l){t=e[u]}else{t=new l(e)[u]}if(!I(t.url)||t.method!=="GET"){throw c.errors.exception({header:"Cache.put",message:"Expected an http/s scheme when method is not GET"})}const r=A[u];if(r.status===206){throw c.errors.exception({header:"Cache.put",message:"Got 206 status"})}if(r.headersList.contains("vary")){const e=o(r.headersList.get("vary"));for(const A of e){if(A==="*"){throw c.errors.exception({header:"Cache.put",message:"Got * vary field value"})}}}if(r.body&&(i(r.body.stream)||r.body.stream.locked)){throw c.errors.exception({header:"Cache.put",message:"Response body is locked or disturbed"})}const s=E(r);const n=d();if(r.body!=null){const e=r.body.stream;const A=e.getReader();p(A).then(n.resolve,n.reject)}else{n.resolve(undefined)}const a=[];const g={type:"put",request:t,response:s};a.push(g);const Q=await n.promise;if(s.body!=null){s.body.source=Q}const C=d();let h=null;try{this.#t(a)}catch(e){h=e}queueMicrotask((()=>{if(h===null){C.resolve()}else{C.reject(h)}}));return C.promise}async delete(e,A={}){c.brandCheck(this,Cache);c.argumentLengthCheck(arguments,1,{header:"Cache.delete"});e=c.converters.RequestInfo(e);A=c.converters.CacheQueryOptions(A);let t=null;if(e instanceof l){t=e[u];if(t.method!=="GET"&&!A.ignoreMethod){return false}}else{m(typeof e==="string");t=new l(e)[u]}const r=[];const s={type:"delete",request:t,options:A};r.push(s);const o=d();let n=null;let i;try{i=this.#t(r)}catch(e){n=e}queueMicrotask((()=>{if(n===null){o.resolve(!!i?.length)}else{o.reject(n)}}));return o.promise}async keys(e=undefined,A={}){c.brandCheck(this,Cache);if(e!==undefined)e=c.converters.RequestInfo(e);A=c.converters.CacheQueryOptions(A);let t=null;if(e!==undefined){if(e instanceof l){t=e[u];if(t.method!=="GET"&&!A.ignoreMethod){return[]}}else if(typeof e==="string"){t=new l(e)[u]}}const r=d();const s=[];if(e===undefined){for(const e of this.#e){s.push(e[0])}}else{const e=this.#A(t,A);for(const A of e){s.push(A[0])}}queueMicrotask((()=>{const e=[];for(const A of s){const t=new l("https://a");t[u]=A;t[Q][a]=A.headersList;t[Q][C]="immutable";t[h]=A.client;e.push(t)}r.resolve(Object.freeze(e))}));return r.promise}#t(e){const A=this.#e;const t=[...A];const r=[];const s=[];try{for(const t of e){if(t.type!=="delete"&&t.type!=="put"){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:'operation type does not match "delete" or "put"'})}if(t.type==="delete"&&t.response!=null){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"delete operation should not have an associated response"})}if(this.#A(t.request,t.options,r).length){throw new DOMException("???","InvalidStateError")}let e;if(t.type==="delete"){e=this.#A(t.request,t.options);if(e.length===0){return[]}for(const t of e){const e=A.indexOf(t);m(e!==-1);A.splice(e,1)}}else if(t.type==="put"){if(t.response==null){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"put operation should have an associated response"})}const s=t.request;if(!I(s.url)){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"expected http or https scheme"})}if(s.method!=="GET"){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"not get method"})}if(t.options!=null){throw c.errors.exception({header:"Cache.#batchCacheOperations",message:"options must not be defined"})}e=this.#A(t.request);for(const t of e){const e=A.indexOf(t);m(e!==-1);A.splice(e,1)}A.push([t.request,t.response]);r.push([t.request,t.response])}s.push([t.request,t.response])}return s}catch(e){this.#e.length=0;this.#e=t;throw e}}#A(e,A,t){const r=[];const s=t??this.#e;for(const t of s){const[s,o]=t;if(this.#r(e,s,o,A)){r.push(t)}}return r}#r(e,A,t=null,r){const n=new URL(e.url);const i=new URL(A.url);if(r?.ignoreSearch){i.search="";n.search=""}if(!s(n,i,true)){return false}if(t==null||r?.ignoreVary||!t.headersList.contains("vary")){return true}const a=o(t.headersList.get("vary"));for(const t of a){if(t==="*"){return false}const r=A.headersList.get(t);const s=e.headersList.get(t);if(r!==s){return false}}return true}}Object.defineProperties(Cache.prototype,{[Symbol.toStringTag]:{value:"Cache",configurable:true},match:n,matchAll:n,add:n,addAll:n,put:n,delete:n,keys:n});const w=[{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(w);c.converters.MultiCacheQueryOptions=c.dictionaryConverter([...w,{key:"cacheName",converter:c.converters.DOMString}]);c.converters.Response=c.interfaceConverter(g);c.converters["sequence"]=c.sequenceConverter(c.converters.RequestInfo);e.exports={Cache:Cache}},2472:(e,A,t)=>{"use strict";const{kConstruct:r}=t(7202);const{Cache:s}=t(4937);const{webidl:o}=t(6684);const{kEnumerableProperty:n}=t(2806);class CacheStorage{#s=new Map;constructor(){if(arguments[0]!==r){o.illegalConstructor()}}async match(e,A={}){o.brandCheck(this,CacheStorage);o.argumentLengthCheck(arguments,1,{header:"CacheStorage.match"});e=o.converters.RequestInfo(e);A=o.converters.MultiCacheQueryOptions(A);if(A.cacheName!=null){if(this.#s.has(A.cacheName)){const t=this.#s.get(A.cacheName);const o=new s(r,t);return await o.match(e,A)}}else{for(const t of this.#s.values()){const o=new s(r,t);const n=await o.match(e,A);if(n!==undefined){return n}}}}async has(e){o.brandCheck(this,CacheStorage);o.argumentLengthCheck(arguments,1,{header:"CacheStorage.has"});e=o.converters.DOMString(e);return this.#s.has(e)}async open(e){o.brandCheck(this,CacheStorage);o.argumentLengthCheck(arguments,1,{header:"CacheStorage.open"});e=o.converters.DOMString(e);if(this.#s.has(e)){const A=this.#s.get(e);return new s(r,A)}const A=[];this.#s.set(e,A);return new s(r,A)}async delete(e){o.brandCheck(this,CacheStorage);o.argumentLengthCheck(arguments,1,{header:"CacheStorage.delete"});e=o.converters.DOMString(e);return this.#s.delete(e)}async keys(){o.brandCheck(this,CacheStorage);const e=this.#s.keys();return[...e]}}Object.defineProperties(CacheStorage.prototype,{[Symbol.toStringTag]:{value:"CacheStorage",configurable:true},match:n,has:n,open:n,delete:n,keys:n});e.exports={CacheStorage:CacheStorage}},7202:(e,A,t)=>{"use strict";e.exports={kConstruct:t(7781).kConstruct}},4127:(e,A,t)=>{"use strict";const r=t(2613);const{URLSerializer:s}=t(7160);const{isValidHeaderName:o}=t(9913);function urlEquals(e,A,t=false){const r=s(e,t);const o=s(A,t);return r===o}function fieldValues(e){r(e!==null);const A=[];for(let t of e.split(",")){t=t.trim();if(!t.length){continue}else if(!o(t)){continue}A.push(t)}return A}e.exports={urlEquals:urlEquals,fieldValues:fieldValues}},1247:(e,A,t)=>{"use strict";const r=t(2613);const s=t(9278);const o=t(8611);const{pipeline:n}=t(2203);const i=t(2806);const a=t(6190);const c=t(1289);const g=t(6915);const{RequestContentLengthMismatchError:E,ResponseContentLengthMismatchError:l,InvalidArgumentError:u,RequestAbortedError:Q,HeadersTimeoutError:C,HeadersOverflowError:h,SocketError:B,InformationalError:I,BodyTimeoutError:d,HTTPParserError:p,ResponseExceededMaxSizeError:m,ClientDestroyedError:y}=t(7221);const w=t(4470);const{kUrl:R,kReset:b,kServerName:D,kClient:k,kBusy:F,kParser:S,kConnect:T,kBlocking:N,kResuming:U,kRunning:L,kPending:G,kSize:v,kWriting:M,kQueue:H,kConnected:Y,kConnecting:O,kNeedDrain:_,kNoRef:P,kKeepAliveDefaultTimeout:J,kHostHeader:V,kPendingIdx:x,kRunningIdx:q,kError:W,kPipelining:j,kSocket:Z,kKeepAliveTimeoutValue:X,kMaxHeadersSize:K,kKeepAliveMaxTimeout:z,kKeepAliveTimeoutThreshold:$,kHeadersTimeout:ee,kBodyTimeout:Ae,kStrictContentLength:te,kConnector:re,kMaxRedirections:se,kMaxRequests:oe,kCounter:ne,kClose:ie,kDestroy:ae,kDispatch:ce,kInterceptors:ge,kLocalAddress:Ee,kMaxResponseSize:le,kHTTPConnVersion:ue,kHost:Qe,kHTTP2Session:Ce,kHTTP2SessionState:he,kHTTP2BuildRequest:Be,kHTTP2CopyHeaders:Ie,kHTTP1BuildRequest:de}=t(7781);let pe;try{pe=t(5675)}catch{pe={constants:{}}}const{constants:{HTTP2_HEADER_AUTHORITY:fe,HTTP2_HEADER_METHOD:me,HTTP2_HEADER_PATH:ye,HTTP2_HEADER_SCHEME:we,HTTP2_HEADER_CONTENT_LENGTH:Re,HTTP2_HEADER_EXPECT:be,HTTP2_HEADER_STATUS:De}}=pe;let ke=false;const Fe=Buffer[Symbol.species];const Se=Symbol("kClosedResolve");const Te={};try{const e=t(1637);Te.sendHeaders=e.channel("undici:client:sendHeaders");Te.beforeConnect=e.channel("undici:client:beforeConnect");Te.connectError=e.channel("undici:client:connectError");Te.connected=e.channel("undici:client:connected")}catch{Te.sendHeaders={hasSubscribers:false};Te.beforeConnect={hasSubscribers:false};Te.connectError={hasSubscribers:false};Te.connected={hasSubscribers:false}}class Client extends g{constructor(e,{interceptors:A,maxHeaderSize:t,headersTimeout:r,socketTimeout:n,requestTimeout:a,connectTimeout:c,bodyTimeout:g,idleTimeout:E,keepAlive:l,keepAliveTimeout:Q,maxKeepAliveTimeout:C,keepAliveMaxTimeout:h,keepAliveTimeoutThreshold:B,socketPath:I,pipelining:d,tls:p,strictContentLength:m,maxCachedSessions:y,maxRedirections:b,connect:k,maxRequestsPerClient:F,localAddress:S,maxResponseSize:T,autoSelectFamily:N,autoSelectFamilyAttemptTimeout:L,allowH2:G,maxConcurrentStreams:v}={}){super();if(l!==undefined){throw new u("unsupported keepAlive, use pipelining=0 instead")}if(n!==undefined){throw new u("unsupported socketTimeout, use headersTimeout & bodyTimeout instead")}if(a!==undefined){throw new u("unsupported requestTimeout, use headersTimeout & bodyTimeout instead")}if(E!==undefined){throw new u("unsupported idleTimeout, use keepAliveTimeout instead")}if(C!==undefined){throw new u("unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead")}if(t!=null&&!Number.isFinite(t)){throw new u("invalid maxHeaderSize")}if(I!=null&&typeof I!=="string"){throw new u("invalid socketPath")}if(c!=null&&(!Number.isFinite(c)||c<0)){throw new u("invalid connectTimeout")}if(Q!=null&&(!Number.isFinite(Q)||Q<=0)){throw new u("invalid keepAliveTimeout")}if(h!=null&&(!Number.isFinite(h)||h<=0)){throw new u("invalid keepAliveMaxTimeout")}if(B!=null&&!Number.isFinite(B)){throw new u("invalid keepAliveTimeoutThreshold")}if(r!=null&&(!Number.isInteger(r)||r<0)){throw new u("headersTimeout must be a positive integer or zero")}if(g!=null&&(!Number.isInteger(g)||g<0)){throw new u("bodyTimeout must be a positive integer or zero")}if(k!=null&&typeof k!=="function"&&typeof k!=="object"){throw new u("connect must be a function or an object")}if(b!=null&&(!Number.isInteger(b)||b<0)){throw new u("maxRedirections must be a positive number")}if(F!=null&&(!Number.isInteger(F)||F<0)){throw new u("maxRequestsPerClient must be a positive number")}if(S!=null&&(typeof S!=="string"||s.isIP(S)===0)){throw new u("localAddress must be valid string IP address")}if(T!=null&&(!Number.isInteger(T)||T<-1)){throw new u("maxResponseSize must be a positive number")}if(L!=null&&(!Number.isInteger(L)||L<-1)){throw new u("autoSelectFamilyAttemptTimeout must be a positive number")}if(G!=null&&typeof G!=="boolean"){throw new u("allowH2 must be a valid boolean value")}if(v!=null&&(typeof v!=="number"||v<1)){throw new u("maxConcurrentStreams must be a possitive integer, greater than 0")}if(typeof k!=="function"){k=w({...p,maxCachedSessions:y,allowH2:G,socketPath:I,timeout:c,...i.nodeHasAutoSelectFamily&&N?{autoSelectFamily:N,autoSelectFamilyAttemptTimeout:L}:undefined,...k})}this[ge]=A&&A.Client&&Array.isArray(A.Client)?A.Client:[Ue({maxRedirections:b})];this[R]=i.parseOrigin(e);this[re]=k;this[Z]=null;this[j]=d!=null?d:1;this[K]=t||o.maxHeaderSize;this[J]=Q==null?4e3:Q;this[z]=h==null?6e5:h;this[$]=B==null?1e3:B;this[X]=this[J];this[D]=null;this[Ee]=S!=null?S:null;this[U]=0;this[_]=0;this[V]=`host: ${this[R].hostname}${this[R].port?`:${this[R].port}`:""}\r\n`;this[Ae]=g!=null?g:3e5;this[ee]=r!=null?r:3e5;this[te]=m==null?true:m;this[se]=b;this[oe]=F;this[Se]=null;this[le]=T>-1?T:-1;this[ue]="h1";this[Ce]=null;this[he]=!G?null:{openStreams:0,maxConcurrentStreams:v!=null?v:100};this[Qe]=`${this[R].hostname}${this[R].port?`:${this[R].port}`:""}`;this[H]=[];this[q]=0;this[x]=0}get pipelining(){return this[j]}set pipelining(e){this[j]=e;resume(this,true)}get[G](){return this[H].length-this[x]}get[L](){return this[x]-this[q]}get[v](){return this[H].length-this[q]}get[Y](){return!!this[Z]&&!this[O]&&!this[Z].destroyed}get[F](){const e=this[Z];return e&&(e[b]||e[M]||e[N])||this[v]>=(this[j]||1)||this[G]>0}[T](e){connect(this);this.once("connect",e)}[ce](e,A){const t=e.origin||this[R].origin;const r=this[ue]==="h2"?c[Be](t,e,A):c[de](t,e,A);this[H].push(r);if(this[U]){}else if(i.bodyLength(r.body)==null&&i.isIterable(r.body)){this[U]=1;process.nextTick(resume,this)}else{resume(this,true)}if(this[U]&&this[_]!==2&&this[F]){this[_]=2}return this[_]<2}async[ie](){return new Promise((e=>{if(!this[v]){e(null)}else{this[Se]=e}}))}async[ae](e){return new Promise((A=>{const t=this[H].splice(this[x]);for(let A=0;A{if(this[Se]){this[Se]();this[Se]=null}A()};if(this[Ce]!=null){i.destroy(this[Ce],e);this[Ce]=null;this[he]=null}if(!this[Z]){queueMicrotask(callback)}else{i.destroy(this[Z].on("close",callback),e)}resume(this)}))}}function onHttp2SessionError(e){r(e.code!=="ERR_TLS_CERT_ALTNAME_INVALID");this[Z][W]=e;onError(this[k],e)}function onHttp2FrameError(e,A,t){const r=new I(`HTTP/2: "frameError" received - type ${e}, code ${A}`);if(t===0){this[Z][W]=r;onError(this[k],r)}}function onHttp2SessionEnd(){i.destroy(this,new B("other side closed"));i.destroy(this[Z],new B("other side closed"))}function onHTTP2GoAway(e){const A=this[k];const t=new I(`HTTP/2: "GOAWAY" frame received with code ${e}`);A[Z]=null;A[Ce]=null;if(A.destroyed){r(this[G]===0);const e=A[H].splice(A[q]);for(let A=0;A0){const e=A[H][A[q]];A[H][A[q]++]=null;errorRequest(A,e,t)}A[x]=A[q];r(A[L]===0);A.emit("disconnect",A[R],[A],t);resume(A)}const Ne=t(5766);const Ue=t(9421);const Le=Buffer.alloc(0);async function lazyllhttp(){const e=process.env.JEST_WORKER_ID?t(2108):undefined;let A;try{A=await WebAssembly.compile(Buffer.from(t(2084),"base64"))}catch(r){A=await WebAssembly.compile(Buffer.from(e||t(2108),"base64"))}return await WebAssembly.instantiate(A,{env:{wasm_on_url:(e,A,t)=>0,wasm_on_status:(e,A,t)=>{r.strictEqual(Me.ptr,e);const s=A-Oe+He.byteOffset;return Me.onStatus(new Fe(He.buffer,s,t))||0},wasm_on_message_begin:e=>{r.strictEqual(Me.ptr,e);return Me.onMessageBegin()||0},wasm_on_header_field:(e,A,t)=>{r.strictEqual(Me.ptr,e);const s=A-Oe+He.byteOffset;return Me.onHeaderField(new Fe(He.buffer,s,t))||0},wasm_on_header_value:(e,A,t)=>{r.strictEqual(Me.ptr,e);const s=A-Oe+He.byteOffset;return Me.onHeaderValue(new Fe(He.buffer,s,t))||0},wasm_on_headers_complete:(e,A,t,s)=>{r.strictEqual(Me.ptr,e);return Me.onHeadersComplete(A,Boolean(t),Boolean(s))||0},wasm_on_body:(e,A,t)=>{r.strictEqual(Me.ptr,e);const s=A-Oe+He.byteOffset;return Me.onBody(new Fe(He.buffer,s,t))||0},wasm_on_message_complete:e=>{r.strictEqual(Me.ptr,e);return Me.onMessageComplete()||0}}})}let Ge=null;let ve=lazyllhttp();ve.catch();let Me=null;let He=null;let Ye=0;let Oe=null;const _e=1;const Pe=2;const Je=3;class Parser{constructor(e,A,{exports:t}){r(Number.isFinite(e[K])&&e[K]>0);this.llhttp=t;this.ptr=this.llhttp.llhttp_alloc(Ne.TYPE.RESPONSE);this.client=e;this.socket=A;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[K];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,A){this.timeoutType=A;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}r(this.ptr!=null);r(Me==null);this.llhttp.llhttp_resume(this.ptr);r(this.timeoutType===Pe);if(this.timeout){if(this.timeout.refresh){this.timeout.refresh()}}this.paused=false;this.execute(this.socket.read()||Le);this.readMore()}readMore(){while(!this.paused&&this.ptr){const e=this.socket.read();if(e===null){break}this.execute(e)}}execute(e){r(this.ptr!=null);r(Me==null);r(!this.paused);const{socket:A,llhttp:t}=this;if(e.length>Ye){if(Oe){t.free(Oe)}Ye=Math.ceil(e.length/4096)*4096;Oe=t.malloc(Ye)}new Uint8Array(t.memory.buffer,Oe,Ye).set(e);try{let r;try{He=e;Me=this;r=t.llhttp_execute(this.ptr,Oe,e.length)}catch(e){throw e}finally{Me=null;He=null}const s=t.llhttp_get_error_pos(this.ptr)-Oe;if(r===Ne.ERROR.PAUSED_UPGRADE){this.onUpgrade(e.slice(s))}else if(r===Ne.ERROR.PAUSED){this.paused=true;A.unshift(e.slice(s))}else if(r!==Ne.ERROR.OK){const A=t.llhttp_get_error_reason(this.ptr);let o="";if(A){const e=new Uint8Array(t.memory.buffer,A).indexOf(0);o="Response does not match the HTTP/1.1 protocol ("+Buffer.from(t.memory.buffer,A,e).toString()+")"}throw new p(o,Ne.ERROR[r],e.slice(s))}}catch(e){i.destroy(A,e)}}destroy(){r(this.ptr!=null);r(Me==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:A}=this;if(e.destroyed){return-1}const t=A[H][A[q]];if(!t){return-1}}onHeaderField(e){const A=this.headers.length;if((A&1)===0){this.headers.push(e)}else{this.headers[A-1]=Buffer.concat([this.headers[A-1],e])}this.trackHeader(e.length)}onHeaderValue(e){let A=this.headers.length;if((A&1)===1){this.headers.push(e);A+=1}else{this.headers[A-1]=Buffer.concat([this.headers[A-1],e])}const t=this.headers[A-2];if(t.length===10&&t.toString().toLowerCase()==="keep-alive"){this.keepAlive+=e.toString()}else if(t.length===10&&t.toString().toLowerCase()==="connection"){this.connection+=e.toString()}else if(t.length===14&&t.toString().toLowerCase()==="content-length"){this.contentLength+=e.toString()}this.trackHeader(e.length)}trackHeader(e){this.headersSize+=e;if(this.headersSize>=this.headersMaxSize){i.destroy(this.socket,new h)}}onUpgrade(e){const{upgrade:A,client:t,socket:s,headers:o,statusCode:n}=this;r(A);const a=t[H][t[q]];r(a);r(!s.destroyed);r(s===t[Z]);r(!this.paused);r(a.upgrade||a.method==="CONNECT");this.statusCode=null;this.statusText="";this.shouldKeepAlive=null;r(this.headers.length%2===0);this.headers=[];this.headersSize=0;s.unshift(e);s[S].destroy();s[S]=null;s[k]=null;s[W]=null;s.removeListener("error",onSocketError).removeListener("readable",onSocketReadable).removeListener("end",onSocketEnd).removeListener("close",onSocketClose);t[Z]=null;t[H][t[q]++]=null;t.emit("disconnect",t[R],[t],new I("upgrade"));try{a.onUpgrade(n,o,s)}catch(e){i.destroy(s,e)}resume(t)}onHeadersComplete(e,A,t){const{client:s,socket:o,headers:n,statusText:a}=this;if(o.destroyed){return-1}const c=s[H][s[q]];if(!c){return-1}r(!this.upgrade);r(this.statusCode<200);if(e===100){i.destroy(o,new B("bad response",i.getSocketInfo(o)));return-1}if(A&&!c.upgrade){i.destroy(o,new B("bad upgrade",i.getSocketInfo(o)));return-1}r.strictEqual(this.timeoutType,_e);this.statusCode=e;this.shouldKeepAlive=t||c.method==="HEAD"&&!o[b]&&this.connection.toLowerCase()==="keep-alive";if(this.statusCode>=200){const e=c.bodyTimeout!=null?c.bodyTimeout:s[Ae];this.setTimeout(e,Pe)}else if(this.timeout){if(this.timeout.refresh){this.timeout.refresh()}}if(c.method==="CONNECT"){r(s[L]===1);this.upgrade=true;return 2}if(A){r(s[L]===1);this.upgrade=true;return 2}r(this.headers.length%2===0);this.headers=[];this.headersSize=0;if(this.shouldKeepAlive&&s[j]){const e=this.keepAlive?i.parseKeepAliveTimeout(this.keepAlive):null;if(e!=null){const A=Math.min(e-s[$],s[z]);if(A<=0){o[b]=true}else{s[X]=A}}else{s[X]=s[J]}}else{o[b]=true}const g=c.onHeaders(e,n,this.resume,a)===false;if(c.aborted){return-1}if(c.method==="HEAD"){return 1}if(e<200){return 1}if(o[N]){o[N]=false;resume(s)}return g?Ne.ERROR.PAUSED:0}onBody(e){const{client:A,socket:t,statusCode:s,maxResponseSize:o}=this;if(t.destroyed){return-1}const n=A[H][A[q]];r(n);r.strictEqual(this.timeoutType,Pe);if(this.timeout){if(this.timeout.refresh){this.timeout.refresh()}}r(s>=200);if(o>-1&&this.bytesRead+e.length>o){i.destroy(t,new m);return-1}this.bytesRead+=e.length;if(n.onData(e)===false){return Ne.ERROR.PAUSED}}onMessageComplete(){const{client:e,socket:A,statusCode:t,upgrade:s,headers:o,contentLength:n,bytesRead:a,shouldKeepAlive:c}=this;if(A.destroyed&&(!t||c)){return-1}if(s){return}const g=e[H][e[q]];r(g);r(t>=100);this.statusCode=null;this.statusText="";this.bytesRead=0;this.contentLength="";this.keepAlive="";this.connection="";r(this.headers.length%2===0);this.headers=[];this.headersSize=0;if(t<200){return}if(g.method!=="HEAD"&&n&&a!==parseInt(n,10)){i.destroy(A,new l);return-1}g.onComplete(o);e[H][e[q]++]=null;if(A[M]){r.strictEqual(e[L],0);i.destroy(A,new I("reset"));return Ne.ERROR.PAUSED}else if(!c){i.destroy(A,new I("reset"));return Ne.ERROR.PAUSED}else if(A[b]&&e[L]===0){i.destroy(A,new I("reset"));return Ne.ERROR.PAUSED}else if(e[j]===1){setImmediate(resume,e)}else{resume(e)}}}function onParserTimeout(e){const{socket:A,timeoutType:t,client:s}=e;if(t===_e){if(!A[M]||A.writableNeedDrain||s[L]>1){r(!e.paused,"cannot be paused while waiting for headers");i.destroy(A,new C)}}else if(t===Pe){if(!e.paused){i.destroy(A,new d)}}else if(t===Je){r(s[L]===0&&s[X]);i.destroy(A,new I("socket idle timeout"))}}function onSocketReadable(){const{[S]:e}=this;if(e){e.readMore()}}function onSocketError(e){const{[k]:A,[S]:t}=this;r(e.code!=="ERR_TLS_CERT_ALTNAME_INVALID");if(A[ue]!=="h2"){if(e.code==="ECONNRESET"&&t.statusCode&&!t.shouldKeepAlive){t.onMessageComplete();return}}this[W]=e;onError(this[k],e)}function onError(e,A){if(e[L]===0&&A.code!=="UND_ERR_INFO"&&A.code!=="UND_ERR_SOCKET"){r(e[x]===e[q]);const t=e[H].splice(e[q]);for(let r=0;r0&&t.code!=="UND_ERR_INFO"){const A=e[H][e[q]];e[H][e[q]++]=null;errorRequest(e,A,t)}e[x]=e[q];r(e[L]===0);e.emit("disconnect",e[R],[e],t);resume(e)}async function connect(e){r(!e[O]);r(!e[Z]);let{host:A,hostname:t,protocol:o,port:n}=e[R];if(t[0]==="["){const e=t.indexOf("]");r(e!==-1);const A=t.substring(1,e);r(s.isIP(A));t=A}e[O]=true;if(Te.beforeConnect.hasSubscribers){Te.beforeConnect.publish({connectParams:{host:A,hostname:t,protocol:o,port:n,servername:e[D],localAddress:e[Ee]},connector:e[re]})}try{const s=await new Promise(((r,s)=>{e[re]({host:A,hostname:t,protocol:o,port:n,servername:e[D],localAddress:e[Ee]},((e,A)=>{if(e){s(e)}else{r(A)}}))}));if(e.destroyed){i.destroy(s.on("error",(()=>{})),new y);return}e[O]=false;r(s);const a=s.alpnProtocol==="h2";if(a){if(!ke){ke=true;process.emitWarning("H2 support is experimental, expect them to change at any time.",{code:"UNDICI-H2"})}const A=pe.connect(e[R],{createConnection:()=>s,peerMaxConcurrentStreams:e[he].maxConcurrentStreams});e[ue]="h2";A[k]=e;A[Z]=s;A.on("error",onHttp2SessionError);A.on("frameError",onHttp2FrameError);A.on("end",onHttp2SessionEnd);A.on("goaway",onHTTP2GoAway);A.on("close",onSocketClose);A.unref();e[Ce]=A;s[Ce]=A}else{if(!Ge){Ge=await ve;ve=null}s[P]=false;s[M]=false;s[b]=false;s[N]=false;s[S]=new Parser(e,s,Ge)}s[ne]=0;s[oe]=e[oe];s[k]=e;s[W]=null;s.on("error",onSocketError).on("readable",onSocketReadable).on("end",onSocketEnd).on("close",onSocketClose);e[Z]=s;if(Te.connected.hasSubscribers){Te.connected.publish({connectParams:{host:A,hostname:t,protocol:o,port:n,servername:e[D],localAddress:e[Ee]},connector:e[re],socket:s})}e.emit("connect",e[R],[e])}catch(s){if(e.destroyed){return}e[O]=false;if(Te.connectError.hasSubscribers){Te.connectError.publish({connectParams:{host:A,hostname:t,protocol:o,port:n,servername:e[D],localAddress:e[Ee]},connector:e[re],error:s})}if(s.code==="ERR_TLS_CERT_ALTNAME_INVALID"){r(e[L]===0);while(e[G]>0&&e[H][e[x]].servername===e[D]){const A=e[H][e[x]++];errorRequest(e,A,s)}}else{onError(e,s)}e.emit("connectionError",e[R],[e],s)}resume(e)}function emitDrain(e){e[_]=0;e.emit("drain",e[R],[e])}function resume(e,A){if(e[U]===2){return}e[U]=2;_resume(e,A);e[U]=0;if(e[q]>256){e[H].splice(0,e[q]);e[x]-=e[q];e[q]=0}}function _resume(e,A){while(true){if(e.destroyed){r(e[G]===0);return}if(e[Se]&&!e[v]){e[Se]();e[Se]=null;return}const t=e[Z];if(t&&!t.destroyed&&t.alpnProtocol!=="h2"){if(e[v]===0){if(!t[P]&&t.unref){t.unref();t[P]=true}}else if(t[P]&&t.ref){t.ref();t[P]=false}if(e[v]===0){if(t[S].timeoutType!==Je){t[S].setTimeout(e[X],Je)}}else if(e[L]>0&&t[S].statusCode<200){if(t[S].timeoutType!==_e){const A=e[H][e[q]];const r=A.headersTimeout!=null?A.headersTimeout:e[ee];t[S].setTimeout(r,_e)}}}if(e[F]){e[_]=2}else if(e[_]===2){if(A){e[_]=1;process.nextTick(emitDrain,e)}else{emitDrain(e)}continue}if(e[G]===0){return}if(e[L]>=(e[j]||1)){return}const s=e[H][e[x]];if(e[R].protocol==="https:"&&e[D]!==s.servername){if(e[L]>0){return}e[D]=s.servername;if(t&&t.servername!==s.servername){i.destroy(t,new I("servername changed"));return}}if(e[O]){return}if(!t&&!e[Ce]){connect(e);return}if(t.destroyed||t[M]||t[b]||t[N]){return}if(e[L]>0&&!s.idempotent){return}if(e[L]>0&&(s.upgrade||s.method==="CONNECT")){return}if(e[L]>0&&i.bodyLength(s.body)!==0&&(i.isStream(s.body)||i.isAsyncIterable(s.body))){return}if(!s.aborted&&write(e,s)){e[x]++}else{e[H].splice(e[x],1)}}}function shouldSendContentLength(e){return e!=="GET"&&e!=="HEAD"&&e!=="OPTIONS"&&e!=="TRACE"&&e!=="CONNECT"}function write(e,A){if(e[ue]==="h2"){writeH2(e,e[Ce],A);return}const{body:t,method:s,path:o,host:n,upgrade:a,headers:c,blocking:g,reset:l}=A;const u=s==="PUT"||s==="POST"||s==="PATCH";if(t&&typeof t.read==="function"){t.read(0)}const C=i.bodyLength(t);let h=C;if(h===null){h=A.contentLength}if(h===0&&!u){h=null}if(shouldSendContentLength(s)&&h>0&&A.contentLength!==null&&A.contentLength!==h){if(e[te]){errorRequest(e,A,new E);return false}process.emitWarning(new E)}const B=e[Z];try{A.onConnect((t=>{if(A.aborted||A.completed){return}errorRequest(e,A,t||new Q);i.destroy(B,new I("aborted"))}))}catch(t){errorRequest(e,A,t)}if(A.aborted){return false}if(s==="HEAD"){B[b]=true}if(a||s==="CONNECT"){B[b]=true}if(l!=null){B[b]=l}if(e[oe]&&B[ne]++>=e[oe]){B[b]=true}if(g){B[N]=true}let d=`${s} ${o} HTTP/1.1\r\n`;if(typeof n==="string"){d+=`host: ${n}\r\n`}else{d+=e[V]}if(a){d+=`connection: upgrade\r\nupgrade: ${a}\r\n`}else if(e[j]&&!B[b]){d+="connection: keep-alive\r\n"}else{d+="connection: close\r\n"}if(c){d+=c}if(Te.sendHeaders.hasSubscribers){Te.sendHeaders.publish({request:A,headers:d,socket:B})}if(!t||C===0){if(h===0){B.write(`${d}content-length: 0\r\n\r\n`,"latin1")}else{r(h===null,"no body must not have content length");B.write(`${d}\r\n`,"latin1")}A.onRequestSent()}else if(i.isBuffer(t)){r(h===t.byteLength,"buffer body must have content length");B.cork();B.write(`${d}content-length: ${h}\r\n\r\n`,"latin1");B.write(t);B.uncork();A.onBodySent(t);A.onRequestSent();if(!u){B[b]=true}}else if(i.isBlobLike(t)){if(typeof t.stream==="function"){writeIterable({body:t.stream(),client:e,request:A,socket:B,contentLength:h,header:d,expectsPayload:u})}else{writeBlob({body:t,client:e,request:A,socket:B,contentLength:h,header:d,expectsPayload:u})}}else if(i.isStream(t)){writeStream({body:t,client:e,request:A,socket:B,contentLength:h,header:d,expectsPayload:u})}else if(i.isIterable(t)){writeIterable({body:t,client:e,request:A,socket:B,contentLength:h,header:d,expectsPayload:u})}else{r(false)}return true}function writeH2(e,A,t){const{body:s,method:o,path:n,host:a,upgrade:g,expectContinue:l,signal:u,headers:C}=t;let h;if(typeof C==="string")h=c[Ie](C.trim());else h=C;if(g){errorRequest(e,t,new Error("Upgrade not supported for H2"));return false}try{t.onConnect((A=>{if(t.aborted||t.completed){return}errorRequest(e,t,A||new Q)}))}catch(A){errorRequest(e,t,A)}if(t.aborted){return false}let B;const d=e[he];h[fe]=a||e[Qe];h[me]=o;if(o==="CONNECT"){A.ref();B=A.request(h,{endStream:false,signal:u});if(B.id&&!B.pending){t.onUpgrade(null,null,B);++d.openStreams}else{B.once("ready",(()=>{t.onUpgrade(null,null,B);++d.openStreams}))}B.once("close",(()=>{d.openStreams-=1;if(d.openStreams===0)A.unref()}));return true}h[ye]=n;h[we]="https";const p=o==="PUT"||o==="POST"||o==="PATCH";if(s&&typeof s.read==="function"){s.read(0)}let m=i.bodyLength(s);if(m==null){m=t.contentLength}if(m===0||!p){m=null}if(shouldSendContentLength(o)&&m>0&&t.contentLength!=null&&t.contentLength!==m){if(e[te]){errorRequest(e,t,new E);return false}process.emitWarning(new E)}if(m!=null){r(s,"no body must not have content length");h[Re]=`${m}`}A.ref();const y=o==="GET"||o==="HEAD";if(l){h[be]="100-continue";B=A.request(h,{endStream:y,signal:u});B.once("continue",writeBodyH2)}else{B=A.request(h,{endStream:y,signal:u});writeBodyH2()}++d.openStreams;B.once("response",(e=>{const{[De]:A,...r}=e;if(t.onHeaders(Number(A),r,B.resume.bind(B),"")===false){B.pause()}}));B.once("end",(()=>{t.onComplete([])}));B.on("data",(e=>{if(t.onData(e)===false){B.pause()}}));B.once("close",(()=>{d.openStreams-=1;if(d.openStreams===0){A.unref()}}));B.once("error",(function(A){if(e[Ce]&&!e[Ce].destroyed&&!this.closed&&!this.destroyed){d.streams-=1;i.destroy(B,A)}}));B.once("frameError",((A,r)=>{const s=new I(`HTTP/2: "frameError" received - type ${A}, code ${r}`);errorRequest(e,t,s);if(e[Ce]&&!e[Ce].destroyed&&!this.closed&&!this.destroyed){d.streams-=1;i.destroy(B,s)}}));return true;function writeBodyH2(){if(!s){t.onRequestSent()}else if(i.isBuffer(s)){r(m===s.byteLength,"buffer body must have content length");B.cork();B.write(s);B.uncork();B.end();t.onBodySent(s);t.onRequestSent()}else if(i.isBlobLike(s)){if(typeof s.stream==="function"){writeIterable({client:e,request:t,contentLength:m,h2stream:B,expectsPayload:p,body:s.stream(),socket:e[Z],header:""})}else{writeBlob({body:s,client:e,request:t,contentLength:m,expectsPayload:p,h2stream:B,header:"",socket:e[Z]})}}else if(i.isStream(s)){writeStream({body:s,client:e,request:t,contentLength:m,expectsPayload:p,socket:e[Z],h2stream:B,header:""})}else if(i.isIterable(s)){writeIterable({body:s,client:e,request:t,contentLength:m,expectsPayload:p,header:"",h2stream:B,socket:e[Z]})}else{r(false)}}}function writeStream({h2stream:e,body:A,client:t,request:s,socket:o,contentLength:a,header:c,expectsPayload:g}){r(a!==0||t[L]===0,"stream body cannot be pipelined");if(t[ue]==="h2"){const u=n(A,e,(t=>{if(t){i.destroy(A,t);i.destroy(e,t)}else{s.onRequestSent()}}));u.on("data",onPipeData);u.once("end",(()=>{u.removeListener("data",onPipeData);i.destroy(u)}));function onPipeData(e){s.onBodySent(e)}return}let E=false;const l=new AsyncWriter({socket:o,request:s,contentLength:a,client:t,expectsPayload:g,header:c});const onData=function(e){if(E){return}try{if(!l.write(e)&&this.pause){this.pause()}}catch(e){i.destroy(this,e)}};const onDrain=function(){if(E){return}if(A.resume){A.resume()}};const onAbort=function(){if(E){return}const e=new Q;queueMicrotask((()=>onFinished(e)))};const onFinished=function(e){if(E){return}E=true;r(o.destroyed||o[M]&&t[L]<=1);o.off("drain",onDrain).off("error",onFinished);A.removeListener("data",onData).removeListener("end",onFinished).removeListener("error",onFinished).removeListener("close",onAbort);if(!e){try{l.end()}catch(A){e=A}}l.destroy(e);if(e&&(e.code!=="UND_ERR_INFO"||e.message!=="reset")){i.destroy(A,e)}else{i.destroy(A)}};A.on("data",onData).on("end",onFinished).on("error",onFinished).on("close",onAbort);if(A.resume){A.resume()}o.on("drain",onDrain).on("error",onFinished)}async function writeBlob({h2stream:e,body:A,client:t,request:s,socket:o,contentLength:n,header:a,expectsPayload:c}){r(n===A.size,"blob body must have content length");const g=t[ue]==="h2";try{if(n!=null&&n!==A.size){throw new E}const r=Buffer.from(await A.arrayBuffer());if(g){e.cork();e.write(r);e.uncork()}else{o.cork();o.write(`${a}content-length: ${n}\r\n\r\n`,"latin1");o.write(r);o.uncork()}s.onBodySent(r);s.onRequestSent();if(!c){o[b]=true}resume(t)}catch(A){i.destroy(g?e:o,A)}}async function writeIterable({h2stream:e,body:A,client:t,request:s,socket:o,contentLength:n,header:i,expectsPayload:a}){r(n!==0||t[L]===0,"iterator body cannot be pipelined");let c=null;function onDrain(){if(c){const e=c;c=null;e()}}const waitForDrain=()=>new Promise(((e,A)=>{r(c===null);if(o[W]){A(o[W])}else{c=e}}));if(t[ue]==="h2"){e.on("close",onDrain).on("drain",onDrain);try{for await(const t of A){if(o[W]){throw o[W]}const A=e.write(t);s.onBodySent(t);if(!A){await waitForDrain()}}}catch(A){e.destroy(A)}finally{s.onRequestSent();e.end();e.off("close",onDrain).off("drain",onDrain)}return}o.on("close",onDrain).on("drain",onDrain);const g=new AsyncWriter({socket:o,request:s,contentLength:n,client:t,expectsPayload:a,header:i});try{for await(const e of A){if(o[W]){throw o[W]}if(!g.write(e)){await waitForDrain()}}g.end()}catch(e){g.destroy(e)}finally{o.off("close",onDrain).off("drain",onDrain)}}class AsyncWriter{constructor({socket:e,request:A,contentLength:t,client:r,expectsPayload:s,header:o}){this.socket=e;this.request=A;this.contentLength=t;this.client=r;this.bytesWritten=0;this.expectsPayload=s;this.header=o;e[M]=true}write(e){const{socket:A,request:t,contentLength:r,client:s,bytesWritten:o,expectsPayload:n,header:i}=this;if(A[W]){throw A[W]}if(A.destroyed){return false}const a=Buffer.byteLength(e);if(!a){return true}if(r!==null&&o+a>r){if(s[te]){throw new E}process.emitWarning(new E)}A.cork();if(o===0){if(!n){A[b]=true}if(r===null){A.write(`${i}transfer-encoding: chunked\r\n`,"latin1")}else{A.write(`${i}content-length: ${r}\r\n\r\n`,"latin1")}}if(r===null){A.write(`\r\n${a.toString(16)}\r\n`,"latin1")}this.bytesWritten+=a;const c=A.write(e);A.uncork();t.onBodySent(e);if(!c){if(A[S].timeout&&A[S].timeoutType===_e){if(A[S].timeout.refresh){A[S].timeout.refresh()}}}return c}end(){const{socket:e,contentLength:A,client:t,bytesWritten:r,expectsPayload:s,header:o,request:n}=this;n.onRequestSent();e[M]=false;if(e[W]){throw e[W]}if(e.destroyed){return}if(r===0){if(s){e.write(`${o}content-length: 0\r\n\r\n`,"latin1")}else{e.write(`${o}\r\n`,"latin1")}}else if(A===null){e.write("\r\n0\r\n\r\n","latin1")}if(A!==null&&r!==A){if(t[te]){throw new E}else{process.emitWarning(new E)}}if(e[S].timeout&&e[S].timeoutType===_e){if(e[S].timeout.refresh){e[S].timeout.refresh()}}resume(t)}destroy(e){const{socket:A,client:t}=this;A[M]=false;if(e){r(t[L]<=1,"pipeline should only contain this request");i.destroy(A,e)}}}function errorRequest(e,A,t){try{A.onError(t);r(A.aborted)}catch(t){e.emit("error",t)}}e.exports=Client},4904:(e,A,t)=>{"use strict";const{kConnected:r,kSize:s}=t(7781);class CompatWeakRef{constructor(e){this.value=e}deref(){return this.value[r]===0&&this.value[s]===0?undefined:this.value}}class CompatFinalizer{constructor(e){this.finalizer=e}register(e,A){if(e.on){e.on("disconnect",(()=>{if(e[r]===0&&e[s]===0){this.finalizer(A)}}))}}}e.exports=function(){if(process.env.NODE_V8_COVERAGE){return{WeakRef:CompatWeakRef,FinalizationRegistry:CompatFinalizer}}return{WeakRef:global.WeakRef||CompatWeakRef,FinalizationRegistry:global.FinalizationRegistry||CompatFinalizer}}},9387:e=>{"use strict";const A=1024;const t=4096;e.exports={maxAttributeValueSize:A,maxNameValuePairSize:t}},4346:(e,A,t)=>{"use strict";const{parseSetCookie:r}=t(8249);const{stringify:s,getHeadersList:o}=t(7556);const{webidl:n}=t(6684);const{Headers:i}=t(1815);function getCookies(e){n.argumentLengthCheck(arguments,1,{header:"getCookies"});n.brandCheck(e,i,{strict:false});const A=e.get("cookie");const t={};if(!A){return t}for(const e of A.split(";")){const[A,...r]=e.split("=");t[A.trim()]=r.join("=")}return t}function deleteCookie(e,A,t){n.argumentLengthCheck(arguments,2,{header:"deleteCookie"});n.brandCheck(e,i,{strict:false});A=n.converters.DOMString(A);t=n.converters.DeleteCookieAttributes(t);setCookie(e,{name:A,value:"",expires:new Date(0),...t})}function getSetCookies(e){n.argumentLengthCheck(arguments,1,{header:"getSetCookies"});n.brandCheck(e,i,{strict:false});const A=o(e).cookies;if(!A){return[]}return A.map((e=>r(Array.isArray(e)?e[1]:e)))}function setCookie(e,A){n.argumentLengthCheck(arguments,2,{header:"setCookie"});n.brandCheck(e,i,{strict:false});A=n.converters.Cookie(A);const t=s(A);if(t){e.append("Set-Cookie",s(A))}}n.converters.DeleteCookieAttributes=n.dictionaryConverter([{converter:n.nullableConverter(n.converters.DOMString),key:"path",defaultValue:null},{converter:n.nullableConverter(n.converters.DOMString),key:"domain",defaultValue:null}]);n.converters.Cookie=n.dictionaryConverter([{converter:n.converters.DOMString,key:"name"},{converter:n.converters.DOMString,key:"value"},{converter:n.nullableConverter((e=>{if(typeof e==="number"){return n.converters["unsigned long long"](e)}return new Date(e)})),key:"expires",defaultValue:null},{converter:n.nullableConverter(n.converters["long long"]),key:"maxAge",defaultValue:null},{converter:n.nullableConverter(n.converters.DOMString),key:"domain",defaultValue:null},{converter:n.nullableConverter(n.converters.DOMString),key:"path",defaultValue:null},{converter:n.nullableConverter(n.converters.boolean),key:"secure",defaultValue:null},{converter:n.nullableConverter(n.converters.boolean),key:"httpOnly",defaultValue:null},{converter:n.converters.USVString,key:"sameSite",allowedValues:["Strict","Lax","None"]},{converter:n.sequenceConverter(n.converters.DOMString),key:"unparsed",defaultValue:[]}]);e.exports={getCookies:getCookies,deleteCookie:deleteCookie,getSetCookies:getSetCookies,setCookie:setCookie}},8249:(e,A,t)=>{"use strict";const{maxNameValuePairSize:r,maxAttributeValueSize:s}=t(9387);const{isCTLExcludingHtab:o}=t(7556);const{collectASequenceOfCodePointsFast:n}=t(7160);const i=t(2613);function parseSetCookie(e){if(o(e)){return null}let A="";let t="";let s="";let i="";if(e.includes(";")){const r={position:0};A=n(";",e,r);t=e.slice(r.position)}else{A=e}if(!A.includes("=")){i=A}else{const e={position:0};s=n("=",A,e);i=A.slice(e.position+1)}s=s.trim();i=i.trim();if(s.length+i.length>r){return null}return{name:s,value:i,...parseUnparsedAttributes(t)}}function parseUnparsedAttributes(e,A={}){if(e.length===0){return A}i(e[0]===";");e=e.slice(1);let t="";if(e.includes(";")){t=n(";",e,{position:0});e=e.slice(t.length)}else{t=e;e=""}let r="";let o="";if(t.includes("=")){const e={position:0};r=n("=",t,e);o=t.slice(e.position+1)}else{r=t}r=r.trim();o=o.trim();if(o.length>s){return parseUnparsedAttributes(e,A)}const a=r.toLowerCase();if(a==="expires"){const e=new Date(o);A.expires=e}else if(a==="max-age"){const t=o.charCodeAt(0);if((t<48||t>57)&&o[0]!=="-"){return parseUnparsedAttributes(e,A)}if(!/^\d+$/.test(o)){return parseUnparsedAttributes(e,A)}const r=Number(o);A.maxAge=r}else if(a==="domain"){let e=o;if(e[0]==="."){e=e.slice(1)}e=e.toLowerCase();A.domain=e}else if(a==="path"){let e="";if(o.length===0||o[0]!=="/"){e="/"}else{e=o}A.path=e}else if(a==="secure"){A.secure=true}else if(a==="httponly"){A.httpOnly=true}else if(a==="samesite"){let e="Default";const t=o.toLowerCase();if(t.includes("none")){e="None"}if(t.includes("strict")){e="Strict"}if(t.includes("lax")){e="Lax"}A.sameSite=e}else{A.unparsed??=[];A.unparsed.push(`${r}=${o}`)}return parseUnparsedAttributes(e,A)}e.exports={parseSetCookie:parseSetCookie,parseUnparsedAttributes:parseUnparsedAttributes}},7556:(e,A,t)=>{"use strict";const r=t(2613);const{kHeadersList:s}=t(7781);function isCTLExcludingHtab(e){if(e.length===0){return false}for(const A of e){const e=A.charCodeAt(0);if(e>=0||e<=8||(e>=10||e<=31)||e===127){return false}}}function validateCookieName(e){for(const A of e){const e=A.charCodeAt(0);if(e<=32||e>127||A==="("||A===")"||A===">"||A==="<"||A==="@"||A===","||A===";"||A===":"||A==="\\"||A==='"'||A==="/"||A==="["||A==="]"||A==="?"||A==="="||A==="{"||A==="}"){throw new Error("Invalid cookie name")}}}function validateCookieValue(e){for(const A of e){const e=A.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 A of e){const e=A.charCodeAt(0);if(e<33||A===";"){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 A=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];const t=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];const r=A[e.getUTCDay()];const s=e.getUTCDate().toString().padStart(2,"0");const o=t[e.getUTCMonth()];const n=e.getUTCFullYear();const i=e.getUTCHours().toString().padStart(2,"0");const a=e.getUTCMinutes().toString().padStart(2,"0");const c=e.getUTCSeconds().toString().padStart(2,"0");return`${r}, ${s} ${o} ${n} ${i}:${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 A=[`${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){A.push("Secure")}if(e.httpOnly){A.push("HttpOnly")}if(typeof e.maxAge==="number"){validateCookieMaxAge(e.maxAge);A.push(`Max-Age=${e.maxAge}`)}if(e.domain){validateCookieDomain(e.domain);A.push(`Domain=${e.domain}`)}if(e.path){validateCookiePath(e.path);A.push(`Path=${e.path}`)}if(e.expires&&e.expires.toString()!=="Invalid Date"){A.push(`Expires=${toIMFDate(e.expires)}`)}if(e.sameSite){A.push(`SameSite=${e.sameSite}`)}for(const t of e.unparsed){if(!t.includes("=")){throw new Error("Invalid unparsed")}const[e,...r]=t.split("=");A.push(`${e.trim()}=${r.join("=")}`)}return A.join("; ")}let o;function getHeadersList(e){if(e[s]){return e[s]}if(!o){o=Object.getOwnPropertySymbols(e).find((e=>e.description==="headers list"));r(o,"Headers cannot be parsed")}const A=e[o];r(A);return A}e.exports={isCTLExcludingHtab:isCTLExcludingHtab,stringify:stringify,getHeadersList:getHeadersList}},4470:(e,A,t)=>{"use strict";const r=t(9278);const s=t(2613);const o=t(2806);const{InvalidArgumentError:n,ConnectTimeoutError:i}=t(7221);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,A)}}}function buildConnector({allowH2:e,maxCachedSessions:A,socketPath:i,timeout:g,...E}){if(A!=null&&(!Number.isInteger(A)||A<0)){throw new n("maxCachedSessions must be a positive integer or zero")}const l={path:i,...E};const u=new c(A==null?100:A);g=g==null?1e4:g;e=e!=null?e:false;return function connect({hostname:A,host:n,protocol:i,port:c,servername:E,localAddress:Q,httpSocket:C},h){let B;if(i==="https:"){if(!a){a=t(4756)}E=E||l.servername||o.getServerName(n)||null;const r=E||A;const i=u.get(r)||null;s(r);B=a.connect({highWaterMark:16384,...l,servername:E,session:i,localAddress:Q,ALPNProtocols:e?["http/1.1","h2"]:["http/1.1"],socket:C,port:c||443,host:A});B.on("session",(function(e){u.set(r,e)}))}else{s(!C,"httpSocket can only be sent on TLS update");B=r.connect({highWaterMark:64*1024,...l,localAddress:Q,port:c||80,host:A})}if(l.keepAlive==null||l.keepAlive){const e=l.keepAliveInitialDelay===undefined?6e4:l.keepAliveInitialDelay;B.setKeepAlive(true,e)}const I=setupTimeout((()=>onConnectTimeout(B)),g);B.setNoDelay(true).once(i==="https:"?"secureConnect":"connect",(function(){I();if(h){const e=h;h=null;e(null,this)}})).on("error",(function(e){I();if(h){const A=h;h=null;A(e)}}));return B}}function setupTimeout(e,A){if(!A){return()=>{}}let t=null;let r=null;const s=setTimeout((()=>{t=setImmediate((()=>{if(process.platform==="win32"){r=setImmediate((()=>e()))}else{e()}}))}),A);return()=>{clearTimeout(s);clearImmediate(t);clearImmediate(r)}}function onConnectTimeout(e){o.destroy(e,new i)}e.exports=buildConnector},4457:e=>{"use strict";const A={};const t=["Accept","Accept-Encoding","Accept-Language","Accept-Ranges","Access-Control-Allow-Credentials","Access-Control-Allow-Headers","Access-Control-Allow-Methods","Access-Control-Allow-Origin","Access-Control-Expose-Headers","Access-Control-Max-Age","Access-Control-Request-Headers","Access-Control-Request-Method","Age","Allow","Alt-Svc","Alt-Used","Authorization","Cache-Control","Clear-Site-Data","Connection","Content-Disposition","Content-Encoding","Content-Language","Content-Length","Content-Location","Content-Range","Content-Security-Policy","Content-Security-Policy-Report-Only","Content-Type","Cookie","Cross-Origin-Embedder-Policy","Cross-Origin-Opener-Policy","Cross-Origin-Resource-Policy","Date","Device-Memory","Downlink","ECT","ETag","Expect","Expect-CT","Expires","Forwarded","From","Host","If-Match","If-Modified-Since","If-None-Match","If-Range","If-Unmodified-Since","Keep-Alive","Last-Modified","Link","Location","Max-Forwards","Origin","Permissions-Policy","Pragma","Proxy-Authenticate","Proxy-Authorization","RTT","Range","Referer","Referrer-Policy","Refresh","Retry-After","Sec-WebSocket-Accept","Sec-WebSocket-Extensions","Sec-WebSocket-Key","Sec-WebSocket-Protocol","Sec-WebSocket-Version","Server","Server-Timing","Service-Worker-Allowed","Service-Worker-Navigation-Preload","Set-Cookie","SourceMap","Strict-Transport-Security","Supports-Loading-Mode","TE","Timing-Allow-Origin","Trailer","Transfer-Encoding","Upgrade","Upgrade-Insecure-Requests","User-Agent","Vary","Via","WWW-Authenticate","X-Content-Type-Options","X-DNS-Prefetch-Control","X-Frame-Options","X-Permitted-Cross-Domain-Policies","X-Powered-By","X-Requested-With","X-XSS-Protection"];for(let e=0;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,A,t,r){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=r;this.status=A;this.statusCode=A;this.headers=t}}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,A){super(e);Error.captureStackTrace(this,SocketError);this.name="SocketError";this.message=e||"Socket error";this.code="UND_ERR_SOCKET";this.socket=A}}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,A,t){super(e);Error.captureStackTrace(this,HTTPParserError);this.name="HTTPParserError";this.code=A?`HPE_${A}`:undefined;this.data=t?t.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"}}class RequestRetryError extends UndiciError{constructor(e,A,{headers:t,data:r}){super(e);Error.captureStackTrace(this,RequestRetryError);this.name="RequestRetryError";this.message=e||"Request retry error";this.code="UND_ERR_REQ_RETRY";this.statusCode=A;this.data=r;this.headers=t}}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,RequestRetryError:RequestRetryError}},1289:(e,A,t)=>{"use strict";const{InvalidArgumentError:r,NotSupportedError:s}=t(7221);const o=t(2613);const{kHTTP2BuildRequest:n,kHTTP2CopyHeaders:i,kHTTP1BuildRequest:a}=t(7781);const c=t(2806);const g=/^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;const E=/[^\t\x20-\x7e\x80-\xff]/;const l=/[^\u0021-\u00ff]/;const u=Symbol("handler");const Q={};let C;try{const e=t(1637);Q.create=e.channel("undici:request:create");Q.bodySent=e.channel("undici:request:bodySent");Q.headers=e.channel("undici:request:headers");Q.trailers=e.channel("undici:request:trailers");Q.error=e.channel("undici:request:error")}catch{Q.create={hasSubscribers:false};Q.bodySent={hasSubscribers:false};Q.headers={hasSubscribers:false};Q.trailers={hasSubscribers:false};Q.error={hasSubscribers:false}}class Request{constructor(e,{path:A,method:s,body:o,headers:n,query:i,idempotent:a,blocking:E,upgrade:h,headersTimeout:B,bodyTimeout:I,reset:d,throwOnError:p,expectContinue:m},y){if(typeof A!=="string"){throw new r("path must be a string")}else if(A[0]!=="/"&&!(A.startsWith("http://")||A.startsWith("https://"))&&s!=="CONNECT"){throw new r("path must be an absolute URL or start with a slash")}else if(l.exec(A)!==null){throw new r("invalid request path")}if(typeof s!=="string"){throw new r("method must be a string")}else if(g.exec(s)===null){throw new r("invalid request method")}if(h&&typeof h!=="string"){throw new r("upgrade must be a string")}if(B!=null&&(!Number.isFinite(B)||B<0)){throw new r("invalid headersTimeout")}if(I!=null&&(!Number.isFinite(I)||I<0)){throw new r("invalid bodyTimeout")}if(d!=null&&typeof d!=="boolean"){throw new r("invalid reset")}if(m!=null&&typeof m!=="boolean"){throw new r("invalid expectContinue")}this.headersTimeout=B;this.bodyTimeout=I;this.throwOnError=p===true;this.method=s;this.abort=null;if(o==null){this.body=null}else if(c.isStream(o)){this.body=o;const e=this.body._readableState;if(!e||!e.autoDestroy){this.endHandler=function autoDestroy(){c.destroy(this)};this.body.on("end",this.endHandler)}this.errorHandler=e=>{if(this.abort){this.abort(e)}else{this.error=e}};this.body.on("error",this.errorHandler)}else if(c.isBuffer(o)){this.body=o.byteLength?o:null}else if(ArrayBuffer.isView(o)){this.body=o.buffer.byteLength?Buffer.from(o.buffer,o.byteOffset,o.byteLength):null}else if(o instanceof ArrayBuffer){this.body=o.byteLength?Buffer.from(o):null}else if(typeof o==="string"){this.body=o.length?Buffer.from(o):null}else if(c.isFormDataLike(o)||c.isIterable(o)||c.isBlobLike(o)){this.body=o}else{throw new r("body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable")}this.completed=false;this.aborted=false;this.upgrade=h||null;this.path=i?c.buildURL(A,i):A;this.origin=e;this.idempotent=a==null?s==="HEAD"||s==="GET":a;this.blocking=E==null?false:E;this.reset=d==null?null:d;this.host=null;this.contentLength=null;this.contentType=null;this.headers="";this.expectContinue=m!=null?m:false;if(Array.isArray(n)){if(n.length%2!==0){throw new r("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"),kRetryHandlerDefaultRetry:Symbol("retry agent default retry"),kConstruct:Symbol("constructable")}},2806:(e,A,t)=>{"use strict";const r=t(2613);const{kDestroyed:s,kBodyUsed:o}=t(7781);const{IncomingMessage:n}=t(8611);const i=t(2203);const a=t(9278);const{InvalidArgumentError:c}=t(7221);const{Blob:g}=t(181);const E=t(9023);const{stringify:l}=t(3480);const{headerNameLowerCasedRecord:u}=t(4457);const[Q,C]=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 g&&e instanceof g||e&&typeof e==="object"&&(typeof e.stream==="function"||typeof e.arrayBuffer==="function")&&/^(Blob|File)$/.test(e[Symbol.toStringTag])}function buildURL(e,A){if(e.includes("?")||e.includes("#")){throw new Error('Query params cannot be passed when url already contains "?" or "#".')}const t=l(A);if(t){e+="?"+t}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 A=e.port!=null?e.port:e.protocol==="https:"?443:80;let t=e.origin!=null?e.origin:`${e.protocol}//${e.hostname}:${A}`;let r=e.path!=null?e.path:`${e.pathname||""}${e.search||""}`;if(t.endsWith("/")){t=t.substring(0,t.length-1)}if(r&&!r.startsWith("/")){r=`/${r}`}e=new URL(t+r)}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 A=e.indexOf("]");r(A!==-1);return e.substring(1,A)}const A=e.indexOf(":");if(A===-1)return e;return e.substring(0,A)}function getServerName(e){if(!e){return null}r.strictEqual(typeof e,"string");const A=getHostname(e);if(a.isIP(A)){return""}return A}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 A=e._readableState;return A&&A.objectMode===false&&A.ended===true&&Number.isFinite(A.length)?A.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[s])}function isReadableAborted(e){const A=e&&e._readableState;return isDestroyed(e)&&A&&!A.endEmitted}function destroy(e,A){if(e==null||!isStream(e)||isDestroyed(e)){return}if(typeof e.destroy==="function"){if(Object.getPrototypeOf(e).constructor===n){e.socket=null}e.destroy(A)}else if(A){process.nextTick(((e,A)=>{e.emit("error",A)}),e,A)}if(e.destroyed!==true){e[s]=true}}const h=/timeout=(\d+)/;function parseKeepAliveTimeout(e){const A=e.toString().match(h);return A?parseInt(A[1],10)*1e3:null}function headerNameToString(e){return u[e]||e.toLowerCase()}function parseHeaders(e,A={}){if(!Array.isArray(e))return e;for(let t=0;te.toString("utf8")))}else{A[r]=e[t+1].toString("utf8")}}else{if(!Array.isArray(s)){s=[s];A[r]=s}s.push(e[t+1].toString("utf8"))}}if("content-length"in A&&"content-disposition"in A){A["content-disposition"]=Buffer.from(A["content-disposition"]).toString("latin1")}return A}function parseRawHeaders(e){const A=[];let t=false;let r=-1;for(let s=0;s{e.close()}))}else{const A=Buffer.isBuffer(r)?r:Buffer.from(r);e.enqueue(new Uint8Array(A))}return e.desiredSize>0},async cancel(e){await A.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}}}function addAbortListener(e,A){if("addEventListener"in e){e.addEventListener("abort",A,{once:true});return()=>e.removeEventListener("abort",A)}e.addListener("abort",A);return()=>e.removeListener("abort",A)}const I=!!String.prototype.toWellFormed;function toUSVString(e){if(I){return`${e}`.toWellFormed()}else if(E.toUSVString){return E.toUSVString(e)}return`${e}`}function parseRangeHeader(e){if(e==null||e==="")return{start:0,end:null,size:null};const A=e?e.match(/^bytes (\d+)-(\d+)\/(\d+)?$/):null;return A?{start:parseInt(A[1]),end:A[2]?parseInt(A[2]):null,size:A[3]?parseInt(A[3]):null}:null}const d=Object.create(null);d.enumerable=true;e.exports={kEnumerableProperty:d,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,headerNameToString:headerNameToString,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,parseRangeHeader:parseRangeHeader,nodeMajor:Q,nodeMinor:C,nodeHasAutoSelectFamily:Q>18||Q===18&&C>=13,safeHTTPMethods:["GET","HEAD","OPTIONS","TRACE"]}},6915:(e,A,t)=>{"use strict";const r=t(8841);const{ClientDestroyedError:s,ClientClosedError:o,InvalidArgumentError:n}=t(7221);const{kDestroy:i,kClose:a,kDispatch:c,kInterceptors:g}=t(7781);const E=Symbol("destroyed");const l=Symbol("closed");const u=Symbol("onDestroyed");const Q=Symbol("onClosed");const C=Symbol("Intercepted Dispatch");class DispatcherBase extends r{constructor(){super();this[E]=false;this[u]=null;this[l]=false;this[Q]=[]}get destroyed(){return this[E]}get closed(){return this[l]}get interceptors(){return this[g]}set interceptors(e){if(e){for(let A=e.length-1;A>=0;A--){const e=this[g][A];if(typeof e!=="function"){throw new n("interceptor must be an function")}}}this[g]=e}close(e){if(e===undefined){return new Promise(((e,A)=>{this.close(((t,r)=>t?A(t):e(r)))}))}if(typeof e!=="function"){throw new n("invalid callback")}if(this[E]){queueMicrotask((()=>e(new s,null)));return}if(this[l]){if(this[Q]){this[Q].push(e)}else{queueMicrotask((()=>e(null,null)))}return}this[l]=true;this[Q].push(e);const onClosed=()=>{const e=this[Q];this[Q]=null;for(let A=0;Athis.destroy())).then((()=>{queueMicrotask(onClosed)}))}destroy(e,A){if(typeof e==="function"){A=e;e=null}if(A===undefined){return new Promise(((A,t)=>{this.destroy(e,((e,r)=>e?t(e):A(r)))}))}if(typeof A!=="function"){throw new n("invalid callback")}if(this[E]){if(this[u]){this[u].push(A)}else{queueMicrotask((()=>A(null,null)))}return}if(!e){e=new s}this[E]=true;this[u]=this[u]||[];this[u].push(A);const onDestroyed=()=>{const e=this[u];this[u]=null;for(let A=0;A{queueMicrotask(onDestroyed)}))}[C](e,A){if(!this[g]||this[g].length===0){this[C]=this[c];return this[c](e,A)}let t=this[c].bind(this);for(let e=this[g].length-1;e>=0;e--){t=this[g][e](t)}this[C]=t;return t(e,A)}dispatch(e,A){if(!A||typeof A!=="object"){throw new n("handler must be an object")}try{if(!e||typeof e!=="object"){throw new n("opts must be an object.")}if(this[E]||this[u]){throw new s}if(this[l]){throw new o}return this[C](e,A)}catch(e){if(typeof A.onError!=="function"){throw new n("invalid onError method")}A.onError(e);return false}}}e.exports=DispatcherBase},8841:(e,A,t)=>{"use strict";const r=t(4434);class Dispatcher extends r{dispatch(){throw new Error("not implemented")}close(){throw new Error("not implemented")}destroy(){throw new Error("not implemented")}}e.exports=Dispatcher},8689:(e,A,t)=>{"use strict";const r=t(1597);const s=t(2806);const{ReadableStreamFrom:o,isBlobLike:n,isReadableStreamLike:i,readableStreamClose:a,createDeferredPromise:c,fullyReadBody:g}=t(9913);const{FormData:E}=t(1499);const{kState:l}=t(648);const{webidl:u}=t(6684);const{DOMException:Q,structuredClone:C}=t(6040);const{Blob:h,File:B}=t(181);const{kBodyUsed:I}=t(7781);const d=t(2613);const{isErrored:p}=t(2806);const{isUint8Array:m,isArrayBuffer:y}=t(8253);const{File:w}=t(4019);const{parseMIMEType:R,serializeAMimeType:b}=t(7160);let D=globalThis.ReadableStream;const k=B??w;const F=new TextEncoder;const S=new TextDecoder;function extractBody(e,A=false){if(!D){D=t(3774).ReadableStream}let r=null;if(e instanceof D){r=e}else if(n(e)){r=e.stream()}else{r=new D({async pull(e){e.enqueue(typeof g==="string"?F.encode(g):g);queueMicrotask((()=>a(e)))},start(){},type:undefined})}d(i(r));let c=null;let g=null;let E=null;let l=null;if(typeof e==="string"){g=e;l="text/plain;charset=UTF-8"}else if(e instanceof URLSearchParams){g=e.toString();l="application/x-www-form-urlencoded;charset=UTF-8"}else if(y(e)){g=new Uint8Array(e.slice())}else if(ArrayBuffer.isView(e)){g=new Uint8Array(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength))}else if(s.isFormDataLike(e)){const A=`----formdata-undici-0${`${Math.floor(Math.random()*1e11)}`.padStart(11,"0")}`;const t=`--${A}\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 r=[];const s=new Uint8Array([13,10]);E=0;let o=false;for(const[A,n]of e){if(typeof n==="string"){const e=F.encode(t+`; name="${escape(normalizeLinefeeds(A))}"`+`\r\n\r\n${normalizeLinefeeds(n)}\r\n`);r.push(e);E+=e.byteLength}else{const e=F.encode(`${t}; name="${escape(normalizeLinefeeds(A))}"`+(n.name?`; filename="${escape(n.name)}"`:"")+"\r\n"+`Content-Type: ${n.type||"application/octet-stream"}\r\n\r\n`);r.push(e,n,s);if(typeof n.size==="number"){E+=e.byteLength+n.size+s.byteLength}else{o=true}}}const n=F.encode(`--${A}--`);r.push(n);E+=n.byteLength;if(o){E=null}g=e;c=async function*(){for(const e of r){if(e.stream){yield*e.stream()}else{yield e}}};l="multipart/form-data; boundary="+A}else if(n(e)){g=e;E=e.size;if(e.type){l=e.type}}else if(typeof e[Symbol.asyncIterator]==="function"){if(A){throw new TypeError("keepalive")}if(s.isDisturbed(e)||e.locked){throw new TypeError("Response body object should not be disturbed or locked")}r=e instanceof D?e:o(e)}if(typeof g==="string"||s.isBuffer(g)){E=Buffer.byteLength(g)}if(c!=null){let A;r=new D({async start(){A=c(e)[Symbol.asyncIterator]()},async pull(e){const{value:t,done:s}=await A.next();if(s){queueMicrotask((()=>{e.close()}))}else{if(!p(r)){e.enqueue(new Uint8Array(t))}}return e.desiredSize>0},async cancel(e){await A.return()},type:undefined})}const u={stream:r,source:g,length:E};return[u,l]}function safelyExtractBody(e,A=false){if(!D){D=t(3774).ReadableStream}if(e instanceof D){d(!s.isDisturbed(e),"The body has already been consumed.");d(!e.locked,"The stream is locked.")}return extractBody(e,A)}function cloneBody(e){const[A,t]=e.stream.tee();const r=C(t,{transfer:[t]});const[,s]=r.tee();e.stream=A;return{stream:s,length:e.length,source:e.source}}async function*consumeBody(e){if(e){if(m(e)){yield e}else{const A=e.stream;if(s.isDisturbed(A)){throw new TypeError("The body has already been consumed.")}if(A.locked){throw new TypeError("The stream is locked.")}A[I]=true;yield*A}}}function throwIfAborted(e){if(e.aborted){throw new Q("The operation was aborted.","AbortError")}}function bodyMixinMethods(e){const A={blob(){return specConsumeBody(this,(e=>{let A=bodyMimeType(this);if(A==="failure"){A=""}else if(A){A=b(A)}return new h([e],{type:A})}),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(){u.brandCheck(this,e);throwIfAborted(this[l]);const A=this.headers.get("Content-Type");if(/multipart\/form-data/.test(A)){const e={};for(const[A,t]of this.headers)e[A.toLowerCase()]=t;const A=new E;let t;try{t=new r({headers:e,preservePath:true})}catch(e){throw new Q(`${e}`,"AbortError")}t.on("field",((e,t)=>{A.append(e,t)}));t.on("file",((e,t,r,s,o)=>{const n=[];if(s==="base64"||s.toLowerCase()==="base64"){let s="";t.on("data",(e=>{s+=e.toString().replace(/[\r\n]/gm,"");const A=s.length-s.length%4;n.push(Buffer.from(s.slice(0,A),"base64"));s=s.slice(A)}));t.on("end",(()=>{n.push(Buffer.from(s,"base64"));A.append(e,new k(n,r,{type:o}))}))}else{t.on("data",(e=>{n.push(e)}));t.on("end",(()=>{A.append(e,new k(n,r,{type:o}))}))}}));const s=new Promise(((e,A)=>{t.on("finish",e);t.on("error",(e=>A(new TypeError(e))))}));if(this.body!==null)for await(const e of consumeBody(this[l].body))t.write(e);t.end();await s;return A}else if(/application\/x-www-form-urlencoded/.test(A)){let e;try{let A="";const t=new TextDecoder("utf-8",{ignoreBOM:true});for await(const e of consumeBody(this[l].body)){if(!m(e)){throw new TypeError("Expected Uint8Array chunk")}A+=t.decode(e,{stream:true})}A+=t.decode();e=new URLSearchParams(A)}catch(e){throw Object.assign(new TypeError,{cause:e})}const A=new E;for(const[t,r]of e){A.append(t,r)}return A}else{await Promise.resolve();throwIfAborted(this[l]);throw u.errors.exception({header:`${e.name}.formData`,message:"Could not parse content as FormData."})}}};return A}function mixinBody(e){Object.assign(e.prototype,bodyMixinMethods(e))}async function specConsumeBody(e,A,t){u.brandCheck(e,t);throwIfAborted(e[l]);if(bodyUnusable(e[l].body)){throw new TypeError("Body is unusable")}const r=c();const errorSteps=e=>r.reject(e);const successSteps=e=>{try{r.resolve(A(e))}catch(e){errorSteps(e)}};if(e[l].body==null){successSteps(new Uint8Array);return r.promise}await g(e[l].body,successSteps,errorSteps);return r.promise}function bodyUnusable(e){return e!=null&&(e.stream.locked||s.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 A=S.decode(e);return A}function parseJSONFromBytes(e){return JSON.parse(utf8DecodeBytes(e))}function bodyMimeType(e){const{headersList:A}=e[l];const t=A.get("content-type");if(t===null){return"failure"}return R(t)}e.exports={extractBody:extractBody,safelyExtractBody:safelyExtractBody,cloneBody:cloneBody,mixinBody:mixinBody}},6040:(e,A,t)=>{"use strict";const{MessageChannel:r,receiveMessageOnPort:s}=t(8167);const o=["GET","HEAD","POST"];const n=new Set(o);const i=[101,204,205,304];const a=[301,302,303,307,308];const c=new Set(a);const g=["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 E=new Set(g);const l=["","no-referrer","no-referrer-when-downgrade","same-origin","origin","strict-origin","origin-when-cross-origin","strict-origin-when-cross-origin","unsafe-url"];const u=new Set(l);const Q=["follow","manual","error"];const C=["GET","HEAD","OPTIONS","TRACE"];const h=new Set(C);const B=["navigate","same-origin","no-cors","cors"];const I=["omit","same-origin","include"];const d=["default","no-store","reload","no-cache","force-cache","only-if-cached"];const p=["content-encoding","content-language","content-location","content-type","content-length"];const m=["half"];const y=["CONNECT","TRACE","TRACK"];const w=new Set(y);const R=["audio","audioworklet","font","image","manifest","paintworklet","script","style","track","video","xslt",""];const b=new Set(R);const D=globalThis.DOMException??(()=>{try{atob("~")}catch(e){return Object.getPrototypeOf(e).constructor}})();let k;const F=globalThis.structuredClone??function structuredClone(e,A=undefined){if(arguments.length===0){throw new TypeError("missing argument")}if(!k){k=new r}k.port1.unref();k.port2.unref();k.port1.postMessage(e,A?.transfer);return s(k.port2).message};e.exports={DOMException:D,structuredClone:F,subresource:R,forbiddenMethods:y,requestBodyHeader:p,referrerPolicy:l,requestRedirect:Q,requestMode:B,requestCredentials:I,requestCache:d,redirectStatus:a,corsSafeListedMethods:o,nullBodyStatus:i,safeMethods:C,badPorts:g,requestDuplex:m,subresourceSet:b,badPortsSet:E,redirectStatusSet:c,corsSafeListedMethodsSet:n,safeMethodsSet:h,forbiddenMethodsSet:w,referrerPolicySet:u}},7160:(e,A,t)=>{const r=t(2613);const{atob:s}=t(181);const{isomorphicDecode:o}=t(9913);const n=new TextEncoder;const i=/^[!#$%&'*+-.^_|~A-Za-z0-9]+$/;const a=/(\u000A|\u000D|\u0009|\u0020)/;const c=/[\u0009|\u0020-\u007E|\u0080-\u00FF]/;function dataURLProcessor(e){r(e.protocol==="data:");let A=URLSerializer(e,true);A=A.slice(5);const t={position:0};let s=collectASequenceOfCodePointsFast(",",A,t);const n=s.length;s=removeASCIIWhitespace(s,true,true);if(t.position>=A.length){return"failure"}t.position++;const i=A.slice(n+1);let a=stringPercentDecode(i);if(/;(\u0020){0,}base64$/i.test(s)){const e=o(a);a=forgivingBase64(e);if(a==="failure"){return"failure"}s=s.slice(0,-6);s=s.replace(/(\u0020)+$/,"");s=s.slice(0,-1)}if(s.startsWith(";")){s="text/plain"+s}let c=parseMIMEType(s);if(c==="failure"){c=parseMIMEType("text/plain;charset=US-ASCII")}return{mimeType:c,body:a}}function URLSerializer(e,A=false){if(!A){return e.href}const t=e.href;const r=e.hash.length;return r===0?t:t.substring(0,t.length-r)}function collectASequenceOfCodePoints(e,A,t){let r="";while(t.positione.length){return"failure"}A.position++;let r=collectASequenceOfCodePointsFast(";",e,A);r=removeHTTPWhitespace(r,false,true);if(r.length===0||!i.test(r)){return"failure"}const s=t.toLowerCase();const o=r.toLowerCase();const n={type:s,subtype:o,parameters:new Map,essence:`${s}/${o}`};while(A.positiona.test(e)),e,A);let t=collectASequenceOfCodePoints((e=>e!==";"&&e!=="="),e,A);t=t.toLowerCase();if(A.positione.length){break}let r=null;if(e[A.position]==='"'){r=collectAnHTTPQuotedString(e,A,true);collectASequenceOfCodePointsFast(";",e,A)}else{r=collectASequenceOfCodePointsFast(";",e,A);r=removeHTTPWhitespace(r,false,true);if(r.length===0){continue}}if(t.length!==0&&i.test(t)&&(r.length===0||c.test(r))&&!n.parameters.has(t)){n.parameters.set(t,r)}}return n}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 A=s(e);const t=new Uint8Array(A.length);for(let e=0;ee!=='"'&&e!=="\\"),e,A);if(A.position>=e.length){break}const t=e[A.position];A.position++;if(t==="\\"){if(A.position>=e.length){o+="\\";break}o+=e[A.position];A.position++}else{r(t==='"');break}}if(t){return o}return e.slice(s,A.position)}function serializeAMimeType(e){r(e!=="failure");const{parameters:A,essence:t}=e;let s=t;for(let[e,t]of A.entries()){s+=";";s+=e;s+="=";if(!i.test(t)){t=t.replace(/(\\|")/g,"\\$1");t='"'+t;t+='"'}s+=t}return s}function isHTTPWhiteSpace(e){return e==="\r"||e==="\n"||e==="\t"||e===" "}function removeHTTPWhitespace(e,A=true,t=true){let r=0;let s=e.length-1;if(A){for(;r0&&isHTTPWhiteSpace(e[s]);s--);}return e.slice(r,s+1)}function isASCIIWhitespace(e){return e==="\r"||e==="\n"||e==="\t"||e==="\f"||e===" "}function removeASCIIWhitespace(e,A=true,t=true){let r=0;let s=e.length-1;if(A){for(;r0&&isASCIIWhitespace(e[s]);s--);}return e.slice(r,s+1)}e.exports={dataURLProcessor:dataURLProcessor,URLSerializer:URLSerializer,collectASequenceOfCodePoints:collectASequenceOfCodePoints,collectASequenceOfCodePointsFast:collectASequenceOfCodePointsFast,stringPercentDecode:stringPercentDecode,parseMIMEType:parseMIMEType,collectAnHTTPQuotedString:collectAnHTTPQuotedString,serializeAMimeType:serializeAMimeType}},4019:(e,A,t)=>{"use strict";const{Blob:r,File:s}=t(181);const{types:o}=t(9023);const{kState:n}=t(648);const{isBlobLike:i}=t(9913);const{webidl:a}=t(6684);const{parseMIMEType:c,serializeAMimeType:g}=t(7160);const{kEnumerableProperty:E}=t(2806);const l=new TextEncoder;class File extends r{constructor(e,A,t={}){a.argumentLengthCheck(arguments,2,{header:"File constructor"});e=a.converters["sequence"](e);A=a.converters.USVString(A);t=a.converters.FilePropertyBag(t);const r=A;let s=t.type;let o;e:{if(s){s=c(s);if(s==="failure"){s="";break e}s=g(s).toLowerCase()}o=t.lastModified}super(processBlobParts(e,t),{type:s});this[n]={name:r,lastModified:o,type:s}}get name(){a.brandCheck(this,File);return this[n].name}get lastModified(){a.brandCheck(this,File);return this[n].lastModified}get type(){a.brandCheck(this,File);return this[n].type}}class FileLike{constructor(e,A,t={}){const r=A;const s=t.type;const o=t.lastModified??Date.now();this[n]={blobLike:e,name:r,type:s,lastModified:o}}stream(...e){a.brandCheck(this,FileLike);return this[n].blobLike.stream(...e)}arrayBuffer(...e){a.brandCheck(this,FileLike);return this[n].blobLike.arrayBuffer(...e)}slice(...e){a.brandCheck(this,FileLike);return this[n].blobLike.slice(...e)}text(...e){a.brandCheck(this,FileLike);return this[n].blobLike.text(...e)}get size(){a.brandCheck(this,FileLike);return this[n].blobLike.size}get type(){a.brandCheck(this,FileLike);return this[n].blobLike.type}get name(){a.brandCheck(this,FileLike);return this[n].name}get lastModified(){a.brandCheck(this,FileLike);return this[n].lastModified}get[Symbol.toStringTag](){return"File"}}Object.defineProperties(File.prototype,{[Symbol.toStringTag]:{value:"File",configurable:true},name:E,lastModified:E});a.converters.Blob=a.interfaceConverter(r);a.converters.BlobPart=function(e,A){if(a.util.Type(e)==="Object"){if(i(e)){return a.converters.Blob(e,{strict:false})}if(ArrayBuffer.isView(e)||o.isAnyArrayBuffer(e)){return a.converters.BufferSource(e,A)}}return a.converters.USVString(e,A)};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,A){const t=[];for(const r of e){if(typeof r==="string"){let e=r;if(A.endings==="native"){e=convertLineEndingsNative(e)}t.push(l.encode(e))}else if(o.isAnyArrayBuffer(r)||o.isTypedArray(r)){if(!r.buffer){t.push(new Uint8Array(r))}else{t.push(new Uint8Array(r.buffer,r.byteOffset,r.byteLength))}}else if(i(r)){t.push(r)}}return t}function convertLineEndingsNative(e){let A="\n";if(process.platform==="win32"){A="\r\n"}return e.replace(/\r?\n/g,A)}function isFileLike(e){return s&&e instanceof s||e instanceof File||e&&(typeof e.stream==="function"||typeof e.arrayBuffer==="function")&&e[Symbol.toStringTag]==="File"}e.exports={File:File,FileLike:FileLike,isFileLike:isFileLike}},1499:(e,A,t)=>{"use strict";const{isBlobLike:r,toUSVString:s,makeIterator:o}=t(9913);const{kState:n}=t(648);const{File:i,FileLike:a,isFileLike:c}=t(4019);const{webidl:g}=t(6684);const{Blob:E,File:l}=t(181);const u=l??i;class FormData{constructor(e){if(e!==undefined){throw g.errors.conversionFailed({prefix:"FormData constructor",argument:"Argument 1",types:["undefined"]})}this[n]=[]}append(e,A,t=undefined){g.brandCheck(this,FormData);g.argumentLengthCheck(arguments,2,{header:"FormData.append"});if(arguments.length===3&&!r(A)){throw new TypeError("Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'")}e=g.converters.USVString(e);A=r(A)?g.converters.Blob(A,{strict:false}):g.converters.USVString(A);t=arguments.length===3?g.converters.USVString(t):undefined;const s=makeEntry(e,A,t);this[n].push(s)}delete(e){g.brandCheck(this,FormData);g.argumentLengthCheck(arguments,1,{header:"FormData.delete"});e=g.converters.USVString(e);this[n]=this[n].filter((A=>A.name!==e))}get(e){g.brandCheck(this,FormData);g.argumentLengthCheck(arguments,1,{header:"FormData.get"});e=g.converters.USVString(e);const A=this[n].findIndex((A=>A.name===e));if(A===-1){return null}return this[n][A].value}getAll(e){g.brandCheck(this,FormData);g.argumentLengthCheck(arguments,1,{header:"FormData.getAll"});e=g.converters.USVString(e);return this[n].filter((A=>A.name===e)).map((e=>e.value))}has(e){g.brandCheck(this,FormData);g.argumentLengthCheck(arguments,1,{header:"FormData.has"});e=g.converters.USVString(e);return this[n].findIndex((A=>A.name===e))!==-1}set(e,A,t=undefined){g.brandCheck(this,FormData);g.argumentLengthCheck(arguments,2,{header:"FormData.set"});if(arguments.length===3&&!r(A)){throw new TypeError("Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'")}e=g.converters.USVString(e);A=r(A)?g.converters.Blob(A,{strict:false}):g.converters.USVString(A);t=arguments.length===3?s(t):undefined;const o=makeEntry(e,A,t);const i=this[n].findIndex((A=>A.name===e));if(i!==-1){this[n]=[...this[n].slice(0,i),o,...this[n].slice(i+1).filter((A=>A.name!==e))]}else{this[n].push(o)}}entries(){g.brandCheck(this,FormData);return o((()=>this[n].map((e=>[e.name,e.value]))),"FormData","key+value")}keys(){g.brandCheck(this,FormData);return o((()=>this[n].map((e=>[e.name,e.value]))),"FormData","key")}values(){g.brandCheck(this,FormData);return o((()=>this[n].map((e=>[e.name,e.value]))),"FormData","value")}forEach(e,A=globalThis){g.brandCheck(this,FormData);g.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[t,r]of this){e.apply(A,[r,t,this])}}}FormData.prototype[Symbol.iterator]=FormData.prototype.entries;Object.defineProperties(FormData.prototype,{[Symbol.toStringTag]:{value:"FormData",configurable:true}});function makeEntry(e,A,t){e=Buffer.from(e).toString("utf8");if(typeof A==="string"){A=Buffer.from(A).toString("utf8")}else{if(!c(A)){A=A instanceof E?new u([A],"blob",{type:A.type}):new a(A,"blob",{type:A.type})}if(t!==undefined){const e={type:A.type,lastModified:A.lastModified};A=l&&A instanceof l||A instanceof i?new u([A],t,e):new a(A,t,e)}}return{name:e,value:A}}e.exports={FormData:FormData}},574:e=>{"use strict";const A=Symbol.for("undici.globalOrigin.1");function getGlobalOrigin(){return globalThis[A]}function setGlobalOrigin(e){if(e===undefined){Object.defineProperty(globalThis,A,{value:undefined,writable:true,enumerable:false,configurable:false});return}const t=new URL(e);if(t.protocol!=="http:"&&t.protocol!=="https:"){throw new TypeError(`Only http & https urls are allowed, received ${t.protocol}`)}Object.defineProperty(globalThis,A,{value:t,writable:true,enumerable:false,configurable:false})}e.exports={getGlobalOrigin:getGlobalOrigin,setGlobalOrigin:setGlobalOrigin}},1815:(e,A,t)=>{"use strict";const{kHeadersList:r,kConstruct:s}=t(7781);const{kGuard:o}=t(648);const{kEnumerableProperty:n}=t(2806);const{makeIterator:i,isValidHeaderName:a,isValidHeaderValue:c}=t(9913);const{webidl:g}=t(6684);const E=t(2613);const l=Symbol("headers map");const u=Symbol("headers map sorted");function isHTTPWhiteSpaceCharCode(e){return e===10||e===13||e===9||e===32}function headerValueNormalize(e){let A=0;let t=e.length;while(t>A&&isHTTPWhiteSpaceCharCode(e.charCodeAt(t-1)))--t;while(t>A&&isHTTPWhiteSpaceCharCode(e.charCodeAt(A)))++A;return A===0&&t===e.length?e:e.substring(A,t)}function fill(e,A){if(Array.isArray(A)){for(let t=0;t>","record"]})}}function appendHeader(e,A,t){t=headerValueNormalize(t);if(!a(A)){throw g.errors.invalidArgument({prefix:"Headers.append",value:A,type:"header name"})}else if(!c(t)){throw g.errors.invalidArgument({prefix:"Headers.append",value:t,type:"header value"})}if(e[o]==="immutable"){throw new TypeError("immutable")}else if(e[o]==="request-no-cors"){}return e[r].append(A,t)}class HeadersList{cookies=null;constructor(e){if(e instanceof HeadersList){this[l]=new Map(e[l]);this[u]=e[u];this.cookies=e.cookies===null?null:[...e.cookies]}else{this[l]=new Map(e);this[u]=null}}contains(e){e=e.toLowerCase();return this[l].has(e)}clear(){this[l].clear();this[u]=null;this.cookies=null}append(e,A){this[u]=null;const t=e.toLowerCase();const r=this[l].get(t);if(r){const e=t==="cookie"?"; ":", ";this[l].set(t,{name:r.name,value:`${r.value}${e}${A}`})}else{this[l].set(t,{name:e,value:A})}if(t==="set-cookie"){this.cookies??=[];this.cookies.push(A)}}set(e,A){this[u]=null;const t=e.toLowerCase();if(t==="set-cookie"){this.cookies=[A]}this[l].set(t,{name:e,value:A})}delete(e){this[u]=null;e=e.toLowerCase();if(e==="set-cookie"){this.cookies=null}this[l].delete(e)}get(e){const A=this[l].get(e.toLowerCase());return A===undefined?null:A.value}*[Symbol.iterator](){for(const[e,{value:A}]of this[l]){yield[e,A]}}get entries(){const e={};if(this[l].size){for(const{name:A,value:t}of this[l].values()){e[A]=t}}return e}}class Headers{constructor(e=undefined){if(e===s){return}this[r]=new HeadersList;this[o]="none";if(e!==undefined){e=g.converters.HeadersInit(e);fill(this,e)}}append(e,A){g.brandCheck(this,Headers);g.argumentLengthCheck(arguments,2,{header:"Headers.append"});e=g.converters.ByteString(e);A=g.converters.ByteString(A);return appendHeader(this,e,A)}delete(e){g.brandCheck(this,Headers);g.argumentLengthCheck(arguments,1,{header:"Headers.delete"});e=g.converters.ByteString(e);if(!a(e)){throw g.errors.invalidArgument({prefix:"Headers.delete",value:e,type:"header name"})}if(this[o]==="immutable"){throw new TypeError("immutable")}else if(this[o]==="request-no-cors"){}if(!this[r].contains(e)){return}this[r].delete(e)}get(e){g.brandCheck(this,Headers);g.argumentLengthCheck(arguments,1,{header:"Headers.get"});e=g.converters.ByteString(e);if(!a(e)){throw g.errors.invalidArgument({prefix:"Headers.get",value:e,type:"header name"})}return this[r].get(e)}has(e){g.brandCheck(this,Headers);g.argumentLengthCheck(arguments,1,{header:"Headers.has"});e=g.converters.ByteString(e);if(!a(e)){throw g.errors.invalidArgument({prefix:"Headers.has",value:e,type:"header name"})}return this[r].contains(e)}set(e,A){g.brandCheck(this,Headers);g.argumentLengthCheck(arguments,2,{header:"Headers.set"});e=g.converters.ByteString(e);A=g.converters.ByteString(A);A=headerValueNormalize(A);if(!a(e)){throw g.errors.invalidArgument({prefix:"Headers.set",value:e,type:"header name"})}else if(!c(A)){throw g.errors.invalidArgument({prefix:"Headers.set",value:A,type:"header value"})}if(this[o]==="immutable"){throw new TypeError("immutable")}else if(this[o]==="request-no-cors"){}this[r].set(e,A)}getSetCookie(){g.brandCheck(this,Headers);const e=this[r].cookies;if(e){return[...e]}return[]}get[u](){if(this[r][u]){return this[r][u]}const e=[];const A=[...this[r]].sort(((e,A)=>e[0]e),"Headers","key")}return i((()=>[...this[u].values()]),"Headers","key")}values(){g.brandCheck(this,Headers);if(this[o]==="immutable"){const e=this[u];return i((()=>e),"Headers","value")}return i((()=>[...this[u].values()]),"Headers","value")}entries(){g.brandCheck(this,Headers);if(this[o]==="immutable"){const e=this[u];return i((()=>e),"Headers","key+value")}return i((()=>[...this[u].values()]),"Headers","key+value")}forEach(e,A=globalThis){g.brandCheck(this,Headers);g.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[t,r]of this){e.apply(A,[r,t,this])}}[Symbol.for("nodejs.util.inspect.custom")](){g.brandCheck(this,Headers);return this[r]}}Headers.prototype[Symbol.iterator]=Headers.prototype.entries;Object.defineProperties(Headers.prototype,{append:n,delete:n,get:n,has:n,set:n,getSetCookie:n,keys:n,values:n,entries:n,forEach:n,[Symbol.iterator]:{enumerable:false},[Symbol.toStringTag]:{value:"Headers",configurable:true}});g.converters.HeadersInit=function(e){if(g.util.Type(e)==="Object"){if(e[Symbol.iterator]){return g.converters["sequence>"](e)}return g.converters["record"](e)}throw g.errors.conversionFailed({prefix:"Headers constructor",argument:"Argument 1",types:["sequence>","record"]})};e.exports={fill:fill,Headers:Headers,HeadersList:HeadersList}},5697:(e,A,t)=>{"use strict";const{Response:r,makeNetworkError:s,makeAppropriateNetworkError:o,filterResponse:n,makeResponse:i}=t(7862);const{Headers:a}=t(1815);const{Request:c,makeRequest:g}=t(1940);const E=t(3106);const{bytesMatch:l,makePolicyContainer:u,clonePolicyContainer:Q,requestBadPort:C,TAOCheck:h,appendRequestOriginHeader:B,responseLocationURL:I,requestCurrentURL:d,setRequestReferrerPolicyOnRedirect:p,tryUpgradeRequestToAPotentiallyTrustworthyURL:m,createOpaqueTimingInfo:y,appendFetchMetadata:w,corsCheck:R,crossOriginResourcePolicyCheck:b,determineRequestsReferrer:D,coarsenedSharedCurrentTime:k,createDeferredPromise:F,isBlobLike:S,sameOrigin:T,isCancelled:N,isAborted:U,isErrorLike:L,fullyReadBody:G,readableStreamClose:v,isomorphicEncode:M,urlIsLocal:H,urlIsHttpHttpsScheme:Y,urlHasHttpsScheme:O}=t(9913);const{kState:_,kHeaders:P,kGuard:J,kRealm:V}=t(648);const x=t(2613);const{safelyExtractBody:q}=t(8689);const{redirectStatusSet:W,nullBodyStatus:j,safeMethodsSet:Z,requestBodyHeader:X,subresourceSet:K,DOMException:z}=t(6040);const{kHeadersList:$}=t(7781);const ee=t(4434);const{Readable:Ae,pipeline:te}=t(2203);const{addAbortListener:re,isErrored:se,isReadable:oe,nodeMajor:ne,nodeMinor:ie}=t(2806);const{dataURLProcessor:ae,serializeAMimeType:ce}=t(7160);const{TransformStream:ge}=t(3774);const{getGlobalDispatcher:Ee}=t(6875);const{webidl:le}=t(6684);const{STATUS_CODES:ue}=t(8611);const Qe=["GET","HEAD"];let Ce;let he=globalThis.ReadableStream;class Fetch extends ee{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,A={}){le.argumentLengthCheck(arguments,1,{header:"globalThis.fetch"});const t=F();let s;try{s=new c(e,A)}catch(e){t.reject(e);return t.promise}const o=s[_];if(s.signal.aborted){abortFetch(t,o,null,s.signal.reason);return t.promise}const n=o.client.globalObject;if(n?.constructor?.name==="ServiceWorkerGlobalScope"){o.serviceWorkers="none"}let i=null;const a=null;let g=false;let E=null;re(s.signal,(()=>{g=true;x(E!=null);E.abort(s.signal.reason);abortFetch(t,o,i,s.signal.reason)}));const handleFetchDone=e=>finalizeAndReportTiming(e,"fetch");const processResponse=e=>{if(g){return Promise.resolve()}if(e.aborted){abortFetch(t,o,i,E.serializedAbortReason);return Promise.resolve()}if(e.type==="error"){t.reject(Object.assign(new TypeError("fetch failed"),{cause:e.error}));return Promise.resolve()}i=new r;i[_]=e;i[V]=a;i[P][$]=e.headersList;i[P][J]="immutable";i[P][V]=a;t.resolve(i)};E=fetching({request:o,processResponseEndOfBody:handleFetchDone,processResponse:processResponse,dispatcher:A.dispatcher??Ee()});return t.promise}function finalizeAndReportTiming(e,A="other"){if(e.type==="error"&&e.aborted){return}if(!e.urlList?.length){return}const t=e.urlList[0];let r=e.timingInfo;let s=e.cacheState;if(!Y(t)){return}if(r===null){return}if(!e.timingAllowPassed){r=y({startTime:r.startTime});s=""}r.endTime=k();e.timingInfo=r;markResourceTiming(r,t,A,globalThis,s)}function markResourceTiming(e,A,t,r,s){if(ne>18||ne===18&&ie>=2){performance.markResourceTiming(e,A.href,t,r,s)}}function abortFetch(e,A,t,r){if(!r){r=new z("The operation was aborted.","AbortError")}e.reject(r);if(A.body!=null&&oe(A.body?.stream)){A.body.stream.cancel(r).catch((e=>{if(e.code==="ERR_INVALID_STATE"){return}throw e}))}if(t==null){return}const s=t[_];if(s.body!=null&&oe(s.body?.stream)){s.body.stream.cancel(r).catch((e=>{if(e.code==="ERR_INVALID_STATE"){return}throw e}))}}function fetching({request:e,processRequestBodyChunkLength:A,processRequestEndOfBody:t,processResponse:r,processResponseEndOfBody:s,processResponseConsumeBody:o,useParallelQueue:n=false,dispatcher:i}){let a=null;let c=false;if(e.client!=null){a=e.client.globalObject;c=e.client.crossOriginIsolatedCapability}const g=k(c);const E=y({startTime:g});const l={controller:new Fetch(i),request:e,timingInfo:E,processRequestBodyChunkLength:A,processRequestEndOfBody:t,processResponse:r,processResponseConsumeBody:o,processResponseEndOfBody:s,taskDestination:a,crossOriginIsolatedCapability:c};x(!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=Q(e.client.policyContainer)}else{e.policyContainer=u()}}if(!e.headersList.contains("accept")){const A="*/*";e.headersList.append("accept",A)}if(!e.headersList.contains("accept-language")){e.headersList.append("accept-language","*")}if(e.priority===null){}if(K.has(e.destination)){}mainFetch(l).catch((e=>{l.controller.terminate(e)}));return l.controller}async function mainFetch(e,A=false){const t=e.request;let r=null;if(t.localURLsOnly&&!H(d(t))){r=s("local URLs only")}m(t);if(C(t)==="blocked"){r=s("bad port")}if(t.referrerPolicy===""){t.referrerPolicy=t.policyContainer.referrerPolicy}if(t.referrer!=="no-referrer"){t.referrer=D(t)}if(r===null){r=await(async()=>{const A=d(t);if(T(A,t.url)&&t.responseTainting==="basic"||A.protocol==="data:"||(t.mode==="navigate"||t.mode==="websocket")){t.responseTainting="basic";return await schemeFetch(e)}if(t.mode==="same-origin"){return s('request mode cannot be "same-origin"')}if(t.mode==="no-cors"){if(t.redirect!=="follow"){return s('redirect mode cannot be "follow" for "no-cors" request')}t.responseTainting="opaque";return await schemeFetch(e)}if(!Y(d(t))){return s("URL scheme must be a HTTP(S) scheme")}t.responseTainting="cors";return await httpFetch(e)})()}if(A){return r}if(r.status!==0&&!r.internalResponse){if(t.responseTainting==="cors"){}if(t.responseTainting==="basic"){r=n(r,"basic")}else if(t.responseTainting==="cors"){r=n(r,"cors")}else if(t.responseTainting==="opaque"){r=n(r,"opaque")}else{x(false)}}let o=r.status===0?r:r.internalResponse;if(o.urlList.length===0){o.urlList.push(...t.urlList)}if(!t.timingAllowFailed){r.timingAllowPassed=true}if(r.type==="opaque"&&o.status===206&&o.rangeRequested&&!t.headers.contains("range")){r=o=s()}if(r.status!==0&&(t.method==="HEAD"||t.method==="CONNECT"||j.includes(o.status))){o.body=null;e.controller.dump=true}if(t.integrity){const processBodyError=A=>fetchFinale(e,s(A));if(t.responseTainting==="opaque"||r.body==null){processBodyError(r.error);return}const processBody=A=>{if(!l(A,t.integrity)){processBodyError("integrity mismatch");return}r.body=q(A)[0];fetchFinale(e,r)};await G(r.body,processBody,processBodyError)}else{fetchFinale(e,r)}}function schemeFetch(e){if(N(e)&&e.request.redirectCount===0){return Promise.resolve(o(e))}const{request:A}=e;const{protocol:r}=d(A);switch(r){case"about:":{return Promise.resolve(s("about scheme is not supported"))}case"blob:":{if(!Ce){Ce=t(181).resolveObjectURL}const e=d(A);if(e.search.length!==0){return Promise.resolve(s("NetworkError when attempting to fetch resource."))}const r=Ce(e.toString());if(A.method!=="GET"||!S(r)){return Promise.resolve(s("invalid method"))}const o=q(r);const n=o[0];const a=M(`${n.length}`);const c=o[1]??"";const g=i({statusText:"OK",headersList:[["content-length",{name:"Content-Length",value:a}],["content-type",{name:"Content-Type",value:c}]]});g.body=n;return Promise.resolve(g)}case"data:":{const e=d(A);const t=ae(e);if(t==="failure"){return Promise.resolve(s("failed to fetch the data URL"))}const r=ce(t.mimeType);return Promise.resolve(i({statusText:"OK",headersList:[["content-type",{name:"Content-Type",value:r}]],body:q(t.body)[0]}))}case"file:":{return Promise.resolve(s("not implemented... yet..."))}case"http:":case"https:":{return httpFetch(e).catch((e=>s(e)))}default:{return Promise.resolve(s("unknown scheme"))}}}function finalizeResponse(e,A){e.request.done=true;if(e.processResponseDone!=null){queueMicrotask((()=>e.processResponseDone(A)))}}function fetchFinale(e,A){if(A.type==="error"){A.urlList=[e.request.urlList[0]];A.timingInfo=y({startTime:e.timingInfo.startTime})}const processResponseEndOfBody=()=>{e.request.done=true;if(e.processResponseEndOfBody!=null){queueMicrotask((()=>e.processResponseEndOfBody(A)))}};if(e.processResponse!=null){queueMicrotask((()=>e.processResponse(A)))}if(A.body==null){processResponseEndOfBody()}else{const identityTransformAlgorithm=(e,A)=>{A.enqueue(e)};const e=new ge({start(){},transform:identityTransformAlgorithm,flush:processResponseEndOfBody},{size(){return 1}},{size(){return 1}});A.body={stream:A.body.stream.pipeThrough(e)}}if(e.processResponseConsumeBody!=null){const processBody=t=>e.processResponseConsumeBody(A,t);const processBodyError=t=>e.processResponseConsumeBody(A,t);if(A.body==null){queueMicrotask((()=>processBody(null)))}else{return G(A.body,processBody,processBodyError)}return Promise.resolve()}}async function httpFetch(e){const A=e.request;let t=null;let r=null;const o=e.timingInfo;if(A.serviceWorkers==="all"){}if(t===null){if(A.redirect==="follow"){A.serviceWorkers="none"}r=t=await httpNetworkOrCacheFetch(e);if(A.responseTainting==="cors"&&R(A,t)==="failure"){return s("cors failure")}if(h(A,t)==="failure"){A.timingAllowFailed=true}}if((A.responseTainting==="opaque"||t.type==="opaque")&&b(A.origin,A.client,A.destination,r)==="blocked"){return s("blocked")}if(W.has(r.status)){if(A.redirect!=="manual"){e.controller.connection.destroy()}if(A.redirect==="error"){t=s("unexpected redirect")}else if(A.redirect==="manual"){t=r}else if(A.redirect==="follow"){t=await httpRedirectFetch(e,t)}else{x(false)}}t.timingInfo=o;return t}function httpRedirectFetch(e,A){const t=e.request;const r=A.internalResponse?A.internalResponse:A;let o;try{o=I(r,d(t).hash);if(o==null){return A}}catch(e){return Promise.resolve(s(e))}if(!Y(o)){return Promise.resolve(s("URL scheme must be a HTTP(S) scheme"))}if(t.redirectCount===20){return Promise.resolve(s("redirect count exceeded"))}t.redirectCount+=1;if(t.mode==="cors"&&(o.username||o.password)&&!T(t,o)){return Promise.resolve(s('cross origin not allowed for request mode "cors"'))}if(t.responseTainting==="cors"&&(o.username||o.password)){return Promise.resolve(s('URL cannot contain credentials for request mode "cors"'))}if(r.status!==303&&t.body!=null&&t.body.source==null){return Promise.resolve(s())}if([301,302].includes(r.status)&&t.method==="POST"||r.status===303&&!Qe.includes(t.method)){t.method="GET";t.body=null;for(const e of X){t.headersList.delete(e)}}if(!T(d(t),o)){t.headersList.delete("authorization");t.headersList.delete("proxy-authorization",true);t.headersList.delete("cookie");t.headersList.delete("host")}if(t.body!=null){x(t.body.source!=null);t.body=q(t.body.source)[0]}const n=e.timingInfo;n.redirectEndTime=n.postRedirectStartTime=k(e.crossOriginIsolatedCapability);if(n.redirectStartTime===0){n.redirectStartTime=n.startTime}t.urlList.push(o);p(t,r);return mainFetch(e,true)}async function httpNetworkOrCacheFetch(e,A=false,t=false){const r=e.request;let n=null;let i=null;let a=null;const c=null;const E=false;if(r.window==="no-window"&&r.redirect==="error"){n=e;i=r}else{i=g(r);n={...e};n.request=i}const l=r.credentials==="include"||r.credentials==="same-origin"&&r.responseTainting==="basic";const u=i.body?i.body.length:null;let Q=null;if(i.body==null&&["POST","PUT"].includes(i.method)){Q="0"}if(u!=null){Q=M(`${u}`)}if(Q!=null){i.headersList.append("content-length",Q)}if(u!=null&&i.keepalive){}if(i.referrer instanceof URL){i.headersList.append("referer",M(i.referrer.href))}B(i);w(i);if(!i.headersList.contains("user-agent")){i.headersList.append("user-agent",typeof esbuildDetection==="undefined"?"undici":"node")}if(i.cache==="default"&&(i.headersList.contains("if-modified-since")||i.headersList.contains("if-none-match")||i.headersList.contains("if-unmodified-since")||i.headersList.contains("if-match")||i.headersList.contains("if-range"))){i.cache="no-store"}if(i.cache==="no-cache"&&!i.preventNoCacheCacheControlHeaderModification&&!i.headersList.contains("cache-control")){i.headersList.append("cache-control","max-age=0")}if(i.cache==="no-store"||i.cache==="reload"){if(!i.headersList.contains("pragma")){i.headersList.append("pragma","no-cache")}if(!i.headersList.contains("cache-control")){i.headersList.append("cache-control","no-cache")}}if(i.headersList.contains("range")){i.headersList.append("accept-encoding","identity")}if(!i.headersList.contains("accept-encoding")){if(O(d(i))){i.headersList.append("accept-encoding","br, gzip, deflate")}else{i.headersList.append("accept-encoding","gzip, deflate")}}i.headersList.delete("host");if(l){}if(c==null){i.cache="no-store"}if(i.mode!=="no-store"&&i.mode!=="reload"){}if(a==null){if(i.mode==="only-if-cached"){return s("only if cached")}const e=await httpNetworkFetch(n,l,t);if(!Z.has(i.method)&&e.status>=200&&e.status<=399){}if(E&&e.status===304){}if(a==null){a=e}}a.urlList=[...i.urlList];if(i.headersList.contains("range")){a.rangeRequested=true}a.requestIncludesCredentials=l;if(a.status===407){if(r.window==="no-window"){return s()}if(N(e)){return o(e)}return s("proxy authentication required")}if(a.status===421&&!t&&(r.body==null||r.body.source!=null)){if(N(e)){return o(e)}e.controller.connection.destroy();a=await httpNetworkOrCacheFetch(e,A,true)}if(A){}return a}async function httpNetworkFetch(e,A=false,r=false){x(!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 n=e.request;let c=null;const g=e.timingInfo;const l=null;if(l==null){n.cache="no-store"}const u=r?"yes":"no";if(n.mode==="websocket"){}else{}let Q=null;if(n.body==null&&e.processRequestEndOfBody){queueMicrotask((()=>e.processRequestEndOfBody()))}else if(n.body!=null){const processBodyChunk=async function*(A){if(N(e)){return}yield A;e.processRequestBodyChunkLength?.(A.byteLength)};const processEndOfBody=()=>{if(N(e)){return}if(e.processRequestEndOfBody){e.processRequestEndOfBody()}};const processBodyError=A=>{if(N(e)){return}if(A.name==="AbortError"){e.controller.abort()}else{e.controller.terminate(A)}};Q=async function*(){try{for await(const e of n.body.stream){yield*processBodyChunk(e)}processEndOfBody()}catch(e){processBodyError(e)}}()}try{const{body:A,status:t,statusText:r,headersList:s,socket:o}=await dispatch({body:Q});if(o){c=i({status:t,statusText:r,headersList:s,socket:o})}else{const o=A[Symbol.asyncIterator]();e.controller.next=()=>o.next();c=i({status:t,statusText:r,headersList:s})}}catch(A){if(A.name==="AbortError"){e.controller.connection.destroy();return o(e,A)}return s(A)}const pullAlgorithm=()=>{e.controller.resume()};const cancelAlgorithm=A=>{e.controller.abort(A)};if(!he){he=t(3774).ReadableStream}const C=new he({async start(A){e.controller.controller=A},async pull(e){await pullAlgorithm(e)},async cancel(e){await cancelAlgorithm(e)}},{highWaterMark:0,size(){return 1}});c.body={stream:C};e.controller.on("terminated",onAborted);e.controller.resume=async()=>{while(true){let A;let t;try{const{done:t,value:r}=await e.controller.next();if(U(e)){break}A=t?undefined:r}catch(r){if(e.controller.ended&&!g.encodedBodySize){A=undefined}else{A=r;t=true}}if(A===undefined){v(e.controller.controller);finalizeResponse(e,c);return}g.decodedBodySize+=A?.byteLength??0;if(t){e.controller.terminate(A);return}e.controller.controller.enqueue(new Uint8Array(A));if(se(C)){e.controller.terminate();return}if(!e.controller.controller.desiredSize){return}}};function onAborted(A){if(U(e)){c.aborted=true;if(oe(C)){e.controller.controller.error(e.controller.serializedAbortReason)}}else{if(oe(C)){e.controller.controller.error(new TypeError("terminated",{cause:L(A)?A:undefined}))}}e.controller.connection.destroy()}return c;async function dispatch({body:A}){const t=d(n);const r=e.controller.dispatcher;return new Promise(((s,o)=>r.dispatch({path:t.pathname+t.search,origin:t.origin,method:n.method,body:e.controller.dispatcher.isMockActive?n.body&&(n.body.source||n.body.stream):A,headers:n.headersList.entries,maxRedirections:0,upgrade:n.mode==="websocket"?"websocket":undefined},{body:null,abort:null,onConnect(A){const{connection:t}=e.controller;if(t.destroyed){A(new z("The operation was aborted.","AbortError"))}else{e.controller.on("terminated",A);this.abort=t.abort=A}},onHeaders(e,A,t,r){if(e<200){return}let o=[];let i="";const c=new a;if(Array.isArray(A)){for(let e=0;ee.trim()))}else if(t.toLowerCase()==="location"){i=r}c[$].append(t,r)}}else{const e=Object.keys(A);for(const t of e){const e=A[t];if(t.toLowerCase()==="content-encoding"){o=e.toLowerCase().split(",").map((e=>e.trim())).reverse()}else if(t.toLowerCase()==="location"){i=e}c[$].append(t,e)}}this.body=new Ae({read:t});const g=[];const l=n.redirect==="follow"&&i&&W.has(e);if(n.method!=="HEAD"&&n.method!=="CONNECT"&&!j.includes(e)&&!l){for(const e of o){if(e==="x-gzip"||e==="gzip"){g.push(E.createGunzip({flush:E.constants.Z_SYNC_FLUSH,finishFlush:E.constants.Z_SYNC_FLUSH}))}else if(e==="deflate"){g.push(E.createInflate())}else if(e==="br"){g.push(E.createBrotliDecompress())}else{g.length=0;break}}}s({status:e,statusText:r,headersList:c[$],body:g.length?te(this.body,...g,(()=>{})):this.body.on("error",(()=>{}))});return true},onData(A){if(e.controller.dump){return}const t=A;g.encodedBodySize+=t.byteLength;return this.body.push(t)},onComplete(){if(this.abort){e.controller.off("terminated",this.abort)}e.controller.ended=true;this.body.push(null)},onError(A){if(this.abort){e.controller.off("terminated",this.abort)}this.body?.destroy(A);e.controller.terminate(A);o(A)},onUpgrade(e,A,t){if(e!==101){return}const r=new a;for(let e=0;e{"use strict";const{extractBody:r,mixinBody:s,cloneBody:o}=t(8689);const{Headers:n,fill:i,HeadersList:a}=t(1815);const{FinalizationRegistry:c}=t(4904)();const g=t(2806);const{isValidHTTPToken:E,sameOrigin:l,normalizeMethod:u,makePolicyContainer:Q,normalizeMethodRecord:C}=t(9913);const{forbiddenMethodsSet:h,corsSafeListedMethodsSet:B,referrerPolicy:I,requestRedirect:d,requestMode:p,requestCredentials:m,requestCache:y,requestDuplex:w}=t(6040);const{kEnumerableProperty:R}=g;const{kHeaders:b,kSignal:D,kState:k,kGuard:F,kRealm:S}=t(648);const{webidl:T}=t(6684);const{getGlobalOrigin:N}=t(574);const{URLSerializer:U}=t(7160);const{kHeadersList:L,kConstruct:G}=t(7781);const v=t(2613);const{getMaxListeners:M,setMaxListeners:H,getEventListeners:Y,defaultMaxListeners:O}=t(4434);let _=globalThis.TransformStream;const P=Symbol("abortController");const J=new c((({signal:e,abort:A})=>{e.removeEventListener("abort",A)}));class Request{constructor(e,A={}){if(e===G){return}T.argumentLengthCheck(arguments,1,{header:"Request constructor"});e=T.converters.RequestInfo(e);A=T.converters.RequestInit(A);this[S]={settingsObject:{baseUrl:N(),get origin(){return this.baseUrl?.origin},policyContainer:Q()}};let s=null;let o=null;const c=this[S].settingsObject.baseUrl;let I=null;if(typeof e==="string"){let A;try{A=new URL(e,c)}catch(A){throw new TypeError("Failed to parse URL from "+e,{cause:A})}if(A.username||A.password){throw new TypeError("Request cannot be constructed from a URL that includes credentials: "+e)}s=makeRequest({urlList:[A]});o="cors"}else{v(e instanceof Request);s=e[k];I=e[D]}const d=this[S].settingsObject.origin;let p="client";if(s.window?.constructor?.name==="EnvironmentSettingsObject"&&l(s.window,d)){p=s.window}if(A.window!=null){throw new TypeError(`'window' option '${p}' must be null`)}if("window"in A){p="no-window"}s=makeRequest({method:s.method,headersList:s.headersList,unsafeRequest:s.unsafeRequest,client:this[S].settingsObject,window:p,priority:s.priority,origin:s.origin,referrer:s.referrer,referrerPolicy:s.referrerPolicy,mode:s.mode,credentials:s.credentials,cache:s.cache,redirect:s.redirect,integrity:s.integrity,keepalive:s.keepalive,reloadNavigation:s.reloadNavigation,historyNavigation:s.historyNavigation,urlList:[...s.urlList]});const m=Object.keys(A).length!==0;if(m){if(s.mode==="navigate"){s.mode="same-origin"}s.reloadNavigation=false;s.historyNavigation=false;s.origin="client";s.referrer="client";s.referrerPolicy="";s.url=s.urlList[s.urlList.length-1];s.urlList=[s.url]}if(A.referrer!==undefined){const e=A.referrer;if(e===""){s.referrer="no-referrer"}else{let A;try{A=new URL(e,c)}catch(A){throw new TypeError(`Referrer "${e}" is not a valid URL.`,{cause:A})}if(A.protocol==="about:"&&A.hostname==="client"||d&&!l(A,this[S].settingsObject.baseUrl)){s.referrer="client"}else{s.referrer=A}}}if(A.referrerPolicy!==undefined){s.referrerPolicy=A.referrerPolicy}let y;if(A.mode!==undefined){y=A.mode}else{y=o}if(y==="navigate"){throw T.errors.exception({header:"Request constructor",message:"invalid request mode navigate."})}if(y!=null){s.mode=y}if(A.credentials!==undefined){s.credentials=A.credentials}if(A.cache!==undefined){s.cache=A.cache}if(s.cache==="only-if-cached"&&s.mode!=="same-origin"){throw new TypeError("'only-if-cached' can be set only with 'same-origin' mode")}if(A.redirect!==undefined){s.redirect=A.redirect}if(A.integrity!=null){s.integrity=String(A.integrity)}if(A.keepalive!==undefined){s.keepalive=Boolean(A.keepalive)}if(A.method!==undefined){let e=A.method;if(!E(e)){throw new TypeError(`'${e}' is not a valid HTTP method.`)}if(h.has(e.toUpperCase())){throw new TypeError(`'${e}' HTTP method is unsupported.`)}e=C[e]??u(e);s.method=e}if(A.signal!==undefined){I=A.signal}this[k]=s;const w=new AbortController;this[D]=w.signal;this[D][S]=this[S];if(I!=null){if(!I||typeof I.aborted!=="boolean"||typeof I.addEventListener!=="function"){throw new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")}if(I.aborted){w.abort(I.reason)}else{this[P]=w;const e=new WeakRef(w);const abort=function(){const A=e.deref();if(A!==undefined){A.abort(this.reason)}};try{if(typeof M==="function"&&M(I)===O){H(100,I)}else if(Y(I,"abort").length>=O){H(100,I)}}catch{}g.addAbortListener(I,abort);J.register(w,{signal:I,abort:abort})}}this[b]=new n(G);this[b][L]=s.headersList;this[b][F]="request";this[b][S]=this[S];if(y==="no-cors"){if(!B.has(s.method)){throw new TypeError(`'${s.method} is unsupported in no-cors mode.`)}this[b][F]="request-no-cors"}if(m){const e=this[b][L];const t=A.headers!==undefined?A.headers:new a(e);e.clear();if(t instanceof a){for(const[A,r]of t){e.append(A,r)}e.cookies=t.cookies}else{i(this[b],t)}}const R=e instanceof Request?e[k].body:null;if((A.body!=null||R!=null)&&(s.method==="GET"||s.method==="HEAD")){throw new TypeError("Request with GET/HEAD method cannot have body.")}let U=null;if(A.body!=null){const[e,t]=r(A.body,s.keepalive);U=e;if(t&&!this[b][L].contains("content-type")){this[b].append("content-type",t)}}const V=U??R;if(V!=null&&V.source==null){if(U!=null&&A.duplex==null){throw new TypeError("RequestInit: duplex option is required when sending a body.")}if(s.mode!=="same-origin"&&s.mode!=="cors"){throw new TypeError('If request is made from ReadableStream, mode should be "same-origin" or "cors"')}s.useCORSPreflightFlag=true}let x=V;if(U==null&&R!=null){if(g.isDisturbed(R.stream)||R.stream.locked){throw new TypeError("Cannot construct a Request with a Request object that has already been used.")}if(!_){_=t(3774).TransformStream}const e=new _;R.stream.pipeThrough(e);x={source:R.source,length:R.length,stream:e.readable}}this[k].body=x}get method(){T.brandCheck(this,Request);return this[k].method}get url(){T.brandCheck(this,Request);return U(this[k].url)}get headers(){T.brandCheck(this,Request);return this[b]}get destination(){T.brandCheck(this,Request);return this[k].destination}get referrer(){T.brandCheck(this,Request);if(this[k].referrer==="no-referrer"){return""}if(this[k].referrer==="client"){return"about:client"}return this[k].referrer.toString()}get referrerPolicy(){T.brandCheck(this,Request);return this[k].referrerPolicy}get mode(){T.brandCheck(this,Request);return this[k].mode}get credentials(){return this[k].credentials}get cache(){T.brandCheck(this,Request);return this[k].cache}get redirect(){T.brandCheck(this,Request);return this[k].redirect}get integrity(){T.brandCheck(this,Request);return this[k].integrity}get keepalive(){T.brandCheck(this,Request);return this[k].keepalive}get isReloadNavigation(){T.brandCheck(this,Request);return this[k].reloadNavigation}get isHistoryNavigation(){T.brandCheck(this,Request);return this[k].historyNavigation}get signal(){T.brandCheck(this,Request);return this[D]}get body(){T.brandCheck(this,Request);return this[k].body?this[k].body.stream:null}get bodyUsed(){T.brandCheck(this,Request);return!!this[k].body&&g.isDisturbed(this[k].body.stream)}get duplex(){T.brandCheck(this,Request);return"half"}clone(){T.brandCheck(this,Request);if(this.bodyUsed||this.body?.locked){throw new TypeError("unusable")}const e=cloneRequest(this[k]);const A=new Request(G);A[k]=e;A[S]=this[S];A[b]=new n(G);A[b][L]=e.headersList;A[b][F]=this[b][F];A[b][S]=this[b][S];const t=new AbortController;if(this.signal.aborted){t.abort(this.signal.reason)}else{g.addAbortListener(this.signal,(()=>{t.abort(this.signal.reason)}))}A[D]=t.signal;return A}}s(Request);function makeRequest(e){const A={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};A.url=A.urlList[0];return A}function cloneRequest(e){const A=makeRequest({...e,body:null});if(e.body!=null){A.body=o(e.body)}return A}Object.defineProperties(Request.prototype,{method:R,url:R,headers:R,redirect:R,clone:R,signal:R,duplex:R,destination:R,body:R,bodyUsed:R,isHistoryNavigation:R,isReloadNavigation:R,keepalive:R,integrity:R,cache:R,credentials:R,attribute:R,referrerPolicy:R,referrer:R,mode:R,[Symbol.toStringTag]:{value:"Request",configurable:true}});T.converters.Request=T.interfaceConverter(Request);T.converters.RequestInfo=function(e){if(typeof e==="string"){return T.converters.USVString(e)}if(e instanceof Request){return T.converters.Request(e)}return T.converters.USVString(e)};T.converters.AbortSignal=T.interfaceConverter(AbortSignal);T.converters.RequestInit=T.dictionaryConverter([{key:"method",converter:T.converters.ByteString},{key:"headers",converter:T.converters.HeadersInit},{key:"body",converter:T.nullableConverter(T.converters.BodyInit)},{key:"referrer",converter:T.converters.USVString},{key:"referrerPolicy",converter:T.converters.DOMString,allowedValues:I},{key:"mode",converter:T.converters.DOMString,allowedValues:p},{key:"credentials",converter:T.converters.DOMString,allowedValues:m},{key:"cache",converter:T.converters.DOMString,allowedValues:y},{key:"redirect",converter:T.converters.DOMString,allowedValues:d},{key:"integrity",converter:T.converters.DOMString},{key:"keepalive",converter:T.converters.boolean},{key:"signal",converter:T.nullableConverter((e=>T.converters.AbortSignal(e,{strict:false})))},{key:"window",converter:T.converters.any},{key:"duplex",converter:T.converters.DOMString,allowedValues:w}]);e.exports={Request:Request,makeRequest:makeRequest}},7862:(e,A,t)=>{"use strict";const{Headers:r,HeadersList:s,fill:o}=t(1815);const{extractBody:n,cloneBody:i,mixinBody:a}=t(8689);const c=t(2806);const{kEnumerableProperty:g}=c;const{isValidReasonPhrase:E,isCancelled:l,isAborted:u,isBlobLike:Q,serializeJavascriptValueToJSONString:C,isErrorLike:h,isomorphicEncode:B}=t(9913);const{redirectStatusSet:I,nullBodyStatus:d,DOMException:p}=t(6040);const{kState:m,kHeaders:y,kGuard:w,kRealm:R}=t(648);const{webidl:b}=t(6684);const{FormData:D}=t(1499);const{getGlobalOrigin:k}=t(574);const{URLSerializer:F}=t(7160);const{kHeadersList:S,kConstruct:T}=t(7781);const N=t(2613);const{types:U}=t(9023);const L=globalThis.ReadableStream||t(3774).ReadableStream;const G=new TextEncoder("utf-8");class Response{static error(){const e={settingsObject:{}};const A=new Response;A[m]=makeNetworkError();A[R]=e;A[y][S]=A[m].headersList;A[y][w]="immutable";A[y][R]=e;return A}static json(e,A={}){b.argumentLengthCheck(arguments,1,{header:"Response.json"});if(A!==null){A=b.converters.ResponseInit(A)}const t=G.encode(C(e));const r=n(t);const s={settingsObject:{}};const o=new Response;o[R]=s;o[y][w]="response";o[y][R]=s;initializeResponse(o,A,{body:r[0],type:"application/json"});return o}static redirect(e,A=302){const t={settingsObject:{}};b.argumentLengthCheck(arguments,1,{header:"Response.redirect"});e=b.converters.USVString(e);A=b.converters["unsigned short"](A);let r;try{r=new URL(e,k())}catch(A){throw Object.assign(new TypeError("Failed to parse URL from "+e),{cause:A})}if(!I.has(A)){throw new RangeError("Invalid status code "+A)}const s=new Response;s[R]=t;s[y][w]="immutable";s[y][R]=t;s[m].status=A;const o=B(F(r));s[m].headersList.append("location",o);return s}constructor(e=null,A={}){if(e!==null){e=b.converters.BodyInit(e)}A=b.converters.ResponseInit(A);this[R]={settingsObject:{}};this[m]=makeResponse({});this[y]=new r(T);this[y][w]="response";this[y][S]=this[m].headersList;this[y][R]=this[R];let t=null;if(e!=null){const[A,r]=n(e);t={body:A,type:r}}initializeResponse(this,A,t)}get type(){b.brandCheck(this,Response);return this[m].type}get url(){b.brandCheck(this,Response);const e=this[m].urlList;const A=e[e.length-1]??null;if(A===null){return""}return F(A,true)}get redirected(){b.brandCheck(this,Response);return this[m].urlList.length>1}get status(){b.brandCheck(this,Response);return this[m].status}get ok(){b.brandCheck(this,Response);return this[m].status>=200&&this[m].status<=299}get statusText(){b.brandCheck(this,Response);return this[m].statusText}get headers(){b.brandCheck(this,Response);return this[y]}get body(){b.brandCheck(this,Response);return this[m].body?this[m].body.stream:null}get bodyUsed(){b.brandCheck(this,Response);return!!this[m].body&&c.isDisturbed(this[m].body.stream)}clone(){b.brandCheck(this,Response);if(this.bodyUsed||this.body&&this.body.locked){throw b.errors.exception({header:"Response.clone",message:"Body has already been consumed."})}const e=cloneResponse(this[m]);const A=new Response;A[m]=e;A[R]=this[R];A[y][S]=e.headersList;A[y][w]=this[y][w];A[y][R]=this[y][R];return A}}a(Response);Object.defineProperties(Response.prototype,{type:g,url:g,status:g,ok:g,redirected:g,statusText:g,headers:g,clone:g,body:g,bodyUsed:g,[Symbol.toStringTag]:{value:"Response",configurable:true}});Object.defineProperties(Response,{json:g,redirect:g,error:g});function cloneResponse(e){if(e.internalResponse){return filterResponse(cloneResponse(e.internalResponse),e.type)}const A=makeResponse({...e,body:null});if(e.body!=null){A.body=i(e.body)}return A}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 s(e.headersList):new s,urlList:e.urlList?[...e.urlList]:[]}}function makeNetworkError(e){const A=h(e);return makeResponse({type:"error",status:0,error:A?e:new Error(e?String(e):e),aborted:e&&e.name==="AbortError"})}function makeFilteredResponse(e,A){A={internalResponse:e,...A};return new Proxy(e,{get(e,t){return t in A?A[t]:e[t]},set(e,t,r){N(!(t in A));e[t]=r;return true}})}function filterResponse(e,A){if(A==="basic"){return makeFilteredResponse(e,{type:"basic",headersList:e.headersList})}else if(A==="cors"){return makeFilteredResponse(e,{type:"cors",headersList:e.headersList})}else if(A==="opaque"){return makeFilteredResponse(e,{type:"opaque",urlList:Object.freeze([]),status:0,statusText:"",body:null})}else if(A==="opaqueredirect"){return makeFilteredResponse(e,{type:"opaqueredirect",status:0,statusText:"",headersList:[],body:null})}else{N(false)}}function makeAppropriateNetworkError(e,A=null){N(l(e));return u(e)?makeNetworkError(Object.assign(new p("The operation was aborted.","AbortError"),{cause:A})):makeNetworkError(Object.assign(new p("Request was cancelled."),{cause:A}))}function initializeResponse(e,A,t){if(A.status!==null&&(A.status<200||A.status>599)){throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.')}if("statusText"in A&&A.statusText!=null){if(!E(String(A.statusText))){throw new TypeError("Invalid statusText")}}if("status"in A&&A.status!=null){e[m].status=A.status}if("statusText"in A&&A.statusText!=null){e[m].statusText=A.statusText}if("headers"in A&&A.headers!=null){o(e[y],A.headers)}if(t){if(d.includes(e.status)){throw b.errors.exception({header:"Response constructor",message:"Invalid response status code "+e.status})}e[m].body=t.body;if(t.type!=null&&!e[m].headersList.contains("Content-Type")){e[m].headersList.append("content-type",t.type)}}}b.converters.ReadableStream=b.interfaceConverter(L);b.converters.FormData=b.interfaceConverter(D);b.converters.URLSearchParams=b.interfaceConverter(URLSearchParams);b.converters.XMLHttpRequestBodyInit=function(e){if(typeof e==="string"){return b.converters.USVString(e)}if(Q(e)){return b.converters.Blob(e,{strict:false})}if(U.isArrayBuffer(e)||U.isTypedArray(e)||U.isDataView(e)){return b.converters.BufferSource(e)}if(c.isFormDataLike(e)){return b.converters.FormData(e,{strict:false})}if(e instanceof URLSearchParams){return b.converters.URLSearchParams(e)}return b.converters.DOMString(e)};b.converters.BodyInit=function(e){if(e instanceof L){return b.converters.ReadableStream(e)}if(e?.[Symbol.asyncIterator]){return e}return b.converters.XMLHttpRequestBodyInit(e)};b.converters.ResponseInit=b.dictionaryConverter([{key:"status",converter:b.converters["unsigned short"],defaultValue:200},{key:"statusText",converter:b.converters.ByteString,defaultValue:""},{key:"headers",converter:b.converters.HeadersInit}]);e.exports={makeNetworkError:makeNetworkError,makeResponse:makeResponse,makeAppropriateNetworkError:makeAppropriateNetworkError,filterResponse:filterResponse,Response:Response,cloneResponse:cloneResponse}},648:e=>{"use strict";e.exports={kUrl:Symbol("url"),kHeaders:Symbol("headers"),kSignal:Symbol("signal"),kState:Symbol("state"),kGuard:Symbol("guard"),kRealm:Symbol("realm")}},9913:(e,A,t)=>{"use strict";const{redirectStatusSet:r,referrerPolicySet:s,badPortsSet:o}=t(6040);const{getGlobalOrigin:n}=t(574);const{performance:i}=t(2987);const{isBlobLike:a,toUSVString:c,ReadableStreamFrom:g}=t(2806);const E=t(2613);const{isUint8Array:l}=t(8253);let u=[];let Q;try{Q=t(6982);const e=["sha256","sha384","sha512"];u=Q.getHashes().filter((A=>e.includes(A)))}catch{}function responseURL(e){const A=e.urlList;const t=A.length;return t===0?null:A[t-1].toString()}function responseLocationURL(e,A){if(!r.has(e.status)){return null}let t=e.headersList.get("location");if(t!==null&&isValidHeaderValue(t)){t=new URL(t,responseURL(e))}if(t&&!t.hash){t.hash=A}return t}function requestCurrentURL(e){return e.urlList[e.urlList.length-1]}function requestBadPort(e){const A=requestCurrentURL(e);if(urlIsHttpHttpsScheme(A)&&o.has(A.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 A=0;A=32&&t<=126||t>=128&&t<=255)){return false}}return true}function isTokenCharCode(e){switch(e){case 34:case 40:case 41:case 44:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 123:case 125:return false;default:return e>=33&&e<=126}}function isValidHTTPToken(e){if(e.length===0){return false}for(let A=0;A0){for(let e=r.length;e!==0;e--){const A=r[e-1].trim();if(s.has(A)){o=A;break}}}if(o!==""){e.referrerPolicy=o}}function crossOriginResourcePolicyCheck(){return"allowed"}function corsCheck(){return"success"}function TAOCheck(){return"success"}function appendFetchMetadata(e){let A=null;A=e.mode;e.headersList.set("sec-fetch-mode",A)}function appendRequestOriginHeader(e){let A=e.origin;if(e.responseTainting==="cors"||e.mode==="websocket"){if(A){e.headersList.append("origin",A)}}else if(e.method!=="GET"&&e.method!=="HEAD"){switch(e.referrerPolicy){case"no-referrer":A=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))){A=null}break;case"same-origin":if(!sameOrigin(e,requestCurrentURL(e))){A=null}break;default:}if(A){e.headersList.append("origin",A)}}}function coarsenedSharedCurrentTime(e){return i.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 A=e.referrerPolicy;E(A);let t=null;if(e.referrer==="client"){const e=n();if(!e||e.origin==="null"){return"no-referrer"}t=new URL(e)}else if(e.referrer instanceof URL){t=e.referrer}let r=stripURLForReferrer(t);const s=stripURLForReferrer(t,true);if(r.toString().length>4096){r=s}const o=sameOrigin(e,r);const i=isURLPotentiallyTrustworthy(r)&&!isURLPotentiallyTrustworthy(e.url);switch(A){case"origin":return s!=null?s:stripURLForReferrer(t,true);case"unsafe-url":return r;case"same-origin":return o?s:"no-referrer";case"origin-when-cross-origin":return o?r:s;case"strict-origin-when-cross-origin":{const A=requestCurrentURL(e);if(sameOrigin(r,A)){return r}if(isURLPotentiallyTrustworthy(r)&&!isURLPotentiallyTrustworthy(A)){return"no-referrer"}return s}case"strict-origin":case"no-referrer-when-downgrade":default:return i?"no-referrer":s}}function stripURLForReferrer(e,A){E(e instanceof URL);if(e.protocol==="file:"||e.protocol==="about:"||e.protocol==="blank:"){return"no-referrer"}e.username="";e.password="";e.hash="";if(A){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 A=new URL(e);if(A.protocol==="https:"||A.protocol==="wss:"){return true}if(/^127(?:\.[0-9]+){0,2}\.[0-9]+$|^\[(?:0*:)*?:?0*1\]$/.test(A.hostname)||(A.hostname==="localhost"||A.hostname.includes("localhost."))||A.hostname.endsWith(".localhost")){return true}return false}}function bytesMatch(e,A){if(Q===undefined){return true}const t=parseMetadata(A);if(t==="no metadata"){return true}if(t.length===0){return true}const r=getStrongestMetadata(t);const s=filterMetadataListByAlgorithm(t,r);for(const A of s){const t=A.algo;const r=A.hash;let s=Q.createHash(t).update(e).digest("base64");if(s[s.length-1]==="="){if(s[s.length-2]==="="){s=s.slice(0,-2)}else{s=s.slice(0,-1)}}if(compareBase64Mixed(s,r)){return true}}return false}const C=/(?sha256|sha384|sha512)-((?[A-Za-z0-9+/]+|[A-Za-z0-9_-]+)={0,2}(?:\s|$)( +[!-~]*)?)?/i;function parseMetadata(e){const A=[];let t=true;for(const r of e.split(" ")){t=false;const e=C.exec(r);if(e===null||e.groups===undefined||e.groups.algo===undefined){continue}const s=e.groups.algo.toLowerCase();if(u.includes(s)){A.push(e.groups)}}if(t===true){return"no metadata"}return A}function getStrongestMetadata(e){let A=e[0].algo;if(A[3]==="5"){return A}for(let t=1;t{e=t;A=r}));return{promise:t,resolve:e,reject:A}}function isAborted(e){return e.controller.state==="aborted"}function isCancelled(e){return e.controller.state==="aborted"||e.controller.state==="terminated"}const h={delete:"DELETE",DELETE:"DELETE",get:"GET",GET:"GET",head:"HEAD",HEAD:"HEAD",options:"OPTIONS",OPTIONS:"OPTIONS",post:"POST",POST:"POST",put:"PUT",PUT:"PUT"};Object.setPrototypeOf(h,null);function normalizeMethod(e){return h[e.toLowerCase()]??e}function serializeJavascriptValueToJSONString(e){const A=JSON.stringify(e);if(A===undefined){throw new TypeError("Value is not JSON serializable")}E(typeof A==="string");return A}const B=Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));function makeIterator(e,A,t){const r={index:0,kind:t,target:e};const s={next(){if(Object.getPrototypeOf(this)!==s){throw new TypeError(`'next' called on an object that does not implement interface ${A} Iterator.`)}const{index:e,kind:t,target:o}=r;const n=o();const i=n.length;if(e>=i){return{value:undefined,done:true}}const a=n[e];r.index=e+1;return iteratorResult(a,t)},[Symbol.toStringTag]:`${A} Iterator`};Object.setPrototypeOf(s,B);return Object.setPrototypeOf({},s)}function iteratorResult(e,A){let t;switch(A){case"key":{t=e[0];break}case"value":{t=e[1];break}case"key+value":{t=e;break}}return{value:t,done:false}}async function fullyReadBody(e,A,t){const r=A;const s=t;let o;try{o=e.stream.getReader()}catch(e){s(e);return}try{const e=await readAllBytes(o);r(e)}catch(e){s(e)}}let I=globalThis.ReadableStream;function isReadableStreamLike(e){if(!I){I=t(3774).ReadableStream}return e instanceof I||e[Symbol.toStringTag]==="ReadableStream"&&typeof e.tee==="function"}const d=65535;function isomorphicDecode(e){if(e.lengthe+String.fromCharCode(A)),"")}function readableStreamClose(e){try{e.close()}catch(e){if(!e.message.includes("Controller is already closed")){throw e}}}function isomorphicEncode(e){for(let A=0;AObject.prototype.hasOwnProperty.call(e,A));e.exports={isAborted:isAborted,isCancelled:isCancelled,createDeferredPromise:createDeferredPromise,ReadableStreamFrom:g,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:p,isErrorLike:isErrorLike,fullyReadBody:fullyReadBody,bytesMatch:bytesMatch,isReadableStreamLike:isReadableStreamLike,readableStreamClose:readableStreamClose,isomorphicEncode:isomorphicEncode,isomorphicDecode:isomorphicDecode,urlIsLocal:urlIsLocal,urlHasHttpsScheme:urlHasHttpsScheme,urlIsHttpHttpsScheme:urlIsHttpHttpsScheme,readAllBytes:readAllBytes,normalizeMethodRecord:h,parseMetadata:parseMetadata}},6684:(e,A,t)=>{"use strict";const{types:r}=t(9023);const{hasOwn:s,toUSVString:o}=t(9913);const n={};n.converters={};n.util={};n.errors={};n.errors.exception=function(e){return new TypeError(`${e.header}: ${e.message}`)};n.errors.conversionFailed=function(e){const A=e.types.length===1?"":" one of";const t=`${e.argument} could not be converted to`+`${A}: ${e.types.join(", ")}.`;return n.errors.exception({header:e.prefix,message:t})};n.errors.invalidArgument=function(e){return n.errors.exception({header:e.prefix,message:`"${e.value}" is an invalid ${e.type}.`})};n.brandCheck=function(e,A,t=undefined){if(t?.strict!==false&&!(e instanceof A)){throw new TypeError("Illegal invocation")}else{return e?.[Symbol.toStringTag]===A.prototype[Symbol.toStringTag]}};n.argumentLengthCheck=function({length:e},A,t){if(es){throw n.errors.exception({header:"Integer conversion",message:`Value must be between ${o}-${s}, got ${i}.`})}return i}if(!Number.isNaN(i)&&r.clamp===true){i=Math.min(Math.max(i,o),s);if(Math.floor(i)%2===0){i=Math.floor(i)}else{i=Math.ceil(i)}return i}if(Number.isNaN(i)||i===0&&Object.is(0,i)||i===Number.POSITIVE_INFINITY||i===Number.NEGATIVE_INFINITY){return 0}i=n.util.IntegerPart(i);i=i%Math.pow(2,A);if(t==="signed"&&i>=Math.pow(2,A)-1){return i-Math.pow(2,A)}return i};n.util.IntegerPart=function(e){const A=Math.floor(Math.abs(e));if(e<0){return-1*A}return A};n.sequenceConverter=function(e){return A=>{if(n.util.Type(A)!=="Object"){throw n.errors.exception({header:"Sequence",message:`Value of type ${n.util.Type(A)} is not an Object.`})}const t=A?.[Symbol.iterator]?.();const r=[];if(t===undefined||typeof t.next!=="function"){throw n.errors.exception({header:"Sequence",message:"Object is not an iterator."})}while(true){const{done:A,value:s}=t.next();if(A){break}r.push(e(s))}return r}};n.recordConverter=function(e,A){return t=>{if(n.util.Type(t)!=="Object"){throw n.errors.exception({header:"Record",message:`Value of type ${n.util.Type(t)} is not an Object.`})}const s={};if(!r.isProxy(t)){const r=Object.keys(t);for(const o of r){const r=e(o);const n=A(t[o]);s[r]=n}return s}const o=Reflect.ownKeys(t);for(const r of o){const o=Reflect.getOwnPropertyDescriptor(t,r);if(o?.enumerable){const o=e(r);const n=A(t[r]);s[o]=n}}return s}};n.interfaceConverter=function(e){return(A,t={})=>{if(t.strict!==false&&!(A instanceof e)){throw n.errors.exception({header:e.name,message:`Expected ${A} to be an instance of ${e.name}.`})}return A}};n.dictionaryConverter=function(e){return A=>{const t=n.util.Type(A);const r={};if(t==="Null"||t==="Undefined"){return r}else if(t!=="Object"){throw n.errors.exception({header:"Dictionary",message:`Expected ${A} to be one of: Null, Undefined, Object.`})}for(const t of e){const{key:e,defaultValue:o,required:i,converter:a}=t;if(i===true){if(!s(A,e)){throw n.errors.exception({header:"Dictionary",message:`Missing required key "${e}".`})}}let c=A[e];const g=s(t,"defaultValue");if(g&&c!==null){c=c??o}if(i||g||c!==undefined){c=a(c);if(t.allowedValues&&!t.allowedValues.includes(c)){throw n.errors.exception({header:"Dictionary",message:`${c} is not an accepted type. Expected one of ${t.allowedValues.join(", ")}.`})}r[e]=c}}return r}};n.nullableConverter=function(e){return A=>{if(A===null){return A}return e(A)}};n.converters.DOMString=function(e,A={}){if(e===null&&A.legacyNullToEmptyString){return""}if(typeof e==="symbol"){throw new TypeError("Could not convert argument of type symbol to string.")}return String(e)};n.converters.ByteString=function(e){const A=n.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 ${A.charCodeAt(e)} which is greater than 255.`)}}return A};n.converters.USVString=o;n.converters.boolean=function(e){const A=Boolean(e);return A};n.converters.any=function(e){return e};n.converters["long long"]=function(e){const A=n.util.ConvertToInt(e,64,"signed");return A};n.converters["unsigned long long"]=function(e){const A=n.util.ConvertToInt(e,64,"unsigned");return A};n.converters["unsigned long"]=function(e){const A=n.util.ConvertToInt(e,32,"unsigned");return A};n.converters["unsigned short"]=function(e,A){const t=n.util.ConvertToInt(e,16,"unsigned",A);return t};n.converters.ArrayBuffer=function(e,A={}){if(n.util.Type(e)!=="Object"||!r.isAnyArrayBuffer(e)){throw n.errors.conversionFailed({prefix:`${e}`,argument:`${e}`,types:["ArrayBuffer"]})}if(A.allowShared===false&&r.isSharedArrayBuffer(e)){throw n.errors.exception({header:"ArrayBuffer",message:"SharedArrayBuffer is not allowed."})}return e};n.converters.TypedArray=function(e,A,t={}){if(n.util.Type(e)!=="Object"||!r.isTypedArray(e)||e.constructor.name!==A.name){throw n.errors.conversionFailed({prefix:`${A.name}`,argument:`${e}`,types:[A.name]})}if(t.allowShared===false&&r.isSharedArrayBuffer(e.buffer)){throw n.errors.exception({header:"ArrayBuffer",message:"SharedArrayBuffer is not allowed."})}return e};n.converters.DataView=function(e,A={}){if(n.util.Type(e)!=="Object"||!r.isDataView(e)){throw n.errors.exception({header:"DataView",message:"Object is not a DataView."})}if(A.allowShared===false&&r.isSharedArrayBuffer(e.buffer)){throw n.errors.exception({header:"ArrayBuffer",message:"SharedArrayBuffer is not allowed."})}return e};n.converters.BufferSource=function(e,A={}){if(r.isAnyArrayBuffer(e)){return n.converters.ArrayBuffer(e,A)}if(r.isTypedArray(e)){return n.converters.TypedArray(e,e.constructor)}if(r.isDataView(e)){return n.converters.DataView(e,A)}throw new TypeError(`Could not convert ${e} to a BufferSource.`)};n.converters["sequence"]=n.sequenceConverter(n.converters.ByteString);n.converters["sequence>"]=n.sequenceConverter(n.converters["sequence"]);n.converters["record"]=n.recordConverter(n.converters.ByteString,n.converters.ByteString);e.exports={webidl:n}},8450: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}},2150:(e,A,t)=>{"use strict";const{staticPropertyDescriptors:r,readOperation:s,fireAProgressEvent:o}=t(8439);const{kState:n,kError:i,kResult:a,kEvents:c,kAborted:g}=t(6794);const{webidl:E}=t(6684);const{kEnumerableProperty:l}=t(2806);class FileReader extends EventTarget{constructor(){super();this[n]="empty";this[a]=null;this[i]=null;this[c]={loadend:null,error:null,abort:null,load:null,progress:null,loadstart:null}}readAsArrayBuffer(e){E.brandCheck(this,FileReader);E.argumentLengthCheck(arguments,1,{header:"FileReader.readAsArrayBuffer"});e=E.converters.Blob(e,{strict:false});s(this,e,"ArrayBuffer")}readAsBinaryString(e){E.brandCheck(this,FileReader);E.argumentLengthCheck(arguments,1,{header:"FileReader.readAsBinaryString"});e=E.converters.Blob(e,{strict:false});s(this,e,"BinaryString")}readAsText(e,A=undefined){E.brandCheck(this,FileReader);E.argumentLengthCheck(arguments,1,{header:"FileReader.readAsText"});e=E.converters.Blob(e,{strict:false});if(A!==undefined){A=E.converters.DOMString(A)}s(this,e,"Text",A)}readAsDataURL(e){E.brandCheck(this,FileReader);E.argumentLengthCheck(arguments,1,{header:"FileReader.readAsDataURL"});e=E.converters.Blob(e,{strict:false});s(this,e,"DataURL")}abort(){if(this[n]==="empty"||this[n]==="done"){this[a]=null;return}if(this[n]==="loading"){this[n]="done";this[a]=null}this[g]=true;o("abort",this);if(this[n]!=="loading"){o("loadend",this)}}get readyState(){E.brandCheck(this,FileReader);switch(this[n]){case"empty":return this.EMPTY;case"loading":return this.LOADING;case"done":return this.DONE}}get result(){E.brandCheck(this,FileReader);return this[a]}get error(){E.brandCheck(this,FileReader);return this[i]}get onloadend(){E.brandCheck(this,FileReader);return this[c].loadend}set onloadend(e){E.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(){E.brandCheck(this,FileReader);return this[c].error}set onerror(e){E.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(){E.brandCheck(this,FileReader);return this[c].loadstart}set onloadstart(e){E.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(){E.brandCheck(this,FileReader);return this[c].progress}set onprogress(e){E.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(){E.brandCheck(this,FileReader);return this[c].load}set onload(e){E.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(){E.brandCheck(this,FileReader);return this[c].abort}set onabort(e){E.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:r,LOADING:r,DONE:r,readAsArrayBuffer:l,readAsBinaryString:l,readAsText:l,readAsDataURL:l,abort:l,readyState:l,result:l,error:l,onloadstart:l,onprogress:l,onload:l,onabort:l,onerror:l,onloadend:l,[Symbol.toStringTag]:{value:"FileReader",writable:false,enumerable:false,configurable:true}});Object.defineProperties(FileReader,{EMPTY:r,LOADING:r,DONE:r});e.exports={FileReader:FileReader}},7038:(e,A,t)=>{"use strict";const{webidl:r}=t(6684);const s=Symbol("ProgressEvent state");class ProgressEvent extends Event{constructor(e,A={}){e=r.converters.DOMString(e);A=r.converters.ProgressEventInit(A??{});super(e,A);this[s]={lengthComputable:A.lengthComputable,loaded:A.loaded,total:A.total}}get lengthComputable(){r.brandCheck(this,ProgressEvent);return this[s].lengthComputable}get loaded(){r.brandCheck(this,ProgressEvent);return this[s].loaded}get total(){r.brandCheck(this,ProgressEvent);return this[s].total}}r.converters.ProgressEventInit=r.dictionaryConverter([{key:"lengthComputable",converter:r.converters.boolean,defaultValue:false},{key:"loaded",converter:r.converters["unsigned long long"],defaultValue:0},{key:"total",converter:r.converters["unsigned long long"],defaultValue:0},{key:"bubbles",converter:r.converters.boolean,defaultValue:false},{key:"cancelable",converter:r.converters.boolean,defaultValue:false},{key:"composed",converter:r.converters.boolean,defaultValue:false}]);e.exports={ProgressEvent:ProgressEvent}},6794: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")}},8439:(e,A,t)=>{"use strict";const{kState:r,kError:s,kResult:o,kAborted:n,kLastProgressEventFired:i}=t(6794);const{ProgressEvent:a}=t(7038);const{getEncoding:c}=t(8450);const{DOMException:g}=t(6040);const{serializeAMimeType:E,parseMIMEType:l}=t(7160);const{types:u}=t(9023);const{StringDecoder:Q}=t(3193);const{btoa:C}=t(181);const h={enumerable:true,writable:false,configurable:false};function readOperation(e,A,t,a){if(e[r]==="loading"){throw new g("Invalid state","InvalidStateError")}e[r]="loading";e[o]=null;e[s]=null;const c=A.stream();const E=c.getReader();const l=[];let Q=E.read();let C=true;(async()=>{while(!e[n]){try{const{done:c,value:g}=await Q;if(C&&!e[n]){queueMicrotask((()=>{fireAProgressEvent("loadstart",e)}))}C=false;if(!c&&u.isUint8Array(g)){l.push(g);if((e[i]===undefined||Date.now()-e[i]>=50)&&!e[n]){e[i]=Date.now();queueMicrotask((()=>{fireAProgressEvent("progress",e)}))}Q=E.read()}else if(c){queueMicrotask((()=>{e[r]="done";try{const r=packageData(l,t,A.type,a);if(e[n]){return}e[o]=r;fireAProgressEvent("load",e)}catch(A){e[s]=A;fireAProgressEvent("error",e)}if(e[r]!=="loading"){fireAProgressEvent("loadend",e)}}));break}}catch(A){if(e[n]){return}queueMicrotask((()=>{e[r]="done";e[s]=A;fireAProgressEvent("error",e);if(e[r]!=="loading"){fireAProgressEvent("loadend",e)}}));break}}})()}function fireAProgressEvent(e,A){const t=new a(e,{bubbles:false,cancelable:false});A.dispatchEvent(t)}function packageData(e,A,t,r){switch(A){case"DataURL":{let A="data:";const r=l(t||"application/octet-stream");if(r!=="failure"){A+=E(r)}A+=";base64,";const s=new Q("latin1");for(const t of e){A+=C(s.write(t))}A+=C(s.end());return A}case"Text":{let A="failure";if(r){A=c(r)}if(A==="failure"&&t){const e=l(t);if(e!=="failure"){A=c(e.parameters.get("charset"))}}if(A==="failure"){A="UTF-8"}return decode(e,A)}case"ArrayBuffer":{const A=combineByteSequences(e);return A.buffer}case"BinaryString":{let A="";const t=new Q("latin1");for(const r of e){A+=t.write(r)}A+=t.end();return A}}}function decode(e,A){const t=combineByteSequences(e);const r=BOMSniffing(t);let s=0;if(r!==null){A=r;s=r==="UTF-8"?3:2}const o=t.slice(s);return new TextDecoder(A).decode(o)}function BOMSniffing(e){const[A,t,r]=e;if(A===239&&t===187&&r===191){return"UTF-8"}else if(A===254&&t===255){return"UTF-16BE"}else if(A===255&&t===254){return"UTF-16LE"}return null}function combineByteSequences(e){const A=e.reduce(((e,A)=>e+A.byteLength),0);let t=0;return e.reduce(((e,A)=>{e.set(A,t);t+=A.byteLength;return e}),new Uint8Array(A))}e.exports={staticPropertyDescriptors:h,readOperation:readOperation,fireAProgressEvent:fireAProgressEvent}},6875:(e,A,t)=>{"use strict";const r=Symbol.for("undici.globalDispatcher.1");const{InvalidArgumentError:s}=t(7221);const o=t(8787);if(getGlobalDispatcher()===undefined){setGlobalDispatcher(new o)}function setGlobalDispatcher(e){if(!e||typeof e.dispatch!=="function"){throw new s("Argument agent must implement Agent")}Object.defineProperty(globalThis,r,{value:e,writable:true,enumerable:false,configurable:false})}function getGlobalDispatcher(){return globalThis[r]}e.exports={setGlobalDispatcher:setGlobalDispatcher,getGlobalDispatcher:getGlobalDispatcher}},5658: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)}}},8977:(e,A,t)=>{"use strict";const r=t(2806);const{kBodyUsed:s}=t(7781);const o=t(2613);const{InvalidArgumentError:n}=t(7221);const i=t(4434);const a=[300,301,302,303,307,308];const c=Symbol("body");class BodyAsyncIterable{constructor(e){this[c]=e;this[s]=false}async*[Symbol.asyncIterator](){o(!this[s],"disturbed");this[s]=true;yield*this[c]}}class RedirectHandler{constructor(e,A,t,a){if(A!=null&&(!Number.isInteger(A)||A<0)){throw new n("maxRedirections must be a positive number")}r.validateHandler(a,t.method,t.upgrade);this.dispatch=e;this.location=null;this.abort=null;this.opts={...t,maxRedirections:0};this.maxRedirections=A;this.handler=a;this.history=[];if(r.isStream(this.opts.body)){if(r.bodyLength(this.opts.body)===0){this.opts.body.on("data",(function(){o(false)}))}if(typeof this.opts.body.readableDidRead!=="boolean"){this.opts.body[s]=false;i.prototype.on.call(this.opts.body,"data",(function(){this[s]=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)&&r.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,A,t){this.handler.onUpgrade(e,A,t)}onError(e){this.handler.onError(e)}onHeaders(e,A,t,s){this.location=this.history.length>=this.maxRedirections||r.isDisturbed(this.opts.body)?null:parseLocation(e,A);if(this.opts.origin){this.history.push(new URL(this.opts.path,this.opts.origin))}if(!this.location){return this.handler.onHeaders(e,A,t,s)}const{origin:o,pathname:n,search:i}=r.parseURL(new URL(this.location,this.opts.origin&&new URL(this.opts.path,this.opts.origin)));const a=i?`${n}${i}`:n;this.opts.headers=cleanRequestHeaders(this.opts.headers,e===303,this.opts.origin!==o);this.opts.path=a;this.opts.origin=o;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,A){if(a.indexOf(e)===-1){return null}for(let e=0;e{const r=t(2613);const{kRetryHandlerDefaultRetry:s}=t(7781);const{RequestRetryError:o}=t(7221);const{isDisturbed:n,parseHeaders:i,parseRangeHeader:a}=t(2806);function calculateRetryAfterHeader(e){const A=Date.now();const t=new Date(e).getTime()-A;return t}class RetryHandler{constructor(e,A){const{retryOptions:t,...r}=e;const{retry:o,maxRetries:n,maxTimeout:i,minTimeout:a,timeoutFactor:c,methods:g,errorCodes:E,retryAfter:l,statusCodes:u}=t??{};this.dispatch=A.dispatch;this.handler=A.handler;this.opts=r;this.abort=null;this.aborted=false;this.retryOpts={retry:o??RetryHandler[s],retryAfter:l??true,maxTimeout:i??30*1e3,timeout:a??500,timeoutFactor:c??2,maxRetries:n??5,methods:g??["GET","HEAD","OPTIONS","PUT","DELETE","TRACE"],statusCodes:u??[500,502,503,504,429],errorCodes:E??["ECONNRESET","ECONNREFUSED","ENOTFOUND","ENETDOWN","ENETUNREACH","EHOSTDOWN","EHOSTUNREACH","EPIPE"]};this.retryCount=0;this.start=0;this.end=null;this.etag=null;this.resume=null;this.handler.onConnect((e=>{this.aborted=true;if(this.abort){this.abort(e)}else{this.reason=e}}))}onRequestSent(){if(this.handler.onRequestSent){this.handler.onRequestSent()}}onUpgrade(e,A,t){if(this.handler.onUpgrade){this.handler.onUpgrade(e,A,t)}}onConnect(e){if(this.aborted){e(this.reason)}else{this.abort=e}}onBodySent(e){if(this.handler.onBodySent)return this.handler.onBodySent(e)}static[s](e,{state:A,opts:t},r){const{statusCode:s,code:o,headers:n}=e;const{method:i,retryOptions:a}=t;const{maxRetries:c,timeout:g,maxTimeout:E,timeoutFactor:l,statusCodes:u,errorCodes:Q,methods:C}=a;let{counter:h,currentTimeout:B}=A;B=B!=null&&B>0?B:g;if(o&&o!=="UND_ERR_REQ_RETRY"&&o!=="UND_ERR_SOCKET"&&!Q.includes(o)){r(e);return}if(Array.isArray(C)&&!C.includes(i)){r(e);return}if(s!=null&&Array.isArray(u)&&!u.includes(s)){r(e);return}if(h>c){r(e);return}let I=n!=null&&n["retry-after"];if(I){I=Number(I);I=isNaN(I)?calculateRetryAfterHeader(I):I*1e3}const d=I>0?Math.min(I,E):Math.min(B*l**h,E);A.currentTimeout=d;setTimeout((()=>r(null)),d)}onHeaders(e,A,t,s){const n=i(A);this.retryCount+=1;if(e>=300){this.abort(new o("Request failed",e,{headers:n,count:this.retryCount}));return false}if(this.resume!=null){this.resume=null;if(e!==206){return true}const A=a(n["content-range"]);if(!A){this.abort(new o("Content-Range mismatch",e,{headers:n,count:this.retryCount}));return false}if(this.etag!=null&&this.etag!==n.etag){this.abort(new o("ETag mismatch",e,{headers:n,count:this.retryCount}));return false}const{start:s,size:i,end:c=i}=A;r(this.start===s,"content-range mismatch");r(this.end==null||this.end===c,"content-range mismatch");this.resume=t;return true}if(this.end==null){if(e===206){const o=a(n["content-range"]);if(o==null){return this.handler.onHeaders(e,A,t,s)}const{start:i,size:c,end:g=c}=o;r(i!=null&&Number.isFinite(i)&&this.start!==i,"content-range mismatch");r(Number.isFinite(i));r(g!=null&&Number.isFinite(g)&&this.end!==g,"invalid content-length");this.start=i;this.end=g}if(this.end==null){const e=n["content-length"];this.end=e!=null?Number(e):null}r(Number.isFinite(this.start));r(this.end==null||Number.isFinite(this.end),"invalid content-length");this.resume=t;this.etag=n.etag!=null?n.etag:null;return this.handler.onHeaders(e,A,t,s)}const c=new o("Request failed",e,{headers:n,count:this.retryCount});this.abort(c);return false}onData(e){this.start+=e.length;return this.handler.onData(e)}onComplete(e){this.retryCount=0;return this.handler.onComplete(e)}onError(e){if(this.aborted||n(this.opts.body)){return this.handler.onError(e)}this.retryOpts.retry(e,{state:{counter:this.retryCount++,currentTimeout:this.retryAfter},opts:{retryOptions:this.retryOpts,...this.opts}},onRetry.bind(this));function onRetry(e){if(e!=null||this.aborted||n(this.opts.body)){return this.handler.onError(e)}if(this.start!==0){this.opts={...this.opts,headers:{...this.opts.headers,range:`bytes=${this.start}-${this.end??""}`}}}try{this.dispatch(this.opts,this)}catch(e){this.handler.onError(e)}}}}e.exports=RetryHandler},9421:(e,A,t)=>{"use strict";const r=t(8977);function createRedirectInterceptor({maxRedirections:e}){return A=>function Intercept(t,s){const{maxRedirections:o=e}=t;if(!o){return A(t,s)}const n=new r(A,o,t,s);t={...t,maxRedirections:0};return A(t,n)}}e.exports=createRedirectInterceptor},5766:(e,A,t)=>{"use strict";Object.defineProperty(A,"__esModule",{value:true});A.SPECIAL_HEADERS=A.HEADER_STATE=A.MINOR=A.MAJOR=A.CONNECTION_TOKEN_CHARS=A.HEADER_CHARS=A.TOKEN=A.STRICT_TOKEN=A.HEX=A.URL_CHAR=A.STRICT_URL_CHAR=A.USERINFO_CHARS=A.MARK=A.ALPHANUM=A.NUM=A.HEX_MAP=A.NUM_MAP=A.ALPHA=A.FINISH=A.H_METHOD_MAP=A.METHOD_MAP=A.METHODS_RTSP=A.METHODS_ICE=A.METHODS_HTTP=A.METHODS=A.LENIENT_FLAGS=A.FLAGS=A.TYPE=A.ERROR=void 0;const r=t(8974);var s;(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"})(s=A.ERROR||(A.ERROR={}));var o;(function(e){e[e["BOTH"]=0]="BOTH";e[e["REQUEST"]=1]="REQUEST";e[e["RESPONSE"]=2]="RESPONSE"})(o=A.TYPE||(A.TYPE={}));var n;(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"})(n=A.FLAGS||(A.FLAGS={}));var i;(function(e){e[e["HEADERS"]=1]="HEADERS";e[e["CHUNKED_LENGTH"]=2]="CHUNKED_LENGTH";e[e["KEEP_ALIVE"]=4]="KEEP_ALIVE"})(i=A.LENIENT_FLAGS||(A.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=A.METHODS||(A.METHODS={}));A.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];A.METHODS_ICE=[a.SOURCE];A.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];A.METHOD_MAP=r.enumToMap(a);A.H_METHOD_MAP={};Object.keys(A.METHOD_MAP).forEach((e=>{if(/^H/.test(e)){A.H_METHOD_MAP[e]=A.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=A.FINISH||(A.FINISH={}));A.ALPHA=[];for(let e="A".charCodeAt(0);e<="Z".charCodeAt(0);e++){A.ALPHA.push(String.fromCharCode(e));A.ALPHA.push(String.fromCharCode(e+32))}A.NUM_MAP={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9};A.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};A.NUM=["0","1","2","3","4","5","6","7","8","9"];A.ALPHANUM=A.ALPHA.concat(A.NUM);A.MARK=["-","_",".","!","~","*","'","(",")"];A.USERINFO_CHARS=A.ALPHANUM.concat(A.MARK).concat(["%",";",":","&","=","+","$",","]);A.STRICT_URL_CHAR=["!",'"',"$","%","&","'","(",")","*","+",",","-",".","/",":",";","<","=",">","@","[","\\","]","^","_","`","{","|","}","~"].concat(A.ALPHANUM);A.URL_CHAR=A.STRICT_URL_CHAR.concat(["\t","\f"]);for(let e=128;e<=255;e++){A.URL_CHAR.push(e)}A.HEX=A.NUM.concat(["a","b","c","d","e","f","A","B","C","D","E","F"]);A.STRICT_TOKEN=["!","#","$","%","&","'","*","+","-",".","^","_","`","|","~"].concat(A.ALPHANUM);A.TOKEN=A.STRICT_TOKEN.concat([" "]);A.HEADER_CHARS=["\t"];for(let e=32;e<=255;e++){if(e!==127){A.HEADER_CHARS.push(e)}}A.CONNECTION_TOKEN_CHARS=A.HEADER_CHARS.filter((e=>e!==44));A.MAJOR=A.NUM_MAP;A.MINOR=A.MAJOR;var g;(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"})(g=A.HEADER_STATE||(A.HEADER_STATE={}));A.SPECIAL_HEADERS={connection:g.CONNECTION,"content-length":g.CONTENT_LENGTH,"proxy-connection":g.CONNECTION,"transfer-encoding":g.TRANSFER_ENCODING,upgrade:g.UPGRADE}},2108:e=>{e.exports="AGFzbQEAAAABMAhgAX8Bf2ADf39/AX9gBH9/f38Bf2AAAGADf39/AGABfwBgAn9/AGAGf39/f39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQACA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAA0ZFAwMEAAAFAAAAAAAABQEFAAUFBQAABgAAAAAGBgYGAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAABAQcAAAUFAwABBAUBcAESEgUDAQACBggBfwFBgNQECwfRBSIGbWVtb3J5AgALX2luaXRpYWxpemUACRlfX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlAQALbGxodHRwX2luaXQAChhsbGh0dHBfc2hvdWxkX2tlZXBfYWxpdmUAQQxsbGh0dHBfYWxsb2MADAZtYWxsb2MARgtsbGh0dHBfZnJlZQANBGZyZWUASA9sbGh0dHBfZ2V0X3R5cGUADhVsbGh0dHBfZ2V0X2h0dHBfbWFqb3IADxVsbGh0dHBfZ2V0X2h0dHBfbWlub3IAEBFsbGh0dHBfZ2V0X21ldGhvZAARFmxsaHR0cF9nZXRfc3RhdHVzX2NvZGUAEhJsbGh0dHBfZ2V0X3VwZ3JhZGUAEwxsbGh0dHBfcmVzZXQAFA5sbGh0dHBfZXhlY3V0ZQAVFGxsaHR0cF9zZXR0aW5nc19pbml0ABYNbGxodHRwX2ZpbmlzaAAXDGxsaHR0cF9wYXVzZQAYDWxsaHR0cF9yZXN1bWUAGRtsbGh0dHBfcmVzdW1lX2FmdGVyX3VwZ3JhZGUAGhBsbGh0dHBfZ2V0X2Vycm5vABsXbGxodHRwX2dldF9lcnJvcl9yZWFzb24AHBdsbGh0dHBfc2V0X2Vycm9yX3JlYXNvbgAdFGxsaHR0cF9nZXRfZXJyb3JfcG9zAB4RbGxodHRwX2Vycm5vX25hbWUAHxJsbGh0dHBfbWV0aG9kX25hbWUAIBJs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ADcDACAAQShqQgA3AwAgAEEgakIANwMAIABBEGpCADcDACAAQQhqQgA3AwAgAEHdATYCHEEAC3sBAX8CQCAAKAIMIgMNAAJAIAAoAgRFDQAgACABNgIECwJAIAAgASACEMSAgIAAIgMNACAAKAIMDwsgACADNgIcQQAhAyAAKAIEIgFFDQAgACABIAIgACgCCBGBgICAAAAiAUUNACAAIAI2AhQgACABNgIMIAEhAwsgAwvk8wEDDn8DfgR/I4CAgIAAQRBrIgMkgICAgAAgASEEIAEhBSABIQYgASEHIAEhCCABIQkgASEKIAEhCyABIQwgASENIAEhDiABIQ8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgACgCHCIQQX9qDt0B2gEB2QECAwQFBgcICQoLDA0O2AEPENcBERLWARMUFRYXGBkaG+AB3wEcHR7VAR8gISIjJCXUASYnKCkqKyzTAdIBLS7RAdABLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVG2wFHSElKzwHOAUvNAUzMAU1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4ABgQGCAYMBhAGFAYYBhwGIAYkB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QAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAC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FCAAIBA2AgxBACEQDLIBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxNCyAAQdMANgIcIAAgATYCFCAAIBA2AgxBACEQDLEBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxRCyAAQeUANgIcIAAgATYCFCAAIBA2AgxBACEQDLABCyAAQQA2AhwgACABNgIUIABBxoqAgAA2AhAgAEEHNgIMQQAhEAyvAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMSQsgAEHSADYCHCAAIAE2AhQgACAQNgIMQQAhEAyuAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMSQsgAEHTADYCHCAAIAE2AhQgACAQNgIMQQAhEAytAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMTQsgAEHlADYCHCAAIAE2AhQgACAQNgIMQQAhEAysAQsgAEEANgIcIAAgATYCFCAAQdyIgIAANgIQIABBBzYCDEEAIRAMqwELIBBBP0cNASABQQFqIQELQQUhEAyQAQtBACEQIABBADYCHCAAIAE2AhQgAEH9koCAADYCECAAQQc2AgwMqAELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEILIABB0gA2AhwgACABNgIUIAAgEDYCDEEAIRAMpwELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEILIABB0wA2AhwgACABNgIUIAAgEDYCDEEAIRAMpgELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEYLIABB5QA2AhwgACABNgIUIAAgEDYCDEEAIRAMpQELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDD8LIABB0gA2AhwgACAUNgIUIAAgATYCDEEAIRAMpAELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDD8LIABB0wA2AhwgACAUNgIUIAAgATYCDEEAIRAMowELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDEMLIABB5QA2AhwgACAUNgIUIAAgATYCDEEAIRAMogELIABBADYCHCAAIBQ2AhQgAEHDj4CAADYCECAAQQc2AgxBACEQDKEBCyAAQQA2AhwgACABNgIUIABBw4+AgAA2AhAgAEEHNgIMQQAhEAygAQtBACEQIABBADYCHCAAIBQ2AhQgAEGMnICAADYCECAAQQc2AgwMnwELIABBADYCHCAAIBQ2AhQgAEGMnICAADYCECAAQQc2AgxBACEQDJ4BCyAAQQA2AhwgACAUNgIUIABB/pGAgAA2AhAgAEEHNgIMQQAhEAydAQsgAEEANgIcIAAgATYCFCAAQY6bgIAANgIQIABBBjYCDEEAIRAMnAELIBBBFUYNVyAAQQA2AhwgACABNgIUIABBzI6AgAA2AhAgAEEgNgIMQQAhEAybAQsgAEEANgIAIBBBAWohAUEkIRALIAAgEDoAKSAAKAIEIRAgAEEANgIEIAAgECABEKuAgIAAIhANVCABIQEMPgsgAEEANgIAC0EAIRAgAEEANgIcIAAgBDYCFCAAQfGbgIAANgIQIABBBjYCDAyXAQsgAUEVRg1QIABBADYCHCAAIAU2AhQgAEHwjICAADYCECAAQRs2AgxBACEQDJYBCyAAKAIEIQUgAEEANgIEIAAgBSAQEKmAgIAAIgUNASAQQQFqIQULQa0BIRAMewsgAEHBATYCHCAAIAU2AgwgACAQQQFqNgIUQQAhEAyTAQsgACgCBCEGIABBADYCBCAAIAYgEBCpgICAACIGDQEgEEEBaiEGC0GuASEQDHgLIABBwgE2AhwgACAGNgIMIAAgEEEBajYCFEEAIRAMkAELIABBADYCHCAAIAc2AhQgAEGXi4CAADYCECAAQQ02AgxBACEQDI8BCyAAQQA2AhwgACAINgIUIABB45CAgAA2AhAgAEEJNgIMQQAhEAyOAQsgAEEANgIcIAAgCDYCFCAAQZSNgIAANgIQIABBITYCDEEAIRAMjQELQQEhFkEAIRdBACEUQQEhEAsgACAQOgArIAlBAWohCAJAAkAgAC0ALUEQcQ0AAkACQAJAIAAtACoOAwEAAgQLIBZFDQMMAgsgFA0BDAILIBdFDQELIAAoAgQhECAAQQA2AgQgACAQIAgQrYCAgAAiEEUNPSAAQckBNgIcIAAgCDYCFCAAIBA2AgxBACEQDIwBCyAAKAIEIQQgAEEANgIEIAAgBCAIEK2AgIAAIgRFDXYgAEHKATYCHCAAIAg2AhQgACAENgIMQQAhEAyLAQsgACgCBCEEIABBADYCBCAAIAQgCRCtgICAACIERQ10IABBywE2AhwgACAJNgIUIAAgBDYCDEEAIRAMigELIAAoAgQhBCAAQQA2AgQgACAEIAoQrYCAgAAiBEUNciAAQc0BNgIcIAAgCjYCFCAAIAQ2AgxBACEQDIkBCwJAIAstAABBUGoiEEH/AXFBCk8NACAAIBA6ACogC0EBaiEKQbYBIRAMcAsgACgCBCEEIABBADYCBCAAIAQgCxCtgICAACIERQ1wIABBzwE2AhwgACALNgIUIAAgBDYCDEEAIRAMiAELIABBADYCHCAAIAQ2AhQgAEGQs4CAADYCECAAQQg2AgwgAEEANgIAQQAhEAyHAQsgAUEVRg0/IABBADYCHCAAIAw2AhQgAEHMjoCAADYCECAAQSA2AgxBACEQDIYBCyAAQYEEOwEoIAAoAgQhECAAQgA3AwAgACAQIAxBAWoiDBCrgICAACIQRQ04IABB0wE2AhwgACAMNgIUIAAgEDYCDEEAIRAMhQELIABBADYCAAtBACEQIABBADYCHCAAIAQ2AhQgAEHYm4CAADYCECAAQQg2AgwMgwELIAAoAgQhECAAQgA3AwAgACAQIAtBAWoiCxCrgICAACIQDQFBxgEhEAxpCyAAQQI6ACgMVQsgAEHVATYCHCAAIAs2AhQgACAQNgIMQQAhEAyAAQsgEEEVRg03IABBADYCHCAAIAQ2AhQgAEGkjICAADYCECAAQRA2AgxBACEQDH8LIAAtADRBAUcNNCAAIAQgAhC8gICAACIQRQ00IBBBFUcNNSAAQdwBNgIcIAAgBDYCFCAAQdWWgIAANgIQIABBFTYCDEEAIRAMfgtBACEQIABBADYCHCAAQa+LgIAANgIQIABBAjYCDCAAIBRBAWo2AhQMfQtBACEQDGMLQQIhEAxiC0ENIRAMYQtBDyEQDGALQSUhEAxfC0ETIRAMXgtBFSEQDF0LQRYhEAxcC0EXIRAMWwtBGCEQDFoLQRkhEAxZC0EaIRAMWAtBGyEQDFcLQRwhEAxWC0EdIRAMVQtBHyEQDFQLQSEhEAxTC0EjIRAMUgtBxgAhEAxRC0EuIRAMUAtBLyEQDE8LQTshEAxOC0E9IRAMTQtByAAhEAxMC0HJACEQDEsLQcsAIRAMSgtBzAAhEAxJC0HOACEQDEgLQdEAIRAMRwtB1QAhEAxGC0HYACEQDEULQdkAIRAMRAtB2wAhEAxDC0HkACEQDEILQeUAIRAMQQtB8QAhEAxAC0H0ACEQDD8LQY0BIRAMPgtBlwEhEAw9C0GpASEQDDwLQawBIRAMOwtBwAEhEAw6C0G5ASEQDDkLQa8BIRAMOAtBsQEhEAw3C0GyASEQDDYLQbQBIRAMNQtBtQEhEAw0C0G6ASEQDDMLQb0BIRAMMgtBvwEhEAwxC0HBASEQDDALIABBADYCHCAAIAQ2AhQgAEHpi4CAADYCECAAQR82AgxBACEQDEgLIABB2wE2AhwgACAENgIUIABB+paAgAA2AhAgAEEVNgIMQQAhEAxHCyAAQfgANgIcIAAgDDYCFCAAQcqYgIAANgIQIABBFTYCDEEAIRAMRgsgAEHRADYCHCAAIAU2AhQgAEGwl4CAADYCECAAQRU2AgxBACEQDEULIABB+QA2AhwgACABNgIUIAAgEDYCDEEAIRAMRAsgAEH4ADYCHCAAIAE2AhQgAEHKmICAADYCECAAQRU2AgxBACEQDEMLIABB5AA2AhwgACABNgIUIABB45eAgAA2AhAgAEEVNgIMQQAhEAxCCyAAQdcANgIcIAAgATYCFCAAQcmXgIAANgIQIABBFTYCDEEAIRAMQQsgAEEANgIcIAAgATYCFCAAQbmNgIAANgIQIABBGjYCDEEAIRAMQAsgAEHCADYCHCAAIAE2AhQgAEHjmICAADYCECAAQRU2AgxBACEQDD8LIABBADYCBCAAIA8gDxCxgICAACIERQ0BIABBOjYCHCAAIAQ2AgwgACAPQQFqNgIUQQAhEAw+CyAAKAIEIQQgAEEANgIEAkAgACAEIAEQsYCAgAAiBEUNACAAQTs2AhwgACAENgIMIAAgAUEBajYCFEEAIRAMPgsgAUEBaiEBDC0LIA9BAWohAQwtCyAAQQA2AhwgACAPNgIUIABB5JKAgAA2AhAgAEEENgIMQQAhEAw7CyAAQTY2AhwgACAENgIUIAAgAjYCDEEAIRAMOgsgAEEuNgIcIAAgDjYCFCAAIAQ2AgxBACEQDDkLIABB0AA2AhwgACABNgIUIABBkZiAgAA2AhAgAEEVNgIMQQAhEAw4CyANQQFqIQEMLAsgAEEVNgIcIAAgATYCFCAAQYKZgIAANgIQIABBFTYCDEEAIRAMNgsgAEEbNgIcIAAgATYCFCAAQZGXgIAANgIQIABBFTYCDEEAIRAMNQsgAEEPNgIcIAAgATYCFCAAQZGXgIAANgIQIABBFTYCDEEAIRAMNAsgAEELNgIcIAAgATYCFCAAQZGXgIAANgIQIABBFTYCDEEAIRAMMwsgAEEaNgIcIAAgATYCFCAAQYKZgIAANgIQIABBFTYCDEEAIRAMMgsgAEELNgIcIAAgATYCFCAAQYKZgIAANgIQIABBFTYCDEEAIRAMMQsgAEEKNgIcIAAgATYCFCAAQeSWgIAANgIQIABBFTYCDEEAIRAMMAsgAEEeNgIcIAAgATYCFCAAQfmXgIAANgIQIABBFTYCDEEAIRAMLwsgAEEANgIcIAAgEDYCFCAAQdqNgIAANgIQIABBFDYCDEEAIRAMLgsgAEEENgIcIAAgATYCFCAAQbCYgIAANgIQIABBFTYCDEEAIRAMLQsgAEEANgIAIAtBAWohCwtBuAEhEAwSCyAAQQA2AgAgEEEBaiEBQfUAIRAMEQsgASEBAkAgAC0AKUEFRw0AQeMAIRAMEQtB4gAhEAwQC0EAIRAgAEEANgIcIABB5JGAgAA2AhAgAEEHNgIMIAAgFEEBajYCFAwoCyAAQQA2AgAgF0EBaiEBQcAAIRAMDgtBASEBCyAAIAE6ACwgAEEANgIAIBdBAWohAQtBKCEQDAsLIAEhAQtBOCEQDAkLAkAgASIPIAJGDQADQAJAIA8tAABBgL6AgABqLQAAIgFBAUYNACABQQJHDQMgD0EBaiEBDAQLIA9BAWoiDyACRw0AC0E+IRAMIgtBPiEQDCELIABBADoALCAPIQEMAQtBCyEQDAYLQTohEAwFCyABQQFqIQFBLSEQDAQLIAAgAToALCAAQQA2AgAgFkEBaiEBQQwhEAwDCyAAQQA2AgAgF0EBaiEBQQohEAwCCyAAQQA2AgALIABBADoALCANIQFBCSEQDAALC0EAIRAgAEEANgIcIAAgCzYCFCAAQc2QgIAANgIQIABBCTYCDAwXC0EAIRAgAEEANgIcIAAgCjYCFCAAQemKgIAANgIQIABBCTYCDAwWC0EAIRAgAEEANgIcIAAgCTYCFCAAQbeQgIAANgIQIABBCTYCDAwVC0EAIRAgAEEANgIcIAAgCDYCFCAAQZyRgIAANgIQIABBCTYCDAwUC0EAIRAgAEEANgIcIAAgATYCFCAAQc2QgIAANgIQIABBCTYCDAwTC0EAIRAgAEEANgIcIAAgATYCFCAAQemKgIAANgIQIABBCTYCDAwSC0EAIRAgAEEANgIcIAAgATYCFCAAQbeQgIAANgIQIABBCTYCDAwRC0EAIRAgAEEANgIcIAAgATYCFCAAQZyRgIAANgIQIABBCTYCDAwQC0EAIRAgAEEANgIcIAAgATYCFCAAQZeVgIAANgIQIABBDzYCDAwPC0EAIRAgAEEANgIcIAAgATYCFCAAQZeVgIAANgIQIABBDzYCDAwOC0EAIRAgAEEANgIcIAAgATYCFCAAQcCSgIAANgIQIABBCzYCDAwNC0EAIRAgAEEANgIcIAAgATYCFCAAQZWJgIAANgIQIABBCzYCDAwMC0EAIRAgAEEANgIcIAAgATYCFCAAQeGPgIAANgIQIABBCjYCDAwLC0EAIRAgAEEANgIcIAAgATYCFCAAQfuPgIAANgIQIABBCjYCDAwKC0EAIRAgAEEANgIcIAAgATYCFCAAQfGZgIAANgIQIABBAjYCDAwJC0EAIRAgAEEANgIcIAAgATYCFCAAQcSUgIAANgIQIABBAjYCDAwIC0EAIRAgAEEANgIcIAAgATYCFCAAQfKVgIAANgIQIABBAjYCDAwHCyAAQQI2AhwgACABNgIUIABBnJqAgAA2AhAgAEEWNgIMQQAhEAwGC0EBIRAMBQtB1AAhECABIgQgAkYNBCADQQhqIAAgBCACQdjCgIAAQQoQxYCAgAAgAygCDCEEIAMoAggOAwEEAgALEMqAgIAAAAsgAEEANgIcIABBtZqAgAA2AhAgAEEXNgIMIAAgBEEBajYCFEEAIRAMAgsgAEEANgIcIAAgBDYCFCAAQcqagIAANgIQIABBCTYCDEEAIRAMAQsCQCABIgQgAkcNAEEiIRAMAQsgAEGJgICAADYCCCAAIAQ2AgRBISEQCyADQRBqJICAgIAAIBALrwEBAn8gASgCACEGAkACQCACIANGDQAgBCAGaiEEIAYgA2ogAmshByACIAZBf3MgBWoiBmohBQNAAkAgAi0AACAELQAARg0AQQIhBAwDCwJAIAYNAEEAIQQgBSECDAMLIAZBf2ohBiAEQQFqIQQgAkEBaiICIANHDQALIAchBiADIQILIABBATYCACABIAY2AgAgACACNgIEDwsgAUEANgIAIAAgBDYCACAAIAI2AgQLCgAgABDHgICAAAvyNgELfyOAgICAAEEQayIBJICAgIAAAkBBACgCoNCAgAANAEEAEMuAgIAAQYDUhIAAayICQdkASQ0AQQAhAwJAQQAoAuDTgIAAIgQNAEEAQn83AuzTgIAAQQBCgICEgICAwAA3AuTTgIAAQQAgAUEIakFwcUHYqtWqBXMiBDYC4NOAgABBAEEANgL004CAAEEAQQA2AsTTgIAAC0EAIAI2AszTgIAAQQBBgNSEgAA2AsjTgIAAQQBBgNSEgAA2ApjQgIAAQQAgBDYCrNCAgABBAEF/NgKo0ICAAANAIANBxNCAgABqIANBuNCAgABqIgQ2AgAgBCADQbDQgIAAaiIFNgIAIANBvNCAgABqIAU2AgAgA0HM0ICAAGogA0HA0ICAAGoiBTYCACAFIAQ2AgAgA0HU0ICAAGogA0HI0ICAAGoiBDYCACAEIAU2AgAgA0HQ0ICAAGogBDYCACADQSBqIgNBgAJHDQALQYDUhIAAQXhBgNSEgABrQQ9xQQBBgNSEgABBCGpBD3EbIgNqIgRBBGogAkFIaiIFIANrIgNBAXI2AgBBAEEAKALw04CAADYCpNCAgABBACADNgKU0ICAAEEAIAQ2AqDQgIAAQYDUhIAAIAVqQTg2AgQLAkACQAJAAkACQAJAAkACQAJAAkACQAJAIABB7AFLDQACQEEAKAKI0ICAACIGQRAgAEETakFwcSAAQQtJGyICQQN2IgR2IgNBA3FFDQACQAJAIANBAXEgBHJBAXMiBUEDdCIEQbDQgIAAaiIDIARBuNCAgABqKAIAIgQoAggiAkcNAEEAIAZBfiAFd3E2AojQgIAADAELIAMgAjYCCCACIAM2AgwLIARBCGohAyAEIAVBA3QiBUEDcjYCBCAEIAVqIgQgBCgCBEEBcjYCBAwMCyACQQAoApDQgIAAIgdNDQECQCADRQ0AAkACQCADIAR0QQIgBHQiA0EAIANrcnEiA0EAIANrcUF/aiIDIANBDHZBEHEiA3YiBEEFdkEIcSIFIANyIAQgBXYiA0ECdkEEcSIEciADIAR2IgNBAXZBAnEiBHIgAyAEdiIDQQF2QQFxIgRyIAMgBHZqIgRBA3QiA0Gw0ICAAGoiBSADQbjQgIAAaigCACIDKAIIIgBHDQBBACAGQX4gBHdxIgY2AojQgIAADAELIAUgADYCCCAAIAU2AgwLIAMgAkEDcjYCBCADIARBA3QiBGogBCACayIFNgIAIAMgAmoiACAFQQFyNgIEAkAgB0UNACAHQXhxQbDQgIAAaiECQQAoApzQgIAAIQQCQAJAIAZBASAHQQN2dCIIcQ0AQQAgBiAIcjYCiNCAgAAgAiEIDAELIAIoAgghCAsgCCAENgIMIAIgBDYCCCAEIAI2AgwgBCAINgIICyADQQhqIQNBACAANgKc0ICAAEEAIAU2ApDQgIAADAwLQQAoAozQgIAAIglFDQEgCUEAIAlrcUF/aiIDIANBDHZBEHEiA3YiBEEFdkEIcSIFIANyIAQgBXYiA0ECdkEEcSIEciADIAR2IgNBAXZBAnEiBHIgAyAEdiIDQQF2QQFxIgRyIAMgBHZqQQJ0QbjSgIAAaigCACIAKAIEQXhxIAJrIQQgACEFAkADQAJAIAUoAhAiAw0AIAVBFGooAgAiA0UNAgsgAygCBEF4cSACayIFIAQgBSAESSIFGyEEIAMgACAFGyEAIAMhBQwACwsgACgCGCEKAkAgACgCDCIIIABGDQAgACgCCCIDQQAoApjQgIAASRogCCADNgIIIAMgCDYCDAwLCwJAIABBFGoiBSgCACIDDQAgACgCECIDRQ0DIABBEGohBQsDQCAFIQsgAyIIQRRqIgUoAgAiAw0AIAhBEGohBSAIKAIQIgMNAAsgC0EANgIADAoLQX8hAiAAQb9/Sw0AIABBE2oiA0FwcSECQQAoAozQgIAAIgdFDQBBACELAkAgAkGAAkkNAEEfIQsgAkH///8HSw0AIANBCHYiAyADQYD+P2pBEHZBCHEiA3QiBCAEQYDgH2pBEHZBBHEiBHQiBSAFQYCAD2pBEHZBAnEiBXRBD3YgAyAEciAFcmsiA0EBdCACIANBFWp2QQFxckEcaiELC0EAIAJrIQQCQAJAAkACQCALQQJ0QbjSgIAAaigCACIFDQBBACEDQQAhCAwBC0EAIQMgAkEAQRkgC0EBdmsgC0EfRht0IQBBACEIA0ACQCAFKAIEQXhxIAJrIgYgBE8NACAGIQQgBSEIIAYNAEEAIQQgBSEIIAUhAwwDCyADIAVBFGooAgAiBiAGIAUgAEEddkEEcWpBEGooAgAiBUYbIAMgBhshAyAAQQF0IQAgBQ0ACwsCQCADIAhyDQBBACEIQQIgC3QiA0EAIANrciAHcSIDRQ0DIANBACADa3FBf2oiAyADQQx2QRBxIgN2IgVBBXZBCHEiACADciAFIAB2IgNBAnZBBHEiBXIgAyAFdiIDQQF2QQJxIgVyIAMgBXYiA0EBdkEBcSIFciADIAV2akECdEG40oCAAGooAgAhAwsgA0UNAQsDQCADKAIEQXhxIAJrIgYgBEkhAAJAIAMoAhAiBQ0AIANBFGooAgAhBQsgBiAEIAAbIQQgAyAIIAAbIQggBSEDIAUNAAsLIAhFDQAgBEEAKAKQ0ICAACACa08NACAIKAIYIQsCQCAIKAIMIgAgCEYNACAIKAIIIgNBACgCmNCAgABJGiAAIAM2AgggAyAANgIMDAkLAkAgCEEUaiIFKAIAIgMNACAIKAIQIgNFDQMgCEEQaiEFCwNAIAUhBiADIgBBFGoiBSgCACIDDQAgAEEQaiEFIAAoAhAiAw0ACyAGQQA2AgAMCAsCQEEAKAKQ0ICAACIDIAJJDQBBACgCnNCAgAAhBAJAAkAgAyACayIFQRBJDQAgBCACaiIAIAVBAXI2AgRBACAFNgKQ0ICAAEEAIAA2ApzQgIAAIAQgA2ogBTYCACAEIAJBA3I2AgQMAQsgBCADQQNyNgIEIAQgA2oiAyADKAIEQQFyNgIEQQBBADYCnNCAgABBAEEANgKQ0ICAAAsgBEEIaiEDDAoLAkBBACgClNCAgAAiACACTQ0AQQAoAqDQgIAAIgMgAmoiBCAAIAJrIgVBAXI2AgRBACAFNgKU0ICAAEEAIAQ2AqDQgIAAIAMgAkEDcjYCBCADQQhqIQMMCgsCQAJAQQAoAuDTgIAARQ0AQQAoAujTgIAAIQQMAQtBAEJ/NwLs04CAAEEAQoCAhICAgMAANwLk04CAAEEAIAFBDGpBcHFB2KrVqgVzNgLg04CAAEEAQQA2AvTTgIAAQQBBADYCxNOAgABBgIAEIQQLQQAhAwJAIAQgAkHHAGoiB2oiBkEAIARrIgtxIgggAksNAEEAQTA2AvjTgIAADAoLAkBBACgCwNOAgAAiA0UNAAJAQQAoArjTgIAAIgQgCGoiBSAETQ0AIAUgA00NAQtBACEDQQBBMDYC+NOAgAAMCgtBAC0AxNOAgABBBHENBAJAAkACQEEAKAKg0ICAACIERQ0AQcjTgIAAIQMDQAJAIAMoAgAiBSAESw0AIAUgAygCBGogBEsNAwsgAygCCCIDDQALC0EAEMuAgIAAIgBBf0YNBSAIIQYCQEEAKALk04CAACIDQX9qIgQgAHFFDQAgCCAAayAEIABqQQAgA2txaiEGCyAGIAJNDQUgBkH+////B0sNBQJAQQAoAsDTgIAAIgNFDQBBACgCuNOAgAAiBCAGaiIFIARNDQYgBSADSw0GCyAGEMuAgIAAIgMgAEcNAQwHCyAGIABrIAtxIgZB/v///wdLDQQgBhDLgICAACIAIAMoAgAgAygCBGpGDQMgACEDCwJAIANBf0YNACACQcgAaiAGTQ0AAkAgByAGa0EAKALo04CAACIEakEAIARrcSIEQf7///8HTQ0AIAMhAAwHCwJAIAQQy4CAgABBf0YNACAEIAZqIQYgAyEADAcLQQAgBmsQy4CAgAAaDAQLIAMhACADQX9HDQUMAwtBACEIDAcLQQAhAAwFCyAAQX9HDQILQQBBACgCxNOAgABBBHI2AsTTgIAACyAIQf7///8HSw0BIAgQy4CAgAAhAEEAEMuAgIAAIQMgAEF/Rg0BIANBf0YNASAAIANPDQEgAyAAayIGIAJBOGpNDQELQQBBACgCuNOAgAAgBmoiAzYCuNOAgAACQCADQQAoArzTgIAATQ0AQQAgAzYCvNOAgAALAkACQAJAAkBBACgCoNCAgAAiBEUNAEHI04CAACEDA0AgACADKAIAIgUgAygCBCIIakYNAiADKAIIIgMNAAwDCwsCQAJAQQAoApjQgIAAIgNFDQAgACADTw0BC0EAIAA2ApjQgIAAC0EAIQNBACAGNgLM04CAAEEAIAA2AsjTgIAAQQBBfzYCqNCAgABBAEEAKALg04CAADYCrNCAgABBAEEANgLU04CAAANAIANBxNCAgABqIANBuNCAgABqIgQ2AgAgBCADQbDQgIAAaiIFNgIAIANBvNCAgABqIAU2AgAgA0HM0ICAAGogA0HA0ICAAGoiBTYCACAFIAQ2AgAgA0HU0ICAAGogA0HI0ICAAGoiBDYCACAEIAU2AgAgA0HQ0ICAAGogBDYCACADQSBqIgNBgAJHDQALIABBeCAAa0EPcUEAIABBCGpBD3EbIgNqIgQgBkFIaiIFIANrIgNBAXI2AgRBAEEAKALw04CAADYCpNCAgABBACADNgKU0ICAAEEAIAQ2AqDQgIAAIAAgBWpBODYCBAwCCyADLQAMQQhxDQAgBCAFSQ0AIAQgAE8NACAEQXggBGtBD3FBACAEQQhqQQ9xGyIFaiIAQQAoApTQgIAAIAZqIgsgBWsiBUEBcjYCBCADIAggBmo2AgRBAEEAKALw04CAADYCpNCAgABBACAFNgKU0ICAAEEAIAA2AqDQgIAAIAQgC2pBODYCBAwBCwJAIABBACgCmNCAgAAiCE8NAEEAIAA2ApjQgIAAIAAhCAsgACAGaiEFQcjTgIAAIQMCQAJAAkACQAJAAkACQANAIAMoAgAgBUYNASADKAIIIgMNAAwCCwsgAy0ADEEIcUUNAQtByNOAgAAhAwNAAkAgAygCACIFIARLDQAgBSADKAIEaiIFIARLDQMLIAMoAgghAwwACwsgAyAANgIAIAMgAygCBCAGajYCBCAAQXggAGtBD3FBACAAQQhqQQ9xG2oiCyACQQNyNgIEIAVBeCAFa0EPcUEAIAVBCGpBD3EbaiIGIAsgAmoiAmshAwJAIAYgBEcNAEEAIAI2AqDQgIAAQQBBACgClNCAgAAgA2oiAzYClNCAgAAgAiADQQFyNgIEDAMLAkAgBkEAKAKc0ICAAEcNAEEAIAI2ApzQgIAAQQBBACgCkNCAgAAgA2oiAzYCkNCAgAAgAiADQQFyNgIEIAIgA2ogAzYCAAwDCwJAIAYoAgQiBEEDcUEBRw0AIARBeHEhBwJAAkAgBEH/AUsNACAGKAIIIgUgBEEDdiIIQQN0QbDQgIAAaiIARhoCQCAGKAIMIgQgBUcNAEEAQQAoAojQgIAAQX4gCHdxNgKI0ICAAAwCCyAEIABGGiAEIAU2AgggBSAENgIMDAELIAYoAhghCQJAAkAgBigCDCIAIAZGDQAgBigCCCIEIAhJGiAAIAQ2AgggBCAANgIMDAELAkAgBkEUaiIEKAIAIgUNACAGQRBqIgQoAgAiBQ0AQQAhAAwBCwNAIAQhCCAFIgBBFGoiBCgCACIFDQAgAEEQaiEEIAAoAhAiBQ0ACyAIQQA2AgALIAlFDQACQAJAIAYgBigCHCIFQQJ0QbjSgIAAaiIEKAIARw0AIAQgADYCACAADQFBAEEAKAKM0ICAAEF+IAV3cTYCjNCAgAAMAgsgCUEQQRQgCSgCECAGRhtqIAA2AgAgAEUNAQsgACAJNgIYAkAgBigCECIERQ0AIAAgBDYCECAEIAA2AhgLIAYoAhQiBEUNACAAQRRqIAQ2AgAgBCAANgIYCyAHIANqIQMgBiAHaiIGKAIEIQQLIAYgBEF+cTYCBCACIANqIAM2AgAgAiADQQFyNgIEAkAgA0H/AUsNACADQXhxQbDQgIAAaiEEAkACQEEAKAKI0ICAACIFQQEgA0EDdnQiA3ENAEEAIAUgA3I2AojQgIAAIAQhAwwBCyAEKAIIIQMLIAMgAjYCDCAEIAI2AgggAiAENgIMIAIgAzYCCAwDC0EfIQQCQCADQf///wdLDQAgA0EIdiIEIARBgP4/akEQdkEIcSIEdCIFIAVBgOAfakEQdkEEcSIFdCIAIABBgIAPakEQdkECcSIAdEEPdiAEIAVyIAByayIEQQF0IAMgBEEVanZBAXFyQRxqIQQLIAIgBDYCHCACQgA3AhAgBEECdEG40oCAAGohBQJAQQAoAozQgIAAIgBBASAEdCIIcQ0AIAUgAjYCAEEAIAAgCHI2AozQgIAAIAIgBTYCGCACIAI2AgggAiACNgIMDAMLIANBAEEZIARBAXZrIARBH0YbdCEEIAUoAgAhAANAIAAiBSgCBEF4cSADRg0CIARBHXYhACAEQQF0IQQgBSAAQQRxakEQaiIIKAIAIgANAAsgCCACNgIAIAIgBTYCGCACIAI2AgwgAiACNgIIDAILIABBeCAAa0EPcUEAIABBCGpBD3EbIgNqIgsgBkFIaiIIIANrIgNBAXI2AgQgACAIakE4NgIEIAQgBUE3IAVrQQ9xQQAgBUFJakEPcRtqQUFqIgggCCAEQRBqSRsiCEEjNgIEQQBBACgC8NOAgAA2AqTQgIAAQQAgAzYClNCAgABBACALNgKg0ICAACAIQRBqQQApAtDTgIAANwIAIAhBACkCyNOAgAA3AghBACAIQQhqNgLQ04CAAEEAIAY2AszTgIAAQQAgADYCyNOAgABBAEEANgLU04CAACAIQSRqIQMDQCADQQc2AgAgA0EEaiIDIAVJDQALIAggBEYNAyAIIAgoAgRBfnE2AgQgCCAIIARrIgA2AgAgBCAAQQFyNgIEAkAgAEH/AUsNACAAQXhxQbDQgIAAaiEDAkACQEEAKAKI0ICAACIFQQEgAEEDdnQiAHENAEEAIAUgAHI2AojQgIAAIAMhBQwBCyADKAIIIQULIAUgBDYCDCADIAQ2AgggBCADNgIMIAQgBTYCCAwEC0EfIQMCQCAAQf///wdLDQAgAEEIdiIDIANBgP4/akEQdkEIcSIDdCIFIAVBgOAfakEQdkEEcSIFdCIIIAhBgIAPakEQdkECcSIIdEEPdiADIAVyIAhyayIDQQF0IAAgA0EVanZBAXFyQRxqIQMLIAQgAzYCHCAEQgA3AhAgA0ECdEG40oCAAGohBQJAQQAoAozQgIAAIghBASADdCIGcQ0AIAUgBDYCAEEAIAggBnI2AozQgIAAIAQgBTYCGCAEIAQ2AgggBCAENgIMDAQLIABBAEEZIANBAXZrIANBH0YbdCEDIAUoAgAhCANAIAgiBSgCBEF4cSAARg0DIANBHXYhCCADQQF0IQMgBSAIQQRxakEQaiIGKAIAIggNAAsgBiAENgIAIAQgBTYCGCAEIAQ2AgwgBCAENgIIDAMLIAUoAggiAyACNgIMIAUgAjYCCCACQQA2AhggAiAFNgIMIAIgAzYCCAsgC0EIaiEDDAULIAUoAggiAyAENgIMIAUgBDYCCCAEQQA2AhggBCAFNgIMIAQgAzYCCAtBACgClNCAgAAiAyACTQ0AQQAoAqDQgIAAIgQgAmoiBSADIAJrIgNBAXI2AgRBACADNgKU0ICAAEEAIAU2AqDQgIAAIAQgAkEDcjYCBCAEQQhqIQMMAwtBACEDQQBBMDYC+NOAgAAMAgsCQCALRQ0AAkACQCAIIAgoAhwiBUECdEG40oCAAGoiAygCAEcNACADIAA2AgAgAA0BQQAgB0F+IAV3cSIHNgKM0ICAAAwCCyALQRBBFCALKAIQIAhGG2ogADYCACAARQ0BCyAAIAs2AhgCQCAIKAIQIgNFDQAgACADNgIQIAMgADYCGAsgCEEUaigCACIDRQ0AIABBFGogAzYCACADIAA2AhgLAkACQCAEQQ9LDQAgCCAEIAJqIgNBA3I2AgQgCCADaiIDIAMoAgRBAXI2AgQMAQsgCCACaiIAIARBAXI2AgQgCCACQQNyNgIEIAAgBGogBDYCAAJAIARB/wFLDQAgBEF4cUGw0ICAAGohAwJAAkBBACgCiNCAgAAiBUEBIARBA3Z0IgRxDQBBACAFIARyNgKI0ICAACADIQQMAQsgAygCCCEECyAEIAA2AgwgAyAANgIIIAAgAzYCDCAAIAQ2AggMAQtBHyEDAkAgBEH///8HSw0AIARBCHYiAyADQYD+P2pBEHZBCHEiA3QiBSAFQYDgH2pBEHZBBHEiBXQiAiACQYCAD2pBEHZBAnEiAnRBD3YgAyAFciACcmsiA0EBdCAEIANBFWp2QQFxckEcaiEDCyAAIAM2AhwgAEIANwIQIANBAnRBuNKAgABqIQUCQCAHQQEgA3QiAnENACAFIAA2AgBBACAHIAJyNgKM0ICAACAAIAU2AhggACAANgIIIAAgADYCDAwBCyAEQQBBGSADQQF2ayADQR9GG3QhAyAFKAIAIQICQANAIAIiBSgCBEF4cSAERg0BIANBHXYhAiADQQF0IQMgBSACQQRxakEQaiIGKAIAIgINAAsgBiAANgIAIAAgBTYCGCAAIAA2AgwgACAANgIIDAELIAUoAggiAyAANgIMIAUgADYCCCAAQQA2AhggACAFNgIMIAAgAzYCCAsgCEEIaiEDDAELAkAgCkUNAAJAAkAgACAAKAIcIgVBAnRBuNKAgABqIgMoAgBHDQAgAyAINgIAIAgNAUEAIAlBfiAFd3E2AozQgIAADAILIApBEEEUIAooAhAgAEYbaiAINgIAIAhFDQELIAggCjYCGAJAIAAoAhAiA0UNACAIIAM2AhAgAyAINgIYCyAAQRRqKAIAIgNFDQAgCEEUaiADNgIAIAMgCDYCGAsCQAJAIARBD0sNACAAIAQgAmoiA0EDcjYCBCAAIANqIgMgAygCBEEBcjYCBAwBCyAAIAJqIgUgBEEBcjYCBCAAIAJBA3I2AgQgBSAEaiAENgIAAkAgB0UNACAHQXhxQbDQgIAAaiECQQAoApzQgIAAIQMCQAJAQQEgB0EDdnQiCCAGcQ0AQQAgCCAGcjYCiNCAgAAgAiEIDAELIAIoAgghCAsgCCADNgIMIAIgAzYCCCADIAI2AgwgAyAINgIIC0EAIAU2ApzQgIAAQQAgBDYCkNCAgAALIABBCGohAwsgAUEQaiSAgICAACADCwoAIAAQyYCAgAAL4g0BB38CQCAARQ0AIABBeGoiASAAQXxqKAIAIgJBeHEiAGohAwJAIAJBAXENACACQQNxRQ0BIAEgASgCACICayIBQQAoApjQgIAAIgRJDQEgAiAAaiEAAkAgAUEAKAKc0ICAAEYNAAJAIAJB/wFLDQAgASgCCCIEIAJBA3YiBUEDdEGw0ICAAGoiBkYaAkAgASgCDCICIARHDQBBAEEAKAKI0ICAAEF+IAV3cTYCiNCAgAAMAwsgAiAGRhogAiAENgIIIAQgAjYCDAwCCyABKAIYIQcCQAJAIAEoAgwiBiABRg0AIAEoAggiAiAESRogBiACNgIIIAIgBjYCDAwBCwJAIAFBFGoiAigCACIEDQAgAUEQaiICKAIAIgQNAEEAIQYMAQsDQCACIQUgBCIGQRRqIgIoAgAiBA0AIAZBEGohAiAGKAIQIgQNAAsgBUEANgIACyAHRQ0BAkACQCABIAEoAhwiBEECdEG40oCAAGoiAigCAEcNACACIAY2AgAgBg0BQQBBACgCjNCAgABBfiAEd3E2AozQgIAADAMLIAdBEEEUIAcoAhAgAUYbaiAGNgIAIAZFDQILIAYgBzYCGAJAIAEoAhAiAkUNACAGIAI2AhAgAiAGNgIYCyABKAIUIgJFDQEgBkEUaiACNgIAIAIgBjYCGAwBCyADKAIEIgJBA3FBA0cNACADIAJBfnE2AgRBACAANgKQ0ICAACABIABqIAA2AgAgASAAQQFyNgIEDwsgASADTw0AIAMoAgQiAkEBcUUNAAJAAkAgAkECcQ0AAkAgA0EAKAKg0ICAAEcNAEEAIAE2AqDQgIAAQQBBACgClNCAgAAgAGoiADYClNCAgAAgASAAQQFyNgIEIAFBACgCnNCAgABHDQNBAEEANgKQ0ICAAEEAQQA2ApzQgIAADwsCQCADQQAoApzQgIAARw0AQQAgATYCnNCAgABBAEEAKAKQ0ICAACAAaiIANgKQ0ICAACABIABBAXI2AgQgASAAaiAANgIADwsgAkF4cSAAaiEAAkACQCACQf8BSw0AIAMoAggiBCACQQN2IgVBA3RBsNCAgABqIgZGGgJAIAMoAgwiAiAERw0AQQBBACgCiNCAgABBfiAFd3E2AojQgIAADAILIAIgBkYaIAIgBDYCCCAEIAI2AgwMAQsgAygCGCEHAkACQCADKAIMIgYgA0YNACADKAIIIgJBACgCmNCAgABJGiAGIAI2AgggAiAGNgIMDAELAkAgA0EUaiICKAIAIgQNACADQRBqIgIoAgAiBA0AQQAhBgwBCwNAIAIhBSAEIgZBFGoiAigCACIEDQAgBkEQaiECIAYoAhAiBA0ACyAFQQA2AgALIAdFDQACQAJAIAMgAygCHCIEQQJ0QbjSgIAAaiICKAIARw0AIAIgBjYCACAGDQFBAEEAKAKM0ICAAEF+IAR3cTYCjNCAgAAMAgsgB0EQQRQgBygCECADRhtqIAY2AgAgBkUNAQsgBiAHNgIYAkAgAygCECICRQ0AIAYgAjYCECACIAY2AhgLIAMoAhQiAkUNACAGQRRqIAI2AgAgAiAGNgIYCyABIABqIAA2AgAgASAAQQFyNgIEIAFBACgCnNCAgABHDQFBACAANgKQ0ICAAA8LIAMgAkF+cTYCBCABIABqIAA2AgAgASAAQQFyNgIECwJAIABB/wFLDQAgAEF4cUGw0ICAAGohAgJAAkBBACgCiNCAgAAiBEEBIABBA3Z0IgBxDQBBACAEIAByNgKI0ICAACACIQAMAQsgAigCCCEACyAAIAE2AgwgAiABNgIIIAEgAjYCDCABIAA2AggPC0EfIQICQCAAQf///wdLDQAgAEEIdiICIAJBgP4/akEQdkEIcSICdCIEIARBgOAfakEQdkEEcSIEdCIGIAZBgIAPakEQdkECcSIGdEEPdiACIARyIAZyayICQQF0IAAgAkEVanZBAXFyQRxqIQILIAEgAjYCHCABQgA3AhAgAkECdEG40oCAAGohBAJAAkBBACgCjNCAgAAiBkEBIAJ0IgNxDQAgBCABNgIAQQAgBiADcjYCjNCAgAAgASAENgIYIAEgATYCCCABIAE2AgwMAQsgAEEAQRkgAkEBdmsgAkEfRht0IQIgBCgCACEGAkADQCAGIgQoAgRBeHEgAEYNASACQR12IQYgAkEBdCECIAQgBkEEcWpBEGoiAygCACIGDQALIAMgATYCACABIAQ2AhggASABNgIMIAEgATYCCAwBCyAEKAIIIgAgATYCDCAEIAE2AgggAUEANgIYIAEgBDYCDCABIAA2AggLQQBBACgCqNCAgABBf2oiAUF/IAEbNgKo0ICAAAsLBAAAAAtOAAJAIAANAD8AQRB0DwsCQCAAQf//A3ENACAAQX9MDQACQCAAQRB2QAAiAEF/Rw0AQQBBMDYC+NOAgABBfw8LIABBEHQPCxDKgICAAAAL8gICA38BfgJAIAJFDQAgACABOgAAIAIgAGoiA0F/aiABOgAAIAJBA0kNACAAIAE6AAIgACABOgABIANBfWogAToAACADQX5qIAE6AAAgAkEHSQ0AIAAgAToAAyADQXxqIAE6AAAgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBfGogATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQXhqIAE2AgAgAkF0aiABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkFwaiABNgIAIAJBbGogATYCACACQWhqIAE2AgAgAkFkaiABNgIAIAQgA0EEcUEYciIFayICQSBJDQAgAa1CgYCAgBB+IQYgAyAFaiEBA0AgASAGNwMYIAEgBjcDECABIAY3AwggASAGNwMAIAFBIGohASACQWBqIgJBH0sNAAsLIAALC45IAQBBgAgLhkgBAAAAAgAAAAMAAAAAAAAAAAAAAAQAAAAFAAAAAAAAAAAAAAAGAAAABwAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEludmFsaWQgY2hhciBpbiB1cmwgcXVlcnkAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9ib2R5AENvbnRlbnQtTGVuZ3RoIG92ZXJmbG93AENodW5rIHNpemUgb3ZlcmZsb3cAUmVzcG9uc2Ugb3ZlcmZsb3cASW52YWxpZCBtZXRob2QgZm9yIEhUVFAveC54IHJlcXVlc3QASW52YWxpZCBtZXRob2QgZm9yIFJUU1AveC54IHJlcXVlc3QARXhwZWN0ZWQgU09VUkNFIG1ldGhvZCBmb3IgSUNFL3gueCByZXF1ZXN0AEludmFsaWQgY2hhciBpbiB1cmwgZnJhZ21lbnQgc3RhcnQARXhwZWN0ZWQgZG90AFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fc3RhdHVzAEludmFsaWQgcmVzcG9uc2Ugc3RhdHVzAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMAVXNlciBjYWxsYmFjayBlcnJvcgBgb25fcmVzZXRgIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19oZWFkZXJgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2JlZ2luYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlYCBjYWxsYmFjayBlcnJvcgBgb25fc3RhdHVzX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdmVyc2lvbl9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3VybF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWVzc2FnZV9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX21ldGhvZF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lYCBjYWxsYmFjayBlcnJvcgBVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNlcnZlcgBJbnZhbGlkIGhlYWRlciB2YWx1ZSBjaGFyAEludmFsaWQgaGVhZGVyIGZpZWxkIGNoYXIAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl92ZXJzaW9uAEludmFsaWQgbWlub3IgdmVyc2lvbgBJbnZhbGlkIG1ham9yIHZlcnNpb24ARXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgdmVyc2lvbgBFeHBlY3RlZCBDUkxGIGFmdGVyIHZlcnNpb24ASW52YWxpZCBIVFRQIHZlcnNpb24ASW52YWxpZCBoZWFkZXIgdG9rZW4AU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl91cmwASW52YWxpZCBjaGFyYWN0ZXJzIGluIHVybABVbmV4cGVjdGVkIHN0YXJ0IGNoYXIgaW4gdXJsAERvdWJsZSBAIGluIHVybABFbXB0eSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXJhY3RlciBpbiBDb250ZW50LUxlbmd0aABEdXBsaWNhdGUgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyIGluIHVybCBwYXRoAENvbnRlbnQtTGVuZ3RoIGNhbid0IGJlIHByZXNlbnQgd2l0aCBUcmFuc2Zlci1FbmNvZGluZwBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBzaXplAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25faGVhZGVyX3ZhbHVlAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgdmFsdWUATWlzc2luZyBleHBlY3RlZCBMRiBhZnRlciBoZWFkZXIgdmFsdWUASW52YWxpZCBgVHJhbnNmZXItRW5jb2RpbmdgIGhlYWRlciB2YWx1ZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIHF1b3RlIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAFBhdXNlZCBieSBvbl9oZWFkZXJzX2NvbXBsZXRlAEludmFsaWQgRU9GIHN0YXRlAG9uX3Jlc2V0IHBhdXNlAG9uX2NodW5rX2hlYWRlciBwYXVzZQBvbl9tZXNzYWdlX2JlZ2luIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZSBwYXVzZQBvbl9zdGF0dXNfY29tcGxldGUgcGF1c2UAb25fdmVyc2lvbl9jb21wbGV0ZSBwYXVzZQBvbl91cmxfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlIHBhdXNlAG9uX21lc3NhZ2VfY29tcGxldGUgcGF1c2UAb25fbWV0aG9kX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fbmFtZSBwYXVzZQBVbmV4cGVjdGVkIHNwYWNlIGFmdGVyIHN0YXJ0IGxpbmUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fbmFtZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIG5hbWUAUGF1c2Ugb24gQ09OTkVDVC9VcGdyYWRlAFBhdXNlIG9uIFBSSS9VcGdyYWRlAEV4cGVjdGVkIEhUVFAvMiBDb25uZWN0aW9uIFByZWZhY2UAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9tZXRob2QARXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgbWV0aG9kAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25faGVhZGVyX2ZpZWxkAFBhdXNlZABJbnZhbGlkIHdvcmQgZW5jb3VudGVyZWQASW52YWxpZCBtZXRob2QgZW5jb3VudGVyZWQAVW5leHBlY3RlZCBjaGFyIGluIHVybCBzY2hlbWEAUmVxdWVzdCBoYXMgaW52YWxpZCBgVHJhbnNmZXItRW5jb2RpbmdgAFNXSVRDSF9QUk9YWQBVU0VfUFJPWFkATUtBQ1RJVklUWQBVTlBST0NFU1NBQkxFX0VOVElUWQBDT1BZAE1PVkVEX1BFUk1BTkVOVExZAFRPT19FQVJMWQBOT1RJRlkARkFJTEVEX0RFUEVOREVOQ1kAQkFEX0dBVEVXQVkAUExBWQBQVVQAQ0hFQ0tPVVQAR0FURVdBWV9USU1FT1VUAFJFUVVFU1RfVElNRU9VVABORVRXT1JLX0NPTk5FQ1RfVElNRU9VVABDT05ORUNUSU9OX1RJTUVPVVQATE9HSU5fVElNRU9VVABORVRXT1JLX1JFQURfVElNRU9VVABQT1NUAE1JU0RJUkVDVEVEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfTE9BRF9CQUxBTkNFRF9SRVFVRVNUAEJBRF9SRVFVRVNUAEhUVFBfUkVRVUVTVF9TRU5UX1RPX0hUVFBTX1BPUlQAUkVQT1JUAElNX0FfVEVBUE9UAFJFU0VUX0NPTlRFTlQATk9fQ09OVEVOVABQQVJUSUFMX0NPTlRFTlQASFBFX0lOVkFMSURfQ09OU1RBTlQASFBFX0NCX1JFU0VUAEdFVABIUEVfU1RSSUNUAENPTkZMSUNUAFRFTVBPUkFSWV9SRURJUkVDVABQRVJNQU5FTlRfUkVESVJFQ1QAQ09OTkVDVABNVUxUSV9TVEFUVVMASFBFX0lOVkFMSURfU1RBVFVTAFRPT19NQU5ZX1JFUVVFU1RTAEVBUkxZX0hJTlRTAFVOQVZBSUxBQkxFX0ZPUl9MRUdBTF9SRUFTT05TAE9QVElPTlMAU1dJVENISU5HX1BST1RPQ09MUwBWQVJJQU5UX0FMU09fTkVHT1RJQVRFUwBNVUxUSVBMRV9DSE9JQ0VTAElOVEVSTkFMX1NFUlZFUl9FUlJPUgBXRUJfU0VSVkVSX1VOS05PV05fRVJST1IAUkFJTEdVTl9FUlJPUgBJREVOVElUWV9QUk9WSURFUl9BVVRIRU5USUNBVElPTl9FUlJPUgBTU0xfQ0VSVElGSUNBVEVfRVJST1IASU5WQUxJRF9YX0ZPUldBUkRFRF9GT1IAU0VUX1BBUkFNRVRFUgBHRVRfUEFSQU1FVEVSAEhQRV9VU0VSAFNFRV9PVEhFUgBIUEVfQ0JfQ0hVTktfSEVBREVSAE1LQ0FMRU5EQVIAU0VUVVAAV0VCX1NFUlZFUl9JU19ET1dOAFRFQVJET1dOAEhQRV9DTE9TRURfQ09OTkVDVElPTgBIRVVSSVNUSUNfRVhQSVJBVElPTgBESVNDT05ORUNURURfT1BFUkFUSU9OAE5PTl9BVVRIT1JJVEFUSVZFX0lORk9STUFUSU9OAEhQRV9JTlZBTElEX1ZFUlNJT04ASFBFX0NCX01FU1NBR0VfQkVHSU4AU0lURV9JU19GUk9aRU4ASFBFX0lOVkFMSURfSEVBREVSX1RPS0VOAElOVkFMSURfVE9LRU4ARk9SQklEREVOAEVOSEFOQ0VfWU9VUl9DQUxNAEhQRV9JTlZBTElEX1VSTABCTE9DS0VEX0JZX1BBUkVOVEFMX0NPTlRST0wATUtDT0wAQUNMAEhQRV9JTlRFUk5BTABSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFX1VOT0ZGSUNJQUwASFBFX09LAFVOTElOSwBVTkxPQ0sAUFJJAFJFVFJZX1dJVEgASFBFX0lOVkFMSURfQ09OVEVOVF9MRU5HVEgASFBFX1VORVhQRUNURURfQ09OVEVOVF9MRU5HVEgARkxVU0gAUFJPUFBBVENIAE0tU0VBUkNIAFVSSV9UT09fTE9ORwBQUk9DRVNTSU5HAE1JU0NFTExBTkVPVVNfUEVSU0lTVEVOVF9XQVJOSU5HAE1JU0NFTExBTkVPVVNfV0FSTklORwBIUEVfSU5WQUxJRF9UUkFOU0ZFUl9FTkNPRElORwBFeHBlY3RlZCBDUkxGAEhQRV9JTlZBTElEX0NIVU5LX1NJWkUATU9WRQBDT05USU5VRQBIUEVfQ0JfU1RBVFVTX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJTX0NPTVBMRVRFAEhQRV9DQl9WRVJTSU9OX0NPTVBMRVRFAEhQRV9DQl9VUkxfQ09NUExFVEUASFBFX0NCX0NIVU5LX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJfVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9WQUxVRV9DT01QTEVURQBIUEVfQ0JfQ0hVTktfRVhURU5TSU9OX05BTUVfQ09NUExFVEUASFBFX0NCX01FU1NBR0VfQ09NUExFVEUASFBFX0NCX01FVEhPRF9DT01QTEVURQBIUEVfQ0JfSEVBREVSX0ZJRUxEX0NPTVBMRVRFAERFTEVURQBIUEVfSU5WQUxJRF9FT0ZfU1RBVEUASU5WQUxJRF9TU0xfQ0VSVElGSUNBVEUAUEFVU0UATk9fUkVTUE9OU0UAVU5TVVBQT1JURURfTUVESUFfVFlQRQBHT05FAE5PVF9BQ0NFUFRBQkxFAFNFUlZJQ0VfVU5BVkFJTEFCTEUAUkFOR0VfTk9UX1NBVElTRklBQkxFAE9SSUdJTl9JU19VTlJFQUNIQUJMRQBSRVNQT05TRV9JU19TVEFMRQBQVVJHRQBNRVJHRQBSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFAFJFUVVFU1RfSEVBREVSX1RPT19MQVJHRQBQQVlMT0FEX1RPT19MQVJHRQBJTlNVRkZJQ0lFTlRfU1RPUkFHRQBIUEVfUEFVU0VEX1VQR1JBREUASFBFX1BBVVNFRF9IMl9VUEdSQURFAFNPVVJDRQBBTk5PVU5DRQBUUkFDRQBIUEVfVU5FWFBFQ1RFRF9TUEFDRQBERVNDUklCRQBVTlNVQlNDUklCRQBSRUNPUkQASFBFX0lOVkFMSURfTUVUSE9EAE5PVF9GT1VORABQUk9QRklORABVTkJJTkQAUkVCSU5EAFVOQVVUSE9SSVpFRABNRVRIT0RfTk9UX0FMTE9XRUQASFRUUF9WRVJTSU9OX05PVF9TVVBQT1JURUQAQUxSRUFEWV9SRVBPUlRFRABBQ0NFUFRFRABOT1RfSU1QTEVNRU5URUQATE9PUF9ERVRFQ1RFRABIUEVfQ1JfRVhQRUNURUQASFBFX0xGX0VYUEVDVEVEAENSRUFURUQASU1fVVNFRABIUEVfUEFVU0VEAFRJTUVPVVRfT0NDVVJFRABQQVlNRU5UX1JFUVVJUkVEAFBSRUNPTkRJVElPTl9SRVFVSVJFRABQUk9YWV9BVVRIRU5USUNBVElPTl9SRVFVSVJFRABORVRXT1JLX0FVVEhFTlRJQ0FUSU9OX1JFUVVJUkVEAExFTkdUSF9SRVFVSVJFRABTU0xfQ0VSVElGSUNBVEVfUkVRVUlSRUQAVVBHUkFERV9SRVFVSVJFRABQQUdFX0VYUElSRUQAUFJFQ09ORElUSU9OX0ZBSUxFRABFWFBFQ1RBVElPTl9GQUlMRUQAUkVWQUxJREFUSU9OX0ZBSUxFRABTU0xfSEFORFNIQUtFX0ZBSUxFRABMT0NLRUQAVFJBTlNGT1JNQVRJT05fQVBQTElFRABOT1RfTU9ESUZJRUQATk9UX0VYVEVOREVEAEJBTkRXSURUSF9MSU1JVF9FWENFRURFRABTSVRFX0lTX09WRVJMT0FERUQASEVBRABFeHBlY3RlZCBIVFRQLwAAXhMAACYTAAAwEAAA8BcAAJ0TAAAVEgAAORcAAPASAAAKEAAAdRIAAK0SAACCEwAATxQAAH8QAACgFQAAIxQAAIkSAACLFAAATRUAANQRAADPFAAAEBgAAMkWAADcFgAAwREAAOAXAAC7FAAAdBQAAHwVAADlFAAACBcAAB8QAABlFQAAoxQAACgVAAACFQAAmRUAACwQAACLGQAATw8AANQOAABqEAAAzhAAAAIXAACJDgAAbhMAABwTAABmFAAAVhcAAMETAADNEwAAbBMAAGgXAABmFwAAXxcAACITAADODwAAaQ4AANgOAABjFgAAyxMAAKoOAAAoFwAAJhcAAMUTAABdFgAA6BEAAGcTAABlEwAA8hYAAHMTAAAdFwAA+RYAAPMRAADPDgAAzhUAAAwSAACzEQAApREAAGEQAAAyFwAAuxMAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIDAgICAgIAAAICAAICAAICAgICAgICAgIABAAAAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgIAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgACAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAICAgICAAACAgACAgACAgICAgICAgICAAMABAAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbG9zZWVlcC1hbGl2ZQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBY2h1bmtlZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAQEBAQEAAAEBAAEBAAEBAQEBAQEBAQEAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AAAAAAAAAAAAAAAAAAAByYW5zZmVyLWVuY29kaW5ncGdyYWRlDQoNCg0KU00NCg0KVFRQL0NFL1RTUC8AAAAAAAAAAAAAAAABAgABAwAAAAAAAAAAAAAAAAAAAAAAAAQBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAQIAAQMAAAAAAAAAAAAAAAAAAAAAAAAEAQEFAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAQAAAgAAAAAAAAAAAAAAAAAAAAAAAAMEAAAEBAQEBAQEBAQEBAUEBAQEBAQEBAQEBAQABAAGBwQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAIAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOT1VOQ0VFQ0tPVVRORUNURVRFQ1JJQkVMVVNIRVRFQURTRUFSQ0hSR0VDVElWSVRZTEVOREFSVkVPVElGWVBUSU9OU0NIU0VBWVNUQVRDSEdFT1JESVJFQ1RPUlRSQ0hQQVJBTUVURVJVUkNFQlNDUklCRUFSRE9XTkFDRUlORE5LQ0tVQlNDUklCRUhUVFAvQURUUC8="},2084:e=>{e.exports="AGFzbQEAAAABMAhgAX8Bf2ADf39/AX9gBH9/f38Bf2AAAGADf39/AGABfwBgAn9/AGAGf39/f39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQACA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAA0ZFAwMEAAAFAAAAAAAABQEFAAUFBQAABgAAAAAGBgYGAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAABAQcAAAUFAwABBAUBcAESEgUDAQACBggBfwFBgNQECwfRBSIGbWVtb3J5AgALX2luaXRpYWxpemUACRlfX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlAQALbGxodHRwX2luaXQAChhsbGh0dHBfc2hvdWxkX2tlZXBfYWxpdmUAQQxsbGh0dHBfYWxsb2MADAZtYWxsb2MARgtsbGh0dHBfZnJlZQANBGZyZWUASA9sbGh0dHBfZ2V0X3R5cGUADhVsbGh0dHBfZ2V0X2h0dHBfbWFqb3IADxVsbGh0dHBfZ2V0X2h0dHBfbWlub3IAEBFsbGh0dHBfZ2V0X21ldGhvZAARFmxsaHR0cF9nZXRfc3RhdHVzX2NvZGUAEhJsbGh0dHBfZ2V0X3VwZ3JhZGUAEwxsbGh0dHBfcmVzZXQAFA5sbGh0dHBfZXhlY3V0ZQAVFGxsaHR0cF9zZXR0aW5nc19pbml0ABYNbGxodHRwX2ZpbmlzaAAXDGxsaHR0cF9wYXVzZQAYDWxsaHR0cF9yZXN1bWUAGRtsbGh0dHBfcmVzdW1lX2FmdGVyX3VwZ3JhZGUAGhBsbGh0dHBfZ2V0X2Vycm5vABsXbGxodHRwX2dldF9lcnJvcl9yZWFzb24AHBdsbGh0dHBfc2V0X2Vycm9yX3JlYXNvbgAdFGxsaHR0cF9nZXRfZXJyb3JfcG9zAB4RbGxodHRwX2Vycm5vX25hbWUAHxJsbGh0dHBfbWV0aG9kX25hbWUAIBJs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IAH9CwMAIABBMGogAf0LAwAgAEEgaiAB/QsDACAAQd0BNgIcQQALewEBfwJAIAAoAgwiAw0AAkAgACgCBEUNACAAIAE2AgQLAkAgACABIAIQxICAgAAiAw0AIAAoAgwPCyAAIAM2AhxBACEDIAAoAgQiAUUNACAAIAEgAiAAKAIIEYGAgIAAACIBRQ0AIAAgAjYCFCAAIAE2AgwgASEDCyADC+TzAQMOfwN+BH8jgICAgABBEGsiAySAgICAACABIQQgASEFIAEhBiABIQcgASEIIAEhCSABIQogASELIAEhDCABIQ0gASEOIAEhDwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAIcIhBBf2oO3QHaAQHZAQIDBAUGBwgJCgsMDQ7YAQ8Q1wEREtYBExQVFhcYGRob4AHfARwdHtUBHyAhIiMkJdQBJicoKSorLNMB0gEtLtEB0AEvMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUbbAUdISUrPAc4BS80BTMwBTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEB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QAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIBAOxwEAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB4fICEjJSg/QEFERUZHSElKS0xNT1BRUlPeA1dZW1xdYGJlZmdoaWprbG1vcHFyc3R1dnd4eXp7fH1+gAGCAYUBhgGHAYkBiwGMAY0BjgGPAZABkQGUAZUBlgGXAZgBmQGaAZsBnAGdAZ4BnwGgAaEBogGjAaQBpQGmAacBqAGpAaoBqwGsAa0BrgGvAbABsQGyAbMBtAG1AbYBtwG4AbkBugG7AbwBvQG+Ab8BwAHBAcIBwwHEAcUBxgHHAcgByQHKAcsBzAHNAc4BzwHQAdEB0gHTAdQB1QHWAdcB2AHZAdoB2wHcAd0B3gHgAeEB4gHjAeQB5QHmAecB6AHpAeoB6wHsAe0B7gHvAfAB8QHyAfMBmQKkArAC/gL+AgsgASIEIAJHDfMBQd0BIRAM/wMLIAEiECACRw3dAUHDASEQDP4DCyABIgEgAkcNkAFB9wAhEAz9AwsgASIBIAJHDYYBQe8AIRAM/AMLIAEiASACRw1/QeoAIRAM+wMLIAEiASACRw17QegAIRAM+gMLIAEiASACRw14QeYAIRAM+QMLIAEiASACRw0aQRghEAz4AwsgASIBIAJHDRRBEiEQDPcDCyABIgEgAkcNWUHFACEQDPYDCyABIgEgAkcNSkE/IRAM9QMLIAEiASACRw1IQTwhEAz0AwsgASIBIAJHDUFBMSEQDPMDCyAALQAuQQFGDesDDIcCCyAAIAEiASACEMCAgIAAQQFHDeYBIABCADcDIAznAQsgACABIgEgAhC0gICAACIQDecBIAEhAQz1AgsCQCABIgEgAkcNAEEGIRAM8AMLIAAgAUEBaiIBIAIQu4CAgAAiEA3oASABIQEMMQsgAEIANwMgQRIhEAzVAwsgASIQIAJHDStBHSEQDO0DCwJAIAEiASACRg0AIAFBAWohAUEQIRAM1AMLQQchEAzsAwsgAEIAIAApAyAiESACIAEiEGutIhJ9IhMgEyARVhs3AyAgESASViIURQ3lAUEIIRAM6wMLAkAgASIBIAJGDQAgAEGJgICAADYCCCAAIAE2AgQgASEBQRQhEAzSAwtBCSEQDOoDCyABIQEgACkDIFAN5AEgASEBDPICCwJAIAEiASACRw0AQQshEAzpAwsgACABQQFqIgEgAhC2gICAACIQDeUBIAEhAQzyAgsgACABIgEgAhC4gICAACIQDeUBIAEhAQzyAgsgACABIgEgAhC4gICAACIQDeYBIAEhAQwNCyAAIAEiASACELqAgIAAIhAN5wEgASEBDPACCwJAIAEiASACRw0AQQ8hEAzlAwsgAS0AACIQQTtGDQggEEENRw3oASABQQFqIQEM7wILIAAgASIBIAIQuoCAgAAiEA3oASABIQEM8gILA0ACQCABLQAAQfC1gIAAai0AACIQQQFGDQAgEEECRw3rASAAKAIEIRAgAEEANgIEIAAgECABQQFqIgEQuYCAgAAiEA3qASABIQEM9AILIAFBAWoiASACRw0AC0ESIRAM4gMLIAAgASIBIAIQuoCAgAAiEA3pASABIQEMCgsgASIBIAJHDQZBGyEQDOADCwJAIAEiASACRw0AQRYhEAzgAwsgAEGKgICAADYCCCAAIAE2AgQgACABIAIQuICAgAAiEA3qASABIQFBICEQDMYDCwJAIAEiASACRg0AA0ACQCABLQAAQfC3gIAAai0AACIQQQJGDQACQCAQQX9qDgTlAewBAOsB7AELIAFBAWohAUEIIRAMyAMLIAFBAWoiASACRw0AC0EVIRAM3wMLQRUhEAzeAwsDQAJAIAEtAABB8LmAgABqLQAAIhBBAkYNACAQQX9qDgTeAewB4AHrAewBCyABQQFqIgEgAkcNAAtBGCEQDN0DCwJAIAEiASACRg0AIABBi4CAgAA2AgggACABNgIEIAEhAUEHIRAMxAMLQRkhEAzcAwsgAUEBaiEBDAILAkAgASIUIAJHDQBBGiEQDNsDCyAUIQECQCAULQAAQXNqDhTdAu4C7gLuAu4C7gLuAu4C7gLuAu4C7gLuAu4C7gLuAu4C7gLuAgDuAgtBACEQIABBADYCHCAAQa+LgIAANgIQIABBAjYCDCAAIBRBAWo2AhQM2gMLAkAgAS0AACIQQTtGDQAgEEENRw3oASABQQFqIQEM5QILIAFBAWohAQtBIiEQDL8DCwJAIAEiECACRw0AQRwhEAzYAwtCACERIBAhASAQLQAAQVBqDjfnAeYBAQIDBAUGBwgAAAAAAAAACQoLDA0OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPEBESExQAC0EeIRAMvQMLQgIhEQzlAQtCAyERDOQBC0IEIREM4wELQgUhEQziAQtCBiERDOEBC0IHIREM4AELQgghEQzfAQtCCSERDN4BC0IKIREM3QELQgshEQzcAQtCDCERDNsBC0INIREM2gELQg4hEQzZAQtCDyERDNgBC0IKIREM1wELQgshEQzWAQtCDCERDNUBC0INIREM1AELQg4hEQzTAQtCDyERDNIBC0IAIRECQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIBAtAABBUGoON+UB5AEAAQIDBAUGB+YB5gHmAeYB5gHmAeYBCAkKCwwN5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAeYB5gHmAQ4PEBESE+YBC0ICIREM5AELQgMhEQzjAQtCBCERDOIBC0IFIREM4QELQgYhEQzgAQtCByERDN8BC0IIIREM3gELQgkhEQzdAQtCCiERDNwBC0ILIREM2wELQgwhEQzaAQtCDSERDNkBC0IOIREM2AELQg8hEQzXAQtCCiERDNYBC0ILIREM1QELQgwhEQzUAQtCDSERDNMBC0IOIREM0gELQg8hEQzRAQsgAEIAIAApAyAiESACIAEiEGutIhJ9IhMgEyARVhs3AyAgESASViIURQ3SAUEfIRAMwAMLAkAgASIBIAJGDQAgAEGJgICAADYCCCAAIAE2AgQgASEBQSQhEAynAwtBICEQDL8DCyAAIAEiECACEL6AgIAAQX9qDgW2AQDFAgHRAdIBC0ERIRAMpAMLIABBAToALyAQIQEMuwMLIAEiASACRw3SAUEkIRAMuwMLIAEiDSACRw0eQcYAIRAMugMLIAAgASIBIAIQsoCAgAAiEA3UASABIQEMtQELIAEiECACRw0mQdAAIRAMuAMLAkAgASIBIAJHDQBBKCEQDLgDCyAAQQA2AgQgAEGMgICAADYCCCAAIAEgARCxgICAACIQDdMBIAEhAQzYAQsCQCABIhAgAkcNAEEpIRAMtwMLIBAtAAAiAUEgRg0UIAFBCUcN0wEgEEEBaiEBDBULAkAgASIBIAJGDQAgAUEBaiEBDBcLQSohEAy1AwsCQCABIhAgAkcNAEErIRAMtQMLAkAgEC0AACIBQQlGDQAgAUEgRw3VAQsgAC0ALEEIRg3TASAQIQEMkQMLAkAgASIBIAJHDQBBLCEQDLQDCyABLQAAQQpHDdUBIAFBAWohAQzJAgsgASIOIAJHDdUBQS8hEAyyAwsDQAJAIAEtAAAiEEEgRg0AAkAgEEF2ag4EANwB3AEA2gELIAEhAQzgAQsgAUEBaiIBIAJHDQALQTEhEAyxAwtBMiEQIAEiFCACRg2wAyACIBRrIAAoAgAiAWohFSAUIAFrQQNqIRYCQANAIBQtAAAiF0EgciAXIBdBv39qQf8BcUEaSRtB/wFxIAFB8LuAgABqLQAARw0BAkAgAUEDRw0AQQYhAQyWAwsgAUEBaiEBIBRBAWoiFCACRw0ACyAAIBU2AgAMsQMLIABBADYCACAUIQEM2QELQTMhECABIhQgAkYNrwMgAiAUayAAKAIAIgFqIRUgFCABa0EIaiEWAkADQCAULQAAIhdBIHIgFyAXQb9/akH/AXFBGkkbQf8BcSABQfS7gIAAai0AAEcNAQJAIAFBCEcNAEEFIQEMlQMLIAFBAWohASAUQQFqIhQgAkcNAAsgACAVNgIADLADCyAAQQA2AgAgFCEBDNgBC0E0IRAgASIUIAJGDa4DIAIgFGsgACgCACIBaiEVIBQgAWtBBWohFgJAA0AgFC0AACIXQSByIBcgF0G/f2pB/wFxQRpJG0H/AXEgAUHQwoCAAGotAABHDQECQCABQQVHDQBBByEBDJQDCyABQQFqIQEgFEEBaiIUIAJHDQALIAAgFTYCAAyvAwsgAEEANgIAIBQhAQzXAQsCQCABIgEgAkYNAANAAkAgAS0AAEGAvoCAAGotAAAiEEEBRg0AIBBBAkYNCiABIQEM3QELIAFBAWoiASACRw0AC0EwIRAMrgMLQTAhEAytAwsCQCABIgEgAkYNAANAAkAgAS0AACIQQSBGDQAgEEF2ag4E2QHaAdoB2QHaAQsgAUEBaiIBIAJHDQALQTghEAytAwtBOCEQDKwDCwNAAkAgAS0AACIQQSBGDQAgEEEJRw0DCyABQQFqIgEgAkcNAAtBPCEQDKsDCwNAAkAgAS0AACIQQSBGDQACQAJAIBBBdmoOBNoBAQHaAQALIBBBLEYN2wELIAEhAQwECyABQQFqIgEgAkcNAAtBPyEQDKoDCyABIQEM2wELQcAAIRAgASIUIAJGDagDIAIgFGsgACgCACIBaiEWIBQgAWtBBmohFwJAA0AgFC0AAEEgciABQYDAgIAAai0AAEcNASABQQZGDY4DIAFBAWohASAUQQFqIhQgAkcNAAsgACAWNgIADKkDCyAAQQA2AgAgFCEBC0E2IRAMjgMLAkAgASIPIAJHDQBBwQAhEAynAwsgAEGMgICAADYCCCAAIA82AgQgDyEBIAAtACxBf2oOBM0B1QHXAdkBhwMLIAFBAWohAQzMAQsCQCABIgEgAkYNAANAAkAgAS0AACIQQSByIBAgEEG/f2pB/wFxQRpJG0H/AXEiEEEJRg0AIBBBIEYNAAJAAkACQAJAIBBBnX9qDhMAAwMDAwMDAwEDAwMDAwMDAwMCAwsgAUEBaiEBQTEhEAyRAwsgAUEBaiEBQTIhEAyQAwsgAUEBaiEBQTMhEAyPAwsgASEBDNABCyABQQFqIgEgAkcNAAtBNSEQDKUDC0E1IRAMpAMLAkAgASIBIAJGDQADQAJAIAEtAABBgLyAgABqLQAAQQFGDQAgASEBDNMBCyABQQFqIgEgAkcNAAtBPSEQDKQDC0E9IRAMowMLIAAgASIBIAIQsICAgAAiEA3WASABIQEMAQsgEEEBaiEBC0E8IRAMhwMLAkAgASIBIAJHDQBBwgAhEAygAwsCQANAAkAgAS0AAEF3ag4YAAL+Av4ChAP+Av4C/gL+Av4C/gL+Av4C/gL+Av4C/gL+Av4C/gL+Av4C/gIA/gILIAFBAWoiASACRw0AC0HCACEQDKADCyABQQFqIQEgAC0ALUEBcUUNvQEgASEBC0EsIRAMhQMLIAEiASACRw3TAUHEACEQDJ0DCwNAAkAgAS0AAEGQwICAAGotAABBAUYNACABIQEMtwILIAFBAWoiASACRw0AC0HFACEQDJwDCyANLQAAIhBBIEYNswEgEEE6Rw2BAyAAKAIEIQEgAEEANgIEIAAgASANEK+AgIAAIgEN0AEgDUEBaiEBDLMCC0HHACEQIAEiDSACRg2aAyACIA1rIAAoAgAiAWohFiANIAFrQQVqIRcDQCANLQAAIhRBIHIgFCAUQb9/akH/AXFBGkkbQf8BcSABQZDCgIAAai0AAEcNgAMgAUEFRg30AiABQQFqIQEgDUEBaiINIAJHDQALIAAgFjYCAAyaAwtByAAhECABIg0gAkYNmQMgAiANayAAKAIAIgFqIRYgDSABa0EJaiEXA0AgDS0AACIUQSByIBQgFEG/f2pB/wFxQRpJG0H/AXEgAUGWwoCAAGotAABHDf8CAkAgAUEJRw0AQQIhAQz1AgsgAUEBaiEBIA1BAWoiDSACRw0ACyAAIBY2AgAMmQMLAkAgASINIAJHDQBByQAhEAyZAwsCQAJAIA0tAAAiAUEgciABIAFBv39qQf8BcUEaSRtB/wFxQZJ/ag4HAIADgAOAA4ADgAMBgAMLIA1BAWohAUE+IRAMgAMLIA1BAWohAUE/IRAM/wILQcoAIRAgASINIAJGDZcDIAIgDWsgACgCACIBaiEWIA0gAWtBAWohFwNAIA0tAAAiFEEgciAUIBRBv39qQf8BcUEaSRtB/wFxIAFBoMKAgABqLQAARw39AiABQQFGDfACIAFBAWohASANQQFqIg0gAkcNAAsgACAWNgIADJcDC0HLACEQIAEiDSACRg2WAyACIA1rIAAoAgAiAWohFiANIAFrQQ5qIRcDQCANLQAAIhRBIHIgFCAUQb9/akH/AXFBGkkbQf8BcSABQaLCgIAAai0AAEcN/AIgAUEORg3wAiABQQFqIQEgDUEBaiINIAJHDQALIAAgFjYCAAyWAwtBzAAhECABIg0gAkYNlQMgAiANayAAKAIAIgFqIRYgDSABa0EPaiEXA0AgDS0AACIUQSByIBQgFEG/f2pB/wFxQRpJG0H/AXEgAUHAwoCAAGotAABHDfsCAkAgAUEPRw0AQQMhAQzxAgsgAUEBaiEBIA1BAWoiDSACRw0ACyAAIBY2AgAMlQMLQc0AIRAgASINIAJGDZQDIAIgDWsgACgCACIBaiEWIA0gAWtBBWohFwNAIA0tAAAiFEEgciAUIBRBv39qQf8BcUEaSRtB/wFxIAFB0MKAgABqLQAARw36AgJAIAFBBUcNAEEEIQEM8AILIAFBAWohASANQQFqIg0gAkcNAAsgACAWNgIADJQDCwJAIAEiDSACRw0AQc4AIRAMlAMLAkACQAJAAkAgDS0AACIBQSByIAEgAUG/f2pB/wFxQRpJG0H/AXFBnX9qDhMA/QL9Av0C/QL9Av0C/QL9Av0C/QL9Av0CAf0C/QL9AgID/QILIA1BAWohAUHBACEQDP0CCyANQQFqIQFBwgAhEAz8AgsgDUEBaiEBQcMAIRAM+wILIA1BAWohAUHEACEQDPoCCwJAIAEiASACRg0AIABBjYCAgAA2AgggACABNgIEIAEhAUHFACEQDPoCC0HPACEQDJIDCyAQIQECQAJAIBAtAABBdmoOBAGoAqgCAKgCCyAQQQFqIQELQSchEAz4AgsCQCABIgEgAkcNAEHRACEQDJEDCwJAIAEtAABBIEYNACABIQEMjQELIAFBAWohASAALQAtQQFxRQ3HASABIQEMjAELIAEiFyACRw3IAUHSACEQDI8DC0HTACEQIAEiFCACRg2OAyACIBRrIAAoAgAiAWohFiAUIAFrQQFqIRcDQCAULQAAIAFB1sKAgABqLQAARw3MASABQQFGDccBIAFBAWohASAUQQFqIhQgAkcNAAsgACAWNgIADI4DCwJAIAEiASACRw0AQdUAIRAMjgMLIAEtAABBCkcNzAEgAUEBaiEBDMcBCwJAIAEiASACRw0AQdYAIRAMjQMLAkACQCABLQAAQXZqDgQAzQHNAQHNAQsgAUEBaiEBDMcBCyABQQFqIQFBygAhEAzzAgsgACABIgEgAhCugICAACIQDcsBIAEhAUHNACEQDPICCyAALQApQSJGDYUDDKYCCwJAIAEiASACRw0AQdsAIRAMigMLQQAhFEEBIRdBASEWQQAhEAJAAkACQAJAAkACQAJAAkACQCABLQAAQVBqDgrUAdMBAAECAwQFBgjVAQtBAiEQDAYLQQMhEAwFC0EEIRAMBAtBBSEQDAMLQQYhEAwCC0EHIRAMAQtBCCEQC0EAIRdBACEWQQAhFAzMAQtBCSEQQQEhFEEAIRdBACEWDMsBCwJAIAEiASACRw0AQd0AIRAMiQMLIAEtAABBLkcNzAEgAUEBaiEBDKYCCyABIgEgAkcNzAFB3wAhEAyHAwsCQCABIgEgAkYNACAAQY6AgIAANgIIIAAgATYCBCABIQFB0AAhEAzuAgtB4AAhEAyGAwtB4QAhECABIgEgAkYNhQMgAiABayAAKAIAIhRqIRYgASAUa0EDaiEXA0AgAS0AACAUQeLCgIAAai0AAEcNzQEgFEEDRg3MASAUQQFqIRQgAUEBaiIBIAJHDQALIAAgFjYCAAyFAwtB4gAhECABIgEgAkYNhAMgAiABayAAKAIAIhRqIRYgASAUa0ECaiEXA0AgAS0AACAUQebCgIAAai0AAEcNzAEgFEECRg3OASAUQQFqIRQgAUEBaiIBIAJHDQALIAAgFjYCAAyEAwtB4wAhECABIgEgAkYNgwMgAiABayAAKAIAIhRqIRYgASAUa0EDaiEXA0AgAS0AACAUQenCgIAAai0AAEcNywEgFEEDRg3OASAUQQFqIRQgAUEBaiIBIAJHDQALIAAgFjYCAAyDAwsCQCABIgEgAkcNAEHlACEQDIMDCyAAIAFBAWoiASACEKiAgIAAIhANzQEgASEBQdYAIRAM6QILAkAgASIBIAJGDQADQAJAIAEtAAAiEEEgRg0AAkACQAJAIBBBuH9qDgsAAc8BzwHPAc8BzwHPAc8BzwECzwELIAFBAWohAUHSACEQDO0CCyABQQFqIQFB0wAhEAzsAgsgAUEBaiEBQdQAIRAM6wILIAFBAWoiASACRw0AC0HkACEQDIIDC0HkACEQDIEDCwNAAkAgAS0AAEHwwoCAAGotAAAiEEEBRg0AIBBBfmoOA88B0AHRAdIBCyABQQFqIgEgAkcNAAtB5gAhEAyAAwsCQCABIgEgAkYNACABQQFqIQEMAwtB5wAhEAz/AgsDQAJAIAEtAABB8MSAgABqLQAAIhBBAUYNAAJAIBBBfmoOBNIB0wHUAQDVAQsgASEBQdcAIRAM5wILIAFBAWoiASACRw0AC0HoACEQDP4CCwJAIAEiASACRw0AQekAIRAM/gILAkAgAS0AACIQQXZqDhq6AdUB1QG8AdUB1QHVAdUB1QHVAdUB1QHVAdUB1QHVAdUB1QHVAdUB1QHVAcoB1QHVAQDTAQsgAUEBaiEBC0EGIRAM4wILA0ACQCABLQAAQfDGgIAAai0AAEEBRg0AIAEhAQyeAgsgAUEBaiIBIAJHDQALQeoAIRAM+wILAkAgASIBIAJGDQAgAUEBaiEBDAMLQesAIRAM+gILAkAgASIBIAJHDQBB7AAhEAz6AgsgAUEBaiEBDAELAkAgASIBIAJHDQBB7QAhEAz5AgsgAUEBaiEBC0EEIRAM3gILAkAgASIUIAJHDQBB7gAhEAz3AgsgFCEBAkACQAJAIBQtAABB8MiAgABqLQAAQX9qDgfUAdUB1gEAnAIBAtcBCyAUQQFqIQEMCgsgFEEBaiEBDM0BC0EAIRAgAEEANgIcIABBm5KAgAA2AhAgAEEHNgIMIAAgFEEBajYCFAz2AgsCQANAAkAgAS0AAEHwyICAAGotAAAiEEEERg0AAkACQCAQQX9qDgfSAdMB1AHZAQAEAdkBCyABIQFB2gAhEAzgAgsgAUEBaiEBQdwAIRAM3wILIAFBAWoiASACRw0AC0HvACEQDPYCCyABQQFqIQEMywELAkAgASIUIAJHDQBB8AAhEAz1AgsgFC0AAEEvRw3UASAUQQFqIQEMBgsCQCABIhQgAkcNAEHxACEQDPQCCwJAIBQtAAAiAUEvRw0AIBRBAWohAUHdACEQDNsCCyABQXZqIgRBFksN0wFBASAEdEGJgIACcUUN0wEMygILAkAgASIBIAJGDQAgAUEBaiEBQd4AIRAM2gILQfIAIRAM8gILAkAgASIUIAJHDQBB9AAhEAzyAgsgFCEBAkAgFC0AAEHwzICAAGotAABBf2oOA8kClAIA1AELQeEAIRAM2AILAkAgASIUIAJGDQADQAJAIBQtAABB8MqAgABqLQAAIgFBA0YNAAJAIAFBf2oOAssCANUBCyAUIQFB3wAhEAzaAgsgFEEBaiIUIAJHDQALQfMAIRAM8QILQfMAIRAM8AILAkAgASIBIAJGDQAgAEGPgICAADYCCCAAIAE2AgQgASEBQeAAIRAM1wILQfUAIRAM7wILAkAgASIBIAJHDQBB9gAhEAzvAgsgAEGPgICAADYCCCAAIAE2AgQgASEBC0EDIRAM1AILA0AgAS0AAEEgRw3DAiABQQFqIgEgAkcNAAtB9wAhEAzsAgsCQCABIgEgAkcNAEH4ACEQDOwCCyABLQAAQSBHDc4BIAFBAWohAQzvAQsgACABIgEgAhCsgICAACIQDc4BIAEhAQyOAgsCQCABIgQgAkcNAEH6ACEQDOoCCyAELQAAQcwARw3RASAEQQFqIQFBEyEQDM8BCwJAIAEiBCACRw0AQfsAIRAM6QILIAIgBGsgACgCACIBaiEUIAQgAWtBBWohEANAIAQtAAAgAUHwzoCAAGotAABHDdABIAFBBUYNzgEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBB+wAhEAzoAgsCQCABIgQgAkcNAEH8ACEQDOgCCwJAAkAgBC0AAEG9f2oODADRAdEB0QHRAdEB0QHRAdEB0QHRAQHRAQsgBEEBaiEBQeYAIRAMzwILIARBAWohAUHnACEQDM4CCwJAIAEiBCACRw0AQf0AIRAM5wILIAIgBGsgACgCACIBaiEUIAQgAWtBAmohEAJAA0AgBC0AACABQe3PgIAAai0AAEcNzwEgAUECRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQf0AIRAM5wILIABBADYCACAQQQFqIQFBECEQDMwBCwJAIAEiBCACRw0AQf4AIRAM5gILIAIgBGsgACgCACIBaiEUIAQgAWtBBWohEAJAA0AgBC0AACABQfbOgIAAai0AAEcNzgEgAUEFRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQf4AIRAM5gILIABBADYCACAQQQFqIQFBFiEQDMsBCwJAIAEiBCACRw0AQf8AIRAM5QILIAIgBGsgACgCACIBaiEUIAQgAWtBA2ohEAJAA0AgBC0AACABQfzOgIAAai0AAEcNzQEgAUEDRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQf8AIRAM5QILIABBADYCACAQQQFqIQFBBSEQDMoBCwJAIAEiBCACRw0AQYABIRAM5AILIAQtAABB2QBHDcsBIARBAWohAUEIIRAMyQELAkAgASIEIAJHDQBBgQEhEAzjAgsCQAJAIAQtAABBsn9qDgMAzAEBzAELIARBAWohAUHrACEQDMoCCyAEQQFqIQFB7AAhEAzJAgsCQCABIgQgAkcNAEGCASEQDOICCwJAAkAgBC0AAEG4f2oOCADLAcsBywHLAcsBywEBywELIARBAWohAUHqACEQDMkCCyAEQQFqIQFB7QAhEAzIAgsCQCABIgQgAkcNAEGDASEQDOECCyACIARrIAAoAgAiAWohECAEIAFrQQJqIRQCQANAIAQtAAAgAUGAz4CAAGotAABHDckBIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgEDYCAEGDASEQDOECC0EAIRAgAEEANgIAIBRBAWohAQzGAQsCQCABIgQgAkcNAEGEASEQDOACCyACIARrIAAoAgAiAWohFCAEIAFrQQRqIRACQANAIAQtAAAgAUGDz4CAAGotAABHDcgBIAFBBEYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGEASEQDOACCyAAQQA2AgAgEEEBaiEBQSMhEAzFAQsCQCABIgQgAkcNAEGFASEQDN8CCwJAAkAgBC0AAEG0f2oOCADIAcgByAHIAcgByAEByAELIARBAWohAUHvACEQDMYCCyAEQQFqIQFB8AAhEAzFAgsCQCABIgQgAkcNAEGGASEQDN4CCyAELQAAQcUARw3FASAEQQFqIQEMgwILAkAgASIEIAJHDQBBhwEhEAzdAgsgAiAEayAAKAIAIgFqIRQgBCABa0EDaiEQAkADQCAELQAAIAFBiM+AgABqLQAARw3FASABQQNGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBhwEhEAzdAgsgAEEANgIAIBBBAWohAUEtIRAMwgELAkAgASIEIAJHDQBBiAEhEAzcAgsgAiAEayAAKAIAIgFqIRQgBCABa0EIaiEQAkADQCAELQAAIAFB0M+AgABqLQAARw3EASABQQhGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBiAEhEAzcAgsgAEEANgIAIBBBAWohAUEpIRAMwQELAkAgASIBIAJHDQBBiQEhEAzbAgtBASEQIAEtAABB3wBHDcABIAFBAWohAQyBAgsCQCABIgQgAkcNAEGKASEQDNoCCyACIARrIAAoAgAiAWohFCAEIAFrQQFqIRADQCAELQAAIAFBjM+AgABqLQAARw3BASABQQFGDa8CIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQYoBIRAM2QILAkAgASIEIAJHDQBBiwEhEAzZAgsgAiAEayAAKAIAIgFqIRQgBCABa0ECaiEQAkADQCAELQAAIAFBjs+AgABqLQAARw3BASABQQJGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBiwEhEAzZAgsgAEEANgIAIBBBAWohAUECIRAMvgELAkAgASIEIAJHDQBBjAEhEAzYAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQAkADQCAELQAAIAFB8M+AgABqLQAARw3AASABQQFGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBjAEhEAzYAgsgAEEANgIAIBBBAWohAUEfIRAMvQELAkAgASIEIAJHDQBBjQEhEAzXAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQAkADQCAELQAAIAFB8s+AgABqLQAARw2/ASABQQFGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBjQEhEAzXAgsgAEEANgIAIBBBAWohAUEJIRAMvAELAkAgASIEIAJHDQBBjgEhEAzWAgsCQAJAIAQtAABBt39qDgcAvwG/Ab8BvwG/AQG/AQsgBEEBaiEBQfgAIRAMvQILIARBAWohAUH5ACEQDLwCCwJAIAEiBCACRw0AQY8BIRAM1QILIAIgBGsgACgCACIBaiEUIAQgAWtBBWohEAJAA0AgBC0AACABQZHPgIAAai0AAEcNvQEgAUEFRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQY8BIRAM1QILIABBADYCACAQQQFqIQFBGCEQDLoBCwJAIAEiBCACRw0AQZABIRAM1AILIAIgBGsgACgCACIBaiEUIAQgAWtBAmohEAJAA0AgBC0AACABQZfPgIAAai0AAEcNvAEgAUECRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZABIRAM1AILIABBADYCACAQQQFqIQFBFyEQDLkBCwJAIAEiBCACRw0AQZEBIRAM0wILIAIgBGsgACgCACIBaiEUIAQgAWtBBmohEAJAA0AgBC0AACABQZrPgIAAai0AAEcNuwEgAUEGRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZEBIRAM0wILIABBADYCACAQQQFqIQFBFSEQDLgBCwJAIAEiBCACRw0AQZIBIRAM0gILIAIgBGsgACgCACIBaiEUIAQgAWtBBWohEAJAA0AgBC0AACABQaHPgIAAai0AAEcNugEgAUEFRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZIBIRAM0gILIABBADYCACAQQQFqIQFBHiEQDLcBCwJAIAEiBCACRw0AQZMBIRAM0QILIAQtAABBzABHDbgBIARBAWohAUEKIRAMtgELAkAgBCACRw0AQZQBIRAM0AILAkACQCAELQAAQb9/ag4PALkBuQG5AbkBuQG5AbkBuQG5AbkBuQG5AbkBAbkBCyAEQQFqIQFB/gAhEAy3AgsgBEEBaiEBQf8AIRAMtgILAkAgBCACRw0AQZUBIRAMzwILAkACQCAELQAAQb9/ag4DALgBAbgBCyAEQQFqIQFB/QAhEAy2AgsgBEEBaiEEQYABIRAMtQILAkAgBCACRw0AQZYBIRAMzgILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQafPgIAAai0AAEcNtgEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZYBIRAMzgILIABBADYCACAQQQFqIQFBCyEQDLMBCwJAIAQgAkcNAEGXASEQDM0CCwJAAkACQAJAIAQtAABBU2oOIwC4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBuAG4AbgBAbgBuAG4AbgBuAECuAG4AbgBA7gBCyAEQQFqIQFB+wAhEAy2AgsgBEEBaiEBQfwAIRAMtQILIARBAWohBEGBASEQDLQCCyAEQQFqIQRBggEhEAyzAgsCQCAEIAJHDQBBmAEhEAzMAgsgAiAEayAAKAIAIgFqIRQgBCABa0EEaiEQAkADQCAELQAAIAFBqc+AgABqLQAARw20ASABQQRGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBmAEhEAzMAgsgAEEANgIAIBBBAWohAUEZIRAMsQELAkAgBCACRw0AQZkBIRAMywILIAIgBGsgACgCACIBaiEUIAQgAWtBBWohEAJAA0AgBC0AACABQa7PgIAAai0AAEcNswEgAUEFRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZkBIRAMywILIABBADYCACAQQQFqIQFBBiEQDLABCwJAIAQgAkcNAEGaASEQDMoCCyACIARrIAAoAgAiAWohFCAEIAFrQQFqIRACQANAIAQtAAAgAUG0z4CAAGotAABHDbIBIAFBAUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGaASEQDMoCCyAAQQA2AgAgEEEBaiEBQRwhEAyvAQsCQCAEIAJHDQBBmwEhEAzJAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQAkADQCAELQAAIAFBts+AgABqLQAARw2xASABQQFGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBmwEhEAzJAgsgAEEANgIAIBBBAWohAUEnIRAMrgELAkAgBCACRw0AQZwBIRAMyAILAkACQCAELQAAQax/ag4CAAGxAQsgBEEBaiEEQYYBIRAMrwILIARBAWohBEGHASEQDK4CCwJAIAQgAkcNAEGdASEQDMcCCyACIARrIAAoAgAiAWohFCAEIAFrQQFqIRACQANAIAQtAAAgAUG4z4CAAGotAABHDa8BIAFBAUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGdASEQDMcCCyAAQQA2AgAgEEEBaiEBQSYhEAysAQsCQCAEIAJHDQBBngEhEAzGAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQAkADQCAELQAAIAFBus+AgABqLQAARw2uASABQQFGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBngEhEAzGAgsgAEEANgIAIBBBAWohAUEDIRAMqwELAkAgBCACRw0AQZ8BIRAMxQILIAIgBGsgACgCACIBaiEUIAQgAWtBAmohEAJAA0AgBC0AACABQe3PgIAAai0AAEcNrQEgAUECRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQZ8BIRAMxQILIABBADYCACAQQQFqIQFBDCEQDKoBCwJAIAQgAkcNAEGgASEQDMQCCyACIARrIAAoAgAiAWohFCAEIAFrQQNqIRACQANAIAQtAAAgAUG8z4CAAGotAABHDawBIAFBA0YNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGgASEQDMQCCyAAQQA2AgAgEEEBaiEBQQ0hEAypAQsCQCAEIAJHDQBBoQEhEAzDAgsCQAJAIAQtAABBun9qDgsArAGsAawBrAGsAawBrAGsAawBAawBCyAEQQFqIQRBiwEhEAyqAgsgBEEBaiEEQYwBIRAMqQILAkAgBCACRw0AQaIBIRAMwgILIAQtAABB0ABHDakBIARBAWohBAzpAQsCQCAEIAJHDQBBowEhEAzBAgsCQAJAIAQtAABBt39qDgcBqgGqAaoBqgGqAQCqAQsgBEEBaiEEQY4BIRAMqAILIARBAWohAUEiIRAMpgELAkAgBCACRw0AQaQBIRAMwAILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQcDPgIAAai0AAEcNqAEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQaQBIRAMwAILIABBADYCACAQQQFqIQFBHSEQDKUBCwJAIAQgAkcNAEGlASEQDL8CCwJAAkAgBC0AAEGuf2oOAwCoAQGoAQsgBEEBaiEEQZABIRAMpgILIARBAWohAUEEIRAMpAELAkAgBCACRw0AQaYBIRAMvgILAkACQAJAAkACQCAELQAAQb9/ag4VAKoBqgGqAaoBqgGqAaoBqgGqAaoBAaoBqgECqgGqAQOqAaoBBKoBCyAEQQFqIQRBiAEhEAyoAgsgBEEBaiEEQYkBIRAMpwILIARBAWohBEGKASEQDKYCCyAEQQFqIQRBjwEhEAylAgsgBEEBaiEEQZEBIRAMpAILAkAgBCACRw0AQacBIRAMvQILIAIgBGsgACgCACIBaiEUIAQgAWtBAmohEAJAA0AgBC0AACABQe3PgIAAai0AAEcNpQEgAUECRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQacBIRAMvQILIABBADYCACAQQQFqIQFBESEQDKIBCwJAIAQgAkcNAEGoASEQDLwCCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUHCz4CAAGotAABHDaQBIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGoASEQDLwCCyAAQQA2AgAgEEEBaiEBQSwhEAyhAQsCQCAEIAJHDQBBqQEhEAy7AgsgAiAEayAAKAIAIgFqIRQgBCABa0EEaiEQAkADQCAELQAAIAFBxc+AgABqLQAARw2jASABQQRGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBqQEhEAy7AgsgAEEANgIAIBBBAWohAUErIRAMoAELAkAgBCACRw0AQaoBIRAMugILIAIgBGsgACgCACIBaiEUIAQgAWtBAmohEAJAA0AgBC0AACABQcrPgIAAai0AAEcNogEgAUECRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQaoBIRAMugILIABBADYCACAQQQFqIQFBFCEQDJ8BCwJAIAQgAkcNAEGrASEQDLkCCwJAAkACQAJAIAQtAABBvn9qDg8AAQKkAaQBpAGkAaQBpAGkAaQBpAGkAaQBA6QBCyAEQQFqIQRBkwEhEAyiAgsgBEEBaiEEQZQBIRAMoQILIARBAWohBEGVASEQDKACCyAEQQFqIQRBlgEhEAyfAgsCQCAEIAJHDQBBrAEhEAy4AgsgBC0AAEHFAEcNnwEgBEEBaiEEDOABCwJAIAQgAkcNAEGtASEQDLcCCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUHNz4CAAGotAABHDZ8BIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEGtASEQDLcCCyAAQQA2AgAgEEEBaiEBQQ4hEAycAQsCQCAEIAJHDQBBrgEhEAy2AgsgBC0AAEHQAEcNnQEgBEEBaiEBQSUhEAybAQsCQCAEIAJHDQBBrwEhEAy1AgsgAiAEayAAKAIAIgFqIRQgBCABa0EIaiEQAkADQCAELQAAIAFB0M+AgABqLQAARw2dASABQQhGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBrwEhEAy1AgsgAEEANgIAIBBBAWohAUEqIRAMmgELAkAgBCACRw0AQbABIRAMtAILAkACQCAELQAAQat/ag4LAJ0BnQGdAZ0BnQGdAZ0BnQGdAQGdAQsgBEEBaiEEQZoBIRAMmwILIARBAWohBEGbASEQDJoCCwJAIAQgAkcNAEGxASEQDLMCCwJAAkAgBC0AAEG/f2oOFACcAZwBnAGcAZwBnAGcAZwBnAGcAZwBnAGcAZwBnAGcAZwBnAEBnAELIARBAWohBEGZASEQDJoCCyAEQQFqIQRBnAEhEAyZAgsCQCAEIAJHDQBBsgEhEAyyAgsgAiAEayAAKAIAIgFqIRQgBCABa0EDaiEQAkADQCAELQAAIAFB2c+AgABqLQAARw2aASABQQNGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBsgEhEAyyAgsgAEEANgIAIBBBAWohAUEhIRAMlwELAkAgBCACRw0AQbMBIRAMsQILIAIgBGsgACgCACIBaiEUIAQgAWtBBmohEAJAA0AgBC0AACABQd3PgIAAai0AAEcNmQEgAUEGRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQbMBIRAMsQILIABBADYCACAQQQFqIQFBGiEQDJYBCwJAIAQgAkcNAEG0ASEQDLACCwJAAkACQCAELQAAQbt/ag4RAJoBmgGaAZoBmgGaAZoBmgGaAQGaAZoBmgGaAZoBApoBCyAEQQFqIQRBnQEhEAyYAgsgBEEBaiEEQZ4BIRAMlwILIARBAWohBEGfASEQDJYCCwJAIAQgAkcNAEG1ASEQDK8CCyACIARrIAAoAgAiAWohFCAEIAFrQQVqIRACQANAIAQtAAAgAUHkz4CAAGotAABHDZcBIAFBBUYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEG1ASEQDK8CCyAAQQA2AgAgEEEBaiEBQSghEAyUAQsCQCAEIAJHDQBBtgEhEAyuAgsgAiAEayAAKAIAIgFqIRQgBCABa0ECaiEQAkADQCAELQAAIAFB6s+AgABqLQAARw2WASABQQJGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBtgEhEAyuAgsgAEEANgIAIBBBAWohAUEHIRAMkwELAkAgBCACRw0AQbcBIRAMrQILAkACQCAELQAAQbt/ag4OAJYBlgGWAZYBlgGWAZYBlgGWAZYBlgGWAQGWAQsgBEEBaiEEQaEBIRAMlAILIARBAWohBEGiASEQDJMCCwJAIAQgAkcNAEG4ASEQDKwCCyACIARrIAAoAgAiAWohFCAEIAFrQQJqIRACQANAIAQtAAAgAUHtz4CAAGotAABHDZQBIAFBAkYNASABQQFqIQEgBEEBaiIEIAJHDQALIAAgFDYCAEG4ASEQDKwCCyAAQQA2AgAgEEEBaiEBQRIhEAyRAQsCQCAEIAJHDQBBuQEhEAyrAgsgAiAEayAAKAIAIgFqIRQgBCABa0EBaiEQAkADQCAELQAAIAFB8M+AgABqLQAARw2TASABQQFGDQEgAUEBaiEBIARBAWoiBCACRw0ACyAAIBQ2AgBBuQEhEAyrAgsgAEEANgIAIBBBAWohAUEgIRAMkAELAkAgBCACRw0AQboBIRAMqgILIAIgBGsgACgCACIBaiEUIAQgAWtBAWohEAJAA0AgBC0AACABQfLPgIAAai0AAEcNkgEgAUEBRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQboBIRAMqgILIABBADYCACAQQQFqIQFBDyEQDI8BCwJAIAQgAkcNAEG7ASEQDKkCCwJAAkAgBC0AAEG3f2oOBwCSAZIBkgGSAZIBAZIBCyAEQQFqIQRBpQEhEAyQAgsgBEEBaiEEQaYBIRAMjwILAkAgBCACRw0AQbwBIRAMqAILIAIgBGsgACgCACIBaiEUIAQgAWtBB2ohEAJAA0AgBC0AACABQfTPgIAAai0AAEcNkAEgAUEHRg0BIAFBAWohASAEQQFqIgQgAkcNAAsgACAUNgIAQbwBIRAMqAILIABBADYCACAQQQFqIQFBGyEQDI0BCwJAIAQgAkcNAEG9ASEQDKcCCwJAAkACQCAELQAAQb5/ag4SAJEBkQGRAZEBkQGRAZEBkQGRAQGRAZEBkQGRAZEBkQECkQELIARBAWohBEGkASEQDI8CCyAEQQFqIQRBpwEhEAyOAgsgBEEBaiEEQagBIRAMjQILAkAgBCACRw0AQb4BIRAMpgILIAQtAABBzgBHDY0BIARBAWohBAzPAQsCQCAEIAJHDQBBvwEhEAylAgsCQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAELQAAQb9/ag4VAAECA5wBBAUGnAGcAZwBBwgJCgucAQwNDg+cAQsgBEEBaiEBQegAIRAMmgILIARBAWohAUHpACEQDJkCCyAEQQFqIQFB7gAhEAyYAgsgBEEBaiEBQfIAIRAMlwILIARBAWohAUHzACEQDJYCCyAEQQFqIQFB9gAhEAyVAgsgBEEBaiEBQfcAIRAMlAILIARBAWohAUH6ACEQDJMCCyAEQQFqIQRBgwEhEAySAgsgBEEBaiEEQYQBIRAMkQILIARBAWohBEGFASEQDJACCyAEQQFqIQRBkgEhEAyPAgsgBEEBaiEEQZgBIRAMjgILIARBAWohBEGgASEQDI0CCyAEQQFqIQRBowEhEAyMAgsgBEEBaiEEQaoBIRAMiwILAkAgBCACRg0AIABBkICAgAA2AgggACAENgIEQasBIRAMiwILQcABIRAMowILIAAgBSACEKqAgIAAIgENiwEgBSEBDFwLAkAgBiACRg0AIAZBAWohBQyNAQtBwgEhEAyhAgsDQAJAIBAtAABBdmoOBIwBAACPAQALIBBBAWoiECACRw0AC0HDASEQDKACCwJAIAcgAkYNACAAQZGAgIAANgIIIAAgBzYCBCAHIQFBASEQDIcCC0HEASEQDJ8CCwJAIAcgAkcNAEHFASEQDJ8CCwJAAkAgBy0AAEF2ag4EAc4BzgEAzgELIAdBAWohBgyNAQsgB0EBaiEFDIkBCwJAIAcgAkcNAEHGASEQDJ4CCwJAAkAgBy0AAEF2ag4XAY8BjwEBjwGPAY8BjwGPAY8BjwGPAY8BjwGPAY8BjwGPAY8BjwGPAY8BAI8BCyAHQQFqIQcLQbABIRAMhAILAkAgCCACRw0AQcgBIRAMnQILIAgtAABBIEcNjQEgAEEAOwEyIAhBAWohAUGzASEQDIMCCyABIRcCQANAIBciByACRg0BIActAABBUGpB/wFxIhBBCk8NzAECQCAALwEyIhRBmTNLDQAgACAUQQpsIhQ7ATIgEEH//wNzIBRB/v8DcUkNACAHQQFqIRcgACAUIBBqIhA7ATIgEEH//wNxQegHSQ0BCwtBACEQIABBADYCHCAAQcGJgIAANgIQIABBDTYCDCAAIAdBAWo2AhQMnAILQccBIRAMmwILIAAgCCACEK6AgIAAIhBFDcoBIBBBFUcNjAEgAEHIATYCHCAAIAg2AhQgAEHJl4CAADYCECAAQRU2AgxBACEQDJoCCwJAIAkgAkcNAEHMASEQDJoCC0EAIRRBASEXQQEhFkEAIRACQAJAAkACQAJAAkACQAJAAkAgCS0AAEFQag4KlgGVAQABAgMEBQYIlwELQQIhEAwGC0EDIRAMBQtBBCEQDAQLQQUhEAwDC0EGIRAMAgtBByEQDAELQQghEAtBACEXQQAhFkEAIRQMjgELQQkhEEEBIRRBACEXQQAhFgyNAQsCQCAKIAJHDQBBzgEhEAyZAgsgCi0AAEEuRw2OASAKQQFqIQkMygELIAsgAkcNjgFB0AEhEAyXAgsCQCALIAJGDQAgAEGOgICAADYCCCAAIAs2AgRBtwEhEAz+AQtB0QEhEAyWAgsCQCAEIAJHDQBB0gEhEAyWAgsgAiAEayAAKAIAIhBqIRQgBCAQa0EEaiELA0AgBC0AACAQQfzPgIAAai0AAEcNjgEgEEEERg3pASAQQQFqIRAgBEEBaiIEIAJHDQALIAAgFDYCAEHSASEQDJUCCyAAIAwgAhCsgICAACIBDY0BIAwhAQy4AQsCQCAEIAJHDQBB1AEhEAyUAgsgAiAEayAAKAIAIhBqIRQgBCAQa0EBaiEMA0AgBC0AACAQQYHQgIAAai0AAEcNjwEgEEEBRg2OASAQQQFqIRAgBEEBaiIEIAJHDQALIAAgFDYCAEHUASEQDJMCCwJAIAQgAkcNAEHWASEQDJMCCyACIARrIAAoAgAiEGohFCAEIBBrQQJqIQsDQCAELQAAIBBBg9CAgABqLQAARw2OASAQQQJGDZABIBBBAWohECAEQQFqIgQgAkcNAAsgACAUNgIAQdYBIRAMkgILAkAgBCACRw0AQdcBIRAMkgILAkACQCAELQAAQbt/ag4QAI8BjwGPAY8BjwGPAY8BjwGPAY8BjwGPAY8BjwEBjwELIARBAWohBEG7ASEQDPkBCyAEQQFqIQRBvAEhEAz4AQsCQCAEIAJHDQBB2AEhEAyRAgsgBC0AAEHIAEcNjAEgBEEBaiEEDMQBCwJAIAQgAkYNACAAQZCAgIAANgIIIAAgBDYCBEG+ASEQDPcBC0HZASEQDI8CCwJAIAQgAkcNAEHaASEQDI8CCyAELQAAQcgARg3DASAAQQE6ACgMuQELIABBAjoALyAAIAQgAhCmgICAACIQDY0BQcIBIRAM9AELIAAtAChBf2oOArcBuQG4AQsDQAJAIAQtAABBdmoOBACOAY4BAI4BCyAEQQFqIgQgAkcNAAtB3QEhEAyLAgsgAEEAOgAvIAAtAC1BBHFFDYQCCyAAQQA6AC8gAEEBOgA0IAEhAQyMAQsgEEEVRg3aASAAQQA2AhwgACABNgIUIABBp46AgAA2AhAgAEESNgIMQQAhEAyIAgsCQCAAIBAgAhC0gICAACIEDQAgECEBDIECCwJAIARBFUcNACAAQQM2AhwgACAQNgIUIABBsJiAgAA2AhAgAEEVNgIMQQAhEAyIAgsgAEEANgIcIAAgEDYCFCAAQaeOgIAANgIQIABBEjYCDEEAIRAMhwILIBBBFUYN1gEgAEEANgIcIAAgATYCFCAAQdqNgIAANgIQIABBFDYCDEEAIRAMhgILIAAoAgQhFyAAQQA2AgQgECARp2oiFiEBIAAgFyAQIBYgFBsiEBC1gICAACIURQ2NASAAQQc2AhwgACAQNgIUIAAgFDYCDEEAIRAMhQILIAAgAC8BMEGAAXI7ATAgASEBC0EqIRAM6gELIBBBFUYN0QEgAEEANgIcIAAgATYCFCAAQYOMgIAANgIQIABBEzYCDEEAIRAMggILIBBBFUYNzwEgAEEANgIcIAAgATYCFCAAQZqPgIAANgIQIABBIjYCDEEAIRAMgQILIAAoAgQhECAAQQA2AgQCQCAAIBAgARC3gICAACIQDQAgAUEBaiEBDI0BCyAAQQw2AhwgACAQNgIMIAAgAUEBajYCFEEAIRAMgAILIBBBFUYNzAEgAEEANgIcIAAgATYCFCAAQZqPgIAANgIQIABBIjYCDEEAIRAM/wELIAAoAgQhECAAQQA2AgQCQCAAIBAgARC3gICAACIQDQAgAUEBaiEBDIwBCyAAQQ02AhwgACAQNgIMIAAgAUEBajYCFEEAIRAM/gELIBBBFUYNyQEgAEEANgIcIAAgATYCFCAAQcaMgIAANgIQIABBIzYCDEEAIRAM/QELIAAoAgQhECAAQQA2AgQCQCAAIBAgARC5gICAACIQDQAgAUEBaiEBDIsBCyAAQQ42AhwgACAQNgIMIAAgAUEBajYCFEEAIRAM/AELIABBADYCHCAAIAE2AhQgAEHAlYCAADYCECAAQQI2AgxBACEQDPsBCyAQQRVGDcUBIABBADYCHCAAIAE2AhQgAEHGjICAADYCECAAQSM2AgxBACEQDPoBCyAAQRA2AhwgACABNgIUIAAgEDYCDEEAIRAM+QELIAAoAgQhBCAAQQA2AgQCQCAAIAQgARC5gICAACIEDQAgAUEBaiEBDPEBCyAAQRE2AhwgACAENgIMIAAgAUEBajYCFEEAIRAM+AELIBBBFUYNwQEgAEEANgIcIAAgATYCFCAAQcaMgIAANgIQIABBIzYCDEEAIRAM9wELIAAoAgQhECAAQQA2AgQCQCAAIBAgARC5gICAACIQDQAgAUEBaiEBDIgBCyAAQRM2AhwgACAQNgIMIAAgAUEBajYCFEEAIRAM9gELIAAoAgQhBCAAQQA2AgQCQCAAIAQgARC5gICAACIEDQAgAUEBaiEBDO0BCyAAQRQ2AhwgACAENgIMIAAgAUEBajYCFEEAIRAM9QELIBBBFUYNvQEgAEEANgIcIAAgATYCFCAAQZqPgIAANgIQIABBIjYCDEEAIRAM9AELIAAoAgQhECAAQQA2AgQCQCAAIBAgARC3gICAACIQDQAgAUEBaiEBDIYBCyAAQRY2AhwgACAQNgIMIAAgAUEBajYCFEEAIRAM8wELIAAoAgQhBCAAQQA2AgQCQCAAIAQgARC3gICAACIEDQAgAUEBaiEBDOkBCyAAQRc2AhwgACAENgIMIAAgAUEBajYCFEEAIRAM8gELIABBADYCHCAAIAE2AhQgAEHNk4CAADYCECAAQQw2AgxBACEQDPEBC0IBIRELIBBBAWohAQJAIAApAyAiEkL//////////w9WDQAgACASQgSGIBGENwMgIAEhAQyEAQsgAEEANgIcIAAgATYCFCAAQa2JgIAANgIQIABBDDYCDEEAIRAM7wELIABBADYCHCAAIBA2AhQgAEHNk4CAADYCECAAQQw2AgxBACEQDO4BCyAAKAIEIRcgAEEANgIEIBAgEadqIhYhASAAIBcgECAWIBQbIhAQtYCAgAAiFEUNcyAAQQU2AhwgACAQNgIUIAAgFDYCDEEAIRAM7QELIABBADYCHCAAIBA2AhQgAEGqnICAADYCECAAQQ82AgxBACEQDOwBCyAAIBAgAhC0gICAACIBDQEgECEBC0EOIRAM0QELAkAgAUEVRw0AIABBAjYCHCAAIBA2AhQgAEGwmICAADYCECAAQRU2AgxBACEQDOoBCyAAQQA2AhwgACAQNgIUIABBp46AgAA2AhAgAEESNgIMQQAhEAzpAQsgAUEBaiEQAkAgAC8BMCIBQYABcUUNAAJAIAAgECACELuAgIAAIgENACAQIQEMcAsgAUEVRw26ASAAQQU2AhwgACAQNgIUIABB+ZeAgAA2AhAgAEEVNgIMQQAhEAzpAQsCQCABQaAEcUGgBEcNACAALQAtQQJxDQAgAEEANgIcIAAgEDYCFCAAQZaTgIAANgIQIABBBDYCDEEAIRAM6QELIAAgECACEL2AgIAAGiAQIQECQAJAAkACQAJAIAAgECACELOAgIAADhYCAQAEBAQEBAQEBAQEBAQEBAQEBAQDBAsgAEEBOgAuCyAAIAAvATBBwAByOwEwIBAhAQtBJiEQDNEBCyAAQSM2AhwgACAQNgIUIABBpZaAgAA2AhAgAEEVNgIMQQAhEAzpAQsgAEEANgIcIAAgEDYCFCAAQdWLgIAANgIQIABBETYCDEEAIRAM6AELIAAtAC1BAXFFDQFBwwEhEAzOAQsCQCANIAJGDQADQAJAIA0tAABBIEYNACANIQEMxAELIA1BAWoiDSACRw0AC0ElIRAM5wELQSUhEAzmAQsgACgCBCEEIABBADYCBCAAIAQgDRCvgICAACIERQ2tASAAQSY2AhwgACAENgIMIAAgDUEBajYCFEEAIRAM5QELIBBBFUYNqwEgAEEANgIcIAAgATYCFCAAQf2NgIAANgIQIABBHTYCDEEAIRAM5AELIABBJzYCHCAAIAE2AhQgACAQNgIMQQAhEAzjAQsgECEBQQEhFAJAAkACQAJAAkACQAJAIAAtACxBfmoOBwYFBQMBAgAFCyAAIAAvATBBCHI7ATAMAwtBAiEUDAELQQQhFAsgAEEBOgAsIAAgAC8BMCAUcjsBMAsgECEBC0ErIRAMygELIABBADYCHCAAIBA2AhQgAEGrkoCAADYCECAAQQs2AgxBACEQDOIBCyAAQQA2AhwgACABNgIUIABB4Y+AgAA2AhAgAEEKNgIMQQAhEAzhAQsgAEEAOgAsIBAhAQy9AQsgECEBQQEhFAJAAkACQAJAAkAgAC0ALEF7ag4EAwECAAULIAAgAC8BMEEIcjsBMAwDC0ECIRQMAQtBBCEUCyAAQQE6ACwgACAALwEwIBRyOwEwCyAQIQELQSkhEAzFAQsgAEEANgIcIAAgATYCFCAAQfCUgIAANgIQIABBAzYCDEEAIRAM3QELAkAgDi0AAEENRw0AIAAoAgQhASAAQQA2AgQCQCAAIAEgDhCxgICAACIBDQAgDkEBaiEBDHULIABBLDYCHCAAIAE2AgwgACAOQQFqNgIUQQAhEAzdAQsgAC0ALUEBcUUNAUHEASEQDMMBCwJAIA4gAkcNAEEtIRAM3AELAkACQANAAkAgDi0AAEF2ag4EAgAAAwALIA5BAWoiDiACRw0AC0EtIRAM3QELIAAoAgQhASAAQQA2AgQCQCAAIAEgDhCxgICAACIBDQAgDiEBDHQLIABBLDYCHCAAIA42AhQgACABNgIMQQAhEAzcAQsgACgCBCEBIABBADYCBAJAIAAgASAOELGAgIAAIgENACAOQQFqIQEMcwsgAEEsNgIcIAAgATYCDCAAIA5BAWo2AhRBACEQDNsBCyAAKAIEIQQgAEEANgIEIAAgBCAOELGAgIAAIgQNoAEgDiEBDM4BCyAQQSxHDQEgAUEBaiEQQQEhAQJAAkACQAJAAkAgAC0ALEF7ag4EAwECBAALIBAhAQwEC0ECIQEMAQtBBCEBCyAAQQE6ACwgACAALwEwIAFyOwEwIBAhAQwBCyAAIAAvATBBCHI7ATAgECEBC0E5IRAMvwELIABBADoALCABIQELQTQhEAy9AQsgACAALwEwQSByOwEwIAEhAQwCCyAAKAIEIQQgAEEANgIEAkAgACAEIAEQsYCAgAAiBA0AIAEhAQzHAQsgAEE3NgIcIAAgATYCFCAAIAQ2AgxBACEQDNQBCyAAQQg6ACwgASEBC0EwIRAMuQELAkAgAC0AKEEBRg0AIAEhAQwECyAALQAtQQhxRQ2TASABIQEMAwsgAC0AMEEgcQ2UAUHFASEQDLcBCwJAIA8gAkYNAAJAA0ACQCAPLQAAQVBqIgFB/wFxQQpJDQAgDyEBQTUhEAy6AQsgACkDICIRQpmz5syZs+bMGVYNASAAIBFCCn4iETcDICARIAGtQv8BgyISQn+FVg0BIAAgESASfDcDICAPQQFqIg8gAkcNAAtBOSEQDNEBCyAAKAIEIQIgAEEANgIEIAAgAiAPQQFqIgQQsYCAgAAiAg2VASAEIQEMwwELQTkhEAzPAQsCQCAALwEwIgFBCHFFDQAgAC0AKEEBRw0AIAAtAC1BCHFFDZABCyAAIAFB9/sDcUGABHI7ATAgDyEBC0E3IRAMtAELIAAgAC8BMEEQcjsBMAyrAQsgEEEVRg2LASAAQQA2AhwgACABNgIUIABB8I6AgAA2AhAgAEEcNgIMQQAhEAzLAQsgAEHDADYCHCAAIAE2AgwgACANQQFqNgIUQQAhEAzKAQsCQCABLQAAQTpHDQAgACgCBCEQIABBADYCBAJAIAAgECABEK+AgIAAIhANACABQQFqIQEMYwsgAEHDADYCHCAAIBA2AgwgACABQQFqNgIUQQAhEAzKAQsgAEEANgIcIAAgATYCFCAAQbGRgIAANgIQIABBCjYCDEEAIRAMyQELIABBADYCHCAAIAE2AhQgAEGgmYCAADYCECAAQR42AgxBACEQDMgBCyAAQQA2AgALIABBgBI7ASogACAXQQFqIgEgAhCogICAACIQDQEgASEBC0HHACEQDKwBCyAQQRVHDYMBIABB0QA2AhwgACABNgIUIABB45eAgAA2AhAgAEEVNgIMQQAhEAzEAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMXgsgAEHSADYCHCAAIAE2AhQgACAQNgIMQQAhEAzDAQsgAEEANgIcIAAgFDYCFCAAQcGogIAANgIQIABBBzYCDCAAQQA2AgBBACEQDMIBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxdCyAAQdMANgIcIAAgATYCFCAAIBA2AgxBACEQDMEBC0EAIRAgAEEANgIcIAAgATYCFCAAQYCRgIAANgIQIABBCTYCDAzAAQsgEEEVRg19IABBADYCHCAAIAE2AhQgAEGUjYCAADYCECAAQSE2AgxBACEQDL8BC0EBIRZBACEXQQAhFEEBIRALIAAgEDoAKyABQQFqIQECQAJAIAAtAC1BEHENAAJAAkACQCAALQAqDgMBAAIECyAWRQ0DDAILIBQNAQwCCyAXRQ0BCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQrYCAgAAiEA0AIAEhAQxcCyAAQdgANgIcIAAgATYCFCAAIBA2AgxBACEQDL4BCyAAKAIEIQQgAEEANgIEAkAgACAEIAEQrYCAgAAiBA0AIAEhAQytAQsgAEHZADYCHCAAIAE2AhQgACAENgIMQQAhEAy9AQsgACgCBCEEIABBADYCBAJAIAAgBCABEK2AgIAAIgQNACABIQEMqwELIABB2gA2AhwgACABNgIUIAAgBDYCDEEAIRAMvAELIAAoAgQhBCAAQQA2AgQCQCAAIAQgARCtgICAACIEDQAgASEBDKkBCyAAQdwANgIcIAAgATYCFCAAIAQ2AgxBACEQDLsBCwJAIAEtAABBUGoiEEH/AXFBCk8NACAAIBA6ACogAUEBaiEBQc8AIRAMogELIAAoAgQhBCAAQQA2AgQCQCAAIAQgARCtgICAACIEDQAgASEBDKcBCyAAQd4ANgIcIAAgATYCFCAAIAQ2AgxBACEQDLoBCyAAQQA2AgAgF0EBaiEBAkAgAC0AKUEjTw0AIAEhAQxZCyAAQQA2AhwgACABNgIUIABB04mAgAA2AhAgAEEINgIMQQAhEAy5AQsgAEEANgIAC0EAIRAgAEEANgIcIAAgATYCFCAAQZCzgIAANgIQIABBCDYCDAy3AQsgAEEANgIAIBdBAWohAQJAIAAtAClBIUcNACABIQEMVgsgAEEANgIcIAAgATYCFCAAQZuKgIAANgIQIABBCDYCDEEAIRAMtgELIABBADYCACAXQQFqIQECQCAALQApIhBBXWpBC08NACABIQEMVQsCQCAQQQZLDQBBASAQdEHKAHFFDQAgASEBDFULQQAhECAAQQA2AhwgACABNgIUIABB94mAgAA2AhAgAEEINgIMDLUBCyAQQRVGDXEgAEEANgIcIAAgATYCFCAAQbmNgIAANgIQIABBGjYCDEEAIRAMtAELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDFQLIABB5QA2AhwgACABNgIUIAAgEDYCDEEAIRAMswELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDE0LIABB0gA2AhwgACABNgIUIAAgEDYCDEEAIRAMsgELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDE0LIABB0wA2AhwgACABNgIUIAAgEDYCDEEAIRAMsQELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDFELIABB5QA2AhwgACABNgIUIAAgEDYCDEEAIRAMsAELIABBADYCHCAAIAE2AhQgAEHGioCAADYCECAAQQc2AgxBACEQDK8BCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxJCyAAQdIANgIcIAAgATYCFCAAIBA2AgxBACEQDK4BCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxJCyAAQdMANgIcIAAgATYCFCAAIBA2AgxBACEQDK0BCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxNCyAAQeUANgIcIAAgATYCFCAAIBA2AgxBACEQDKwBCyAAQQA2AhwgACABNgIUIABB3IiAgAA2AhAgAEEHNgIMQQAhEAyrAQsgEEE/Rw0BIAFBAWohAQtBBSEQDJABC0EAIRAgAEEANgIcIAAgATYCFCAAQf2SgIAANgIQIABBBzYCDAyoAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMQgsgAEHSADYCHCAAIAE2AhQgACAQNgIMQQAhEAynAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMQgsgAEHTADYCHCAAIAE2AhQgACAQNgIMQQAhEAymAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMRgsgAEHlADYCHCAAIAE2AhQgACAQNgIMQQAhEAylAQsgACgCBCEBIABBADYCBAJAIAAgASAUEKeAgIAAIgENACAUIQEMPwsgAEHSADYCHCAAIBQ2AhQgACABNgIMQQAhEAykAQsgACgCBCEBIABBADYCBAJAIAAgASAUEKeAgIAAIgENACAUIQEMPwsgAEHTADYCHCAAIBQ2AhQgACABNgIMQQAhEAyjAQsgACgCBCEBIABBADYCBAJAIAAgASAUEKeAgIAAIgENACAUIQEMQwsgAEHlADYCHCAAIBQ2AhQgACABNgIMQQAhEAyiAQsgAEEANgIcIAAgFDYCFCAAQcOPgIAANgIQIABBBzYCDEEAIRAMoQELIABBADYCHCAAIAE2AhQgAEHDj4CAADYCECAAQQc2AgxBACEQDKABC0EAIRAgAEEANgIcIAAgFDYCFCAAQYycgIAANgIQIABBBzYCDAyfAQsgAEEANgIcIAAgFDYCFCAAQYycgIAANgIQIABBBzYCDEEAIRAMngELIABBADYCHCAAIBQ2AhQgAEH+kYCAADYCECAAQQc2AgxBACEQDJ0BCyAAQQA2AhwgACABNgIUIABBjpuAgAA2AhAgAEEGNgIMQQAhEAycAQsgEEEVRg1XIABBADYCHCAAIAE2AhQgAEHMjoCAADYCECAAQSA2AgxBACEQDJsBCyAAQQA2AgAgEEEBaiEBQSQhEAsgACAQOgApIAAoAgQhECAAQQA2AgQgACAQIAEQq4CAgAAiEA1UIAEhAQw+CyAAQQA2AgALQQAhECAAQQA2AhwgACAENgIUIABB8ZuAgAA2AhAgAEEGNgIMDJcBCyABQRVGDVAgAEEANgIcIAAgBTYCFCAAQfCMgIAANgIQIABBGzYCDEEAIRAMlgELIAAoAgQhBSAAQQA2AgQgACAFIBAQqYCAgAAiBQ0BIBBBAWohBQtBrQEhEAx7CyAAQcEBNgIcIAAgBTYCDCAAIBBBAWo2AhRBACEQDJMBCyAAKAIEIQYgAEEANgIEIAAgBiAQEKmAgIAAIgYNASAQQQFqIQYLQa4BIRAMeAsgAEHCATYCHCAAIAY2AgwgACAQQQFqNgIUQQAhEAyQAQsgAEEANgIcIAAgBzYCFCAAQZeLgIAANgIQIABBDTYCDEEAIRAMjwELIABBADYCHCAAIAg2AhQgAEHjkICAADYCECAAQQk2AgxBACEQDI4BCyAAQQA2AhwgACAINgIUIABBlI2AgAA2AhAgAEEhNgIMQQAhEAyNAQtBASEWQQAhF0EAIRRBASEQCyAAIBA6ACsgCUEBaiEIAkACQCAALQAtQRBxDQACQAJAAkAgAC0AKg4DAQACBAsgFkUNAwwCCyAUDQEMAgsgF0UNAQsgACgCBCEQIABBADYCBCAAIBAgCBCtgICAACIQRQ09IABByQE2AhwgACAINgIUIAAgEDYCDEEAIRAMjAELIAAoAgQhBCAAQQA2AgQgACAEIAgQrYCAgAAiBEUNdiAAQcoBNgIcIAAgCDYCFCAAIAQ2AgxBACEQDIsBCyAAKAIEIQQgAEEANgIEIAAgBCAJEK2AgIAAIgRFDXQgAEHLATYCHCAAIAk2AhQgACAENgIMQQAhEAyKAQsgACgCBCEEIABBADYCBCAAIAQgChCtgICAACIERQ1yIABBzQE2AhwgACAKNgIUIAAgBDYCDEEAIRAMiQELAkAgCy0AAEFQaiIQQf8BcUEKTw0AIAAgEDoAKiALQQFqIQpBtgEhEAxwCyAAKAIEIQQgAEEANgIEIAAgBCALEK2AgIAAIgRFDXAgAEHPATYCHCAAIAs2AhQgACAENgIMQQAhEAyIAQsgAEEANgIcIAAgBDYCFCAAQZCzgIAANgIQIABBCDYCDCAAQQA2AgBBACEQDIcBCyABQRVGDT8gAEEANgIcIAAgDDYCFCAAQcyOgIAANgIQIABBIDYCDEEAIRAMhgELIABBgQQ7ASggACgCBCEQIABCADcDACAAIBAgDEEBaiIMEKuAgIAAIhBFDTggAEHTATYCHCAAIAw2AhQgACAQNgIMQQAhEAyFAQsgAEEANgIAC0EAIRAgAEEANgIcIAAgBDYCFCAAQdibgIAANgIQIABBCDYCDAyDAQsgACgCBCEQIABCADcDACAAIBAgC0EBaiILEKuAgIAAIhANAUHGASEQDGkLIABBAjoAKAxVCyAAQdUBNgIcIAAgCzYCFCAAIBA2AgxBACEQDIABCyAQQRVGDTcgAEEANgIcIAAgBDYCFCAAQaSMgIAANgIQIABBEDYCDEEAIRAMfwsgAC0ANEEBRw00IAAgBCACELyAgIAAIhBFDTQgEEEVRw01IABB3AE2AhwgACAENgIUIABB1ZaAgAA2AhAgAEEVNgIMQQAhEAx+C0EAIRAgAEEANgIcIABBr4uAgAA2AhAgAEECNgIMIAAgFEEBajYCFAx9C0EAIRAMYwtBAiEQDGILQQ0hEAxhC0EPIRAMYAtBJSEQDF8LQRMhEAxeC0EVIRAMXQtBFiEQDFwLQRchEAxbC0EYIRAMWgtBGSEQDFkLQRohEAxYC0EbIRAMVwtBHCEQDFYLQR0hEAxVC0EfIRAMVAtBISEQDFMLQSMhEAxSC0HGACEQDFELQS4hEAxQC0EvIRAMTwtBOyEQDE4LQT0hEAxNC0HIACEQDEwLQckAIRAMSwtBywAhEAxKC0HMACEQDEkLQc4AIRAMSAtB0QAhEAxHC0HVACEQDEYLQdgAIRAMRQtB2QAhEAxEC0HbACEQDEMLQeQAIRAMQgtB5QAhEAxBC0HxACEQDEALQfQAIRAMPwtBjQEhEAw+C0GXASEQDD0LQakBIRAMPAtBrAEhEAw7C0HAASEQDDoLQbkBIRAMOQtBrwEhEAw4C0GxASEQDDcLQbIBIRAMNgtBtAEhEAw1C0G1ASEQDDQLQboBIRAMMwtBvQEhEAwyC0G/ASEQDDELQcEBIRAMMAsgAEEANgIcIAAgBDYCFCAAQemLgIAANgIQIABBHzYCDEEAIRAMSAsgAEHbATYCHCAAIAQ2AhQgAEH6loCAADYCECAAQRU2AgxBACEQDEcLIABB+AA2AhwgACAMNgIUIABBypiAgAA2AhAgAEEVNgIMQQAhEAxGCyAAQdEANgIcIAAgBTYCFCAAQbCXgIAANgIQIABBFTYCDEEAIRAMRQsgAEH5ADYCHCAAIAE2AhQgACAQNgIMQQAhEAxECyAAQfgANgIcIAAgATYCFCAAQcqYgIAANgIQIABBFTYCDEEAIRAMQwsgAEHkADYCHCAAIAE2AhQgAEHjl4CAADYCECAAQRU2AgxBACEQDEILIABB1wA2AhwgACABNgIUIABByZeAgAA2AhAgAEEVNgIMQQAhEAxBCyAAQQA2AhwgACABNgIUIABBuY2AgAA2AhAgAEEaNgIMQQAhEAxACyAAQcIANgIcIAAgATYCFCAAQeOYgIAANgIQIABBFTYCDEEAIRAMPwsgAEEANgIEIAAgDyAPELGAgIAAIgRFDQEgAEE6NgIcIAAgBDYCDCAAIA9BAWo2AhRBACEQDD4LIAAoAgQhBCAAQQA2AgQCQCAAIAQgARCxgICAACIERQ0AIABBOzYCHCAAIAQ2AgwgACABQQFqNgIUQQAhEAw+CyABQQFqIQEMLQsgD0EBaiEBDC0LIABBADYCHCAAIA82AhQgAEHkkoCAADYCECAAQQQ2AgxBACEQDDsLIABBNjYCHCAAIAQ2AhQgACACNgIMQQAhEAw6CyAAQS42AhwgACAONgIUIAAgBDYCDEEAIRAMOQsgAEHQADYCHCAAIAE2AhQgAEGRmICAADYCECAAQRU2AgxBACEQDDgLIA1BAWohAQwsCyAAQRU2AhwgACABNgIUIABBgpmAgAA2AhAgAEEVNgIMQQAhEAw2CyAAQRs2AhwgACABNgIUIABBkZeAgAA2AhAgAEEVNgIMQQAhEAw1CyAAQQ82AhwgACABNgIUIABBkZeAgAA2AhAgAEEVNgIMQQAhEAw0CyAAQQs2AhwgACABNgIUIABBkZeAgAA2AhAgAEEVNgIMQQAhEAwzCyAAQRo2AhwgACABNgIUIABBgpmAgAA2AhAgAEEVNgIMQQAhEAwyCyAAQQs2AhwgACABNgIUIABBgpmAgAA2AhAgAEEVNgIMQQAhEAwxCyAAQQo2AhwgACABNgIUIABB5JaAgAA2AhAgAEEVNgIMQQAhEAwwCyAAQR42AhwgACABNgIUIABB+ZeAgAA2AhAgAEEVNgIMQQAhEAwvCyAAQQA2AhwgACAQNgIUIABB2o2AgAA2AhAgAEEUNgIMQQAhEAwuCyAAQQQ2AhwgACABNgIUIABBsJiAgAA2AhAgAEEVNgIMQQAhEAwtCyAAQQA2AgAgC0EBaiELC0G4ASEQDBILIABBADYCACAQQQFqIQFB9QAhEAwRCyABIQECQCAALQApQQVHDQBB4wAhEAwRC0HiACEQDBALQQAhECAAQQA2AhwgAEHkkYCAADYCECAAQQc2AgwgACAUQQFqNgIUDCgLIABBADYCACAXQQFqIQFBwAAhEAwOC0EBIQELIAAgAToALCAAQQA2AgAgF0EBaiEBC0EoIRAMCwsgASEBC0E4IRAMCQsCQCABIg8gAkYNAANAAkAgDy0AAEGAvoCAAGotAAAiAUEBRg0AIAFBAkcNAyAPQQFqIQEMBAsgD0EBaiIPIAJHDQALQT4hEAwiC0E+IRAMIQsgAEEAOgAsIA8hAQwBC0ELIRAMBgtBOiEQDAULIAFBAWohAUEtIRAMBAsgACABOgAsIABBADYCACAWQQFqIQFBDCEQDAMLIABBADYCACAXQQFqIQFBCiEQDAILIABBADYCAAsgAEEAOgAsIA0hAUEJIRAMAAsLQQAhECAAQQA2AhwgACALNgIUIABBzZCAgAA2AhAgAEEJNgIMDBcLQQAhECAAQQA2AhwgACAKNgIUIABB6YqAgAA2AhAgAEEJNgIMDBYLQQAhECAAQQA2AhwgACAJNgIUIABBt5CAgAA2AhAgAEEJNgIMDBULQQAhECAAQQA2AhwgACAINgIUIABBnJGAgAA2AhAgAEEJNgIMDBQLQQAhECAAQQA2AhwgACABNgIUIABBzZCAgAA2AhAgAEEJNgIMDBMLQQAhECAAQQA2AhwgACABNgIUIABB6YqAgAA2AhAgAEEJNgIMDBILQQAhECAAQQA2AhwgACABNgIUIABBt5CAgAA2AhAgAEEJNgIMDBELQQAhECAAQQA2AhwgACABNgIUIABBnJGAgAA2AhAgAEEJNgIMDBALQQAhECAAQQA2AhwgACABNgIUIABBl5WAgAA2AhAgAEEPNgIMDA8LQQAhECAAQQA2AhwgACABNgIUIABBl5WAgAA2AhAgAEEPNgIMDA4LQQAhECAAQQA2AhwgACABNgIUIABBwJKAgAA2AhAgAEELNgIMDA0LQQAhECAAQQA2AhwgACABNgIUIABBlYmAgAA2AhAgAEELNgIMDAwLQQAhECAAQQA2AhwgACABNgIUIABB4Y+AgAA2AhAgAEEKNgIMDAsLQQAhECAAQQA2AhwgACABNgIUIABB+4+AgAA2AhAgAEEKNgIMDAoLQQAhECAAQQA2AhwgACABNgIUIABB8ZmAgAA2AhAgAEECNgIMDAkLQQAhECAAQQA2AhwgACABNgIUIABBxJSAgAA2AhAgAEECNgIMDAgLQQAhECAAQQA2AhwgACABNgIUIABB8pWAgAA2AhAgAEECNgIMDAcLIABBAjYCHCAAIAE2AhQgAEGcmoCAADYCECAAQRY2AgxBACEQDAYLQQEhEAwFC0HUACEQIAEiBCACRg0EIANBCGogACAEIAJB2MKAgABBChDFgICAACADKAIMIQQgAygCCA4DAQQCAAsQyoCAgAAACyAAQQA2AhwgAEG1moCAADYCECAAQRc2AgwgACAEQQFqNgIUQQAhEAwCCyAAQQA2AhwgACAENgIUIABBypqAgAA2AhAgAEEJNgIMQQAhEAwBCwJAIAEiBCACRw0AQSIhEAwBCyAAQYmAgIAANgIIIAAgBDYCBEEhIRALIANBEGokgICAgAAgEAuvAQECfyABKAIAIQYCQAJAIAIgA0YNACAEIAZqIQQgBiADaiACayEHIAIgBkF/cyAFaiIGaiEFA0ACQCACLQAAIAQtAABGDQBBAiEEDAMLAkAgBg0AQQAhBCAFIQIMAwsgBkF/aiEGIARBAWohBCACQQFqIgIgA0cNAAsgByEGIAMhAgsgAEEBNgIAIAEgBjYCACAAIAI2AgQPCyABQQA2AgAgACAENgIAIAAgAjYCBAsKACAAEMeAgIAAC/I2AQt/I4CAgIAAQRBrIgEkgICAgAACQEEAKAKg0ICAAA0AQQAQy4CAgABBgNSEgABrIgJB2QBJDQBBACEDAkBBACgC4NOAgAAiBA0AQQBCfzcC7NOAgABBAEKAgISAgIDAADcC5NOAgABBACABQQhqQXBxQdiq1aoFcyIENgLg04CAAEEAQQA2AvTTgIAAQQBBADYCxNOAgAALQQAgAjYCzNOAgABBAEGA1ISAADYCyNOAgABBAEGA1ISAADYCmNCAgABBACAENgKs0ICAAEEAQX82AqjQgIAAA0AgA0HE0ICAAGogA0G40ICAAGoiBDYCACAEIANBsNCAgABqIgU2AgAgA0G80ICAAGogBTYCACADQczQgIAAaiADQcDQgIAAaiIFNgIAIAUgBDYCACADQdTQgIAAaiADQcjQgIAAaiIENgIAIAQgBTYCACADQdDQgIAAaiAENgIAIANBIGoiA0GAAkcNAAtBgNSEgABBeEGA1ISAAGtBD3FBAEGA1ISAAEEIakEPcRsiA2oiBEEEaiACQUhqIgUgA2siA0EBcjYCAEEAQQAoAvDTgIAANgKk0ICAAEEAIAM2ApTQgIAAQQAgBDYCoNCAgABBgNSEgAAgBWpBODYCBAsCQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAEHsAUsNAAJAQQAoAojQgIAAIgZBECAAQRNqQXBxIABBC0kbIgJBA3YiBHYiA0EDcUUNAAJAAkAgA0EBcSAEckEBcyIFQQN0IgRBsNCAgABqIgMgBEG40ICAAGooAgAiBCgCCCICRw0AQQAgBkF+IAV3cTYCiNCAgAAMAQsgAyACNgIIIAIgAzYCDAsgBEEIaiEDIAQgBUEDdCIFQQNyNgIEIAQgBWoiBCAEKAIEQQFyNgIEDAwLIAJBACgCkNCAgAAiB00NAQJAIANFDQACQAJAIAMgBHRBAiAEdCIDQQAgA2tycSIDQQAgA2txQX9qIgMgA0EMdkEQcSIDdiIEQQV2QQhxIgUgA3IgBCAFdiIDQQJ2QQRxIgRyIAMgBHYiA0EBdkECcSIEciADIAR2IgNBAXZBAXEiBHIgAyAEdmoiBEEDdCIDQbDQgIAAaiIFIANBuNCAgABqKAIAIgMoAggiAEcNAEEAIAZBfiAEd3EiBjYCiNCAgAAMAQsgBSAANgIIIAAgBTYCDAsgAyACQQNyNgIEIAMgBEEDdCIEaiAEIAJrIgU2AgAgAyACaiIAIAVBAXI2AgQCQCAHRQ0AIAdBeHFBsNCAgABqIQJBACgCnNCAgAAhBAJAAkAgBkEBIAdBA3Z0IghxDQBBACAGIAhyNgKI0ICAACACIQgMAQsgAigCCCEICyAIIAQ2AgwgAiAENgIIIAQgAjYCDCAEIAg2AggLIANBCGohA0EAIAA2ApzQgIAAQQAgBTYCkNCAgAAMDAtBACgCjNCAgAAiCUUNASAJQQAgCWtxQX9qIgMgA0EMdkEQcSIDdiIEQQV2QQhxIgUgA3IgBCAFdiIDQQJ2QQRxIgRyIAMgBHYiA0EBdkECcSIEciADIAR2IgNBAXZBAXEiBHIgAyAEdmpBAnRBuNKAgABqKAIAIgAoAgRBeHEgAmshBCAAIQUCQANAAkAgBSgCECIDDQAgBUEUaigCACIDRQ0CCyADKAIEQXhxIAJrIgUgBCAFIARJIgUbIQQgAyAAIAUbIQAgAyEFDAALCyAAKAIYIQoCQCAAKAIMIgggAEYNACAAKAIIIgNBACgCmNCAgABJGiAIIAM2AgggAyAINgIMDAsLAkAgAEEUaiIFKAIAIgMNACAAKAIQIgNFDQMgAEEQaiEFCwNAIAUhCyADIghBFGoiBSgCACIDDQAgCEEQaiEFIAgoAhAiAw0ACyALQQA2AgAMCgtBfyECIABBv39LDQAgAEETaiIDQXBxIQJBACgCjNCAgAAiB0UNAEEAIQsCQCACQYACSQ0AQR8hCyACQf///wdLDQAgA0EIdiIDIANBgP4/akEQdkEIcSIDdCIEIARBgOAfakEQdkEEcSIEdCIFIAVBgIAPakEQdkECcSIFdEEPdiADIARyIAVyayIDQQF0IAIgA0EVanZBAXFyQRxqIQsLQQAgAmshBAJAAkACQAJAIAtBAnRBuNKAgABqKAIAIgUNAEEAIQNBACEIDAELQQAhAyACQQBBGSALQQF2ayALQR9GG3QhAEEAIQgDQAJAIAUoAgRBeHEgAmsiBiAETw0AIAYhBCAFIQggBg0AQQAhBCAFIQggBSEDDAMLIAMgBUEUaigCACIGIAYgBSAAQR12QQRxakEQaigCACIFRhsgAyAGGyEDIABBAXQhACAFDQALCwJAIAMgCHINAEEAIQhBAiALdCIDQQAgA2tyIAdxIgNFDQMgA0EAIANrcUF/aiIDIANBDHZBEHEiA3YiBUEFdkEIcSIAIANyIAUgAHYiA0ECdkEEcSIFciADIAV2IgNBAXZBAnEiBXIgAyAFdiIDQQF2QQFxIgVyIAMgBXZqQQJ0QbjSgIAAaigCACEDCyADRQ0BCwNAIAMoAgRBeHEgAmsiBiAESSEAAkAgAygCECIFDQAgA0EUaigCACEFCyAGIAQgABshBCADIAggABshCCAFIQMgBQ0ACwsgCEUNACAEQQAoApDQgIAAIAJrTw0AIAgoAhghCwJAIAgoAgwiACAIRg0AIAgoAggiA0EAKAKY0ICAAEkaIAAgAzYCCCADIAA2AgwMCQsCQCAIQRRqIgUoAgAiAw0AIAgoAhAiA0UNAyAIQRBqIQULA0AgBSEGIAMiAEEUaiIFKAIAIgMNACAAQRBqIQUgACgCECIDDQALIAZBADYCAAwICwJAQQAoApDQgIAAIgMgAkkNAEEAKAKc0ICAACEEAkACQCADIAJrIgVBEEkNACAEIAJqIgAgBUEBcjYCBEEAIAU2ApDQgIAAQQAgADYCnNCAgAAgBCADaiAFNgIAIAQgAkEDcjYCBAwBCyAEIANBA3I2AgQgBCADaiIDIAMoAgRBAXI2AgRBAEEANgKc0ICAAEEAQQA2ApDQgIAACyAEQQhqIQMMCgsCQEEAKAKU0ICAACIAIAJNDQBBACgCoNCAgAAiAyACaiIEIAAgAmsiBUEBcjYCBEEAIAU2ApTQgIAAQQAgBDYCoNCAgAAgAyACQQNyNgIEIANBCGohAwwKCwJAAkBBACgC4NOAgABFDQBBACgC6NOAgAAhBAwBC0EAQn83AuzTgIAAQQBCgICEgICAwAA3AuTTgIAAQQAgAUEMakFwcUHYqtWqBXM2AuDTgIAAQQBBADYC9NOAgABBAEEANgLE04CAAEGAgAQhBAtBACEDAkAgBCACQccAaiIHaiIGQQAgBGsiC3EiCCACSw0AQQBBMDYC+NOAgAAMCgsCQEEAKALA04CAACIDRQ0AAkBBACgCuNOAgAAiBCAIaiIFIARNDQAgBSADTQ0BC0EAIQNBAEEwNgL404CAAAwKC0EALQDE04CAAEEEcQ0EAkACQAJAQQAoAqDQgIAAIgRFDQBByNOAgAAhAwNAAkAgAygCACIFIARLDQAgBSADKAIEaiAESw0DCyADKAIIIgMNAAsLQQAQy4CAgAAiAEF/Rg0FIAghBgJAQQAoAuTTgIAAIgNBf2oiBCAAcUUNACAIIABrIAQgAGpBACADa3FqIQYLIAYgAk0NBSAGQf7///8HSw0FAkBBACgCwNOAgAAiA0UNAEEAKAK404CAACIEIAZqIgUgBE0NBiAFIANLDQYLIAYQy4CAgAAiAyAARw0BDAcLIAYgAGsgC3EiBkH+////B0sNBCAGEMuAgIAAIgAgAygCACADKAIEakYNAyAAIQMLAkAgA0F/Rg0AIAJByABqIAZNDQACQCAHIAZrQQAoAujTgIAAIgRqQQAgBGtxIgRB/v///wdNDQAgAyEADAcLAkAgBBDLgICAAEF/Rg0AIAQgBmohBiADIQAMBwtBACAGaxDLgICAABoMBAsgAyEAIANBf0cNBQwDC0EAIQgMBwtBACEADAULIABBf0cNAgtBAEEAKALE04CAAEEEcjYCxNOAgAALIAhB/v///wdLDQEgCBDLgICAACEAQQAQy4CAgAAhAyAAQX9GDQEgA0F/Rg0BIAAgA08NASADIABrIgYgAkE4ak0NAQtBAEEAKAK404CAACAGaiIDNgK404CAAAJAIANBACgCvNOAgABNDQBBACADNgK804CAAAsCQAJAAkACQEEAKAKg0ICAACIERQ0AQcjTgIAAIQMDQCAAIAMoAgAiBSADKAIEIghqRg0CIAMoAggiAw0ADAMLCwJAAkBBACgCmNCAgAAiA0UNACAAIANPDQELQQAgADYCmNCAgAALQQAhA0EAIAY2AszTgIAAQQAgADYCyNOAgABBAEF/NgKo0ICAAEEAQQAoAuDTgIAANgKs0ICAAEEAQQA2AtTTgIAAA0AgA0HE0ICAAGogA0G40ICAAGoiBDYCACAEIANBsNCAgABqIgU2AgAgA0G80ICAAGogBTYCACADQczQgIAAaiADQcDQgIAAaiIFNgIAIAUgBDYCACADQdTQgIAAaiADQcjQgIAAaiIENgIAIAQgBTYCACADQdDQgIAAaiAENgIAIANBIGoiA0GAAkcNAAsgAEF4IABrQQ9xQQAgAEEIakEPcRsiA2oiBCAGQUhqIgUgA2siA0EBcjYCBEEAQQAoAvDTgIAANgKk0ICAAEEAIAM2ApTQgIAAQQAgBDYCoNCAgAAgACAFakE4NgIEDAILIAMtAAxBCHENACAEIAVJDQAgBCAATw0AIARBeCAEa0EPcUEAIARBCGpBD3EbIgVqIgBBACgClNCAgAAgBmoiCyAFayIFQQFyNgIEIAMgCCAGajYCBEEAQQAoAvDTgIAANgKk0ICAAEEAIAU2ApTQgIAAQQAgADYCoNCAgAAgBCALakE4NgIEDAELAkAgAEEAKAKY0ICAACIITw0AQQAgADYCmNCAgAAgACEICyAAIAZqIQVByNOAgAAhAwJAAkACQAJAAkACQAJAA0AgAygCACAFRg0BIAMoAggiAw0ADAILCyADLQAMQQhxRQ0BC0HI04CAACEDA0ACQCADKAIAIgUgBEsNACAFIAMoAgRqIgUgBEsNAwsgAygCCCEDDAALCyADIAA2AgAgAyADKAIEIAZqNgIEIABBeCAAa0EPcUEAIABBCGpBD3EbaiILIAJBA3I2AgQgBUF4IAVrQQ9xQQAgBUEIakEPcRtqIgYgCyACaiICayEDAkAgBiAERw0AQQAgAjYCoNCAgABBAEEAKAKU0ICAACADaiIDNgKU0ICAACACIANBAXI2AgQMAwsCQCAGQQAoApzQgIAARw0AQQAgAjYCnNCAgABBAEEAKAKQ0ICAACADaiIDNgKQ0ICAACACIANBAXI2AgQgAiADaiADNgIADAMLAkAgBigCBCIEQQNxQQFHDQAgBEF4cSEHAkACQCAEQf8BSw0AIAYoAggiBSAEQQN2IghBA3RBsNCAgABqIgBGGgJAIAYoAgwiBCAFRw0AQQBBACgCiNCAgABBfiAId3E2AojQgIAADAILIAQgAEYaIAQgBTYCCCAFIAQ2AgwMAQsgBigCGCEJAkACQCAGKAIMIgAgBkYNACAGKAIIIgQgCEkaIAAgBDYCCCAEIAA2AgwMAQsCQCAGQRRqIgQoAgAiBQ0AIAZBEGoiBCgCACIFDQBBACEADAELA0AgBCEIIAUiAEEUaiIEKAIAIgUNACAAQRBqIQQgACgCECIFDQALIAhBADYCAAsgCUUNAAJAAkAgBiAGKAIcIgVBAnRBuNKAgABqIgQoAgBHDQAgBCAANgIAIAANAUEAQQAoAozQgIAAQX4gBXdxNgKM0ICAAAwCCyAJQRBBFCAJKAIQIAZGG2ogADYCACAARQ0BCyAAIAk2AhgCQCAGKAIQIgRFDQAgACAENgIQIAQgADYCGAsgBigCFCIERQ0AIABBFGogBDYCACAEIAA2AhgLIAcgA2ohAyAGIAdqIgYoAgQhBAsgBiAEQX5xNgIEIAIgA2ogAzYCACACIANBAXI2AgQCQCADQf8BSw0AIANBeHFBsNCAgABqIQQCQAJAQQAoAojQgIAAIgVBASADQQN2dCIDcQ0AQQAgBSADcjYCiNCAgAAgBCEDDAELIAQoAgghAwsgAyACNgIMIAQgAjYCCCACIAQ2AgwgAiADNgIIDAMLQR8hBAJAIANB////B0sNACADQQh2IgQgBEGA/j9qQRB2QQhxIgR0IgUgBUGA4B9qQRB2QQRxIgV0IgAgAEGAgA9qQRB2QQJxIgB0QQ92IAQgBXIgAHJrIgRBAXQgAyAEQRVqdkEBcXJBHGohBAsgAiAENgIcIAJCADcCECAEQQJ0QbjSgIAAaiEFAkBBACgCjNCAgAAiAEEBIAR0IghxDQAgBSACNgIAQQAgACAIcjYCjNCAgAAgAiAFNgIYIAIgAjYCCCACIAI2AgwMAwsgA0EAQRkgBEEBdmsgBEEfRht0IQQgBSgCACEAA0AgACIFKAIEQXhxIANGDQIgBEEddiEAIARBAXQhBCAFIABBBHFqQRBqIggoAgAiAA0ACyAIIAI2AgAgAiAFNgIYIAIgAjYCDCACIAI2AggMAgsgAEF4IABrQQ9xQQAgAEEIakEPcRsiA2oiCyAGQUhqIgggA2siA0EBcjYCBCAAIAhqQTg2AgQgBCAFQTcgBWtBD3FBACAFQUlqQQ9xG2pBQWoiCCAIIARBEGpJGyIIQSM2AgRBAEEAKALw04CAADYCpNCAgABBACADNgKU0ICAAEEAIAs2AqDQgIAAIAhBEGpBACkC0NOAgAA3AgAgCEEAKQLI04CAADcCCEEAIAhBCGo2AtDTgIAAQQAgBjYCzNOAgABBACAANgLI04CAAEEAQQA2AtTTgIAAIAhBJGohAwNAIANBBzYCACADQQRqIgMgBUkNAAsgCCAERg0DIAggCCgCBEF+cTYCBCAIIAggBGsiADYCACAEIABBAXI2AgQCQCAAQf8BSw0AIABBeHFBsNCAgABqIQMCQAJAQQAoAojQgIAAIgVBASAAQQN2dCIAcQ0AQQAgBSAAcjYCiNCAgAAgAyEFDAELIAMoAgghBQsgBSAENgIMIAMgBDYCCCAEIAM2AgwgBCAFNgIIDAQLQR8hAwJAIABB////B0sNACAAQQh2IgMgA0GA/j9qQRB2QQhxIgN0IgUgBUGA4B9qQRB2QQRxIgV0IgggCEGAgA9qQRB2QQJxIgh0QQ92IAMgBXIgCHJrIgNBAXQgACADQRVqdkEBcXJBHGohAwsgBCADNgIcIARCADcCECADQQJ0QbjSgIAAaiEFAkBBACgCjNCAgAAiCEEBIAN0IgZxDQAgBSAENgIAQQAgCCAGcjYCjNCAgAAgBCAFNgIYIAQgBDYCCCAEIAQ2AgwMBAsgAEEAQRkgA0EBdmsgA0EfRht0IQMgBSgCACEIA0AgCCIFKAIEQXhxIABGDQMgA0EddiEIIANBAXQhAyAFIAhBBHFqQRBqIgYoAgAiCA0ACyAGIAQ2AgAgBCAFNgIYIAQgBDYCDCAEIAQ2AggMAwsgBSgCCCIDIAI2AgwgBSACNgIIIAJBADYCGCACIAU2AgwgAiADNgIICyALQQhqIQMMBQsgBSgCCCIDIAQ2AgwgBSAENgIIIARBADYCGCAEIAU2AgwgBCADNgIIC0EAKAKU0ICAACIDIAJNDQBBACgCoNCAgAAiBCACaiIFIAMgAmsiA0EBcjYCBEEAIAM2ApTQgIAAQQAgBTYCoNCAgAAgBCACQQNyNgIEIARBCGohAwwDC0EAIQNBAEEwNgL404CAAAwCCwJAIAtFDQACQAJAIAggCCgCHCIFQQJ0QbjSgIAAaiIDKAIARw0AIAMgADYCACAADQFBACAHQX4gBXdxIgc2AozQgIAADAILIAtBEEEUIAsoAhAgCEYbaiAANgIAIABFDQELIAAgCzYCGAJAIAgoAhAiA0UNACAAIAM2AhAgAyAANgIYCyAIQRRqKAIAIgNFDQAgAEEUaiADNgIAIAMgADYCGAsCQAJAIARBD0sNACAIIAQgAmoiA0EDcjYCBCAIIANqIgMgAygCBEEBcjYCBAwBCyAIIAJqIgAgBEEBcjYCBCAIIAJBA3I2AgQgACAEaiAENgIAAkAgBEH/AUsNACAEQXhxQbDQgIAAaiEDAkACQEEAKAKI0ICAACIFQQEgBEEDdnQiBHENAEEAIAUgBHI2AojQgIAAIAMhBAwBCyADKAIIIQQLIAQgADYCDCADIAA2AgggACADNgIMIAAgBDYCCAwBC0EfIQMCQCAEQf///wdLDQAgBEEIdiIDIANBgP4/akEQdkEIcSIDdCIFIAVBgOAfakEQdkEEcSIFdCICIAJBgIAPakEQdkECcSICdEEPdiADIAVyIAJyayIDQQF0IAQgA0EVanZBAXFyQRxqIQMLIAAgAzYCHCAAQgA3AhAgA0ECdEG40oCAAGohBQJAIAdBASADdCICcQ0AIAUgADYCAEEAIAcgAnI2AozQgIAAIAAgBTYCGCAAIAA2AgggACAANgIMDAELIARBAEEZIANBAXZrIANBH0YbdCEDIAUoAgAhAgJAA0AgAiIFKAIEQXhxIARGDQEgA0EddiECIANBAXQhAyAFIAJBBHFqQRBqIgYoAgAiAg0ACyAGIAA2AgAgACAFNgIYIAAgADYCDCAAIAA2AggMAQsgBSgCCCIDIAA2AgwgBSAANgIIIABBADYCGCAAIAU2AgwgACADNgIICyAIQQhqIQMMAQsCQCAKRQ0AAkACQCAAIAAoAhwiBUECdEG40oCAAGoiAygCAEcNACADIAg2AgAgCA0BQQAgCUF+IAV3cTYCjNCAgAAMAgsgCkEQQRQgCigCECAARhtqIAg2AgAgCEUNAQsgCCAKNgIYAkAgACgCECIDRQ0AIAggAzYCECADIAg2AhgLIABBFGooAgAiA0UNACAIQRRqIAM2AgAgAyAINgIYCwJAAkAgBEEPSw0AIAAgBCACaiIDQQNyNgIEIAAgA2oiAyADKAIEQQFyNgIEDAELIAAgAmoiBSAEQQFyNgIEIAAgAkEDcjYCBCAFIARqIAQ2AgACQCAHRQ0AIAdBeHFBsNCAgABqIQJBACgCnNCAgAAhAwJAAkBBASAHQQN2dCIIIAZxDQBBACAIIAZyNgKI0ICAACACIQgMAQsgAigCCCEICyAIIAM2AgwgAiADNgIIIAMgAjYCDCADIAg2AggLQQAgBTYCnNCAgABBACAENgKQ0ICAAAsgAEEIaiEDCyABQRBqJICAgIAAIAMLCgAgABDJgICAAAviDQEHfwJAIABFDQAgAEF4aiIBIABBfGooAgAiAkF4cSIAaiEDAkAgAkEBcQ0AIAJBA3FFDQEgASABKAIAIgJrIgFBACgCmNCAgAAiBEkNASACIABqIQACQCABQQAoApzQgIAARg0AAkAgAkH/AUsNACABKAIIIgQgAkEDdiIFQQN0QbDQgIAAaiIGRhoCQCABKAIMIgIgBEcNAEEAQQAoAojQgIAAQX4gBXdxNgKI0ICAAAwDCyACIAZGGiACIAQ2AgggBCACNgIMDAILIAEoAhghBwJAAkAgASgCDCIGIAFGDQAgASgCCCICIARJGiAGIAI2AgggAiAGNgIMDAELAkAgAUEUaiICKAIAIgQNACABQRBqIgIoAgAiBA0AQQAhBgwBCwNAIAIhBSAEIgZBFGoiAigCACIEDQAgBkEQaiECIAYoAhAiBA0ACyAFQQA2AgALIAdFDQECQAJAIAEgASgCHCIEQQJ0QbjSgIAAaiICKAIARw0AIAIgBjYCACAGDQFBAEEAKAKM0ICAAEF+IAR3cTYCjNCAgAAMAwsgB0EQQRQgBygCECABRhtqIAY2AgAgBkUNAgsgBiAHNgIYAkAgASgCECICRQ0AIAYgAjYCECACIAY2AhgLIAEoAhQiAkUNASAGQRRqIAI2AgAgAiAGNgIYDAELIAMoAgQiAkEDcUEDRw0AIAMgAkF+cTYCBEEAIAA2ApDQgIAAIAEgAGogADYCACABIABBAXI2AgQPCyABIANPDQAgAygCBCICQQFxRQ0AAkACQCACQQJxDQACQCADQQAoAqDQgIAARw0AQQAgATYCoNCAgABBAEEAKAKU0ICAACAAaiIANgKU0ICAACABIABBAXI2AgQgAUEAKAKc0ICAAEcNA0EAQQA2ApDQgIAAQQBBADYCnNCAgAAPCwJAIANBACgCnNCAgABHDQBBACABNgKc0ICAAEEAQQAoApDQgIAAIABqIgA2ApDQgIAAIAEgAEEBcjYCBCABIABqIAA2AgAPCyACQXhxIABqIQACQAJAIAJB/wFLDQAgAygCCCIEIAJBA3YiBUEDdEGw0ICAAGoiBkYaAkAgAygCDCICIARHDQBBAEEAKAKI0ICAAEF+IAV3cTYCiNCAgAAMAgsgAiAGRhogAiAENgIIIAQgAjYCDAwBCyADKAIYIQcCQAJAIAMoAgwiBiADRg0AIAMoAggiAkEAKAKY0ICAAEkaIAYgAjYCCCACIAY2AgwMAQsCQCADQRRqIgIoAgAiBA0AIANBEGoiAigCACIEDQBBACEGDAELA0AgAiEFIAQiBkEUaiICKAIAIgQNACAGQRBqIQIgBigCECIEDQALIAVBADYCAAsgB0UNAAJAAkAgAyADKAIcIgRBAnRBuNKAgABqIgIoAgBHDQAgAiAGNgIAIAYNAUEAQQAoAozQgIAAQX4gBHdxNgKM0ICAAAwCCyAHQRBBFCAHKAIQIANGG2ogBjYCACAGRQ0BCyAGIAc2AhgCQCADKAIQIgJFDQAgBiACNgIQIAIgBjYCGAsgAygCFCICRQ0AIAZBFGogAjYCACACIAY2AhgLIAEgAGogADYCACABIABBAXI2AgQgAUEAKAKc0ICAAEcNAUEAIAA2ApDQgIAADwsgAyACQX5xNgIEIAEgAGogADYCACABIABBAXI2AgQLAkAgAEH/AUsNACAAQXhxQbDQgIAAaiECAkACQEEAKAKI0ICAACIEQQEgAEEDdnQiAHENAEEAIAQgAHI2AojQgIAAIAIhAAwBCyACKAIIIQALIAAgATYCDCACIAE2AgggASACNgIMIAEgADYCCA8LQR8hAgJAIABB////B0sNACAAQQh2IgIgAkGA/j9qQRB2QQhxIgJ0IgQgBEGA4B9qQRB2QQRxIgR0IgYgBkGAgA9qQRB2QQJxIgZ0QQ92IAIgBHIgBnJrIgJBAXQgACACQRVqdkEBcXJBHGohAgsgASACNgIcIAFCADcCECACQQJ0QbjSgIAAaiEEAkACQEEAKAKM0ICAACIGQQEgAnQiA3ENACAEIAE2AgBBACAGIANyNgKM0ICAACABIAQ2AhggASABNgIIIAEgATYCDAwBCyAAQQBBGSACQQF2ayACQR9GG3QhAiAEKAIAIQYCQANAIAYiBCgCBEF4cSAARg0BIAJBHXYhBiACQQF0IQIgBCAGQQRxakEQaiIDKAIAIgYNAAsgAyABNgIAIAEgBDYCGCABIAE2AgwgASABNgIIDAELIAQoAggiACABNgIMIAQgATYCCCABQQA2AhggASAENgIMIAEgADYCCAtBAEEAKAKo0ICAAEF/aiIBQX8gARs2AqjQgIAACwsEAAAAC04AAkAgAA0APwBBEHQPCwJAIABB//8DcQ0AIABBf0wNAAJAIABBEHZAACIAQX9HDQBBAEEwNgL404CAAEF/DwsgAEEQdA8LEMqAgIAAAAvyAgIDfwF+AkAgAkUNACAAIAE6AAAgAiAAaiIDQX9qIAE6AAAgAkEDSQ0AIAAgAToAAiAAIAE6AAEgA0F9aiABOgAAIANBfmogAToAACACQQdJDQAgACABOgADIANBfGogAToAACACQQlJDQAgAEEAIABrQQNxIgRqIgMgAUH/AXFBgYKECGwiATYCACADIAIgBGtBfHEiBGoiAkF8aiABNgIAIARBCUkNACADIAE2AgggAyABNgIEIAJBeGogATYCACACQXRqIAE2AgAgBEEZSQ0AIAMgATYCGCADIAE2AhQgAyABNgIQIAMgATYCDCACQXBqIAE2AgAgAkFsaiABNgIAIAJBaGogATYCACACQWRqIAE2AgAgBCADQQRxQRhyIgVrIgJBIEkNACABrUKBgICAEH4hBiADIAVqIQEDQCABIAY3AxggASAGNwMQIAEgBjcDCCABIAY3AwAgAUEgaiEBIAJBYGoiAkEfSw0ACwsgAAsLjkgBAEGACAuGSAEAAAACAAAAAwAAAAAAAAAAAAAABAAAAAUAAAAAAAAAAAAAAAYAAAAHAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW52YWxpZCBjaGFyIGluIHVybCBxdWVyeQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2JvZHkAQ29udGVudC1MZW5ndGggb3ZlcmZsb3cAQ2h1bmsgc2l6ZSBvdmVyZmxvdwBSZXNwb25zZSBvdmVyZmxvdwBJbnZhbGlkIG1ldGhvZCBmb3IgSFRUUC94LnggcmVxdWVzdABJbnZhbGlkIG1ldGhvZCBmb3IgUlRTUC94LnggcmVxdWVzdABFeHBlY3RlZCBTT1VSQ0UgbWV0aG9kIGZvciBJQ0UveC54IHJlcXVlc3QASW52YWxpZCBjaGFyIGluIHVybCBmcmFnbWVudCBzdGFydABFeHBlY3RlZCBkb3QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9zdGF0dXMASW52YWxpZCByZXNwb25zZSBzdGF0dXMASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucwBVc2VyIGNhbGxiYWNrIGVycm9yAGBvbl9yZXNldGAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2hlYWRlcmAgY2FsbGJhY2sgZXJyb3IAYG9uX21lc3NhZ2VfYmVnaW5gIGNhbGxiYWNrIGVycm9yAGBvbl9jaHVua19leHRlbnNpb25fdmFsdWVgIGNhbGxiYWNrIGVycm9yAGBvbl9zdGF0dXNfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl92ZXJzaW9uX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdXJsX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAEVtcHR5IENvbnRlbnQtTGVuZ3RoAEludmFsaWQgY2hhcmFjdGVyIGluIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBNaXNzaW5nIGV4cGVjdGVkIExGIGFmdGVyIGhlYWRlciB2YWx1ZQBJbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AgaGVhZGVyIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGUgdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZWQgdmFsdWUAUGF1c2VkIGJ5IG9uX2hlYWRlcnNfY29tcGxldGUASW52YWxpZCBFT0Ygc3RhdGUAb25fcmVzZXQgcGF1c2UAb25fY2h1bmtfaGVhZGVyIHBhdXNlAG9uX21lc3NhZ2VfYmVnaW4gcGF1c2UAb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlIHBhdXNlAG9uX3N0YXR1c19jb21wbGV0ZSBwYXVzZQBvbl92ZXJzaW9uX2NvbXBsZXRlIHBhdXNlAG9uX3VybF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19jb21wbGV0ZSBwYXVzZQBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGUgcGF1c2UAb25fbWVzc2FnZV9jb21wbGV0ZSBwYXVzZQBvbl9tZXRob2RfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lIHBhdXNlAFVuZXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgc3RhcnQgbGluZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgbmFtZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AAU1dJVENIX1BST1hZAFVTRV9QUk9YWQBNS0FDVElWSVRZAFVOUFJPQ0VTU0FCTEVfRU5USVRZAENPUFkATU9WRURfUEVSTUFORU5UTFkAVE9PX0VBUkxZAE5PVElGWQBGQUlMRURfREVQRU5ERU5DWQBCQURfR0FURVdBWQBQTEFZAFBVVABDSEVDS09VVABHQVRFV0FZX1RJTUVPVVQAUkVRVUVTVF9USU1FT1VUAE5FVFdPUktfQ09OTkVDVF9USU1FT1VUAENPTk5FQ1RJT05fVElNRU9VVABMT0dJTl9USU1FT1VUAE5FVFdPUktfUkVBRF9USU1FT1VUAFBPU1QATUlTRElSRUNURURfUkVRVUVTVABDTElFTlRfQ0xPU0VEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9MT0FEX0JBTEFOQ0VEX1JFUVVFU1QAQkFEX1JFUVVFU1QASFRUUF9SRVFVRVNUX1NFTlRfVE9fSFRUUFNfUE9SVABSRVBPUlQASU1fQV9URUFQT1QAUkVTRVRfQ09OVEVOVABOT19DT05URU5UAFBBUlRJQUxfQ09OVEVOVABIUEVfSU5WQUxJRF9DT05TVEFOVABIUEVfQ0JfUkVTRVQAR0VUAEhQRV9TVFJJQ1QAQ09ORkxJQ1QAVEVNUE9SQVJZX1JFRElSRUNUAFBFUk1BTkVOVF9SRURJUkVDVABDT05ORUNUAE1VTFRJX1NUQVRVUwBIUEVfSU5WQUxJRF9TVEFUVVMAVE9PX01BTllfUkVRVUVTVFMARUFSTFlfSElOVFMAVU5BVkFJTEFCTEVfRk9SX0xFR0FMX1JFQVNPTlMAT1BUSU9OUwBTV0lUQ0hJTkdfUFJPVE9DT0xTAFZBUklBTlRfQUxTT19ORUdPVElBVEVTAE1VTFRJUExFX0NIT0lDRVMASU5URVJOQUxfU0VSVkVSX0VSUk9SAFdFQl9TRVJWRVJfVU5LTk9XTl9FUlJPUgBSQUlMR1VOX0VSUk9SAElERU5USVRZX1BST1ZJREVSX0FVVEhFTlRJQ0FUSU9OX0VSUk9SAFNTTF9DRVJUSUZJQ0FURV9FUlJPUgBJTlZBTElEX1hfRk9SV0FSREVEX0ZPUgBTRVRfUEFSQU1FVEVSAEdFVF9QQVJBTUVURVIASFBFX1VTRVIAU0VFX09USEVSAEhQRV9DQl9DSFVOS19IRUFERVIATUtDQUxFTkRBUgBTRVRVUABXRUJfU0VSVkVSX0lTX0RPV04AVEVBUkRPV04ASFBFX0NMT1NFRF9DT05ORUNUSU9OAEhFVVJJU1RJQ19FWFBJUkFUSU9OAERJU0NPTk5FQ1RFRF9PUEVSQVRJT04ATk9OX0FVVEhPUklUQVRJVkVfSU5GT1JNQVRJT04ASFBFX0lOVkFMSURfVkVSU0lPTgBIUEVfQ0JfTUVTU0FHRV9CRUdJTgBTSVRFX0lTX0ZST1pFTgBIUEVfSU5WQUxJRF9IRUFERVJfVE9LRU4ASU5WQUxJRF9UT0tFTgBGT1JCSURERU4ARU5IQU5DRV9ZT1VSX0NBTE0ASFBFX0lOVkFMSURfVVJMAEJMT0NLRURfQllfUEFSRU5UQUxfQ09OVFJPTABNS0NPTABBQ0wASFBFX0lOVEVSTkFMAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0VfVU5PRkZJQ0lBTABIUEVfT0sAVU5MSU5LAFVOTE9DSwBQUkkAUkVUUllfV0lUSABIUEVfSU5WQUxJRF9DT05URU5UX0xFTkdUSABIUEVfVU5FWFBFQ1RFRF9DT05URU5UX0xFTkdUSABGTFVTSABQUk9QUEFUQ0gATS1TRUFSQ0gAVVJJX1RPT19MT05HAFBST0NFU1NJTkcATUlTQ0VMTEFORU9VU19QRVJTSVNURU5UX1dBUk5JTkcATUlTQ0VMTEFORU9VU19XQVJOSU5HAEhQRV9JTlZBTElEX1RSQU5TRkVSX0VOQ09ESU5HAEV4cGVjdGVkIENSTEYASFBFX0lOVkFMSURfQ0hVTktfU0laRQBNT1ZFAENPTlRJTlVFAEhQRV9DQl9TVEFUVVNfQ09NUExFVEUASFBFX0NCX0hFQURFUlNfQ09NUExFVEUASFBFX0NCX1ZFUlNJT05fQ09NUExFVEUASFBFX0NCX1VSTF9DT01QTEVURQBIUEVfQ0JfQ0hVTktfQ09NUExFVEUASFBFX0NCX0hFQURFUl9WQUxVRV9DT01QTEVURQBIUEVfQ0JfQ0hVTktfRVhURU5TSU9OX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fTkFNRV9DT01QTEVURQBIUEVfQ0JfTUVTU0FHRV9DT01QTEVURQBIUEVfQ0JfTUVUSE9EX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJfRklFTERfQ09NUExFVEUAREVMRVRFAEhQRV9JTlZBTElEX0VPRl9TVEFURQBJTlZBTElEX1NTTF9DRVJUSUZJQ0FURQBQQVVTRQBOT19SRVNQT05TRQBVTlNVUFBPUlRFRF9NRURJQV9UWVBFAEdPTkUATk9UX0FDQ0VQVEFCTEUAU0VSVklDRV9VTkFWQUlMQUJMRQBSQU5HRV9OT1RfU0FUSVNGSUFCTEUAT1JJR0lOX0lTX1VOUkVBQ0hBQkxFAFJFU1BPTlNFX0lTX1NUQUxFAFBVUkdFAE1FUkdFAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0UAUkVRVUVTVF9IRUFERVJfVE9PX0xBUkdFAFBBWUxPQURfVE9PX0xBUkdFAElOU1VGRklDSUVOVF9TVE9SQUdFAEhQRV9QQVVTRURfVVBHUkFERQBIUEVfUEFVU0VEX0gyX1VQR1JBREUAU09VUkNFAEFOTk9VTkNFAFRSQUNFAEhQRV9VTkVYUEVDVEVEX1NQQUNFAERFU0NSSUJFAFVOU1VCU0NSSUJFAFJFQ09SRABIUEVfSU5WQUxJRF9NRVRIT0QATk9UX0ZPVU5EAFBST1BGSU5EAFVOQklORABSRUJJTkQAVU5BVVRIT1JJWkVEAE1FVEhPRF9OT1RfQUxMT1dFRABIVFRQX1ZFUlNJT05fTk9UX1NVUFBPUlRFRABBTFJFQURZX1JFUE9SVEVEAEFDQ0VQVEVEAE5PVF9JTVBMRU1FTlRFRABMT09QX0RFVEVDVEVEAEhQRV9DUl9FWFBFQ1RFRABIUEVfTEZfRVhQRUNURUQAQ1JFQVRFRABJTV9VU0VEAEhQRV9QQVVTRUQAVElNRU9VVF9PQ0NVUkVEAFBBWU1FTlRfUkVRVUlSRUQAUFJFQ09ORElUSU9OX1JFUVVJUkVEAFBST1hZX0FVVEhFTlRJQ0FUSU9OX1JFUVVJUkVEAE5FVFdPUktfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATEVOR1RIX1JFUVVJUkVEAFNTTF9DRVJUSUZJQ0FURV9SRVFVSVJFRABVUEdSQURFX1JFUVVJUkVEAFBBR0VfRVhQSVJFRABQUkVDT05ESVRJT05fRkFJTEVEAEVYUEVDVEFUSU9OX0ZBSUxFRABSRVZBTElEQVRJT05fRkFJTEVEAFNTTF9IQU5EU0hBS0VfRkFJTEVEAExPQ0tFRABUUkFOU0ZPUk1BVElPTl9BUFBMSUVEAE5PVF9NT0RJRklFRABOT1RfRVhURU5ERUQAQkFORFdJRFRIX0xJTUlUX0VYQ0VFREVEAFNJVEVfSVNfT1ZFUkxPQURFRABIRUFEAEV4cGVjdGVkIEhUVFAvAABeEwAAJhMAADAQAADwFwAAnRMAABUSAAA5FwAA8BIAAAoQAAB1EgAArRIAAIITAABPFAAAfxAAAKAVAAAjFAAAiRIAAIsUAABNFQAA1BEAAM8UAAAQGAAAyRYAANwWAADBEQAA4BcAALsUAAB0FAAAfBUAAOUUAAAIFwAAHxAAAGUVAACjFAAAKBUAAAIVAACZFQAALBAAAIsZAABPDwAA1A4AAGoQAADOEAAAAhcAAIkOAABuEwAAHBMAAGYUAABWFwAAwRMAAM0TAABsEwAAaBcAAGYXAABfFwAAIhMAAM4PAABpDgAA2A4AAGMWAADLEwAAqg4AACgXAAAmFwAAxRMAAF0WAADoEQAAZxMAAGUTAADyFgAAcxMAAB0XAAD5FgAA8xEAAM8OAADOFQAADBIAALMRAAClEQAAYRAAADIXAAC7EwAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgMCAgICAgAAAgIAAgIAAgICAgICAgICAgAEAAAAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAIAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgICAgIAAAICAAICAAICAgICAgICAgIAAwAEAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgIAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgACAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsb3NlZWVwLWFsaXZlAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQFjaHVua2VkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVjdGlvbmVudC1sZW5ndGhvbnJveHktY29ubmVjdGlvbgAAAAAAAAAAAAAAAAAAAHJhbnNmZXItZW5jb2RpbmdwZ3JhZGUNCg0KDQpTTQ0KDQpUVFAvQ0UvVFNQLwAAAAAAAAAAAAAAAAECAAEDAAAAAAAAAAAAAAAAAAAAAAAABAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAABAgABAwAAAAAAAAAAAAAAAAAAAAAAAAQBAQUBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAABAAACAAAAAAAAAAAAAAAAAAAAAAAAAwQAAAQEBAQEBAQEBAQEBQQEBAQEBAQEBAQEBAAEAAYHBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQABAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAgAAAAACAAAAAAAAAAAAAAAAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5PVU5DRUVDS09VVE5FQ1RFVEVDUklCRUxVU0hFVEVBRFNFQVJDSFJHRUNUSVZJVFlMRU5EQVJWRU9USUZZUFRJT05TQ0hTRUFZU1RBVENIR0VPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFSFRUUC9BRFRQLw=="},8974:(e,A)=>{"use strict";Object.defineProperty(A,"__esModule",{value:true});A.enumToMap=void 0;function enumToMap(e){const A={};Object.keys(e).forEach((t=>{const r=e[t];if(typeof r==="number"){A[t]=r}}));return A}A.enumToMap=enumToMap},3703:(e,A,t)=>{"use strict";const{kClients:r}=t(7781);const s=t(8787);const{kAgent:o,kMockAgentSet:n,kMockAgentGet:i,kDispatches:a,kIsMockActive:c,kNetConnect:g,kGetNetConnect:E,kOptions:l,kFactory:u}=t(3647);const Q=t(7739);const C=t(1986);const{matchValue:h,buildMockOptions:B}=t(1779);const{InvalidArgumentError:I,UndiciError:d}=t(7221);const p=t(8841);const m=t(5051);const y=t(1600);class FakeWeakRef{constructor(e){this.value=e}deref(){return this.value}}class MockAgent extends p{constructor(e){super(e);this[g]=true;this[c]=true;if(e&&e.agent&&typeof e.agent.dispatch!=="function"){throw new I("Argument opts.agent must implement Agent")}const A=e&&e.agent?e.agent:new s(e);this[o]=A;this[r]=A[r];this[l]=B(e)}get(e){let A=this[i](e);if(!A){A=this[u](e);this[n](e,A)}return A}dispatch(e,A){this.get(e.origin);return this[o].dispatch(e,A)}async close(){await this[o].close();this[r].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[g])){this[g].push(e)}else{this[g]=[e]}}else if(typeof e==="undefined"){this[g]=true}else{throw new I("Unsupported matcher. Must be one of String|Function|RegExp.")}}disableNetConnect(){this[g]=false}get isMockActive(){return this[c]}[n](e,A){this[r].set(e,new FakeWeakRef(A))}[u](e){const A=Object.assign({agent:this},this[l]);return this[l]&&this[l].connections===1?new Q(e,A):new C(e,A)}[i](e){const A=this[r].get(e);if(A){return A.deref()}if(typeof e!=="string"){const A=this[u]("http://localhost:9999");this[n](e,A);return A}for(const[A,t]of Array.from(this[r])){const r=t.deref();if(r&&typeof A!=="string"&&h(A,e)){const A=this[u](e);this[n](e,A);A[a]=r[a];return A}}}[E](){return this[g]}pendingInterceptors(){const e=this[r];return Array.from(e.entries()).flatMap((([e,A])=>A.deref()[a].map((A=>({...A,origin:e}))))).filter((({pending:e})=>e))}assertNoPendingInterceptors({pendingInterceptorsFormatter:e=new y}={}){const A=this.pendingInterceptors();if(A.length===0){return}const t=new m("interceptor","interceptors").pluralize(A.length);throw new d(`\n${t.count} ${t.noun} ${t.is} pending:\n\n${e.format(A)}\n`.trim())}}e.exports=MockAgent},7739:(e,A,t)=>{"use strict";const{promisify:r}=t(9023);const s=t(1247);const{buildMockDispatch:o}=t(1779);const{kDispatches:n,kMockAgent:i,kClose:a,kOriginalClose:c,kOrigin:g,kOriginalDispatch:E,kConnected:l}=t(3647);const{MockInterceptor:u}=t(2013);const Q=t(7781);const{InvalidArgumentError:C}=t(7221);class MockClient extends s{constructor(e,A){super(e,A);if(!A||!A.agent||typeof A.agent.dispatch!=="function"){throw new C("Argument opts.agent must implement Agent")}this[i]=A.agent;this[g]=e;this[n]=[];this[l]=1;this[E]=this.dispatch;this[c]=this.close.bind(this);this.dispatch=o.call(this);this.close=this[a]}get[Q.kConnected](){return this[l]}intercept(e){return new u(e,this[n])}async[a](){await r(this[c])();this[l]=0;this[i][Q.kClients].delete(this[g])}}e.exports=MockClient},2567:(e,A,t)=>{"use strict";const{UndiciError:r}=t(7221);class MockNotMatchedError extends r{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}},2013:(e,A,t)=>{"use strict";const{getResponseData:r,buildKey:s,addMockDispatch:o}=t(1779);const{kDispatches:n,kDispatchKey:i,kDefaultHeaders:a,kDefaultTrailers:c,kContentLength:g,kMockDispatch:E}=t(3647);const{InvalidArgumentError:l}=t(7221);const{buildURL:u}=t(2806);class MockScope{constructor(e){this[E]=e}delay(e){if(typeof e!=="number"||!Number.isInteger(e)||e<=0){throw new l("waitInMs must be a valid integer > 0")}this[E].delay=e;return this}persist(){this[E].persist=true;return this}times(e){if(typeof e!=="number"||!Number.isInteger(e)||e<=0){throw new l("repeatTimes must be a valid integer > 0")}this[E].times=e;return this}}class MockInterceptor{constructor(e,A){if(typeof e!=="object"){throw new l("opts must be an object")}if(typeof e.path==="undefined"){throw new l("opts.path must be defined")}if(typeof e.method==="undefined"){e.method="GET"}if(typeof e.path==="string"){if(e.query){e.path=u(e.path,e.query)}else{const A=new URL(e.path,"data://");e.path=A.pathname+A.search}}if(typeof e.method==="string"){e.method=e.method.toUpperCase()}this[i]=s(e);this[n]=A;this[a]={};this[c]={};this[g]=false}createMockScopeDispatchData(e,A,t={}){const s=r(A);const o=this[g]?{"content-length":s.length}:{};const n={...this[a],...o,...t.headers};const i={...this[c],...t.trailers};return{statusCode:e,data:A,headers:n,trailers:i}}validateReplyParameters(e,A,t){if(typeof e==="undefined"){throw new l("statusCode must be defined")}if(typeof A==="undefined"){throw new l("data must be defined")}if(typeof t!=="object"){throw new l("responseOptions must be an object")}}reply(e){if(typeof e==="function"){const wrappedDefaultsCallback=A=>{const t=e(A);if(typeof t!=="object"){throw new l("reply options callback must return an object")}const{statusCode:r,data:s="",responseOptions:o={}}=t;this.validateReplyParameters(r,s,o);return{...this.createMockScopeDispatchData(r,s,o)}};const A=o(this[n],this[i],wrappedDefaultsCallback);return new MockScope(A)}const[A,t="",r={}]=[...arguments];this.validateReplyParameters(A,t,r);const s=this.createMockScopeDispatchData(A,t,r);const a=o(this[n],this[i],s);return new MockScope(a)}replyWithError(e){if(typeof e==="undefined"){throw new l("error must be defined")}const A=o(this[n],this[i],{error:e});return new MockScope(A)}defaultReplyHeaders(e){if(typeof e==="undefined"){throw new l("headers must be defined")}this[a]=e;return this}defaultReplyTrailers(e){if(typeof e==="undefined"){throw new l("trailers must be defined")}this[c]=e;return this}replyContentLength(){this[g]=true;return this}}e.exports.MockInterceptor=MockInterceptor;e.exports.MockScope=MockScope},1986:(e,A,t)=>{"use strict";const{promisify:r}=t(9023);const s=t(4094);const{buildMockDispatch:o}=t(1779);const{kDispatches:n,kMockAgent:i,kClose:a,kOriginalClose:c,kOrigin:g,kOriginalDispatch:E,kConnected:l}=t(3647);const{MockInterceptor:u}=t(2013);const Q=t(7781);const{InvalidArgumentError:C}=t(7221);class MockPool extends s{constructor(e,A){super(e,A);if(!A||!A.agent||typeof A.agent.dispatch!=="function"){throw new C("Argument opts.agent must implement Agent")}this[i]=A.agent;this[g]=e;this[n]=[];this[l]=1;this[E]=this.dispatch;this[c]=this.close.bind(this);this.dispatch=o.call(this);this.close=this[a]}get[Q.kConnected](){return this[l]}intercept(e){return new u(e,this[n])}async[a](){await r(this[c])();this[l]=0;this[i][Q.kClients].delete(this[g])}}e.exports=MockPool},3647: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")}},1779:(e,A,t)=>{"use strict";const{MockNotMatchedError:r}=t(2567);const{kDispatches:s,kMockAgent:o,kOriginalDispatch:n,kOrigin:i,kGetNetConnect:a}=t(3647);const{buildURL:c,nop:g}=t(2806);const{STATUS_CODES:E}=t(8611);const{types:{isPromise:l}}=t(9023);function matchValue(e,A){if(typeof e==="string"){return e===A}if(e instanceof RegExp){return e.test(A)}if(typeof e==="function"){return e(A)===true}return false}function lowerCaseEntries(e){return Object.fromEntries(Object.entries(e).map((([e,A])=>[e.toLocaleLowerCase(),A])))}function getHeaderByName(e,A){if(Array.isArray(e)){for(let t=0;t!e)).filter((({path:e})=>matchValue(safeUrl(e),s)));if(o.length===0){throw new r(`Mock dispatch not matched for path '${s}'`)}o=o.filter((({method:e})=>matchValue(e,A.method)));if(o.length===0){throw new r(`Mock dispatch not matched for method '${A.method}'`)}o=o.filter((({body:e})=>typeof e!=="undefined"?matchValue(e,A.body):true));if(o.length===0){throw new r(`Mock dispatch not matched for body '${A.body}'`)}o=o.filter((e=>matchHeaders(e,A.headers)));if(o.length===0){throw new r(`Mock dispatch not matched for headers '${typeof A.headers==="object"?JSON.stringify(A.headers):A.headers}'`)}return o[0]}function addMockDispatch(e,A,t){const r={timesInvoked:0,times:1,persist:false,consumed:false};const s=typeof t==="function"?{callback:t}:{...t};const o={...r,...A,pending:true,data:{error:null,...s}};e.push(o);return o}function deleteMockDispatch(e,A){const t=e.findIndex((e=>{if(!e.consumed){return false}return matchKey(e,A)}));if(t!==-1){e.splice(t,1)}}function buildKey(e){const{path:A,method:t,body:r,headers:s,query:o}=e;return{path:A,method:t,body:r,headers:s,query:o}}function generateKeyValues(e){return Object.entries(e).reduce(((e,[A,t])=>[...e,Buffer.from(`${A}`),Array.isArray(t)?t.map((e=>Buffer.from(`${e}`))):Buffer.from(`${t}`)]),[])}function getStatusText(e){return E[e]||"unknown"}async function getResponse(e){const A=[];for await(const t of e){A.push(t)}return Buffer.concat(A).toString("utf8")}function mockDispatch(e,A){const t=buildKey(e);const r=getMockDispatch(this[s],t);r.timesInvoked++;if(r.data.callback){r.data={...r.data,...r.data.callback(e)}}const{data:{statusCode:o,data:n,headers:i,trailers:a,error:c},delay:E,persist:u}=r;const{timesInvoked:Q,times:C}=r;r.consumed=!u&&Q>=C;r.pending=Q0){setTimeout((()=>{handleReply(this[s])}),E)}else{handleReply(this[s])}function handleReply(r,s=n){const c=Array.isArray(e.headers)?buildHeadersFromArray(e.headers):e.headers;const E=typeof s==="function"?s({...e,headers:c}):s;if(l(E)){E.then((e=>handleReply(r,e)));return}const u=getResponseData(E);const Q=generateKeyValues(i);const C=generateKeyValues(a);A.abort=g;A.onHeaders(o,Q,resume,getStatusText(o));A.onData(Buffer.from(u));A.onComplete(C);deleteMockDispatch(r,t)}function resume(){}return true}function buildMockDispatch(){const e=this[o];const A=this[i];const t=this[n];return function dispatch(s,o){if(e.isMockActive){try{mockDispatch.call(this,s,o)}catch(n){if(n instanceof r){const i=e[a]();if(i===false){throw new r(`${n.message}: subsequent request to origin ${A} was not allowed (net.connect disabled)`)}if(checkNetConnect(i,A)){t.call(this,s,o)}else{throw new r(`${n.message}: subsequent request to origin ${A} was not allowed (net.connect is not enabled for this origin)`)}}else{throw n}}}else{t.call(this,s,o)}}}function checkNetConnect(e,A){const t=new URL(A);if(e===true){return true}else if(Array.isArray(e)&&e.some((e=>matchValue(e,t.host)))){return true}return false}function buildMockOptions(e){if(e){const{agent:A,...t}=e;return t}}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}},1600:(e,A,t)=>{"use strict";const{Transform:r}=t(2203);const{Console:s}=t(4236);e.exports=class PendingInterceptorsFormatter{constructor({disableColors:e}={}){this.transform=new r({transform(e,A,t){t(null,e)}});this.logger=new s({stdout:this.transform,inspectOptions:{colors:!e&&!process.env.CI}})}format(e){const A=e.map((({method:e,path:A,data:{statusCode:t},persist:r,times:s,timesInvoked:o,origin:n})=>({Method:e,Origin:n,Path:A,"Status code":t,Persistent:r?"✅":"❌",Invocations:o,Remaining:r?Infinity:s-o})));this.logger.table(A);return this.transform.read().toString()}}},5051:e=>{"use strict";const A={pronoun:"it",is:"is",was:"was",this:"this"};const t={pronoun:"they",is:"are",was:"were",this:"these"};e.exports=class Pluralizer{constructor(e,A){this.singular=e;this.plural=A}pluralize(e){const r=e===1;const s=r?A:t;const o=r?this.singular:this.plural;return{...s,count:e,noun:o}}}},5631:e=>{"use strict";const A=2048;const t=A-1;class FixedCircularBuffer{constructor(){this.bottom=0;this.top=0;this.list=new Array(A);this.next=null}isEmpty(){return this.top===this.bottom}isFull(){return(this.top+1&t)===this.bottom}push(e){this.list[this.top]=e;this.top=this.top+1&t}shift(){const e=this.list[this.bottom];if(e===undefined)return null;this.list[this.bottom]=undefined;this.bottom=this.bottom+1&t;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 A=e.shift();if(e.isEmpty()&&e.next!==null){this.tail=e.next}return A}}},5934:(e,A,t)=>{"use strict";const r=t(6915);const s=t(5631);const{kConnected:o,kSize:n,kRunning:i,kPending:a,kQueued:c,kBusy:g,kFree:E,kUrl:l,kClose:u,kDestroy:Q,kDispatch:C}=t(7781);const h=t(6752);const B=Symbol("clients");const I=Symbol("needDrain");const d=Symbol("queue");const p=Symbol("closed resolve");const m=Symbol("onDrain");const y=Symbol("onConnect");const w=Symbol("onDisconnect");const R=Symbol("onConnectionError");const b=Symbol("get dispatcher");const D=Symbol("add client");const k=Symbol("remove client");const F=Symbol("stats");class PoolBase extends r{constructor(){super();this[d]=new s;this[B]=[];this[c]=0;const e=this;this[m]=function onDrain(A,t){const r=e[d];let s=false;while(!s){const A=r.shift();if(!A){break}e[c]--;s=!this.dispatch(A.opts,A.handler)}this[I]=s;if(!this[I]&&e[I]){e[I]=false;e.emit("drain",A,[e,...t])}if(e[p]&&r.isEmpty()){Promise.all(e[B].map((e=>e.close()))).then(e[p])}};this[y]=(A,t)=>{e.emit("connect",A,[e,...t])};this[w]=(A,t,r)=>{e.emit("disconnect",A,[e,...t],r)};this[R]=(A,t,r)=>{e.emit("connectionError",A,[e,...t],r)};this[F]=new h(this)}get[g](){return this[I]}get[o](){return this[B].filter((e=>e[o])).length}get[E](){return this[B].filter((e=>e[o]&&!e[I])).length}get[a](){let e=this[c];for(const{[a]:A}of this[B]){e+=A}return e}get[i](){let e=0;for(const{[i]:A}of this[B]){e+=A}return e}get[n](){let e=this[c];for(const{[n]:A}of this[B]){e+=A}return e}get stats(){return this[F]}async[u](){if(this[d].isEmpty()){return Promise.all(this[B].map((e=>e.close())))}else{return new Promise((e=>{this[p]=e}))}}async[Q](e){while(true){const A=this[d].shift();if(!A){break}A.handler.onError(e)}return Promise.all(this[B].map((A=>A.destroy(e))))}[C](e,A){const t=this[b]();if(!t){this[I]=true;this[d].push({opts:e,handler:A});this[c]++}else if(!t.dispatch(e,A)){t[I]=true;this[I]=!this[b]()}return!this[I]}[D](e){e.on("drain",this[m]).on("connect",this[y]).on("disconnect",this[w]).on("connectionError",this[R]);this[B].push(e);if(this[I]){process.nextTick((()=>{if(this[I]){this[m](e[l],[this,e])}}))}return this}[k](e){e.close((()=>{const A=this[B].indexOf(e);if(A!==-1){this[B].splice(A,1)}}));this[I]=this[B].some((e=>!e[I]&&e.closed!==true&&e.destroyed!==true))}}e.exports={PoolBase:PoolBase,kClients:B,kNeedDrain:I,kAddClient:D,kRemoveClient:k,kGetDispatcher:b}},6752:(e,A,t)=>{const{kFree:r,kConnected:s,kPending:o,kQueued:n,kRunning:i,kSize:a}=t(7781);const c=Symbol("pool");class PoolStats{constructor(e){this[c]=e}get connected(){return this[c][s]}get free(){return this[c][r]}get pending(){return this[c][o]}get queued(){return this[c][n]}get running(){return this[c][i]}get size(){return this[c][a]}}e.exports=PoolStats},4094:(e,A,t)=>{"use strict";const{PoolBase:r,kClients:s,kNeedDrain:o,kAddClient:n,kGetDispatcher:i}=t(5934);const a=t(1247);const{InvalidArgumentError:c}=t(7221);const g=t(2806);const{kUrl:E,kInterceptors:l}=t(7781);const u=t(4470);const Q=Symbol("options");const C=Symbol("connections");const h=Symbol("factory");function defaultFactory(e,A){return new a(e,A)}class Pool extends r{constructor(e,{connections:A,factory:t=defaultFactory,connect:r,connectTimeout:s,tls:o,maxCachedSessions:n,socketPath:i,autoSelectFamily:a,autoSelectFamilyAttemptTimeout:B,allowH2:I,...d}={}){super();if(A!=null&&(!Number.isFinite(A)||A<0)){throw new c("invalid connections")}if(typeof t!=="function"){throw new c("factory must be a function.")}if(r!=null&&typeof r!=="function"&&typeof r!=="object"){throw new c("connect must be a function or an object")}if(typeof r!=="function"){r=u({...o,maxCachedSessions:n,allowH2:I,socketPath:i,timeout:s,...g.nodeHasAutoSelectFamily&&a?{autoSelectFamily:a,autoSelectFamilyAttemptTimeout:B}:undefined,...r})}this[l]=d.interceptors&&d.interceptors.Pool&&Array.isArray(d.interceptors.Pool)?d.interceptors.Pool:[];this[C]=A||null;this[E]=g.parseOrigin(e);this[Q]={...g.deepClone(d),connect:r,allowH2:I};this[Q].interceptors=d.interceptors?{...d.interceptors}:undefined;this[h]=t}[i](){let e=this[s].find((e=>!e[o]));if(e){return e}if(!this[C]||this[s].length{"use strict";const{kProxy:r,kClose:s,kDestroy:o,kInterceptors:n}=t(7781);const{URL:i}=t(7016);const a=t(8787);const c=t(4094);const g=t(6915);const{InvalidArgumentError:E,RequestAbortedError:l}=t(7221);const u=t(4470);const Q=Symbol("proxy agent");const C=Symbol("proxy client");const h=Symbol("proxy headers");const B=Symbol("request tls settings");const I=Symbol("proxy tls settings");const d=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 E("Proxy opts.uri is mandatory")}return{uri:e.uri,protocol:e.protocol||"https"}}function defaultFactory(e,A){return new c(e,A)}class ProxyAgent extends g{constructor(e){super(e);this[r]=buildProxyOptions(e);this[Q]=new a(e);this[n]=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 E("Proxy opts.uri is mandatory")}const{clientFactory:A=defaultFactory}=e;if(typeof A!=="function"){throw new E("Proxy opts.clientFactory must be a function.")}this[B]=e.requestTls;this[I]=e.proxyTls;this[h]=e.headers||{};const t=new i(e.uri);const{origin:s,port:o,host:c,username:g,password:p}=t;if(e.auth&&e.token){throw new E("opts.auth cannot be used in combination with opts.token")}else if(e.auth){this[h]["proxy-authorization"]=`Basic ${e.auth}`}else if(e.token){this[h]["proxy-authorization"]=e.token}else if(g&&p){this[h]["proxy-authorization"]=`Basic ${Buffer.from(`${decodeURIComponent(g)}:${decodeURIComponent(p)}`).toString("base64")}`}const m=u({...e.proxyTls});this[d]=u({...e.requestTls});this[C]=A(t,{connect:m});this[Q]=new a({...e,connect:async(e,A)=>{let t=e.host;if(!e.port){t+=`:${defaultProtocolPort(e.protocol)}`}try{const{socket:r,statusCode:n}=await this[C].connect({origin:s,port:o,path:t,signal:e.signal,headers:{...this[h],host:c}});if(n!==200){r.on("error",(()=>{})).destroy();A(new l(`Proxy response (${n}) !== 200 when HTTP Tunneling`))}if(e.protocol!=="https:"){A(null,r);return}let i;if(this[B]){i=this[B].servername}else{i=e.servername}this[d]({...e,servername:i,httpSocket:r},A)}catch(e){A(e)}}})}dispatch(e,A){const{host:t}=new i(e.origin);const r=buildHeaders(e.headers);throwIfProxyAuthIsSent(r);return this[Q].dispatch({...e,headers:{...r,host:t}},A)}async[s](){await this[Q].close();await this[C].close()}async[o](){await this[Q].destroy();await this[C].destroy()}}function buildHeaders(e){if(Array.isArray(e)){const A={};for(let t=0;te.toLowerCase()==="proxy-authorization"));if(A){throw new E("Proxy-Authorization should be sent in ProxyAgent constructor")}}e.exports=ProxyAgent},6190:e=>{"use strict";let A=Date.now();let t;const r=[];function onTimeout(){A=Date.now();let e=r.length;let t=0;while(t0&&A>=s.state){s.state=-1;s.callback(s.opaque)}if(s.state===-1){s.state=-2;if(t!==e-1){r[t]=r.pop()}else{r.pop()}e-=1}else{t+=1}}if(r.length>0){refreshTimeout()}}function refreshTimeout(){if(t&&t.refresh){t.refresh()}else{clearTimeout(t);t=setTimeout(onTimeout,1e3);if(t.unref){t.unref()}}}class Timeout{constructor(e,A,t){this.callback=e;this.delay=A;this.opaque=t;this.state=-2;this.refresh()}refresh(){if(this.state===-2){r.push(this);if(!t||r.length===1){refreshTimeout()}}this.state=0}clear(){this.state=-1}}e.exports={setTimeout(e,A,t){return A<1e3?setTimeout(e,A,t):new Timeout(e,A,t)},clearTimeout(e){if(e instanceof Timeout){e.clear()}else{clearTimeout(e)}}}},4200:(e,A,t)=>{"use strict";const r=t(1637);const{uid:s,states:o}=t(8763);const{kReadyState:n,kSentClose:i,kByteParser:a,kReceivedClose:c}=t(9879);const{fireEvent:g,failWebsocketConnection:E}=t(1700);const{CloseEvent:l}=t(1521);const{makeRequest:u}=t(1940);const{fetching:Q}=t(5697);const{Headers:C}=t(1815);const{getGlobalDispatcher:h}=t(6875);const{kHeadersList:B}=t(7781);const I={};I.open=r.channel("undici:websocket:open");I.close=r.channel("undici:websocket:close");I.socketError=r.channel("undici:websocket:socket_error");let d;try{d=t(6982)}catch{}function establishWebSocketConnection(e,A,t,r,o){const n=e;n.protocol=e.protocol==="ws:"?"http:":"https:";const i=u({urlList:[n],serviceWorkers:"none",referrer:"no-referrer",mode:"websocket",credentials:"include",cache:"no-store",redirect:"error"});if(o.headers){const e=new C(o.headers)[B];i.headersList=e}const a=d.randomBytes(16).toString("base64");i.headersList.append("sec-websocket-key",a);i.headersList.append("sec-websocket-version","13");for(const e of A){i.headersList.append("sec-websocket-protocol",e)}const c="";const g=Q({request:i,useParallelQueue:true,dispatcher:o.dispatcher??h(),processResponse(e){if(e.type==="error"||e.status!==101){E(t,"Received network error or non-101 status code.");return}if(A.length!==0&&!e.headersList.get("Sec-WebSocket-Protocol")){E(t,"Server did not respond with sent protocols.");return}if(e.headersList.get("Upgrade")?.toLowerCase()!=="websocket"){E(t,'Server did not set Upgrade header to "websocket".');return}if(e.headersList.get("Connection")?.toLowerCase()!=="upgrade"){E(t,'Server did not set Connection header to "upgrade".');return}const o=e.headersList.get("Sec-WebSocket-Accept");const n=d.createHash("sha1").update(a+s).digest("base64");if(o!==n){E(t,"Incorrect hash received in Sec-WebSocket-Accept header.");return}const g=e.headersList.get("Sec-WebSocket-Extensions");if(g!==null&&g!==c){E(t,"Received different permessage-deflate than the one set.");return}const l=e.headersList.get("Sec-WebSocket-Protocol");if(l!==null&&l!==i.headersList.get("Sec-WebSocket-Protocol")){E(t,"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(I.open.hasSubscribers){I.open.publish({address:e.socket.address(),protocol:l,extensions:g})}r(e)}});return g}function onSocketData(e){if(!this.ws[a].write(e)){this.pause()}}function onSocketClose(){const{ws:e}=this;const A=e[i]&&e[c];let t=1005;let r="";const s=e[a].closingInfo;if(s){t=s.code??1005;r=s.reason}else if(!e[i]){t=1006}e[n]=o.CLOSED;g("close",e,l,{wasClean:A,code:t,reason:r});if(I.close.hasSubscribers){I.close.publish({websocket:e,code:t,reason:r})}}function onSocketError(e){const{ws:A}=this;A[n]=o.CLOSING;if(I.socketError.hasSubscribers){I.socketError.publish(e)}this.destroy()}e.exports={establishWebSocketConnection:establishWebSocketConnection}},8763:e=>{"use strict";const A="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";const t={enumerable:true,writable:false,configurable:false};const r={CONNECTING:0,OPEN:1,CLOSING:2,CLOSED:3};const s={CONTINUATION:0,TEXT:1,BINARY:2,CLOSE:8,PING:9,PONG:10};const o=2**16-1;const n={INFO:0,PAYLOADLENGTH_16:2,PAYLOADLENGTH_64:3,READ_DATA:4};const i=Buffer.allocUnsafe(0);e.exports={uid:A,staticPropertyDescriptors:t,states:r,opcodes:s,maxUnsigned16Bit:o,parserStates:n,emptyBuffer:i}},1521:(e,A,t)=>{"use strict";const{webidl:r}=t(6684);const{kEnumerableProperty:s}=t(2806);const{MessagePort:o}=t(8167);class MessageEvent extends Event{#o;constructor(e,A={}){r.argumentLengthCheck(arguments,1,{header:"MessageEvent constructor"});e=r.converters.DOMString(e);A=r.converters.MessageEventInit(A);super(e,A);this.#o=A}get data(){r.brandCheck(this,MessageEvent);return this.#o.data}get origin(){r.brandCheck(this,MessageEvent);return this.#o.origin}get lastEventId(){r.brandCheck(this,MessageEvent);return this.#o.lastEventId}get source(){r.brandCheck(this,MessageEvent);return this.#o.source}get ports(){r.brandCheck(this,MessageEvent);if(!Object.isFrozen(this.#o.ports)){Object.freeze(this.#o.ports)}return this.#o.ports}initMessageEvent(e,A=false,t=false,s=null,o="",n="",i=null,a=[]){r.brandCheck(this,MessageEvent);r.argumentLengthCheck(arguments,1,{header:"MessageEvent.initMessageEvent"});return new MessageEvent(e,{bubbles:A,cancelable:t,data:s,origin:o,lastEventId:n,source:i,ports:a})}}class CloseEvent extends Event{#o;constructor(e,A={}){r.argumentLengthCheck(arguments,1,{header:"CloseEvent constructor"});e=r.converters.DOMString(e);A=r.converters.CloseEventInit(A);super(e,A);this.#o=A}get wasClean(){r.brandCheck(this,CloseEvent);return this.#o.wasClean}get code(){r.brandCheck(this,CloseEvent);return this.#o.code}get reason(){r.brandCheck(this,CloseEvent);return this.#o.reason}}class ErrorEvent extends Event{#o;constructor(e,A){r.argumentLengthCheck(arguments,1,{header:"ErrorEvent constructor"});super(e,A);e=r.converters.DOMString(e);A=r.converters.ErrorEventInit(A??{});this.#o=A}get message(){r.brandCheck(this,ErrorEvent);return this.#o.message}get filename(){r.brandCheck(this,ErrorEvent);return this.#o.filename}get lineno(){r.brandCheck(this,ErrorEvent);return this.#o.lineno}get colno(){r.brandCheck(this,ErrorEvent);return this.#o.colno}get error(){r.brandCheck(this,ErrorEvent);return this.#o.error}}Object.defineProperties(MessageEvent.prototype,{[Symbol.toStringTag]:{value:"MessageEvent",configurable:true},data:s,origin:s,lastEventId:s,source:s,ports:s,initMessageEvent:s});Object.defineProperties(CloseEvent.prototype,{[Symbol.toStringTag]:{value:"CloseEvent",configurable:true},reason:s,code:s,wasClean:s});Object.defineProperties(ErrorEvent.prototype,{[Symbol.toStringTag]:{value:"ErrorEvent",configurable:true},message:s,filename:s,lineno:s,colno:s,error:s});r.converters.MessagePort=r.interfaceConverter(o);r.converters["sequence"]=r.sequenceConverter(r.converters.MessagePort);const n=[{key:"bubbles",converter:r.converters.boolean,defaultValue:false},{key:"cancelable",converter:r.converters.boolean,defaultValue:false},{key:"composed",converter:r.converters.boolean,defaultValue:false}];r.converters.MessageEventInit=r.dictionaryConverter([...n,{key:"data",converter:r.converters.any,defaultValue:null},{key:"origin",converter:r.converters.USVString,defaultValue:""},{key:"lastEventId",converter:r.converters.DOMString,defaultValue:""},{key:"source",converter:r.nullableConverter(r.converters.MessagePort),defaultValue:null},{key:"ports",converter:r.converters["sequence"],get defaultValue(){return[]}}]);r.converters.CloseEventInit=r.dictionaryConverter([...n,{key:"wasClean",converter:r.converters.boolean,defaultValue:false},{key:"code",converter:r.converters["unsigned short"],defaultValue:0},{key:"reason",converter:r.converters.USVString,defaultValue:""}]);r.converters.ErrorEventInit=r.dictionaryConverter([...n,{key:"message",converter:r.converters.DOMString,defaultValue:""},{key:"filename",converter:r.converters.USVString,defaultValue:""},{key:"lineno",converter:r.converters["unsigned long"],defaultValue:0},{key:"colno",converter:r.converters["unsigned long"],defaultValue:0},{key:"error",converter:r.converters.any}]);e.exports={MessageEvent:MessageEvent,CloseEvent:CloseEvent,ErrorEvent:ErrorEvent}},9183:(e,A,t)=>{"use strict";const{maxUnsigned16Bit:r}=t(8763);let s;try{s=t(6982)}catch{}class WebsocketFrameSend{constructor(e){this.frameData=e;this.maskKey=s.randomBytes(4)}createFrame(e){const A=this.frameData?.byteLength??0;let t=A;let s=6;if(A>r){s+=8;t=127}else if(A>125){s+=2;t=126}const o=Buffer.allocUnsafe(A+s);o[0]=o[1]=0;o[0]|=128;o[0]=(o[0]&240)+e; +/*! ws. MIT License. Einar Otto Stangvik */o[s-4]=this.maskKey[0];o[s-3]=this.maskKey[1];o[s-2]=this.maskKey[2];o[s-1]=this.maskKey[3];o[1]=t;if(t===126){o.writeUInt16BE(A,2)}else if(t===127){o[2]=o[3]=0;o.writeUIntBE(A,4,6)}o[1]|=128;for(let e=0;e{"use strict";const{Writable:r}=t(2203);const s=t(1637);const{parserStates:o,opcodes:n,states:i,emptyBuffer:a}=t(8763);const{kReadyState:c,kSentClose:g,kResponse:E,kReceivedClose:l}=t(9879);const{isValidStatusCode:u,failWebsocketConnection:Q,websocketMessageReceived:C}=t(1700);const{WebsocketFrameSend:h}=t(9183);const B={};B.ping=s.channel("undici:websocket:ping");B.pong=s.channel("undici:websocket:pong");class ByteParser extends r{#n=[];#i=0;#a=o.INFO;#c={};#g=[];constructor(e){super();this.ws=e}_write(e,A,t){this.#n.push(e);this.#i+=e.length;this.run(t)}run(e){while(true){if(this.#a===o.INFO){if(this.#i<2){return e()}const A=this.consume(2);this.#c.fin=(A[0]&128)!==0;this.#c.opcode=A[0]&15;this.#c.originalOpcode??=this.#c.opcode;this.#c.fragmented=!this.#c.fin&&this.#c.opcode!==n.CONTINUATION;if(this.#c.fragmented&&this.#c.opcode!==n.BINARY&&this.#c.opcode!==n.TEXT){Q(this.ws,"Invalid frame type was fragmented.");return}const t=A[1]&127;if(t<=125){this.#c.payloadLength=t;this.#a=o.READ_DATA}else if(t===126){this.#a=o.PAYLOADLENGTH_16}else if(t===127){this.#a=o.PAYLOADLENGTH_64}if(this.#c.fragmented&&t>125){Q(this.ws,"Fragmented frame exceeded 125 bytes.");return}else if((this.#c.opcode===n.PING||this.#c.opcode===n.PONG||this.#c.opcode===n.CLOSE)&&t>125){Q(this.ws,"Payload length for control frame exceeded 125 bytes.");return}else if(this.#c.opcode===n.CLOSE){if(t===1){Q(this.ws,"Received close frame with a 1-byte body.");return}const e=this.consume(t);this.#c.closeInfo=this.parseCloseBody(false,e);if(!this.ws[g]){const e=Buffer.allocUnsafe(2);e.writeUInt16BE(this.#c.closeInfo.code,0);const A=new h(e);this.ws[E].socket.write(A.createFrame(n.CLOSE),(e=>{if(!e){this.ws[g]=true}}))}this.ws[c]=i.CLOSING;this.ws[l]=true;this.end();return}else if(this.#c.opcode===n.PING){const A=this.consume(t);if(!this.ws[l]){const e=new h(A);this.ws[E].socket.write(e.createFrame(n.PONG));if(B.ping.hasSubscribers){B.ping.publish({payload:A})}}this.#a=o.INFO;if(this.#i>0){continue}else{e();return}}else if(this.#c.opcode===n.PONG){const A=this.consume(t);if(B.pong.hasSubscribers){B.pong.publish({payload:A})}if(this.#i>0){continue}else{e();return}}}else if(this.#a===o.PAYLOADLENGTH_16){if(this.#i<2){return e()}const A=this.consume(2);this.#c.payloadLength=A.readUInt16BE(0);this.#a=o.READ_DATA}else if(this.#a===o.PAYLOADLENGTH_64){if(this.#i<8){return e()}const A=this.consume(8);const t=A.readUInt32BE(0);if(t>2**31-1){Q(this.ws,"Received payload length > 2^31 bytes.");return}const r=A.readUInt32BE(4);this.#c.payloadLength=(t<<8)+r;this.#a=o.READ_DATA}else if(this.#a===o.READ_DATA){if(this.#i=this.#c.payloadLength){const e=this.consume(this.#c.payloadLength);this.#g.push(e);if(!this.#c.fragmented||this.#c.fin&&this.#c.opcode===n.CONTINUATION){const e=Buffer.concat(this.#g);C(this.ws,this.#c.originalOpcode,e);this.#c={};this.#g.length=0}this.#a=o.INFO}}if(this.#i>0){continue}else{e();break}}}consume(e){if(e>this.#i){return null}else if(e===0){return a}if(this.#n[0].length===e){this.#i-=this.#n[0].length;return this.#n.shift()}const A=Buffer.allocUnsafe(e);let t=0;while(t!==e){const r=this.#n[0];const{length:s}=r;if(s+t===e){A.set(this.#n.shift(),t);break}else if(s+t>e){A.set(r.subarray(0,e-t),t);this.#n[0]=r.subarray(e-t);break}else{A.set(this.#n.shift(),t);t+=r.length}}this.#i-=e;return A}parseCloseBody(e,A){let t;if(A.length>=2){t=A.readUInt16BE(0)}if(e){if(!u(t)){return null}return{code:t}}let r=A.subarray(2);if(r[0]===239&&r[1]===187&&r[2]===191){r=r.subarray(3)}if(t!==undefined&&!u(t)){return null}try{r=new TextDecoder("utf-8",{fatal:true}).decode(r)}catch{return null}return{code:t,reason:r}}get closingInfo(){return this.#c.closeInfo}}e.exports={ByteParser:ByteParser}},9879: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")}},1700:(e,A,t)=>{"use strict";const{kReadyState:r,kController:s,kResponse:o,kBinaryType:n,kWebSocketURL:i}=t(9879);const{states:a,opcodes:c}=t(8763);const{MessageEvent:g,ErrorEvent:E}=t(1521);function isEstablished(e){return e[r]===a.OPEN}function isClosing(e){return e[r]===a.CLOSING}function isClosed(e){return e[r]===a.CLOSED}function fireEvent(e,A,t=Event,r){const s=new t(e,r);A.dispatchEvent(s)}function websocketMessageReceived(e,A,t){if(e[r]!==a.OPEN){return}let s;if(A===c.TEXT){try{s=new TextDecoder("utf-8",{fatal:true}).decode(t)}catch{failWebsocketConnection(e,"Received invalid UTF-8 in text frame.");return}}else if(A===c.BINARY){if(e[n]==="blob"){s=new Blob([t])}else{s=new Uint8Array(t).buffer}}fireEvent("message",e,g,{origin:e[i].origin,data:s})}function isValidSubprotocol(e){if(e.length===0){return false}for(const A of e){const e=A.charCodeAt(0);if(e<33||e>126||A==="("||A===")"||A==="<"||A===">"||A==="@"||A===","||A===";"||A===":"||A==="\\"||A==='"'||A==="/"||A==="["||A==="]"||A==="?"||A==="="||A==="{"||A==="}"||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,A){const{[s]:t,[o]:r}=e;t.abort();if(r?.socket&&!r.socket.destroyed){r.socket.destroy()}if(A){fireEvent("error",e,E,{error:new Error(A)})}}e.exports={isEstablished:isEstablished,isClosing:isClosing,isClosed:isClosed,fireEvent:fireEvent,isValidSubprotocol:isValidSubprotocol,isValidStatusCode:isValidStatusCode,failWebsocketConnection:failWebsocketConnection,websocketMessageReceived:websocketMessageReceived}},7045:(e,A,t)=>{"use strict";const{webidl:r}=t(6684);const{DOMException:s}=t(6040);const{URLSerializer:o}=t(7160);const{getGlobalOrigin:n}=t(574);const{staticPropertyDescriptors:i,states:a,opcodes:c,emptyBuffer:g}=t(8763);const{kWebSocketURL:E,kReadyState:l,kController:u,kBinaryType:Q,kResponse:C,kSentClose:h,kByteParser:B}=t(9879);const{isEstablished:I,isClosing:d,isValidSubprotocol:p,failWebsocketConnection:m,fireEvent:y}=t(1700);const{establishWebSocketConnection:w}=t(4200);const{WebsocketFrameSend:R}=t(9183);const{ByteParser:b}=t(4697);const{kEnumerableProperty:D,isBlobLike:k}=t(2806);const{getGlobalDispatcher:F}=t(6875);const{types:S}=t(9023);let T=false;class WebSocket extends EventTarget{#E={open:null,error:null,close:null,message:null};#l=0;#u="";#Q="";constructor(e,A=[]){super();r.argumentLengthCheck(arguments,1,{header:"WebSocket constructor"});if(!T){T=true;process.emitWarning("WebSockets are experimental, expect them to change at any time.",{code:"UNDICI-WS"})}const t=r.converters["DOMString or sequence or WebSocketInit"](A);e=r.converters.USVString(e);A=t.protocols;const o=n();let i;try{i=new URL(e,o)}catch(e){throw new s(e,"SyntaxError")}if(i.protocol==="http:"){i.protocol="ws:"}else if(i.protocol==="https:"){i.protocol="wss:"}if(i.protocol!=="ws:"&&i.protocol!=="wss:"){throw new s(`Expected a ws: or wss: protocol, got ${i.protocol}`,"SyntaxError")}if(i.hash||i.href.endsWith("#")){throw new s("Got fragment","SyntaxError")}if(typeof A==="string"){A=[A]}if(A.length!==new Set(A.map((e=>e.toLowerCase()))).size){throw new s("Invalid Sec-WebSocket-Protocol value","SyntaxError")}if(A.length>0&&!A.every((e=>p(e)))){throw new s("Invalid Sec-WebSocket-Protocol value","SyntaxError")}this[E]=new URL(i.href);this[u]=w(i,A,this,(e=>this.#C(e)),t);this[l]=WebSocket.CONNECTING;this[Q]="blob"}close(e=undefined,A=undefined){r.brandCheck(this,WebSocket);if(e!==undefined){e=r.converters["unsigned short"](e,{clamp:true})}if(A!==undefined){A=r.converters.USVString(A)}if(e!==undefined){if(e!==1e3&&(e<3e3||e>4999)){throw new s("invalid code","InvalidAccessError")}}let t=0;if(A!==undefined){t=Buffer.byteLength(A);if(t>123){throw new s(`Reason must be less than 123 bytes; received ${t}`,"SyntaxError")}}if(this[l]===WebSocket.CLOSING||this[l]===WebSocket.CLOSED){}else if(!I(this)){m(this,"Connection was closed before it was established.");this[l]=WebSocket.CLOSING}else if(!d(this)){const r=new R;if(e!==undefined&&A===undefined){r.frameData=Buffer.allocUnsafe(2);r.frameData.writeUInt16BE(e,0)}else if(e!==undefined&&A!==undefined){r.frameData=Buffer.allocUnsafe(2+t);r.frameData.writeUInt16BE(e,0);r.frameData.write(A,2,"utf-8")}else{r.frameData=g}const s=this[C].socket;s.write(r.createFrame(c.CLOSE),(e=>{if(!e){this[h]=true}}));this[l]=a.CLOSING}else{this[l]=WebSocket.CLOSING}}send(e){r.brandCheck(this,WebSocket);r.argumentLengthCheck(arguments,1,{header:"WebSocket.send"});e=r.converters.WebSocketSendData(e);if(this[l]===WebSocket.CONNECTING){throw new s("Sent before connected.","InvalidStateError")}if(!I(this)||d(this)){return}const A=this[C].socket;if(typeof e==="string"){const t=Buffer.from(e);const r=new R(t);const s=r.createFrame(c.TEXT);this.#l+=t.byteLength;A.write(s,(()=>{this.#l-=t.byteLength}))}else if(S.isArrayBuffer(e)){const t=Buffer.from(e);const r=new R(t);const s=r.createFrame(c.BINARY);this.#l+=t.byteLength;A.write(s,(()=>{this.#l-=t.byteLength}))}else if(ArrayBuffer.isView(e)){const t=Buffer.from(e,e.byteOffset,e.byteLength);const r=new R(t);const s=r.createFrame(c.BINARY);this.#l+=t.byteLength;A.write(s,(()=>{this.#l-=t.byteLength}))}else if(k(e)){const t=new R;e.arrayBuffer().then((e=>{const r=Buffer.from(e);t.frameData=r;const s=t.createFrame(c.BINARY);this.#l+=r.byteLength;A.write(s,(()=>{this.#l-=r.byteLength}))}))}}get readyState(){r.brandCheck(this,WebSocket);return this[l]}get bufferedAmount(){r.brandCheck(this,WebSocket);return this.#l}get url(){r.brandCheck(this,WebSocket);return o(this[E])}get extensions(){r.brandCheck(this,WebSocket);return this.#Q}get protocol(){r.brandCheck(this,WebSocket);return this.#u}get onopen(){r.brandCheck(this,WebSocket);return this.#E.open}set onopen(e){r.brandCheck(this,WebSocket);if(this.#E.open){this.removeEventListener("open",this.#E.open)}if(typeof e==="function"){this.#E.open=e;this.addEventListener("open",e)}else{this.#E.open=null}}get onerror(){r.brandCheck(this,WebSocket);return this.#E.error}set onerror(e){r.brandCheck(this,WebSocket);if(this.#E.error){this.removeEventListener("error",this.#E.error)}if(typeof e==="function"){this.#E.error=e;this.addEventListener("error",e)}else{this.#E.error=null}}get onclose(){r.brandCheck(this,WebSocket);return this.#E.close}set onclose(e){r.brandCheck(this,WebSocket);if(this.#E.close){this.removeEventListener("close",this.#E.close)}if(typeof e==="function"){this.#E.close=e;this.addEventListener("close",e)}else{this.#E.close=null}}get onmessage(){r.brandCheck(this,WebSocket);return this.#E.message}set onmessage(e){r.brandCheck(this,WebSocket);if(this.#E.message){this.removeEventListener("message",this.#E.message)}if(typeof e==="function"){this.#E.message=e;this.addEventListener("message",e)}else{this.#E.message=null}}get binaryType(){r.brandCheck(this,WebSocket);return this[Q]}set binaryType(e){r.brandCheck(this,WebSocket);if(e!=="blob"&&e!=="arraybuffer"){this[Q]="blob"}else{this[Q]=e}}#C(e){this[C]=e;const A=new b(this);A.on("drain",(function onParserDrain(){this.ws[C].socket.resume()}));e.socket.ws=this;this[B]=A;this[l]=a.OPEN;const t=e.headersList.get("sec-websocket-extensions");if(t!==null){this.#Q=t}const r=e.headersList.get("sec-websocket-protocol");if(r!==null){this.#u=r}y("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:i,OPEN:i,CLOSING:i,CLOSED:i,url:D,readyState:D,bufferedAmount:D,onopen:D,onerror:D,onclose:D,close:D,onmessage:D,binaryType:D,send:D,extensions:D,protocol:D,[Symbol.toStringTag]:{value:"WebSocket",writable:false,enumerable:false,configurable:true}});Object.defineProperties(WebSocket,{CONNECTING:i,OPEN:i,CLOSING:i,CLOSED:i});r.converters["sequence"]=r.sequenceConverter(r.converters.DOMString);r.converters["DOMString or sequence"]=function(e){if(r.util.Type(e)==="Object"&&Symbol.iterator in e){return r.converters["sequence"](e)}return r.converters.DOMString(e)};r.converters.WebSocketInit=r.dictionaryConverter([{key:"protocols",converter:r.converters["DOMString or sequence"],get defaultValue(){return[]}},{key:"dispatcher",converter:e=>e,get defaultValue(){return F()}},{key:"headers",converter:r.nullableConverter(r.converters.HeadersInit)}]);r.converters["DOMString or sequence or WebSocketInit"]=function(e){if(r.util.Type(e)==="Object"&&!(Symbol.iterator in e)){return r.converters.WebSocketInit(e)}return{protocols:r.converters["DOMString or sequence"](e)}};r.converters.WebSocketSendData=function(e){if(r.util.Type(e)==="Object"){if(k(e)){return r.converters.Blob(e,{strict:false})}if(ArrayBuffer.isView(e)||S.isAnyArrayBuffer(e)){return r.converters.BufferSource(e)}}return r.converters.USVString(e)};e.exports={WebSocket:WebSocket}},9653:(e,A)=>{"use strict";Object.defineProperty(A,"__esModule",{value:true});function getUserAgent(){if(typeof navigator==="object"&&"userAgent"in navigator){return navigator.userAgent}if(typeof process==="object"&&"version"in process){return`Node.js/${process.version.substr(1)} (${process.platform}; ${process.arch})`}return""}A.getUserAgent=getUserAgent},1459:e=>{e.exports=wrappy;function wrappy(e,A){if(e&&A)return wrappy(e)(A);if(typeof e!=="function")throw new TypeError("need wrapper function");Object.keys(e).forEach((function(A){wrapper[A]=e[A]}));return wrapper;function wrapper(){var A=new Array(arguments.length);for(var t=0;t{"use strict";Object.defineProperty(A,"__esModule",{value:true});A.runGate=runGate;const t=5*60*1e3;const r=5*60*60*1e3;const s="thank you, next";function errorMessage(e){return e instanceof Error?e.message:String(e)}function errorStatus(e){if(!e||typeof e!=="object")return undefined;const A=e;return A.status??A.response?.status}function isTransientApiError(e){const A=errorStatus(e);return A===429||A!==undefined&&A>=500&&A<600}function escapeCell(e){return String(e??"").replaceAll("|","\\|").replaceAll("\n"," ")}function checkState(e){if(!e||e.status!=="completed")return"waiting";return e.conclusion==="success"?"success":"unsuccessful"}function gateDecision(e){if(e.some((e=>e.state==="success"))){return"open"}if(e.length===3&&e.every((e=>e.state==="unsuccessful"))){return"fail"}return"wait"}function sameRepository(e,A){return e.head?.repo?.full_name===A}function sameRevision(e,A){return A.state==="open"&&e.head.sha===A.head.sha&&e.base.ref===A.base.ref&&e.base.sha===A.base.sha}async function runGate({github:e,context:A,core:o}){o.setOutput("skip","false");const n=Date.now();let i="";const{owner:a,repo:c}=A.repo;const g=A.payload.repository?.full_name??"";async function listOpenPulls(A){const t=await e.paginate(e.rest.pulls.list,{owner:a,repo:c,state:"open",per_page:100,...A});return t}async function getPull(A){const{data:t}=await e.rest.pulls.get({owner:a,repo:c,pull_number:A});return t}async function findPredecessor(e){const A=(await listOpenPulls({head:`${a}:${e.base.ref}`})).filter((A=>A.number!==e.number&&A.head.ref===e.base.ref&&sameRepository(A,g)));return{ambiguous:A.length>1,pull:A[0]}}async function findSuccessors(e){return(await listOpenPulls({base:e.head.ref})).filter((A=>A.number!==e.number&&A.base.ref===e.head.ref&&sameRepository(A,g)))}async function discoverTopology(){const e=A.payload.pull_request?.number;if(!e)throw new Error("Missing pull request number");const t=await getPull(e);if(!sameRepository(t,g)){return{current:t,role:"fork",reason:"fork PRs always run immediately"}}if(t.labels.some((e=>e.name===process.env.BYPASS_LABEL))){return{current:t,role:"bypass",reason:"bypass label is present"}}if((await findSuccessors(t)).length===0){return{current:t,role:"top",reason:"no open PR is based on this head branch"}}const r=[];const s=new Set([t.number]);let o=t;while(r.length<3){const e=await findPredecessor(o);if(e.ambiguous){return{current:t,role:"ambiguous",reason:`multiple open PRs have head branch ${o.base.ref}`}}if(!e.pull)break;if(s.has(e.pull.number)){return{current:t,role:"ambiguous",reason:"cycle detected in PR base branches"}}s.add(e.pull.number);r.push(e.pull);o=e.pull}if(r.length<3){return{current:t,role:"first-three",reason:`only ${r.length} open predecessor PR(s) are reachable`}}return{current:t,role:"middle",predecessors:r}}async function latestRequiredCheck(A){const{data:t}=await e.rest.checks.listForRef({owner:a,repo:c,ref:A.head.sha,check_name:s,filter:"latest",per_page:100});const r=t.check_runs.filter((e=>e.name===s&&e.app?.slug==="github-actions"&&e.head_sha===A.head.sha&&e.pull_requests?.some((e=>e.number===A.number&&e.head?.sha===A.head.sha&&e.base?.ref===A.base.ref&&e.base?.sha===A.base.sha)))).sort(((e,A)=>A.id-e.id))[0];return{pull:A,check:r??null,state:checkState(r??null)}}async function stillDecisive(e,A,t){const r=await getPull(e.current.number);if(!sameRevision(e.current,r))return false;const s=t==="open"?[A.find((e=>e.state==="success"))]:A;for(const e of s){const A=await getPull(e.pull.number);if(!sameRevision(e.pull,A))return false;const t=await latestRequiredCheck(A);if(t.state!==e.state||t.check?.id!==e.check?.id){return false}}return true}function fingerprint(e){return JSON.stringify({role:e.role,reason:e.reason,candidates:e.candidates?.map((e=>({number:e.pull.number,head:e.pull.head.sha,base:e.pull.base.sha,status:e.check?.status,conclusion:e.check?.conclusion})))})}async function writeSummary(e,t,r){try{const s=e.current;const i=Math.floor((Date.now()-n)/6e4);const a=["# PR Stack CI Gate","",`- PR: #${s?.number??A.payload.pull_request?.number??"n/a"}`,`- Branches: \`${escapeCell(s?.base?.ref)}\` ← \`${escapeCell(s?.head?.ref)}\``,`- Role: **${escapeCell(e.role)}**`,`- Result: **${escapeCell(t)}**`,`- Reason: ${escapeCell(r)}`,`- Elapsed: ${i} minute(s)`];if(e.candidates?.length){a.push("","| PR | Base ← Head | Head SHA | Base SHA | Check | State |","|---:|---|---|---|---|---|");for(const A of e.candidates){const e=A.check?`[${A.check.status}/${A.check.conclusion??""}](${A.check.html_url})`:"not reported";a.push(`| #${A.pull.number} | \`${escapeCell(A.pull.base.ref)}\` ← \`${escapeCell(A.pull.head.ref)}\` | \`${escapeCell(A.pull.head.sha?.slice(0,12))}\` | \`${escapeCell(A.pull.base.sha?.slice(0,12))}\` | ${e} | ${A.state} |`)}}await o.summary.addRaw(`${a.join("\n")}\n`).write()}catch(e){o.warning(`Could not write PR Stack CI Gate summary: ${errorMessage(e)}`)}}try{if(A.eventName!=="pull_request"){const e=`${A.eventName} runs immediately`;await writeSummary({role:"non-pr",reason:e},"open",e);return}while(true){let e;const a=[];try{e=await discoverTopology();if(e.role==="middle"){for(const A of e.predecessors){a.push(await latestRequiredCheck(A))}const A=gateDecision(a);if(A!=="wait"&&!await stillDecisive(e,a,A)){o.info("Stack/check state changed during verification; retrying");await new Promise((e=>setTimeout(e,t)));continue}}}catch(e){if(!isTransientApiError(e))throw e;if(Date.now()-n>=r){await writeSummary({current:A.payload.pull_request,role:"error",reason:errorMessage(e)},"open","Five-hour transient API error deadline reached; starting full CI");return}o.warning(`Transient GitHub API error (${errorStatus(e)}); retrying in five minutes: ${errorMessage(e)}`);await new Promise((e=>setTimeout(e,t)));continue}if(e.role!=="middle"){await writeSummary(e,"open",e.reason??"");return}const c={...e,candidates:a};const g=gateDecision(a);const E=fingerprint(c);if(E!==i){o.info(`PR #${e.current.number}: ${a.map((e=>`#${e.pull.number}=${e.state}`)).join(", ")}`);i=E}if(g==="open"){const e=a.find((e=>e.state==="success"));await writeSummary(c,"open",`PR #${e.pull.number} passed ${s}`);return}if(g==="fail"){const e=`All three predecessor PRs completed ${s} without success. Rerun this workflow after a predecessor passes, or apply the ${process.env.BYPASS_LABEL} label.`;await writeSummary(c,"failed",e);o.setFailed(e);return}if(Date.now()-n>=r){await writeSummary(c,"open","Five-hour waiting deadline reached; failing open and starting full CI");return}const l=new Date(Date.now()+t);o.info(`No predecessor has passed yet; polling again at ${l.toISOString()}`);await new Promise((e=>setTimeout(e,t)))}}catch(e){o.warning(`PR stack classification failed; failing open and starting full CI: ${String(e)}`);await writeSummary({current:A.payload.pull_request,role:"error",reason:errorMessage(e)},"open",`Classification/API error; failing open: ${errorMessage(e)}`)}}},3887:function(e,A,t){"use strict";var r=this&&this.__createBinding||(Object.create?function(e,A,t,r){if(r===undefined)r=t;var s=Object.getOwnPropertyDescriptor(A,t);if(!s||("get"in s?!A.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return A[t]}}}Object.defineProperty(e,r,s)}:function(e,A,t,r){if(r===undefined)r=t;e[r]=A[t]});var s=this&&this.__setModuleDefault||(Object.create?function(e,A){Object.defineProperty(e,"default",{enumerable:true,value:A})}:function(e,A){e["default"]=A});var o=this&&this.__importStar||function(){var ownKeys=function(e){ownKeys=Object.getOwnPropertyNames||function(e){var A=[];for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))A[A.length]=t;return A};return ownKeys(e)};return function(e){if(e&&e.__esModule)return e;var A={};if(e!=null)for(var t=ownKeys(e),o=0;o{n.warning(`PR stack gate could not start; running full CI: ${String(e)}`);n.setOutput("skip","false")}))},2613:e=>{"use strict";e.exports=require("assert")},290:e=>{"use strict";e.exports=require("async_hooks")},181:e=>{"use strict";e.exports=require("buffer")},5317:e=>{"use strict";e.exports=require("child_process")},4236:e=>{"use strict";e.exports=require("console")},6982:e=>{"use strict";e.exports=require("crypto")},1637:e=>{"use strict";e.exports=require("diagnostics_channel")},4434:e=>{"use strict";e.exports=require("events")},9896:e=>{"use strict";e.exports=require("fs")},8611:e=>{"use strict";e.exports=require("http")},5675:e=>{"use strict";e.exports=require("http2")},5692:e=>{"use strict";e.exports=require("https")},9278:e=>{"use strict";e.exports=require("net")},8474:e=>{"use strict";e.exports=require("node:events")},7075:e=>{"use strict";e.exports=require("node:stream")},7975:e=>{"use strict";e.exports=require("node:util")},857:e=>{"use strict";e.exports=require("os")},6928:e=>{"use strict";e.exports=require("path")},2987:e=>{"use strict";e.exports=require("perf_hooks")},3480:e=>{"use strict";e.exports=require("querystring")},2203:e=>{"use strict";e.exports=require("stream")},3774:e=>{"use strict";e.exports=require("stream/web")},3193:e=>{"use strict";e.exports=require("string_decoder")},3557:e=>{"use strict";e.exports=require("timers")},4756:e=>{"use strict";e.exports=require("tls")},7016:e=>{"use strict";e.exports=require("url")},9023:e=>{"use strict";e.exports=require("util")},8253:e=>{"use strict";e.exports=require("util/types")},8167:e=>{"use strict";e.exports=require("worker_threads")},3106:e=>{"use strict";e.exports=require("zlib")},1582:(e,A,t)=>{"use strict";const r=t(7075).Writable;const s=t(7975).inherits;const o=t(7784);const n=t(1316);const i=t(143);const a=45;const c=Buffer.from("-");const g=Buffer.from("\r\n");const EMPTY_FN=function(){};function Dicer(e){if(!(this instanceof Dicer)){return new Dicer(e)}r.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 A=this;this._hparser=new i(e);this._hparser.on("header",(function(e){A._inHeader=false;A._part.emit("header",e)}))}s(Dicer,r);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 A=e._isPreamble?"Preamble":"Part";e._part.emit("error",new Error(A+" 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{r.prototype.emit.apply(this,arguments)}};Dicer.prototype._write=function(e,A,t){if(!this._hparser&&!this._bparser){return t()}if(this._headerFirst&&this._isPreamble){if(!this._part){this._part=new n(this._partOpts);if(this.listenerCount("preamble")!==0){this.emit("preamble",this._part)}else{this._ignore()}}const A=this._hparser.push(e);if(!this._inHeader&&A!==undefined&&A{"use strict";const r=t(8474).EventEmitter;const s=t(7975).inherits;const o=t(7433);const n=t(7784);const i=Buffer.from("\r\n\r\n");const a=/\r\n/g;const c=/^([^:]+):[ \t]?([\x00-\xFF]+)?$/;function HeaderParser(e){r.call(this);e=e||{};const A=this;this.nread=0;this.maxed=false;this.npairs=0;this.maxHeaderPairs=o(e,"maxHeaderPairs",2e3);this.maxHeaderSize=o(e,"maxHeaderSize",80*1024);this.buffer="";this.header={};this.finished=false;this.ss=new n(i);this.ss.on("info",(function(e,t,r,s){if(t&&!A.maxed){if(A.nread+s-r>=A.maxHeaderSize){s=A.maxHeaderSize-A.nread+r;A.nread=A.maxHeaderSize;A.maxed=true}else{A.nread+=s-r}A.buffer+=t.toString("binary",r,s)}if(e){A._finish()}}))}s(HeaderParser,r);HeaderParser.prototype.push=function(e){const A=this.ss.push(e);if(this.finished){return A}};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 A=e.length;let t,r;for(var s=0;s{"use strict";const r=t(7975).inherits;const s=t(7075).Readable;function PartStream(e){s.call(this,e)}r(PartStream,s);PartStream.prototype._read=function(e){};e.exports=PartStream},7784:(e,A,t)=>{"use strict";const r=t(8474).EventEmitter;const s=t(7975).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 A=e.length;if(A===0){throw new Error("The needle cannot be an empty String/Buffer.")}if(A>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(A);this._lookbehind_size=0;this._needle=e;this._bufpos=0;this._lookbehind=Buffer.alloc(A);for(var t=0;t=0){this.emit("info",false,this._lookbehind,0,this._lookbehind_size);this._lookbehind_size=0}else{const t=this._lookbehind_size+o;if(t>0){this.emit("info",false,this._lookbehind,0,t)}this._lookbehind.copy(this._lookbehind,0,t,this._lookbehind_size-t);this._lookbehind_size-=t;e.copy(this._lookbehind,this._lookbehind_size);this._lookbehind_size+=A;this._bufpos=A;return A}}o+=(o>=0)*this._bufpos;if(e.indexOf(t,o)!==-1){o=e.indexOf(t,o);++this.matches;if(o>0){this.emit("info",true,e,this._bufpos,o)}else{this.emit("info",true)}return this._bufpos=o+r}else{o=A-r}while(o0){this.emit("info",false,e,this._bufpos,o{"use strict";const r=t(7075).Writable;const{inherits:s}=t(7975);const o=t(1582);const n=t(8872);const i=t(8055);const a=t(6065);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:A,...t}=e;this.opts={autoDestroy:false,...t};r.call(this,this.opts);this._done=false;this._parser=this.getParserByHeaders(A);this._finished=false}s(Busboy,r);Busboy.prototype.emit=function(e){if(e==="finish"){if(!this._done){this._parser?.end();return}else if(this._finished){return}this._finished=true}r.prototype.emit.apply(this,arguments)};Busboy.prototype.getParserByHeaders=function(e){const A=a(e["content-type"]);const t={defCharset:this.opts.defCharset,fileHwm:this.opts.fileHwm,headers:e,highWaterMark:this.opts.highWaterMark,isPartAFile:this.opts.isPartAFile,limits:this.opts.limits,parsedConType:A,preservePath:this.opts.preservePath};if(n.detect.test(A[0])){return new n(this,t)}if(i.detect.test(A[0])){return new i(this,t)}throw new Error("Unsupported Content-Type.")};Busboy.prototype._write=function(e,A,t){this._parser.write(e,t)};e.exports=Busboy;e.exports["default"]=Busboy;e.exports.Busboy=Busboy;e.exports.Dicer=o},8872:(e,A,t)=>{"use strict";const{Readable:r}=t(7075);const{inherits:s}=t(7975);const o=t(1582);const n=t(6065);const i=t(6187);const a=t(8404);const c=t(7433);const g=/^boundary$/i;const E=/^form-data$/i;const l=/^charset$/i;const u=/^filename$/i;const Q=/^name$/i;Multipart.detect=/^multipart\/form-data/i;function Multipart(e,A){let t;let r;const s=this;let C;const h=A.limits;const B=A.isPartAFile||((e,A,t)=>A==="application/octet-stream"||t!==undefined);const I=A.parsedConType||[];const d=A.defCharset||"utf8";const p=A.preservePath;const m={highWaterMark:A.fileHwm};for(t=0,r=I.length;tD){s.parser.removeListener("part",onPart);s.parser.on("part",skipPart);e.hitPartsLimit=true;e.emit("partsLimit");return skipPart(A)}if(L){const e=L;e.emit("end");e.removeAllListeners("end")}A.on("header",(function(o){let c;let g;let C;let h;let I;let D;let k=0;if(o["content-type"]){C=n(o["content-type"][0]);if(C[0]){c=C[0].toLowerCase();for(t=0,r=C.length;tw){const r=w-k+e.length;if(r>0){t.push(e.slice(0,r))}t.truncated=true;t.bytesRead=w;A.removeAllListeners("data");t.emit("limit");return}else if(!t.push(e)){s._pause=true}t.bytesRead=k};G=function(){U=undefined;t.push(null)}}else{if(T===b){if(!e.hitFieldsLimit){e.hitFieldsLimit=true;e.emit("fieldsLimit")}return skipPart(A)}++T;++N;let t="";let r=false;L=A;F=function(e){if((k+=e.length)>y){const s=y-(k-e.length);t+=e.toString("binary",0,s);r=true;A.removeAllListeners("data")}else{t+=e.toString("binary")}};G=function(){L=undefined;if(t.length){t=i(t,"binary",h)}e.emit("field",g,t,false,r,I,c);--N;checkFinished()}}A._readableState.sync=false;A.on("data",F);A.on("end",G)})).on("error",(function(e){if(U){U.emit("error",e)}}))})).on("error",(function(A){e.emit("error",A)})).on("finish",(function(){G=true;checkFinished()}))}Multipart.prototype.write=function(e,A){const t=this.parser.write(e);if(t&&!this._pause){A()}else{this._needDrain=!t;this._cb=A}};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){r.call(this,e);this.bytesRead=0;this.truncated=false}s(FileStream,r);FileStream.prototype._read=function(e){};e.exports=Multipart},8055:(e,A,t)=>{"use strict";const r=t(7064);const s=t(6187);const o=t(7433);const n=/^charset$/i;UrlEncoded.detect=/^application\/x-www-form-urlencoded/i;function UrlEncoded(e,A){const t=A.limits;const s=A.parsedConType;this.boy=e;this.fieldSizeLimit=o(t,"fieldSize",1*1024*1024);this.fieldNameSizeLimit=o(t,"fieldNameSize",100);this.fieldsLimit=o(t,"fields",Infinity);let i;for(var a=0,c=s.length;an){this._key+=this.decoder.write(e.toString("binary",n,t))}this._state="val";this._hitLimit=false;this._checkingBytes=true;this._val="";this._bytesVal=0;this._valTrunc=false;this.decoder.reset();n=t+1}else if(r!==undefined){++this._fields;let t;const o=this._keyTrunc;if(r>n){t=this._key+=this.decoder.write(e.toString("binary",n,r))}else{t=this._key}this._hitLimit=false;this._checkingBytes=true;this._key="";this._bytesKey=0;this._keyTrunc=false;this.decoder.reset();if(t.length){this.boy.emit("field",s(t,"binary",this.charset),"",o,false)}n=r+1;if(this._fields===this.fieldsLimit){return A()}}else if(this._hitLimit){if(o>n){this._key+=this.decoder.write(e.toString("binary",n,o))}n=o;if((this._bytesKey=this._key.length)===this.fieldNameSizeLimit){this._checkingBytes=false;this._keyTrunc=true}}else{if(nn){this._val+=this.decoder.write(e.toString("binary",n,r))}this.boy.emit("field",s(this._key,"binary",this.charset),s(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();n=r+1;if(this._fields===this.fieldsLimit){return A()}}else if(this._hitLimit){if(o>n){this._val+=this.decoder.write(e.toString("binary",n,o))}n=o;if(this._val===""&&this.fieldSizeLimit===0||(this._bytesVal=this._val.length)===this.fieldSizeLimit){this._checkingBytes=false;this._valTrunc=true}}else{if(n0){this.boy.emit("field",s(this._key,"binary",this.charset),"",this._keyTrunc,false)}else if(this._state==="val"){this.boy.emit("field",s(this._key,"binary",this.charset),s(this._val,"binary",this.charset),this._keyTrunc,this._valTrunc)}this.boy._done=true;this.boy.emit("finish")};e.exports=UrlEncoded},7064:e=>{"use strict";const A=/\+/g;const t=[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(A," ");let r="";let s=0;let o=0;const n=e.length;for(;so){r+=e.substring(o,s);o=s}this.buffer="";++o}}if(o{"use strict";e.exports=function basename(e){if(typeof e!=="string"){return""}for(var A=e.length-1;A>=0;--A){switch(e.charCodeAt(A)){case 47:case 92:e=e.slice(A+1);return e===".."||e==="."?"":e}}return e===".."||e==="."?"":e}},6187:function(e){"use strict";const A=new TextDecoder("utf-8");const t=new Map([["utf-8",A],["utf8",A]]);function getDecoder(e){let A;while(true){switch(e){case"utf-8":case"utf8":return r.utf8;case"latin1":case"ascii":case"us-ascii":case"iso-8859-1":case"iso8859-1":case"iso88591":case"iso_8859-1":case"windows-1252":case"iso_8859-1:1987":case"cp1252":case"x-cp1252":return r.latin1;case"utf16le":case"utf-16le":case"ucs2":case"ucs-2":return r.utf16le;case"base64":return r.base64;default:if(A===undefined){A=true;e=e.toLowerCase();continue}return r.other.bind(e)}}}const r={utf8:(e,A)=>{if(e.length===0){return""}if(typeof e==="string"){e=Buffer.from(e,A)}return e.utf8Slice(0,e.length)},latin1:(e,A)=>{if(e.length===0){return""}if(typeof e==="string"){return e}return e.latin1Slice(0,e.length)},utf16le:(e,A)=>{if(e.length===0){return""}if(typeof e==="string"){e=Buffer.from(e,A)}return e.ucs2Slice(0,e.length)},base64:(e,A)=>{if(e.length===0){return""}if(typeof e==="string"){e=Buffer.from(e,A)}return e.base64Slice(0,e.length)},other:(e,A)=>{if(e.length===0){return""}if(typeof e==="string"){e=Buffer.from(e,A)}if(t.has(this.toString())){try{return t.get(this).decode(e)}catch{}}return typeof e==="string"?e:e.toString()}};function decodeText(e,A,t){if(e){return getDecoder(t)(e,A)}return e}e.exports=decodeText},7433:e=>{"use strict";e.exports=function getLimit(e,A,t){if(!e||e[A]===undefined||e[A]===null){return t}if(typeof e[A]!=="number"||isNaN(e[A])){throw new TypeError("Limit "+A+" is not a valid number")}return e[A]}},6065:(e,A,t)=>{"use strict";const r=t(6187);const s=/%[a-fA-F0-9][a-fA-F0-9]/g;const o={"%00":"\0","%01":"","%02":"","%03":"","%04":"","%05":"","%06":"","%07":"","%08":"\b","%09":"\t","%0a":"\n","%0A":"\n","%0b":"\v","%0B":"\v","%0c":"\f","%0C":"\f","%0d":"\r","%0D":"\r","%0e":"","%0E":"","%0f":"","%0F":"","%10":"","%11":"","%12":"","%13":"","%14":"","%15":"","%16":"","%17":"","%18":"","%19":"","%1a":"","%1A":"","%1b":"","%1B":"","%1c":"","%1C":"","%1d":"","%1D":"","%1e":"","%1E":"","%1f":"","%1F":"","%20":" ","%21":"!","%22":'"',"%23":"#","%24":"$","%25":"%","%26":"&","%27":"'","%28":"(","%29":")","%2a":"*","%2A":"*","%2b":"+","%2B":"+","%2c":",","%2C":",","%2d":"-","%2D":"-","%2e":".","%2E":".","%2f":"/","%2F":"/","%30":"0","%31":"1","%32":"2","%33":"3","%34":"4","%35":"5","%36":"6","%37":"7","%38":"8","%39":"9","%3a":":","%3A":":","%3b":";","%3B":";","%3c":"<","%3C":"<","%3d":"=","%3D":"=","%3e":">","%3E":">","%3f":"?","%3F":"?","%40":"@","%41":"A","%42":"B","%43":"C","%44":"D","%45":"E","%46":"F","%47":"G","%48":"H","%49":"I","%4a":"J","%4A":"J","%4b":"K","%4B":"K","%4c":"L","%4C":"L","%4d":"M","%4D":"M","%4e":"N","%4E":"N","%4f":"O","%4F":"O","%50":"P","%51":"Q","%52":"R","%53":"S","%54":"T","%55":"U","%56":"V","%57":"W","%58":"X","%59":"Y","%5a":"Z","%5A":"Z","%5b":"[","%5B":"[","%5c":"\\","%5C":"\\","%5d":"]","%5D":"]","%5e":"^","%5E":"^","%5f":"_","%5F":"_","%60":"`","%61":"a","%62":"b","%63":"c","%64":"d","%65":"e","%66":"f","%67":"g","%68":"h","%69":"i","%6a":"j","%6A":"j","%6b":"k","%6B":"k","%6c":"l","%6C":"l","%6d":"m","%6D":"m","%6e":"n","%6E":"n","%6f":"o","%6F":"o","%70":"p","%71":"q","%72":"r","%73":"s","%74":"t","%75":"u","%76":"v","%77":"w","%78":"x","%79":"y","%7a":"z","%7A":"z","%7b":"{","%7B":"{","%7c":"|","%7C":"|","%7d":"}","%7D":"}","%7e":"~","%7E":"~","%7f":"","%7F":"","%80":"€","%81":"","%82":"‚","%83":"ƒ","%84":"„","%85":"…","%86":"†","%87":"‡","%88":"ˆ","%89":"‰","%8a":"Š","%8A":"Š","%8b":"‹","%8B":"‹","%8c":"Œ","%8C":"Œ","%8d":"","%8D":"","%8e":"Ž","%8E":"Ž","%8f":"","%8F":"","%90":"","%91":"‘","%92":"’","%93":"“","%94":"”","%95":"•","%96":"–","%97":"—","%98":"˜","%99":"™","%9a":"š","%9A":"š","%9b":"›","%9B":"›","%9c":"œ","%9C":"œ","%9d":"","%9D":"","%9e":"ž","%9E":"ž","%9f":"Ÿ","%9F":"Ÿ","%a0":" ","%A0":" ","%a1":"¡","%A1":"¡","%a2":"¢","%A2":"¢","%a3":"£","%A3":"£","%a4":"¤","%A4":"¤","%a5":"¥","%A5":"¥","%a6":"¦","%A6":"¦","%a7":"§","%A7":"§","%a8":"¨","%A8":"¨","%a9":"©","%A9":"©","%aa":"ª","%Aa":"ª","%aA":"ª","%AA":"ª","%ab":"«","%Ab":"«","%aB":"«","%AB":"«","%ac":"¬","%Ac":"¬","%aC":"¬","%AC":"¬","%ad":"­","%Ad":"­","%aD":"­","%AD":"­","%ae":"®","%Ae":"®","%aE":"®","%AE":"®","%af":"¯","%Af":"¯","%aF":"¯","%AF":"¯","%b0":"°","%B0":"°","%b1":"±","%B1":"±","%b2":"²","%B2":"²","%b3":"³","%B3":"³","%b4":"´","%B4":"´","%b5":"µ","%B5":"µ","%b6":"¶","%B6":"¶","%b7":"·","%B7":"·","%b8":"¸","%B8":"¸","%b9":"¹","%B9":"¹","%ba":"º","%Ba":"º","%bA":"º","%BA":"º","%bb":"»","%Bb":"»","%bB":"»","%BB":"»","%bc":"¼","%Bc":"¼","%bC":"¼","%BC":"¼","%bd":"½","%Bd":"½","%bD":"½","%BD":"½","%be":"¾","%Be":"¾","%bE":"¾","%BE":"¾","%bf":"¿","%Bf":"¿","%bF":"¿","%BF":"¿","%c0":"À","%C0":"À","%c1":"Á","%C1":"Á","%c2":"Â","%C2":"Â","%c3":"Ã","%C3":"Ã","%c4":"Ä","%C4":"Ä","%c5":"Å","%C5":"Å","%c6":"Æ","%C6":"Æ","%c7":"Ç","%C7":"Ç","%c8":"È","%C8":"È","%c9":"É","%C9":"É","%ca":"Ê","%Ca":"Ê","%cA":"Ê","%CA":"Ê","%cb":"Ë","%Cb":"Ë","%cB":"Ë","%CB":"Ë","%cc":"Ì","%Cc":"Ì","%cC":"Ì","%CC":"Ì","%cd":"Í","%Cd":"Í","%cD":"Í","%CD":"Í","%ce":"Î","%Ce":"Î","%cE":"Î","%CE":"Î","%cf":"Ï","%Cf":"Ï","%cF":"Ï","%CF":"Ï","%d0":"Ð","%D0":"Ð","%d1":"Ñ","%D1":"Ñ","%d2":"Ò","%D2":"Ò","%d3":"Ó","%D3":"Ó","%d4":"Ô","%D4":"Ô","%d5":"Õ","%D5":"Õ","%d6":"Ö","%D6":"Ö","%d7":"×","%D7":"×","%d8":"Ø","%D8":"Ø","%d9":"Ù","%D9":"Ù","%da":"Ú","%Da":"Ú","%dA":"Ú","%DA":"Ú","%db":"Û","%Db":"Û","%dB":"Û","%DB":"Û","%dc":"Ü","%Dc":"Ü","%dC":"Ü","%DC":"Ü","%dd":"Ý","%Dd":"Ý","%dD":"Ý","%DD":"Ý","%de":"Þ","%De":"Þ","%dE":"Þ","%DE":"Þ","%df":"ß","%Df":"ß","%dF":"ß","%DF":"ß","%e0":"à","%E0":"à","%e1":"á","%E1":"á","%e2":"â","%E2":"â","%e3":"ã","%E3":"ã","%e4":"ä","%E4":"ä","%e5":"å","%E5":"å","%e6":"æ","%E6":"æ","%e7":"ç","%E7":"ç","%e8":"è","%E8":"è","%e9":"é","%E9":"é","%ea":"ê","%Ea":"ê","%eA":"ê","%EA":"ê","%eb":"ë","%Eb":"ë","%eB":"ë","%EB":"ë","%ec":"ì","%Ec":"ì","%eC":"ì","%EC":"ì","%ed":"í","%Ed":"í","%eD":"í","%ED":"í","%ee":"î","%Ee":"î","%eE":"î","%EE":"î","%ef":"ï","%Ef":"ï","%eF":"ï","%EF":"ï","%f0":"ð","%F0":"ð","%f1":"ñ","%F1":"ñ","%f2":"ò","%F2":"ò","%f3":"ó","%F3":"ó","%f4":"ô","%F4":"ô","%f5":"õ","%F5":"õ","%f6":"ö","%F6":"ö","%f7":"÷","%F7":"÷","%f8":"ø","%F8":"ø","%f9":"ù","%F9":"ù","%fa":"ú","%Fa":"ú","%fA":"ú","%FA":"ú","%fb":"û","%Fb":"û","%fB":"û","%FB":"û","%fc":"ü","%Fc":"ü","%fC":"ü","%FC":"ü","%fd":"ý","%Fd":"ý","%fD":"ý","%FD":"ý","%fe":"þ","%Fe":"þ","%fE":"þ","%FE":"þ","%ff":"ÿ","%Ff":"ÿ","%fF":"ÿ","%FF":"ÿ"};function encodedReplacer(e){return o[e]}const n=0;const i=1;const a=2;const c=3;function parseParams(e){const A=[];let t=n;let o="";let g=false;let E=false;let l=0;let u="";const Q=e.length;for(var C=0;C ({ name })), + } +} + +function linearStack(length) { + return Array.from({ length }, (_, index) => + pull(index + 1, index ? `branch-${index}` : 'canary', `branch-${index + 1}`) + ) +} + +function check(id, conclusion, status = 'completed', overrides = {}) { + return { + id, + name: 'thank you, next', + status, + conclusion, + html_url: `https://example.test/check/${id}`, + app: { slug: 'github-actions' }, + ...overrides, + } +} + +function coreMock() { + const outputs = new Map() + const failures = [] + const warnings = [] + const logs = [] + const summaries = [] + const summary = { + value: '', + addRaw(value) { + this.value += value + return this + }, + async write() { + summaries.push(this.value) + this.value = '' + }, + } + return { + outputs, + failures, + warnings, + logs, + summaries, + summary, + setOutput(name, value) { + outputs.set(name, String(value)) + }, + setFailed(message) { + failures.push(message) + }, + warning(message) { + warnings.push(message) + }, + info(message) { + logs.push(message) + }, + } +} + +function githubMock(pulls, checks, options) { + const calls = { get: 0, list: 0, checks: 0, refs: [] } + const route = () => {} + const github = { + rest: { + pulls: { + list: route, + async get({ pull_number }) { + calls.get++ + const error = options.getError?.(pull_number, calls.get) + if (error) throw error + const result = pulls.find((item) => item.number === pull_number) + if (!result) throw new Error(`Missing mock PR #${pull_number}`) + return { data: structuredClone(result) } + }, + }, + checks: { + async listForRef({ ref }) { + calls.checks++ + calls.refs.push(ref) + const value = + typeof checks === 'function' + ? checks(ref, calls.checks) + : checks[ref] + const candidates = ( + Array.isArray(value) ? value : value ? [value] : [] + ).map((item) => structuredClone(item)) + for (const candidate of candidates) { + if (!Object.hasOwn(candidate, 'head_sha')) candidate.head_sha = ref + if (!Object.hasOwn(candidate, 'pull_requests')) { + candidate.pull_requests = pulls + .filter((item) => item.head.sha === ref) + .map((item) => ({ + number: item.number, + head: { sha: item.head.sha }, + base: { ref: item.base.ref, sha: item.base.sha }, + })) + } + } + return { data: { check_runs: candidates } } + }, + }, + }, + async paginate(input, params) { + expect(input).toBe(route) + calls.list++ + let result = pulls.filter((item) => item.state === 'open') + if (params.base) + result = result.filter((item) => item.base.ref === params.base) + if (params.head) { + const [owner, ...parts] = params.head.split(':') + const branch = parts.join(':') + result = result.filter( + (item) => + item.head.ref === branch && + item.head.repo.full_name.startsWith(`${owner}/`) + ) + } + return structuredClone(result) + }, + } + return { github, calls } +} + +async function run({ + pulls = [], + current = 1, + checks = {}, + eventName = 'pull_request', + getError, +} = {}) { + jest.useFakeTimers({ now: new Date(0) }) + const core = coreMock() + const { github, calls } = githubMock(pulls, checks, { getError }) + const context = { + repo: { owner: 'vercel', repo: 'next.js' }, + eventName, + payload: { + repository: { full_name: 'vercel/next.js' }, + ...(eventName === 'pull_request' + ? { pull_request: { number: current } } + : {}), + }, + } + const previous = process.env.BYPASS_LABEL + process.env.BYPASS_LABEL = 'CI Bypass PR Stack Optimization' + let settled = false + try { + const result = gate({ core, github, context }).finally(() => { + settled = true + }) + for (let i = 0; i < 80 && !settled; i++) { + await jest.advanceTimersByTimeAsync(5 * 60 * 1000) + } + if (!settled) throw new Error('Gate did not finish within 80 fake polls') + await result + } finally { + jest.useRealTimers() + if (previous === undefined) delete process.env.BYPASS_LABEL + else process.env.BYPASS_LABEL = previous + } + return { core, calls } +} + +test('non-PR runs open immediately', async () => { + const { core } = await run({ eventName: 'push' }) + expect(core.outputs.get('skip')).toBe('false') + expect(core.failures).toEqual([]) + expect(core.warnings).toEqual([]) + expect(core.summaries.join('\n')).toMatch( + /PR: #n\/a.*|push runs immediately/s + ) +}) + +test('fork and bypass PRs open immediately', async () => { + const fork = await run({ + pulls: [pull(1, 'canary', 'fork-work', { repository: 'someone/fork' })], + }) + expect(fork.core.summaries.join('\n')).toContain( + 'fork PRs always run immediately' + ) + const bypass = await run({ + pulls: [ + pull(1, 'canary', 'work', { + labels: ['CI Bypass PR Stack Optimization'], + }), + ], + }) + expect(bypass.core.summaries.join('\n')).toContain('bypass label is present') +}) + +test('first three and the top/leaf PR open immediately', async () => { + const stack = linearStack(5) + for (const current of [1, 2, 3, 5]) { + const { core } = await run({ pulls: stack, current }) + expect(core.failures).toEqual([]) + } +}) + +test('older success releases even when nearer predecessor is pending', async () => { + const stack = linearStack(5) + const { core, calls } = await run({ + pulls: stack, + current: 4, + checks: { + 'head-3': check(3, null, 'in_progress'), + 'head-2': check(2, 'success'), + 'head-1': check(1, 'failure'), + }, + }) + expect(core.failures).toEqual([]) + expect(calls.refs).toEqual(['head-3', 'head-2', 'head-1', 'head-2']) + expect(core.summaries.join('\n')).toContain('PR #2 passed thank you, next') +}) + +test('a real current-head and current-base check association releases', async () => { + // Captured from #99095's passing required check; only branch-chain wiring + // uses synthetic PRs. This guards the live REST response shape. + const stack = linearStack(5) + stack[0].head.ref = realCheck.pull_requests[0].base.ref + stack[1].number = realCheck.pull_requests[0].number + stack[1].base.ref = realCheck.pull_requests[0].base.ref + stack[1].base.sha = realCheck.pull_requests[0].base.sha + stack[1].head.sha = realCheck.head_sha + const result = await run({ + pulls: stack, + current: 4, + checks: { [realCheck.head_sha]: realCheck }, + }) + expect(result.core.failures).toEqual([]) + expect(result.core.summaries.join('\n')).toContain('PR #99095 passed') +}) + +test('three terminal failures fail the gate without running expensive jobs', async () => { + const { core } = await run({ + pulls: linearStack(5), + current: 4, + checks: { + 'head-3': check(3, 'failure'), + 'head-2': check(2, 'cancelled'), + 'head-1': check(1, 'skipped'), + }, + }) + expect(core.failures).toHaveLength(1) + expect(core.failures[0]).toMatch(/All three predecessor PRs/) + expect(core.summaries.join('\n')).toMatch(/Result: \*\*failed\*\*/) +}) + +test('an old-base success cannot release the gate', async () => { + const stack = linearStack(5) + const staleSuccess = check(200, 'success', 'completed', { + pull_requests: [ + { + number: 3, + head: { sha: 'head-3' }, + base: { ref: 'branch-2', sha: 'old-base' }, + }, + ], + }) + const { core } = await run({ + pulls: stack, + current: 4, + checks: { + 'head-3': [staleSuccess, check(100, 'failure')], + 'head-2': check(2, 'cancelled'), + 'head-1': check(1, 'failure'), + }, + }) + expect(core.failures).toHaveLength(1) +}) + +test.each([ + { head_sha: 'old-head-sha' }, + { + pull_requests: [ + { + number: 99, + head: { sha: 'head-3' }, + base: { ref: 'branch-2', sha: 'base-branch-2' }, + }, + ], + }, + { pull_requests: [] }, + { app: { slug: 'untrusted-app' } }, +])('unrelated or stale check does not release (%j)', async (overrides) => { + const { core } = await run({ + pulls: linearStack(5), + current: 4, + checks: { 'head-3': check(3, 'success', 'completed', overrides) }, + }) + expect(core.failures).toEqual([]) + expect(core.summaries.join('\n')).toContain( + 'Five-hour waiting deadline reached' + ) +}) + +test('a later successful predecessor releases within a five-minute poll', async () => { + const calls = new Map() + const checks = (ref) => { + const count = calls.get(ref) ?? 0 + calls.set(ref, count + 1) + if (ref === 'head-3' && count > 0) return check(30, 'success') + if (ref === 'head-2') return check(20, 'failure') + return null + } + const result = await run({ pulls: linearStack(5), current: 4, checks }) + expect(result.core.failures).toEqual([]) + expect(result.core.summaries.join('\n')).toContain( + 'PR #3 passed thank you, next' + ) +}) + +test('a failed predecessor may succeed on a later rerun', async () => { + let reads = 0 + const result = await run({ + pulls: linearStack(5), + current: 4, + checks: (ref) => { + if (ref === 'head-1') + return ++reads > 1 ? check(100, 'success') : check(1, 'failure') + if (ref === 'head-2') return check(2, 'failure') + return null + }, + }) + expect(result.core.summaries.join('\n')).toContain( + 'PR #1 passed thank you, next' + ) +}) + +test('steady pending polls save three REST requests without losing topology refresh', async () => { + const snapshots = [] + const stack = linearStack(5) + const result = await run({ + pulls: stack, + current: 4, + checks: (ref, nth) => { + if (nth % 3 === 0) snapshots.push(nth) + // After two full waiting polls, release on the third poll. + return nth > 6 && ref === 'head-2' ? check(2, 'success') : null + }, + }) + expect(snapshots).toEqual([3, 6, 9]) + // Three initial/fresh topology polls: 1 PR GET + 4 lists + 3 checks each. + // A decisive poll intentionally adds extra validation calls. + expect(result.calls.get).toBe(5) + expect(result.calls.list).toBe(12) + expect(result.calls.checks).toBe(10) + expect(result.core.failures).toEqual([]) +}) + +test('closing a successor makes a waiting middle PR a leaf', async () => { + const stack = linearStack(5) + const result = await run({ + pulls: stack, + current: 4, + checks: (ref, nth) => { + if (nth === 3) stack[4].state = 'closed' + return null + }, + }) + expect(result.core.summaries.join('\n')).toContain( + 'no open PR is based on this head branch' + ) +}) + +test('closing or changing a predecessor rebuilds the chain on the next poll', async () => { + const stack = linearStack(5) + const result = await run({ + pulls: stack, + current: 4, + checks: (ref, nth) => { + if (nth === 3) stack[1].state = 'closed' + return null + }, + }) + expect(result.core.summaries.join('\n')).toContain( + 'only 1 open predecessor PR(s) are reachable' + ) +}) + +test('a retargeted current PR opens on the next poll', async () => { + const stack = linearStack(5) + const result = await run({ + pulls: stack, + current: 4, + checks: (ref, nth) => { + if (nth === 3) stack[3].base.ref = 'canary' + return null + }, + }) + expect(result.core.summaries.join('\n')).toContain( + 'only 0 open predecessor PR(s) are reachable' + ) +}) + +test('an outdated head/base cannot pass decisive revalidation', async () => { + const stack = linearStack(5) + const result = await run({ + pulls: stack, + current: 4, + checks: (ref, nth) => { + if (nth === 2) stack[1].base.sha = 'changed-base' + return ref === 'head-2' + ? check(2, 'success', 'completed', { + pull_requests: [ + { + number: 2, + head: { sha: 'head-2' }, + base: { ref: 'branch-1', sha: 'base-branch-1' }, + }, + ], + }) + : null + }, + }) + expect(result.core.summaries.join('\n')).toContain( + 'Five-hour waiting deadline reached' + ) + expect(result.core.summaries.join('\n')).not.toContain('PR #2 passed') +}) + +test('a predecessor head update invalidates an apparent success', async () => { + const stack = linearStack(5) + const result = await run({ + pulls: stack, + current: 4, + checks: (ref, nth) => { + if (nth === 3) stack[1].head.sha = 'rebased-head' + return ref === 'head-2' ? check(2, 'success') : null + }, + }) + expect(result.core.logs.join('\n')).toContain('changed during verification') + expect(result.calls.refs).toContain('rebased-head') + expect(result.core.summaries.join('\n')).toContain( + 'Five-hour waiting deadline reached' + ) +}) + +test('a failure rerun during decisive revalidation prevents false failure', async () => { + const result = await run({ + pulls: linearStack(5), + current: 4, + checks: (ref, nth) => + ref === 'head-3' && nth >= 4 + ? check(30, 'success') + : check(nth, 'failure'), + }) + expect(result.core.failures).toEqual([]) + expect(result.core.logs.join('\n')).toContain('changed during verification') + expect(result.core.summaries.join('\n')).toContain('PR #3 passed') +}) + +test('five-hour unresolved wait fails open and transient API errors retry', async () => { + const pending = await run({ pulls: linearStack(5), current: 4 }) + expect(pending.core.summaries.join('\n')).toContain( + 'Five-hour waiting deadline reached' + ) + let count = 0 + const temporary = Object.assign(new Error('temporarily unavailable'), { + status: 500, + }) + const recovered = await run({ + pulls: linearStack(5), + current: 4, + checks: { 'head-2': check(2, 'success') }, + getError: () => (count++ === 0 ? temporary : null), + }) + expect(recovered.core.warnings.join('\n')).toContain( + 'Transient GitHub API error' + ) + expect(recovered.core.summaries.join('\n')).toContain('PR #2 passed') +}) + +test('persistent 429 releases only at the five-hour deadline', async () => { + const error = Object.assign(new Error('rate limited'), { status: 429 }) + const result = await run({ + pulls: linearStack(5), + current: 4, + getError: () => error, + }) + expect(result.core.failures).toEqual([]) + expect(result.core.summaries.join('\n')).toContain( + 'Five-hour transient API error deadline reached' + ) +}) + +test('403 and ambiguous chain fail open', async () => { + const unauthorized = await run({ + pulls: linearStack(5), + current: 4, + getError: () => Object.assign(new Error('not authorized'), { status: 403 }), + }) + expect(unauthorized.core.warnings.join('\n')).toContain('failing open') + const stack = linearStack(5) + stack.push(pull(30, 'other', 'branch-3')) + const ambiguous = await run({ pulls: stack, current: 4 }) + expect(ambiguous.core.summaries.join('\n')).toContain('multiple open PRs') +}) + +test('workflow keeps expensive work gated, forks isolated and action pinned to the CI SHA', () => { + const workflow = fs.readFileSync( + path.join(root, '.github/workflows/pr_stack_optimizer.yml'), + 'utf8' + ) + const build = fs.readFileSync( + path.join(root, '.github/workflows/build_and_test.yml'), + 'utf8' + ) + const action = fs.readFileSync(path.join(__dirname, 'action.yml'), 'utf8') + expect(workflow).toContain('runs-on: ubuntu-latest') + expect(workflow).toContain('timeout-minutes: 360') + expect(workflow).toContain('contents: read') + expect(workflow).not.toContain('secrets:') + expect(workflow).toContain('head.repo.full_name != github.repository') + expect( + workflow.match(/head.repo.full_name == github.repository/g) + ).toHaveLength(2) + expect(workflow).toContain('ref: ${{ github.sha }}') + expect(workflow).toContain('persist-credentials: false') + expect(workflow).toContain('.github/actions/pr-stack-ci-gate/action.yml') + expect(workflow).toContain('.github/actions/pr-stack-ci-gate/dist/index.js') + expect(workflow).toContain('uses: ./.github/actions/pr-stack-ci-gate') + expect(action).toContain("using: 'node24'") + expect(action).toContain("main: 'dist/index.js'") + expect(build).toMatch( + /optimize-ci:\n permissions:\n checks: read\n contents: read\n pull-requests: read/ + ) + for (const job of ['changes', 'build-next', 'validate-docs-links']) { + const block = build.match( + new RegExp( + `^ ${job}:\\n([\\s\\S]*?)(?=^ [a-zA-Z0-9_-]+:|(?![\\s\\S]))`, + 'm' + ) + ) + expect(block).not.toBeNull() + expect(block[0]).toContain("needs: ['optimize-ci']") + } + const lint = build.match( + /^ lint:\n([\s\S]*?)(?=^ validate-docs-links:)/m + )?.[1] + expect(lint).toBeDefined() + const commands = [ + 'pnpm lint-no-typescript', + 'pnpm check-examples', + 'pnpm validate-externals-doc', + 'pnpm generate-browser-variant-aliases', + 'pnpm --dir .github/actions/pr-stack-ci-gate install', + 'pnpm --dir .github/actions/pr-stack-ci-gate types', + 'pnpm --dir .github/actions/pr-stack-ci-gate build', + 'pnpm --dir .github/actions/pr-stack-ci-gate test', + 'git diff --exit-code', + ] + let previous = -1 + for (const command of commands) { + const position = lint.indexOf(` ${command}`) + expect(position).toBeGreaterThan(previous) + previous = position + } + expect(build).not.toContain('node --test .github/actions/pr-stack-ci-gate') + expect(build).toContain("needs: ['optimize-ci', 'changes', 'build-next'") +}) diff --git a/.github/actions/pr-stack-ci-gate/jest-typescript-transform.cjs b/.github/actions/pr-stack-ci-gate/jest-typescript-transform.cjs new file mode 100644 index 000000000000..81504ed6272c --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/jest-typescript-transform.cjs @@ -0,0 +1,18 @@ +const ts = require('typescript') + +// Jest runs the TypeScript action source without emitting temporary JS into +// the repository checkout (where repo-wide lint would discover it). +module.exports = { + process(source, filename) { + return { + code: ts.transpileModule(source, { + fileName: filename, + compilerOptions: { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2024, + esModuleInterop: true, + }, + }).outputText, + } + }, +} diff --git a/.github/actions/pr-stack-ci-gate/jest.config.cjs b/.github/actions/pr-stack-ci-gate/jest.config.cjs new file mode 100644 index 000000000000..03c90dea0199 --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/jest.config.cjs @@ -0,0 +1,7 @@ +module.exports = { + rootDir: __dirname, + testEnvironment: 'node', + testMatch: ['/**/*.test.js'], + transform: { '^.+\\.ts$': '/jest-typescript-transform.cjs' }, + clearMocks: true, +} diff --git a/.github/actions/pr-stack-ci-gate/package.json b/.github/actions/pr-stack-ci-gate/package.json new file mode 100644 index 000000000000..3f04737198ba --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/package.json @@ -0,0 +1,17 @@ +{ + "private": true, + "description": "Read-only CI gate for branch-based pull request stacks", + "scripts": { + "build": "ncc build src/index.ts -m -o dist --license licenses.txt", + "types": "tsc --noEmit", + "test": "../../../node_modules/.bin/jest --config jest.config.cjs --runInBand" + }, + "dependencies": { + "@actions/core": "1.11.1", + "@actions/github": "6.0.0" + }, + "devDependencies": { + "@vercel/ncc": "0.38.4", + "typescript": "6.0.2" + } +} diff --git a/.github/actions/pr-stack-ci-gate/src/gate.ts b/.github/actions/pr-stack-ci-gate/src/gate.ts new file mode 100644 index 000000000000..74e6d5c414e1 --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/src/gate.ts @@ -0,0 +1,472 @@ +import type * as coreModule from '@actions/core' +import type { context as githubContext, getOctokit } from '@actions/github' + +type Github = ReturnType +type Context = typeof githubContext +type Core = Pick< + typeof coreModule, + 'setOutput' | 'setFailed' | 'warning' | 'info' | 'summary' +> +type Pull = { + number: number + state: string + base: { ref: string; sha: string } + head: { ref: string; sha: string; repo: { full_name: string } | null } + labels: { name: string }[] +} +type Check = { + id: number + name: string + status: string + conclusion: string | null + html_url: string | null + head_sha: string + app: { slug: string } | null + pull_requests?: { + number: number + head?: { sha?: string } + base?: { ref?: string; sha?: string } + }[] +} +type Role = + | 'fork' + | 'bypass' + | 'top' + | 'ambiguous' + | 'first-three' + | 'middle' + | 'non-pr' + | 'error' +type CandidateState = 'success' | 'waiting' | 'unsuccessful' +type Candidate = { pull: Pull; check: Check | null; state: CandidateState } +type Topology = { + current: Pull + role: Role + reason?: string + predecessors?: Pull[] +} +type Snapshot = { + current?: Pull | null + role: Role + reason?: string + candidates?: Candidate[] +} + +const POLL_INTERVAL_MS = 5 * 60 * 1000 +const WAIT_DEADLINE_MS = 5 * 60 * 60 * 1000 +const REQUIRED_CHECK = 'thank you, next' + +function errorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error) +} + +function errorStatus(error: unknown): number | undefined { + if (!error || typeof error !== 'object') return undefined + const data = error as { status?: number; response?: { status?: number } } + return data.status ?? data.response?.status +} + +function isTransientApiError(error: unknown): boolean { + const status = errorStatus(error) + return ( + status === 429 || (status !== undefined && status >= 500 && status < 600) + ) +} + +function escapeCell(value: unknown): string { + return String(value ?? '') + .replaceAll('|', '\\|') + .replaceAll('\n', ' ') +} + +function checkState(check: Check | null): CandidateState { + if (!check || check.status !== 'completed') return 'waiting' + return check.conclusion === 'success' ? 'success' : 'unsuccessful' +} + +// Any nearby green CI can release this PR despite a flaky predecessor, but +// all three must have finished unsuccessfully before the gate can fail. +function gateDecision(candidates: Candidate[]): 'open' | 'fail' | 'wait' { + if (candidates.some((candidate) => candidate.state === 'success')) { + return 'open' + } + if ( + candidates.length === 3 && + candidates.every((candidate) => candidate.state === 'unsuccessful') + ) { + return 'fail' + } + return 'wait' +} + +function sameRepository(pull: Pull, repository: string): boolean { + return pull.head?.repo?.full_name === repository +} + +function sameRevision(before: Pull, after: Pull): boolean { + return ( + after.state === 'open' && + before.head.sha === after.head.sha && + before.base.ref === after.base.ref && + before.base.sha === after.base.sha + ) +} + +export async function runGate({ + github, + context, + core, +}: { + github: Github + context: Context + core: Core +}): Promise { + // A failed gate stops expensive dependents via its job conclusion; all + // successful gate outcomes retain the reusable workflow's skip=false API. + core.setOutput('skip', 'false') + + const startedAt = Date.now() + let lastFingerprint = '' + const { owner, repo } = context.repo + const repository = context.payload.repository?.full_name ?? '' + + async function listOpenPulls( + parameters: Record + ): Promise { + const pulls = await github.paginate(github.rest.pulls.list, { + owner, + repo, + state: 'open', + per_page: 100, + ...parameters, + }) + return pulls as unknown as Pull[] + } + + async function getPull(number: number): Promise { + const { data } = await github.rest.pulls.get({ + owner, + repo, + pull_number: number, + }) + return data as unknown as Pull + } + + async function findPredecessor(pull: Pull): Promise<{ + ambiguous: boolean + pull?: Pull + }> { + const matches = ( + await listOpenPulls({ head: `${owner}:${pull.base.ref}` }) + ).filter( + (candidate) => + candidate.number !== pull.number && + candidate.head.ref === pull.base.ref && + sameRepository(candidate, repository) + ) + return { ambiguous: matches.length > 1, pull: matches[0] } + } + + async function findSuccessors(pull: Pull): Promise { + return (await listOpenPulls({ base: pull.head.ref })).filter( + (candidate) => + candidate.number !== pull.number && + candidate.base.ref === pull.head.ref && + sameRepository(candidate, repository) + ) + } + + // Base/head branch links also describe stacks not registered in GitHub's + // stack API. A leaf runs immediately so the top PR is never held by polling. + async function discoverTopology(): Promise { + const prNumber = context.payload.pull_request?.number + if (!prNumber) throw new Error('Missing pull request number') + const current = await getPull(prNumber) + + if (!sameRepository(current, repository)) { + return { + current, + role: 'fork', + reason: 'fork PRs always run immediately', + } + } + if ( + current.labels.some((label) => label.name === process.env.BYPASS_LABEL) + ) { + return { current, role: 'bypass', reason: 'bypass label is present' } + } + + if ((await findSuccessors(current)).length === 0) { + return { + current, + role: 'top', + reason: 'no open PR is based on this head branch', + } + } + + const predecessors: Pull[] = [] + const seen = new Set([current.number]) + let cursor = current + while (predecessors.length < 3) { + const result = await findPredecessor(cursor) + if (result.ambiguous) { + return { + current, + role: 'ambiguous', + reason: `multiple open PRs have head branch ${cursor.base.ref}`, + } + } + if (!result.pull) break + if (seen.has(result.pull.number)) { + return { + current, + role: 'ambiguous', + reason: 'cycle detected in PR base branches', + } + } + seen.add(result.pull.number) + predecessors.push(result.pull) + cursor = result.pull + } + if (predecessors.length < 3) { + return { + current, + role: 'first-three', + reason: `only ${predecessors.length} open predecessor PR(s) are reachable`, + } + } + return { current, role: 'middle', predecessors } + } + + // The required check may attach to the PR head rather than its test-merge + // SHA. Match the current PR and base too, rejecting a green check from a + // previous rebase before it can release downstream CI. + async function latestRequiredCheck(pull: Pull): Promise { + const { data } = await github.rest.checks.listForRef({ + owner, + repo, + ref: pull.head.sha, + check_name: REQUIRED_CHECK, + filter: 'latest', + per_page: 100, + }) + const check = (data.check_runs as unknown as Check[]) + .filter( + (candidate) => + candidate.name === REQUIRED_CHECK && + candidate.app?.slug === 'github-actions' && + candidate.head_sha === pull.head.sha && + candidate.pull_requests?.some( + (associated) => + associated.number === pull.number && + associated.head?.sha === pull.head.sha && + associated.base?.ref === pull.base.ref && + associated.base?.sha === pull.base.sha + ) + ) + .sort((a, b) => b.id - a.id)[0] + return { pull, check: check ?? null, state: checkState(check ?? null) } + } + + // pulls.list already includes head/base metadata. Fetching each PR again + // during *every* pending poll wastes three REST requests (11 -> 8). Before + // any decisive open/fail, validate live PR revisions and re-read their checks + // so a rebase, retarget, closure or rerun cannot reuse a stale result. + async function stillDecisive( + topology: Topology, + candidates: Candidate[], + decision: 'open' | 'fail' + ): Promise { + const freshCurrent = await getPull(topology.current.number) + if (!sameRevision(topology.current, freshCurrent)) return false + + const relevant = + decision === 'open' + ? [candidates.find((candidate) => candidate.state === 'success')!] + : candidates + for (const candidate of relevant) { + const fresh = await getPull(candidate.pull.number) + if (!sameRevision(candidate.pull, fresh)) return false + const latest = await latestRequiredCheck(fresh) + if ( + latest.state !== candidate.state || + latest.check?.id !== candidate.check?.id + ) { + return false + } + } + return true + } + + function fingerprint(snapshot: Snapshot): string { + return JSON.stringify({ + role: snapshot.role, + reason: snapshot.reason, + candidates: snapshot.candidates?.map((candidate) => ({ + number: candidate.pull.number, + head: candidate.pull.head.sha, + base: candidate.pull.base.sha, + status: candidate.check?.status, + conclusion: candidate.check?.conclusion, + })), + }) + } + + async function writeSummary( + snapshot: Snapshot, + outcome: 'open' | 'failed', + reason: string + ): Promise { + try { + const current = snapshot.current + const elapsedMinutes = Math.floor((Date.now() - startedAt) / 60000) + const lines = [ + '# PR Stack CI Gate', + '', + `- PR: #${current?.number ?? context.payload.pull_request?.number ?? 'n/a'}`, + `- Branches: \`${escapeCell(current?.base?.ref)}\` ← \`${escapeCell(current?.head?.ref)}\``, + `- Role: **${escapeCell(snapshot.role)}**`, + `- Result: **${escapeCell(outcome)}**`, + `- Reason: ${escapeCell(reason)}`, + `- Elapsed: ${elapsedMinutes} minute(s)`, + ] + if (snapshot.candidates?.length) { + lines.push( + '', + '| PR | Base ← Head | Head SHA | Base SHA | Check | State |', + '|---:|---|---|---|---|---|' + ) + for (const candidate of snapshot.candidates) { + const checkText = candidate.check + ? `[${candidate.check.status}/${candidate.check.conclusion ?? ''}](${candidate.check.html_url})` + : 'not reported' + lines.push( + `| #${candidate.pull.number} | \`${escapeCell(candidate.pull.base.ref)}\` ← \`${escapeCell(candidate.pull.head.ref)}\` | \`${escapeCell(candidate.pull.head.sha?.slice(0, 12))}\` | \`${escapeCell(candidate.pull.base.sha?.slice(0, 12))}\` | ${checkText} | ${candidate.state} |` + ) + } + } + await core.summary.addRaw(`${lines.join('\n')}\n`).write() + } catch (error) { + core.warning( + `Could not write PR Stack CI Gate summary: ${errorMessage(error)}` + ) + } + } + + try { + if (context.eventName !== 'pull_request') { + const reason = `${context.eventName} runs immediately` + await writeSummary({ role: 'non-pr', reason }, 'open', reason) + return + } + + while (true) { + let topology: Topology + const candidates: Candidate[] = [] + try { + topology = await discoverTopology() + if (topology.role === 'middle') { + // Always check all three, even if the nearest is pending: a more + // distant PR can have succeeded. A failed PR may also pass on rerun. + for (const predecessor of topology.predecessors!) { + candidates.push(await latestRequiredCheck(predecessor)) + } + const decision = gateDecision(candidates) + if ( + decision !== 'wait' && + !(await stillDecisive(topology, candidates, decision)) + ) { + core.info('Stack/check state changed during verification; retrying') + await new Promise((resolve) => + setTimeout(resolve, POLL_INTERVAL_MS) + ) + continue + } + } + } catch (error) { + // A brief GitHub outage should not start all waiting CI at once; + // retry transient errors until the same five-hour waiting deadline. + if (!isTransientApiError(error)) throw error + if (Date.now() - startedAt >= WAIT_DEADLINE_MS) { + await writeSummary( + { + current: context.payload.pull_request as unknown as Pull, + role: 'error', + reason: errorMessage(error), + }, + 'open', + 'Five-hour transient API error deadline reached; starting full CI' + ) + return + } + core.warning( + `Transient GitHub API error (${errorStatus(error)}); retrying in five minutes: ${errorMessage(error)}` + ) + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)) + continue + } + + if (topology.role !== 'middle') { + await writeSummary(topology, 'open', topology.reason ?? '') + return + } + const snapshot: Snapshot = { ...topology, candidates } + const decision = gateDecision(candidates) + const currentFingerprint = fingerprint(snapshot) + if (currentFingerprint !== lastFingerprint) { + core.info( + `PR #${topology.current.number}: ${candidates + .map((candidate) => `#${candidate.pull.number}=${candidate.state}`) + .join(', ')}` + ) + lastFingerprint = currentFingerprint + } + + if (decision === 'open') { + const successful = candidates.find( + (candidate) => candidate.state === 'success' + )! + await writeSummary( + snapshot, + 'open', + `PR #${successful.pull.number} passed ${REQUIRED_CHECK}` + ) + return + } + if (decision === 'fail') { + const reason = `All three predecessor PRs completed ${REQUIRED_CHECK} without success. Rerun this workflow after a predecessor passes, or apply the ${process.env.BYPASS_LABEL} label.` + await writeSummary(snapshot, 'failed', reason) + core.setFailed(reason) + return + } + if (Date.now() - startedAt >= WAIT_DEADLINE_MS) { + await writeSummary( + snapshot, + 'open', + 'Five-hour waiting deadline reached; failing open and starting full CI' + ) + return + } + const nextPoll = new Date(Date.now() + POLL_INTERVAL_MS) + core.info( + `No predecessor has passed yet; polling again at ${nextPoll.toISOString()}` + ) + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)) + } + } catch (error) { + // Unexpected errors must run the required full-CI graph, not turn a + // missing classification result into a mergeable skipped-check success. + core.warning( + `PR stack classification failed; failing open and starting full CI: ${String(error)}` + ) + await writeSummary( + { + current: context.payload.pull_request as unknown as Pull | undefined, + role: 'error', + reason: errorMessage(error), + }, + 'open', + `Classification/API error; failing open: ${errorMessage(error)}` + ) + } +} diff --git a/.github/actions/pr-stack-ci-gate/src/index.ts b/.github/actions/pr-stack-ci-gate/src/index.ts new file mode 100644 index 000000000000..afc51e26e2b3 --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/src/index.ts @@ -0,0 +1,20 @@ +import * as core from '@actions/core' +import { context, getOctokit } from '@actions/github' +import { runGate } from './gate' + +async function main(): Promise { + // Never accept a separately supplied token: the workflow's read-only job + // token is the only credential this action should see. + const token = process.env.GITHUB_TOKEN + if (!token) throw new Error('GITHUB_TOKEN is unavailable') + await runGate({ github: getOctokit(token), context, core }) +} + +main().catch((error: unknown) => { + // An unavailable token or unexpected entry-point error cannot make CI green: + // open the gate so the normal, required full-CI graph still runs. + core.warning( + `PR stack gate could not start; running full CI: ${String(error)}` + ) + core.setOutput('skip', 'false') +}) diff --git a/.github/actions/pr-stack-ci-gate/tsconfig.json b/.github/actions/pr-stack-ci-gate/tsconfig.json new file mode 100644 index 000000000000..36e875a83255 --- /dev/null +++ b/.github/actions/pr-stack-ci-gate/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2024", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "rootDir": "./src", + "strict": true, + "skipLibCheck": false, + "esModuleInterop": true + }, + "include": ["src/**/*.ts"] +} diff --git a/.github/pnpm-lock.yaml b/.github/pnpm-lock.yaml index f67fb7ed4a20..454e874a9797 100644 --- a/.github/pnpm-lock.yaml +++ b/.github/pnpm-lock.yaml @@ -75,6 +75,22 @@ importers: specifier: 0.38.4 version: 0.38.4 + actions/pr-stack-ci-gate: + dependencies: + '@actions/core': + specifier: 1.11.1 + version: 1.11.1 + '@actions/github': + specifier: 6.0.0 + version: 6.0.0 + devDependencies: + '@vercel/ncc': + specifier: 0.38.4 + version: 0.38.4 + typescript: + specifier: 6.0.2 + version: 6.0.2 + actions/validate-docs-links: dependencies: '@actions/core': diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 4c481f42dfe1..19607dcb49d3 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -22,11 +22,15 @@ env: jobs: optimize-ci: + permissions: + checks: read + contents: read + pull-requests: read uses: ./.github/workflows/pr_stack_optimizer.yml - secrets: inherit changes: name: Determine changes + needs: ['optimize-ci'] runs-on: ubuntu-latest permissions: contents: read @@ -157,6 +161,7 @@ jobs: build-next: name: build-next + needs: ['optimize-ci'] permissions: contents: read id-token: write @@ -251,11 +256,16 @@ jobs: # `*.browser.{ts,tsx}` sibling was added/removed without running # `pnpm generate-browser-variant-aliases` and committing the result. pnpm generate-browser-variant-aliases + pnpm --dir .github/actions/pr-stack-ci-gate install --frozen-lockfile --ignore-scripts + pnpm --dir .github/actions/pr-stack-ci-gate types + pnpm --dir .github/actions/pr-stack-ci-gate build + pnpm --dir .github/actions/pr-stack-ci-gate test git diff --exit-code stepName: 'lint' secrets: inherit validate-docs-links: + needs: ['optimize-ci'] runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/pr_stack_optimizer.yml b/.github/workflows/pr_stack_optimizer.yml index 3f0b757c10bf..2efbbc725599 100644 --- a/.github/workflows/pr_stack_optimizer.yml +++ b/.github/workflows/pr_stack_optimizer.yml @@ -1,84 +1,68 @@ -# Avoid running the full CI on mid-stack PRs: https://graphite.dev/docs/stacking-and-ci +# Avoid running the full CI concurrently on every PR in a branch-based stack. # -# This supports both Graphite stacks and GitHub's native stacked PRs. For either kind of stack, we -# only run the full CI on the very top and very bottom of the stack. -# -# We still run some high-signal low-cost jobs (e.g. lint, unit tests) on mid-stack PRs, just not -# anything slow (e.g. integration tests). -# -# Because we don't use Graphite's CI batching, full CI will still run on every PR individually -# before it merges. The goal is just to avoid wasting CI capacity when frequently rebasing large -# stacks. -# -# This can by bypassed by labeling a PR with 'CI Bypass PR Stack Optimization' (or the legacy -# 'CI Bypass Graphite Optimization' label), and manually re-running CI. +# A stack is inferred from ordinary PR branch relationships (the next PR's base +# branch is the previous PR's head branch). The first three PRs and every top +# PR run immediately. A middle PR waits until any of its previous three PRs has +# passed `thank you, next`, while a five-hour deadline fails open. -name: PR Stack Optimizer +name: PR Stack CI Gate on: workflow_call: outputs: skip: - description: "'true' if expensive CI checks should be skipped, 'false' otherwise." + description: 'Kept for compatibility; expensive CI is never skipped after the gate opens.' value: ${{ jobs.optimize-ci.outputs.skip }} - secrets: - GRAPHITE_CI_OPTIMIZER_TOKEN: - description: 'The Graphite CI optimization secret' - # secrets are not available in forks, check-skip will just fail-open with a warning - required: false + +permissions: {} + env: - # FYI, if you add this label, you must *push* to the repository again to trigger a new event. Just - # re-running in the GitHub actions UI won't work, as it will re-use the old event with the old - # labels. - HAS_BYPASS_LABEL: |- - ${{ - github.event_name == 'pull_request' && - ( - contains(github.event.pull_request.labels.*.name, 'CI Bypass PR Stack Optimization') || - contains(github.event.pull_request.labels.*.name, 'CI Bypass Graphite Optimization') - ) - }} - # GitHub's native stacked PRs expose the PR's position within the stack. We skip expensive CI on - # mid-stack PRs. Fail open if we don't have `stack.size`. - IS_MID_STACK_NATIVE: |- - ${{ - github.event_name == 'pull_request' && - github.event.pull_request.stack.size != '' && - github.event.pull_request.stack.position != 1 && - github.event.pull_request.stack.position != github.event.pull_request.stack.size - }} + BYPASS_LABEL: CI Bypass PR Stack Optimization + jobs: optimize-ci: - name: PR Stack Optimizer + name: PR Stack CI Gate runs-on: ubuntu-latest + timeout-minutes: 360 + # The build-and-test caller grants these same read scopes; reusable + # workflows cannot increase the caller's GITHUB_TOKEN permissions. + permissions: + checks: read + contents: read + pull-requests: read outputs: - # `== 'true'` comparison: If `step.check-skip` fails (`continue-on-error`), that output will - # be an empty string, which sets our `skip` output to `'false'`. - # - # We skip if either Graphite's optimizer says to skip, or the PR is a mid-stack PR in a - # GitHub native stack. The bypass label overrides both. - skip: ${{ env.HAS_BYPASS_LABEL == 'false' && steps.check-skip.outputs.skip == 'true' }} - # Note: IS_MID_STACK_NATIVE is disabled for now because it doesn't - # understand if we're not on the bottom of the stack, but all the PRs - # below us are merged. - # - # || env.IS_MID_STACK_NATIVE == 'true' + skip: ${{ steps.gate.outputs.skip || steps.fork.outputs.skip }} steps: - - name: Optimize CI - id: check-skip - # Graphite's action is designed to fail open, but still sometimes fails if GH's infra flakes - continue-on-error: true - uses: withgraphite/graphite-ci-action@402a89bc8aa6db18ae1f9c61953d001d5f9d828f # main - with: - graphite_token: ${{ secrets.GRAPHITE_CI_OPTIMIZER_TOKEN }} - - name: Debug Output + # Do not check out or execute code supplied by a fork. It cannot be part + # of a same-repository branch stack, so let its full CI run immediately. + - name: Run full CI for fork PRs + id: fork + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} run: | - echo 'github.event_name: ${{ github.event_name }}' - echo "secrets.GRAPHITE_CI_OPTIMIZER_TOKEN != '': ${GRAPHITE_CI_OPTIMIZER_TOKEN_NON_EMPTY}" - echo 'env.HAS_BYPASS_LABEL: ${{ env.HAS_BYPASS_LABEL }}' - echo 'env.IS_MID_STACK_NATIVE: ${{ env.IS_MID_STACK_NATIVE }}' - echo 'github.event.pull_request.stack.position: ${{ github.event.pull_request.stack.position }}' - echo 'github.event.pull_request.stack.size: ${{ github.event.pull_request.stack.size }}' - echo 'steps.check-skip.outputs.skip: ${{ steps.check-skip.outputs.skip }}' + set -euo pipefail + echo 'skip=false' >> "$GITHUB_OUTPUT" + { + echo '# PR Stack CI Gate' + echo '' + echo '- Role: **fork**' + echo '- Result: **open**' + echo '- Reason: fork PRs always run immediately' + } >> "$GITHUB_STEP_SUMMARY" + + # Fetch the compiled action from the exact commit this CI run tests. + - name: Check out stack gate from this CI commit + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.sha }} + sparse-checkout: | + .github/actions/pr-stack-ci-gate/action.yml + .github/actions/pr-stack-ci-gate/dist/index.js + sparse-checkout-cone-mode: false + persist-credentials: false + - name: Wait for previous stack CI + id: gate + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: ./.github/actions/pr-stack-ci-gate env: - GRAPHITE_CI_OPTIMIZER_TOKEN_NON_EMPTY: ${{ secrets.GRAPHITE_CI_OPTIMIZER_TOKEN != '' }} + GITHUB_TOKEN: ${{ github.token }} diff --git a/packages/next/src/client/components/app-router-instance.ts b/packages/next/src/client/components/app-router-instance.ts index ae8c5e3f6e72..2ea26a3f6b55 100644 --- a/packages/next/src/client/components/app-router-instance.ts +++ b/packages/next/src/client/components/app-router-instance.ts @@ -299,7 +299,7 @@ function gesturePush(href: string, options?: NavigateOptions): void { url, currentUrl, state.renderedSearch, - state.cache, + state.root, state.tree, state.nextUrl, freshnessPolicy, diff --git a/packages/next/src/client/components/app-router-state.ts b/packages/next/src/client/components/app-router-state.ts index e7321f0976bd..28f4958cb5fd 100644 --- a/packages/next/src/client/components/app-router-state.ts +++ b/packages/next/src/client/components/app-router-state.ts @@ -1,3 +1,4 @@ +import type { RootRouteTree } from './segment-cache/cache' import type { FlightRouterState, ScrollRef, @@ -12,6 +13,7 @@ import { beginLockedNavigation, type NavigationLock, type NavigationRequestAccumulation, + type RootNavigationTask, } from './render-tree' import { createHrefFromUrl } from './router-reducer/create-href-from-url' import { @@ -23,6 +25,7 @@ import { spawnStaticStageCacheWrite, writeRuntimePrefetchStreamIntoCache, type FulfilledRouteCacheEntry, + createRootRouteTree, } from './segment-cache/cache' import { discoverKnownRoute } from './segment-cache/optimistic-routes' import { @@ -60,7 +63,7 @@ export function navigate( url: URL, currentUrl: URL, currentRenderedSearch: string, - currentCacheNode: CacheNode | null, + currentRoot: RootRouteTree, currentFlightRouterState: FlightRouterState, nextUrl: string | null, freshnessPolicy: FreshnessPolicy, @@ -89,7 +92,7 @@ export function navigate( url, currentUrl, currentRenderedSearch, - currentCacheNode, + currentRoot, currentFlightRouterState, nextUrl, freshnessPolicy, @@ -105,7 +108,7 @@ export function navigate( url, currentUrl, currentRenderedSearch, - currentCacheNode, + currentRoot, currentFlightRouterState, nextUrl, freshnessPolicy, @@ -122,7 +125,7 @@ function navigateImpl( url: URL, currentUrl: URL, currentRenderedSearch: string, - currentCacheNode: CacheNode | null, + currentRoot: RootRouteTree, currentFlightRouterState: FlightRouterState, nextUrl: string | null, freshnessPolicy: FreshnessPolicy, @@ -147,8 +150,7 @@ function navigateImpl( currentUrl, currentRenderedSearch, nextUrl, - currentCacheNode, - currentFlightRouterState, + currentRoot, freshnessPolicy, scrollBehavior, navigateType, @@ -185,8 +187,7 @@ function navigateImpl( currentUrl, currentRenderedSearch, nextUrl, - currentCacheNode, - currentFlightRouterState, + currentRoot, freshnessPolicy, scrollBehavior, navigateType, @@ -210,7 +211,7 @@ function navigateImpl( currentUrl, currentRenderedSearch, nextUrl, - currentCacheNode, + currentRoot, currentFlightRouterState, freshnessPolicy, scrollBehavior, @@ -231,8 +232,7 @@ export function navigateToKnownRoute( navigationSeed: NavigationSeed, currentUrl: URL, currentRenderedSearch: string, - currentCacheNode: CacheNode | null, - currentFlightRouterState: FlightRouterState, + currentRoot: RootRouteTree, freshnessPolicy: FreshnessPolicy, nextUrl: string | null, scrollBehavior: ScrollBehavior, @@ -279,7 +279,7 @@ export function navigateToKnownRoute( if ( link !== null && link.fetchStrategy === FetchStrategy.Full && - (navigationSeed.routeTree.prefetchHints & + (navigationSeed.root.tree.prefetchHints & (PrefetchHint.SubtreeHasPartialPrefetching | PrefetchHint.SubtreeHasInstantFalse)) === 0 @@ -313,7 +313,7 @@ export function navigateToKnownRoute( require('./segment-cache/navigation-testing-lock') as typeof import('./segment-cache/navigation-testing-lock') const link = getLinkForCurrentNavigation() restrictToShell = shouldRestrictNavigationToShell( - navigationSeed.routeTree.prefetchHints, + navigationSeed.root.tree.prefetchHints, link !== null ? link.fetchStrategy : FetchStrategy.PPR ) } @@ -341,26 +341,23 @@ export function navigateToKnownRoute( // data. If the page segment is fully static and prefetched, the request is // skipped. (This is also how refresh() works.) const isSamePageNavigation = url.href === currentUrl.href - const task = startPPRNavigation( + const navigation = startPPRNavigation( now, currentUrl, currentRenderedSearch, - currentCacheNode, - currentFlightRouterState, - navigationSeed.routeTree, - navigationSeed.metadataVaryPath, + currentRoot, + navigationSeed.root, freshnessPolicy, - navigationSeed.head, navigationSeed.dynamicStaleAt, isSamePageNavigation, accumulation, map, restrictToShell ) - if (task !== null) { + if (navigation !== null) { if (freshnessPolicy !== FreshnessPolicy.Gesture) { spawnDynamicRequests( - task, + navigation, url, nextUrl, freshnessPolicy, @@ -376,8 +373,7 @@ export function navigateToKnownRoute( state, url, nextUrl, - task.route, - task.node, + navigation, navigationSeed.renderedSearch, canonicalUrl, navigateType, @@ -397,8 +393,7 @@ function navigateUsingPrefetchedRouteTree( currentUrl: URL, currentRenderedSearch: string, nextUrl: string | null, - currentCacheNode: CacheNode | null, - currentFlightRouterState: FlightRouterState, + currentRoot: RootRouteTree, freshnessPolicy: FreshnessPolicy, scrollBehavior: ScrollBehavior, navigateType: 'push' | 'replace', @@ -406,17 +401,11 @@ function navigateUsingPrefetchedRouteTree( navigationLock: NavigationLock | null, map: CacheMap ): AppRouterState { - const routeTree = route.tree const canonicalUrl = route.canonicalUrl + url.hash const renderedSearch = route.renderedSearch const prefetchSeed: NavigationSeed = { renderedSearch, - routeTree, - metadataVaryPath: route.metadata.varyPath as any, - head: null, - isHeadPartial: true, - headVaryParams: null, - headStaleTimeSeconds: null, + root: route.root, dynamicStaleAt: computeDynamicStaleAt(now, UnknownDynamicStaleTime), // Not derived from a server response; no base to diverge from. treeDivergedFromBase: false, @@ -429,8 +418,7 @@ function navigateUsingPrefetchedRouteTree( prefetchSeed, currentUrl, currentRenderedSearch, - currentCacheNode, - currentFlightRouterState, + currentRoot, freshnessPolicy, nextUrl, scrollBehavior, @@ -463,7 +451,7 @@ async function navigateToUnknownRoute( currentUrl: URL, currentRenderedSearch: string, nextUrl: string | null, - currentCacheNode: CacheNode | null, + currentRoot: RootRouteTree, currentFlightRouterState: FlightRouterState, freshnessPolicy: FreshnessPolicy, scrollBehavior: ScrollBehavior, @@ -479,7 +467,7 @@ async function navigateToUnknownRoute( // // To avoid duplication of logic, we're going to pretend that the tree // returned by the dynamic request is, in fact, a prefetch tree. Then we can - // use the same server response to write the actual data into the CacheNode + // use the same server response to write the actual data into the render // tree. So it's the same flow as the "happy path" (prefetch, then // navigation), except we use a single server response for both stages. @@ -543,6 +531,7 @@ async function navigateToUnknownRoute( // there's no pathname to parse them from (nor a need to). null, renderedSearch, + null, dynamicStaleTime ) @@ -551,48 +540,45 @@ async function navigateToUnknownRoute( // unknown route - any rewrite detection happens during the traversal inside // discoverKnownRoute. The hasDynamicRewrite param is only set to true when // retrying after a tree mismatch (see dispatchRetryDueToTreeMismatch). - const metadataVaryPath = navigationSeed.metadataVaryPath - if (metadataVaryPath !== null) { - discoverKnownRoute( + discoverKnownRoute( + now, + url.pathname, + url.search as NormalizedSearch, + nextUrl, + null, // No pending entry + navigationSeed.root, + couldBeIntercepted, + // Store a hashless canonical URL: the entry is shared across hashes, and + // a later same-route hash nav appends `url.hash` to it. + createHrefFromUrl(canonicalUrl, false), + navigationSeed.renderedSearch, + supportsPerSegmentPrefetching, + false // hasDynamicRewrite - not a retry, rewrite detection happens during traversal + ) + + if (staticStageResponse !== null) { + spawnStaticStageCacheWrite( now, - url.pathname, - url.search as NormalizedSearch, - nextUrl, - null, // No pending entry - navigationSeed.routeTree, - metadataVaryPath, - couldBeIntercepted, - // Store a hashless canonical URL: the entry is shared across hashes, and - // a later same-route hash nav appends `url.hash` to it. - createHrefFromUrl(canonicalUrl, false), - supportsPerSegmentPrefetching, - false // hasDynamicRewrite - not a retry, rewrite detection happens during traversal + staticStageResponse, + isResponsePartial, + responseHeaders, + currentFlightRouterState, + renderedSearch, + map ) + } - if (staticStageResponse !== null) { - spawnStaticStageCacheWrite( - now, - staticStageResponse, - isResponsePartial, - responseHeaders, - currentFlightRouterState, - renderedSearch, - map - ) - } - - if (runtimePrefetchStream !== null) { - writeRuntimePrefetchStreamIntoCache( - now, - runtimePrefetchStream, - currentFlightRouterState, - renderedSearch, - map - ).catch(() => { - // The runtime prefetch cache write failed. Not fatal — the - // navigation completed normally, we just won't cache runtime data. - }) - } + if (runtimePrefetchStream !== null) { + writeRuntimePrefetchStreamIntoCache( + now, + runtimePrefetchStream, + currentFlightRouterState, + renderedSearch, + map + ).catch(() => { + // The runtime prefetch cache write failed. Not fatal — the + // navigation completed normally, we just won't cache runtime data. + }) } // In the streaming dev render, this single response's seed content may still @@ -618,8 +604,7 @@ async function navigateToUnknownRoute( navigationSeed, currentUrl, currentRenderedSearch, - currentCacheNode, - currentFlightRouterState, + currentRoot, freshnessPolicy, nextUrl, scrollBehavior, @@ -663,7 +648,7 @@ export function completeHardNavigation( // router updates without updating React. renderedSearch: state.renderedSearch, scrollRef: state.scrollRef, - cache: state.cache, + root: state.root, tree: state.tree, nextUrl: state.nextUrl, previousNextUrl: state.previousNextUrl, @@ -676,8 +661,7 @@ export function completeSoftNavigation( oldState: AppRouterState, url: URL, referringNextUrl: string | null, - tree: FlightRouterState, - cache: CacheNode, + navigation: RootNavigationTask, renderedSearch: string, canonicalUrl: string, navigateType: 'push' | 'replace', @@ -691,6 +675,7 @@ export function completeSoftNavigation( // same traversal that computes the tree itself. We should also figure out // what is the minimum information needed for the server to correctly // intercept the route. + const tree = navigation.tree.route const changedPath = computeChangedPath(oldState.tree, tree) const nextUrlForNewRoute = changedPath ? changedPath : oldState.nextUrl @@ -737,7 +722,7 @@ export function completeSoftNavigation( // // If this navigation created new scroll targets (scrollRef !== null), // neutralize them. If it didn't, any prior scroll targets carried - // forward on the cache nodes via reuseSharedCacheNode remain active. + // forward on reused cache nodes remain active. if (scrollRef !== null) { scrollRef.current = false } @@ -801,7 +786,7 @@ export function completeSoftNavigation( ? decodeURIComponent(url.hash.slice(1)) : oldState.scrollRef.hashFragment, }, - cache, + root: createRootRouteTree(navigation.tree.node, navigation.head.node), tree, nextUrl: nextUrlForNewRoute, previousNextUrl, @@ -814,8 +799,7 @@ export function completeTraverseNavigation( state: AppRouterState, url: URL, renderedSearch: string, - cache: CacheNode, - tree: FlightRouterState, + navigation: RootNavigationTask, nextUrl: string | null ) { return { @@ -829,9 +813,9 @@ export function completeTraverseNavigation( preserveCustomHistoryState: true, }, scrollRef: state.scrollRef, - cache, + root: createRootRouteTree(navigation.tree.node, navigation.head.node), // Restore provided tree - tree, + tree: navigation.tree.route, nextUrl, // TODO: We need to restore previousNextUrl, too, which represents the // Next-Url that was used to fetch the data. Anywhere we fetch using the @@ -854,7 +838,7 @@ async function ensurePrefetchThenNavigate( url: URL, currentUrl: URL, currentRenderedSearch: string, - currentCacheNode: CacheNode | null, + currentRoot: RootRouteTree, currentFlightRouterState: FlightRouterState, nextUrl: string | null, freshnessPolicy: FreshnessPolicy, @@ -877,7 +861,7 @@ async function ensurePrefetchThenNavigate( const navigationLockPrefetch = beginNavigationLockPrefetch() const prefetchTask = schedulePrefetchTask( cacheKey, - currentFlightRouterState, + currentRoot, fetchStrategy, PrefetchPriority.Default, null, // onInvalidate @@ -897,7 +881,7 @@ async function ensurePrefetchThenNavigate( url, currentUrl, currentRenderedSearch, - currentCacheNode, + currentRoot, currentFlightRouterState, nextUrl, freshnessPolicy, diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx index 00d29f3e6b7c..312dfa6af582 100644 --- a/packages/next/src/client/components/app-router.tsx +++ b/packages/next/src/client/components/app-router.tsx @@ -1,3 +1,4 @@ +import type { RouteTree } from './segment-cache/cache' import React, { useEffect, useMemo, @@ -15,6 +16,7 @@ import type { AppRouterState, } from './router-reducer/router-reducer-types' import { createHrefFromUrl } from './router-reducer/create-href-from-url' +import { createHeadKey } from './router-reducer/create-segment-key' import { SearchParamsContext, PathnameContext, @@ -26,7 +28,6 @@ import { useActionQueue } from './use-action-queue' import { setLastCommittedTree } from './router-reducer/reducers/committed-state' import { AppRouterAnnouncer } from './app-router-announcer' import { RedirectBoundary } from './redirect-boundary' -import { findHeadInCache } from './router-reducer/reducers/find-head-in-cache' import { unresolvedThenable } from './unresolved-thenable' import { removeBasePath } from '../remove-base-path' import { hasBasePath } from '../has-base-path' @@ -159,8 +160,8 @@ function HistoryUpdater({ // task. Re-prefetch all visible links with the updated values. In most // cases, this will not result in any new network requests, only if // the prefetch result actually varies on one of these inputs. - pingVisibleLinks(appRouterState.nextUrl, appRouterState.tree) - }, [appRouterState.nextUrl, appRouterState.tree]) + pingVisibleLinks(appRouterState.nextUrl, appRouterState.root) + }, [appRouterState.nextUrl, appRouterState.root]) return null } @@ -182,16 +183,15 @@ function copyNextJsInternalHistoryState(data: any) { } function Head({ - headCacheNode, + headRenderTree, }: { - headCacheNode: CacheNode | null + headRenderTree: RouteTree }): React.ReactNode { - // If this segment has a `prefetchHead`, it's the statically prefetched data. - // We should use that on initial render instead of `head`. Then we'll switch - // to `head` when the dynamic response streams in. - const head = headCacheNode !== null ? headCacheNode.head : null - const prefetchHead = - headCacheNode !== null ? headCacheNode.prefetchHead : null + // If the head has a `prefetchRsc`, it's the statically prefetched data. We + // should use that on initial render instead of `rsc`. Then we'll switch to + // `rsc` when the dynamic response streams in. + const head = headRenderTree.data.rsc + const prefetchHead = headRenderTree.data.prefetchRsc // If no prefetch data is available, then we go straight to rendering `head`. const resolvedPrefetchRsc = prefetchHead !== null ? prefetchHead : head @@ -235,7 +235,7 @@ function Router({ }, [canonicalUrl]) if (process.env.NODE_ENV !== 'production') { - const { cache, tree } = state + const { root, tree } = state // This hook is in a conditional but that is ok because `process.env.NODE_ENV` never changes // eslint-disable-next-line react-hooks/rules-of-hooks @@ -245,10 +245,10 @@ function Router({ // @ts-ignore this is for debugging window.nd = { router: publicAppRouterInstance, - cache, + root, tree, } - }, [cache, tree]) + }, [root, tree]) } useEffect(() => { @@ -436,11 +436,7 @@ function Router({ } }, []) - const { cache, tree, nextUrl, scrollRef, previousNextUrl } = state - - const matchingHead = useMemo(() => { - return findHeadInCache(cache, tree[1]) - }, [cache, tree]) + const { root, tree, nextUrl, scrollRef, previousNextUrl } = state // Add memoized pathParams for useParams. const pathParams = useMemo(() => { @@ -466,7 +462,7 @@ function Router({ const layoutRouterContext = useMemo(() => { return { parentTree: tree, - parentCacheNode: cache, + parentRenderTree: root.tree, parentSegmentPath: null, parentParams: {}, parentLoadingData: null, @@ -479,7 +475,7 @@ function Router({ // Root segment is always active isActive: true, } - }, [tree, cache, canonicalUrl]) + }, [tree, root, canonicalUrl]) const globalLayoutRouterContext = useMemo(() => { return { @@ -490,20 +486,17 @@ function Router({ } }, [tree, scrollRef, nextUrl, previousNextUrl]) - let head - if (matchingHead !== null) { - // The head is wrapped in an extra component so we can use - // `useDeferredValue` to swap between the prefetched and final versions of - // the head. (This is what LayoutRouter does for segment data, too.) - // - // The `key` is used to remount the component whenever the head moves to - // a different segment. - const [headCacheNode, headKey] = matchingHead - - head = - } else { - head = null - } + // The head is wrapped in an extra component so we can use + // `useDeferredValue` to swap between the prefetched and final versions of + // the head. (This is what LayoutRouter does for segment data, too.) + // + // The `key` is used to remount the component whenever the head moves to a + // different page, one of its path param values changes (the same inputs as + // LayoutRouter's keys), or its search params change. These are the entries + // of the head's vary path (see getHeadRequestKey). + const head = ( + + ) let content = ( @@ -511,7 +504,7 @@ function Router({ {/* RootLayoutBoundary enables detection of Suspense boundaries around the root layout. When users wrap their layout in , this creates the component stack pattern "Suspense -> RootLayoutBoundary" which dynamic-rendering.ts uses to allow dynamic rendering. */} - {cache.rsc} + {root.tree.data.rsc} ) diff --git a/packages/next/src/client/components/bfcache-state-manager.ts b/packages/next/src/client/components/bfcache-state-manager.ts index f2198d07e96c..91f675e06381 100644 --- a/packages/next/src/client/components/bfcache-state-manager.ts +++ b/packages/next/src/client/components/bfcache-state-manager.ts @@ -1,3 +1,4 @@ +import type { RouteTree } from './segment-cache/cache' import type { CacheNode, FlightRouterState, @@ -9,7 +10,7 @@ const MAX_BF_CACHE_ENTRIES = process.env.__NEXT_CACHE_COMPONENTS ? 3 : 1 export type RouterBFCacheEntry = { tree: FlightRouterState - cacheNode: CacheNode + renderTree: RouteTree stateKey: string // The entries form a linked list, sorted in order of most recently active. next: RouterBFCacheEntry | null @@ -30,14 +31,13 @@ export type RouterBFCacheEntry = { * unmounted, then the React tree would be, too. So, we use React state to * manage it. * - * Note that we don't store the RSC data for the cache entries in this hook — - * the data for inactive segments is stored in the parent CacheNode, which - * *does* have a longer lifetime than the React tree. This hook only determines - * which of those trees should have their *state* preserved, by . + * Each entry retains its render tree, including its RSC data. This hook + * determines which trees should have their React and DOM state preserved + * by . */ export function useRouterBFCache( activeTree: FlightRouterState, - activeCacheNode: CacheNode, + activeRenderTree: RouteTree, activeStateKey: string ): RouterBFCacheEntry { // The currently active entry. The entries form a linked list, sorted in @@ -53,7 +53,7 @@ export function useRouterBFCache( () => { const initialEntry: RouterBFCacheEntry = { tree: activeTree, - cacheNode: activeCacheNode, + renderTree: activeRenderTree, stateKey: activeStateKey, next: null, } @@ -78,7 +78,7 @@ export function useRouterBFCache( // linked list. const newActiveEntry: RouterBFCacheEntry = { tree: activeTree, - cacheNode: activeCacheNode, + renderTree: activeRenderTree, stateKey: activeStateKey, next: null, } @@ -105,7 +105,7 @@ export function useRouterBFCache( n++ const entry: RouterBFCacheEntry = { tree: oldEntry.tree, - cacheNode: oldEntry.cacheNode, + renderTree: oldEntry.renderTree, stateKey: oldEntry.stateKey, next: null, } diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx index 829d00ea265a..3d18d7191273 100644 --- a/packages/next/src/client/components/layout-router.tsx +++ b/packages/next/src/client/components/layout-router.tsx @@ -1,5 +1,7 @@ 'use client' +import type { RouteTree } from './segment-cache/cache' + import type { CacheNode } from '../../shared/lib/app-router-types' import type { LoadingModuleData } from '../../shared/lib/app-router-types' import type { @@ -136,7 +138,7 @@ function getHashFragmentDomNode(hashFragment: string) { interface ScrollHandlerProps { scrollRef: ScrollHandlerRef children: React.ReactNode - cacheNode: CacheNode + renderTree: RouteTree } /** @@ -148,11 +150,11 @@ function InnerScrollHandler(props: ScrollHandlerProps) { useLayoutEffect( () => { - const { scrollRef: scrollHandlerRef, cacheNode } = props + const { scrollRef: scrollHandlerRef, renderTree } = props const scrollRef = scrollHandlerRef.forceScroll ? scrollHandlerRef.scrollRef - : cacheNode.scrollRef + : renderTree.data.scrollRef if (scrollRef === null || !scrollRef.current) return let instance: FragmentInstance | HTMLElement | null = null @@ -277,10 +279,10 @@ function InnerScrollHandler(props: ScrollHandlerProps) { function ScrollHandler({ children, - cacheNode, + renderTree, }: { children: React.ReactNode - cacheNode: CacheNode + renderTree: RouteTree }) { const context = useContext(GlobalLayoutRouterContext) if (!context) { @@ -288,7 +290,7 @@ function ScrollHandler({ } return ( - + {children} ) @@ -301,7 +303,7 @@ function InnerLayoutRouter({ tree, segmentPath, debugNameContext, - cacheNode: maybeCacheNode, + renderTree, params, url, isActive, @@ -309,7 +311,7 @@ function InnerLayoutRouter({ tree: FlightRouterState segmentPath: FlightSegmentPath debugNameContext: string - cacheNode: CacheNode | null + renderTree: RouteTree params: Params url: string isActive: boolean @@ -321,19 +323,6 @@ function InnerLayoutRouter({ throw new Error('invariant global layout router not mounted') } - const cacheNode = - maybeCacheNode !== null - ? maybeCacheNode - : // This segment is not in the cache. Suspend indefinitely. - // - // This should only be reachable for inactive/hidden segments, during - // prerendering The active segment should always be consistent with the - // CacheNode tree. Regardless, if we don't have a matching CacheNode, we - // must suspend rather than render nothing, to prevent showing an - // inconsistent route. - - (use(unresolvedThenable) as never) - // `rsc` represents the renderable node for this segment. // If this segment has a `prefetchRsc`, it's the statically prefetched data. @@ -342,12 +331,14 @@ function InnerLayoutRouter({ // // If no prefetch data is available, then we go straight to rendering `rsc`. const resolvedPrefetchRsc = - cacheNode.prefetchRsc !== null ? cacheNode.prefetchRsc : cacheNode.rsc + renderTree.data.prefetchRsc !== null + ? renderTree.data.prefetchRsc + : renderTree.data.rsc // We use `useDeferredValue` to handle switching between the prefetched and // final values. The second argument is returned on initial render, then it // re-renders with the first argument. - const rsc: any = useDeferredValue(cacheNode.rsc, resolvedPrefetchRsc) + const rsc: any = useDeferredValue(renderTree.data.rsc, resolvedPrefetchRsc) // `rsc` is either a React node or a promise for a React node, except we // special case `null` to represent that this segment's data is missing. If @@ -401,7 +392,7 @@ function InnerLayoutRouter({ = [] do { const tree = bfcacheEntry.tree - const cacheNode = bfcacheEntry.cacheNode + const renderTree = bfcacheEntry.renderTree const stateKey = bfcacheEntry.stateKey const segment = tree[0] /* @@ -676,7 +664,7 @@ export default function OuterLayoutRouter({ const debugNameToDisplay = isVirtual ? undefined : debugNameContext let templateValue = ( - + ) { // For each currently visible link, cancel the existing prefetch task (if it // exists) and schedule a new one. This is effectively the same as if all the // visible links left and then re-entered the viewport. // - // This is called when the Next-Url or the base tree changes, since those + // This is called when the Next-Url or the active cache tree changes, since those // may affect the result of a prefetch task. It's also called after a // cache invalidation. for (const instance of prefetchableAndVisible) { const task = instance.prefetchTask - if (task !== null && !isPrefetchTaskDirty(task, nextUrl, tree)) { + if (task !== null && !isPrefetchTaskDirty(task, nextUrl, root)) { // The cache has not been invalidated, and none of the inputs have // changed. Bail out. continue @@ -391,7 +391,7 @@ export function pingVisibleLinks( const cacheKey = createCacheKey(instance.prefetchHref, nextUrl) instance.prefetchTask = scheduleSegmentPrefetchTask( cacheKey, - tree, + root, instance.fetchStrategy, PrefetchPriority.Default, null, diff --git a/packages/next/src/client/components/match-segments.ts b/packages/next/src/client/components/match-segments.ts deleted file mode 100644 index 77cfcbc9d0a6..000000000000 --- a/packages/next/src/client/components/match-segments.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Segment } from '../../shared/lib/app-router-types' - -export const matchSegment = ( - existingSegment: Segment, - segment: Segment -): boolean => { - // segment is either Array or string - if (typeof existingSegment === 'string') { - if (typeof segment === 'string') { - // Common case: segment is just a string - return existingSegment === segment - } - return false - } - - if (typeof segment === 'string') { - return false - } - return existingSegment[0] === segment[0] && existingSegment[1] === segment[1] -} diff --git a/packages/next/src/client/components/navigation.ts b/packages/next/src/client/components/navigation.ts index 2e1df203d82a..95e6d439a15b 100644 --- a/packages/next/src/client/components/navigation.ts +++ b/packages/next/src/client/components/navigation.ts @@ -177,7 +177,7 @@ export function useRouter(): AppRouterInstance { // a `b` prefix, so the id can be safely concatenated with other keys // without collision. const layout = useContext(LayoutRouterContext) - const bfcacheIdNumber = layout?.parentCacheNode.bfcacheId ?? 0 + const bfcacheIdNumber = layout?.parentRenderTree.data.bfcacheId ?? 0 return useMemo( () => ({ back: router.back, diff --git a/packages/next/src/client/components/prefetch.ts b/packages/next/src/client/components/prefetch.ts index 547407546085..4b17f3632d16 100644 --- a/packages/next/src/client/components/prefetch.ts +++ b/packages/next/src/client/components/prefetch.ts @@ -1,4 +1,5 @@ -import type { FlightRouterState } from '../../shared/lib/app-router-types' +import type { RootRouteTree } from './segment-cache/cache' +import type { CacheNode } from '../../shared/lib/app-router-types' import type { PrefetchOptions } from '../../shared/lib/app-router-context.shared-runtime' import { PrefetchKind } from './router-reducer/router-reducer-types' import { createPrefetchURL } from './app-router-utils' @@ -61,7 +62,7 @@ export function prefetchRoute(href: string, options?: PrefetchOptions): void { prefetch( href, state.nextUrl, - state.tree, + state.root, fetchStrategy, options?.onInvalidate ?? null ) @@ -73,8 +74,8 @@ export function prefetchRoute(href: string, options?: PrefetchOptions): void { * or router.prefetch. It must be validated before we attempt to prefetch it. * @param nextUrl - A special header used by the server for interception routes. * Roughly corresponds to the current URL. - * @param treeAtTimeOfPrefetch - The FlightRouterState at the time the prefetch - * was requested. This is only used when PPR is disabled. + * @param renderTreeAtTimeOfPrefetch - The active render tree and head, and + * their vary paths. * @param fetchStrategy - Whether to prefetch dynamic data, in addition to * static data. This is used by ``. * @param onInvalidate - A callback that will be called when the prefetch cache @@ -90,7 +91,7 @@ export function prefetchRoute(href: string, options?: PrefetchOptions): void { export function prefetch( href: string, nextUrl: string | null, - treeAtTimeOfPrefetch: FlightRouterState, + renderTreeAtTimeOfPrefetch: RootRouteTree, fetchStrategy: PrefetchTaskFetchStrategy, onInvalidate: null | (() => void) ) { @@ -102,7 +103,7 @@ export function prefetch( const cacheKey = createCacheKey(url.href, nextUrl) schedulePrefetchTask( cacheKey, - treeAtTimeOfPrefetch, + renderTreeAtTimeOfPrefetch, fetchStrategy, PrefetchPriority.Default, onInvalidate, diff --git a/packages/next/src/client/components/render-tree.ts b/packages/next/src/client/components/render-tree.ts index 47e34ef6f87a..0715846d2c29 100644 --- a/packages/next/src/client/components/render-tree.ts +++ b/packages/next/src/client/components/render-tree.ts @@ -1,16 +1,13 @@ -import type { - FlightRouterState, - Segment, -} from '../../shared/lib/app-router-types' +import type { FlightRouterState } from '../../shared/lib/app-router-types' import type { CacheNode } from '../../shared/lib/app-router-types' -import type { HeadData, ScrollRef } from '../../shared/lib/app-router-types' +import type { ScrollRef } from '../../shared/lib/app-router-types' import { PrefetchHint } from '../../shared/lib/app-router-types' import { - PAGE_SEGMENT_KEY, DEFAULT_SEGMENT_KEY, NOT_FOUND_SEGMENT_KEY, + PAGE_SEGMENT_KEY, } from '../../shared/lib/segment' -import { matchSegment } from './match-segments' +import { HEAD_REQUEST_KEY } from '../../shared/lib/segment-cache/segment-value-encoding' import { createHrefFromUrl } from './router-reducer/create-href-from-url' import { fetchServerResponse } from './router-reducer/fetch-server-response' import { dispatchAppRouterAction } from './use-action-queue' @@ -28,33 +25,37 @@ import { segmentCacheMap, type SegmentCacheEntry, type RouteTree, + type RootRouteTree, type RSCSegmentData, type RefreshState, type FulfilledRouteCacheEntry, - convertReusedFlightRouterStateToRouteTree, + rebaseInactiveRouteTree, + doesRouteStructureMatch, readSegmentCacheEntryForNavigation, waitForSegmentCacheEntry, invalidateRouteCacheEntries, spawnStaticStageCacheWrite, writeRuntimePrefetchStreamIntoCache, EntryStatus, + MetadataOnlyRequestTree, } from './segment-cache/cache' import { discoverKnownRoute } from './segment-cache/optimistic-routes' -import { urlSearchParamsToParsedUrlQuery } from '../route-params' import type { NormalizedSearch } from './segment-cache/cache-key' import type { CacheMap } from './segment-cache/cache-map' import { getRenderedSearchFromVaryPath, - type PageVaryPath, + compareParams, + ParamsChange, + didReadChangedParam, } from './segment-cache/vary-path' import { readFromBFCache, readFromBFCacheDuringRegularNavigation, writeToBFCache, - writeHeadToBFCache, - updateBFCacheEntryStaleAt, + updateBFCacheEntryFromDynamicResponse, computeDynamicStaleAt, } from './segment-cache/bfcache' +import type { VaryParams } from '../../shared/lib/segment-cache/vary-params-decoding' // This is yet another tree type that is used to track pending promises that // need to be fulfilled once the dynamic data is received. The terminal nodes of @@ -66,20 +67,30 @@ export type NavigationTask = { status: NavigationTaskStatus // The router state that corresponds to the tree that this Task represents. route: FlightRouterState - // The CacheNode that corresponds to the tree that this Task represents. - node: CacheNode + // The render tree being constructed by this task. + node: RouteTree // The tree sent to the server during the dynamic request. If all the segments // are static, then this will be null, and no server request is required. // Otherwise, this is the same as `route`, except with the `refetch` marker // set on the top-most segment that needs to be fetched. dynamicRequestTree: FlightRouterState | null - // The URL that should be used to fetch the dynamic data. This is only set - // when the segment cannot be refetched from the current route, because it's - // part of a "default" parallel slot that was reused during a navigation. - refreshState: RefreshState | null children: Map | null } +// The task-side counterpart of RootRouteTree; collapses into it once tasks +// are route trees. +export type RootNavigationTask = { + tree: NavigationTask + head: NavigationTask +} + +export function createRootNavigationTask( + tree: NavigationTask, + head: NavigationTask +): RootNavigationTask { + return { tree, head } +} + export const enum FreshnessPolicy { Default, Hydration, @@ -154,33 +165,42 @@ export type NavigationLock = Promise const noop = () => {} -export function createInitialCacheNodeForHydration( +export function createInitialRenderTreeForHydration( navigatedAt: number, - initialTree: RouteTree, - seedHead: HeadData, + initialRoot: RootRouteTree, seedDynamicStaleAt: number -): NavigationTask { +): RootNavigationTask { // Create the initial cache node tree, using the data embedded into the // HTML document. const accumulation: NavigationRequestAccumulation = { separateRefreshUrls: null, scrollRef: null, } + const parentNeedsDynamicRequest = false const restrictToShell = false - const task = createCacheNodeOnNavigation( + // Hydration is bound to the shared map. + const map = segmentCacheMap + const tree = createRenderTreeOnNavigation( navigatedAt, - initialTree, - null, + initialRoot.tree, FreshnessPolicy.Hydration, - seedHead, seedDynamicStaleAt, - false, + parentNeedsDynamicRequest, accumulation, - // Hydration is bound to the shared map. - segmentCacheMap, + map, restrictToShell ) - return task + const head = createRenderTreeOnNavigation( + navigatedAt, + initialRoot.head, + FreshnessPolicy.Hydration, + seedDynamicStaleAt, + parentNeedsDynamicRequest, + accumulation, + map, + restrictToShell + ) + return createRootNavigationTask(tree, head) } // Creates a new Cache Node tree (i.e. copy-on-write) that represents the @@ -207,21 +227,18 @@ export function createInitialCacheNodeForHydration( // a synchronous function). Any new trees that do not have prefetch data will // suspend during rendering, until the dynamic data streams in. // -// Returns a Task object, which contains both the updated Cache Node and a path -// to the pending subtrees that need to be resolved by the navigation response. +// Returns the tasks for the route tree and the head, each containing both the +// updated Cache Node and a path to the pending subtrees that need to be +// resolved by the navigation response. // -// A return value of `null` means there were no changes, and the previous tree -// can be reused without initiating a server request. +// A return value of `null` means a full-page (MPA) navigation is required. export function startPPRNavigation( navigatedAt: number, oldUrl: URL, oldRenderedSearch: string, - oldCacheNode: CacheNode | null, - oldRouterState: FlightRouterState, - newRouteTree: RouteTree, - newMetadataVaryPath: PageVaryPath | null, + oldRoot: RootRouteTree, + newRoot: RootRouteTree, freshness: FreshnessPolicy, - seedHead: HeadData | null, seedDynamicStaleAt: number, isSamePageNavigation: boolean, accumulation: NavigationRequestAccumulation, @@ -231,22 +248,37 @@ export function startPPRNavigation( // Instant Navigation Testing API only — restricts segment reads to shell // entries. Always false outside the testing API. See navigation-testing-lock. restrictToShell: boolean -): NavigationTask | null { +): RootNavigationTask | null { const parentNeedsDynamicRequest = false const parentRefreshState = null const oldRootRefreshState: RefreshState = { canonicalUrl: createHrefFromUrl(oldUrl), renderedSearch: oldRenderedSearch as NormalizedSearch, } - return updateCacheNodeOnNavigation( + const tree = updateRenderTreeOnNavigation( navigatedAt, - oldUrl, - oldCacheNode !== null ? oldCacheNode : undefined, - oldRouterState, - newRouteTree, - newMetadataVaryPath, + oldRoot.tree, + newRoot.tree, + freshness, + seedDynamicStaleAt, + isSamePageNavigation, + parentNeedsDynamicRequest, + oldRootRefreshState, + parentRefreshState, + accumulation, + map, + restrictToShell + ) + if (tree === null) { + // The route tree changed at or above the root layout. Perform a full-page + // navigation. + return null + } + const head = updateRenderTreeOnNavigation( + navigatedAt, + oldRoot.head, + newRoot.head, freshness, - seedHead, seedDynamicStaleAt, isSamePageNavigation, parentNeedsDynamicRequest, @@ -256,17 +288,35 @@ export function startPPRNavigation( map, restrictToShell ) + if (head === null) { + // Unreachable: a one-node tree has no root layout to change and no slots. + return null + } + return createRootNavigationTask(tree, head) } -function updateCacheNodeOnNavigation( +// TODO: Unify NavigationTask with CacheNode. +function createNavigationTask( + status: NavigationTaskStatus, + route: FlightRouterState, + node: RouteTree, + dynamicRequestTree: FlightRouterState | null, + children: Map | null +): NavigationTask { + return { + status, + route, + node, + dynamicRequestTree, + children, + } +} + +function updateRenderTreeOnNavigation( navigatedAt: number, - oldUrl: URL, - oldCacheNode: CacheNode | void, - oldRouterState: FlightRouterState, + oldRenderTree: RouteTree, newRouteTree: RouteTree, - newMetadataVaryPath: PageVaryPath | null, freshness: FreshnessPolicy, - seedHead: HeadData | null, seedDynamicStaleAt: number, isSamePageNavigation: boolean, parentNeedsDynamicRequest: boolean, @@ -278,14 +328,10 @@ function updateCacheNodeOnNavigation( // entries. Always false outside the testing API. See navigation-testing-lock. restrictToShell: boolean ): NavigationTask | null { - // Check if this segment matches the one in the previous route. A - // search-param-only difference at a page segment falls through to the - // matched branch — the CacheNode is rebuilt (so data refetches), but the - // bfcacheId carries forward as if the segment had matched. - const oldSegment = oldRouterState[0] - const newSegment = createSegmentFromRouteTree(newRouteTree) - const segmentMatchKind = compareSegments(newSegment, oldSegment) - if (segmentMatchKind === SegmentMatchKind.Change) { + // Check if the route structure changed. If only the params changed, that's + // handled further down. + const newSegment = newRouteTree.segment + if (!doesRouteStructureMatch(oldRenderTree, newRouteTree)) { // This segment does not match the previous route. We're now entering the // new part of the target route. Switch to the "create" path. if ( @@ -312,7 +358,7 @@ function updateCacheNodeOnNavigation( // the root layout. We also only need to compare the subtree that is not // shared. In the common case, this branch is skipped completely. ((newRouteTree.prefetchHints & PrefetchHint.IsRootLayoutOrAbove) !== 0 && - isNavigatingToNewRootLayout(oldRouterState, newRouteTree)) || + isNavigatingToNewRootLayout(oldRenderTree, newRouteTree)) || // The global Not Found route (app/global-not-found.tsx) is a special // case, because it acts like a root layout, but in the router tree, it // is rendered in the same position as app/layout.tsx. @@ -327,12 +373,10 @@ function updateCacheNodeOnNavigation( ) { return null } - return createCacheNodeOnNavigation( + return createRenderTreeOnNavigation( navigatedAt, newRouteTree, - newMetadataVaryPath, freshness, - seedHead, seedDynamicStaleAt, parentNeedsDynamicRequest, accumulation, @@ -342,7 +386,6 @@ function updateCacheNodeOnNavigation( } const newSlots = newRouteTree.slots - const oldRouterStateChildren = oldRouterState[1] let shouldRefreshDynamicData: boolean = false switch (freshness) { @@ -361,76 +404,121 @@ function updateCacheNodeOnNavigation( break } - // TODO: We're not consistent about how we do this check. Some places - // check if the segment starts with PAGE_SEGMENT_KEY, but most seem to - // check if there any any children, which is why I'm doing it here. We - // should probably encode an empty children set as `null` though. Either - // way, we should update all the checks to be consistent. const isLeafSegment = newSlots === null // Get the data for this segment. Since it was part of the previous route, - // usually we just clone the data from the old CacheNode. However, during a - // refresh or a revalidation, there won't be any existing CacheNode. So we - // may need to consult the prefetch cache, like we would for a new segment. - let newCacheNode: CacheNode + // usually we just reuse the data from the old render tree. If the params + // changed, or during a refresh or revalidation, consult the prefetch cache + // or response seed instead. + let newRenderTree: RouteTree let needsDynamicRequest: boolean - if ( - oldCacheNode !== undefined && - !shouldRefreshDynamicData && + const paramsChange = compareParams( + oldRenderTree.varyPath, + newRouteTree.varyPath + ) + if (paramsChange !== ParamsChange.None) { + // Path params are part of LayoutRouter's React key, so changing one + // remounts this segment and everything below it. Generate a new bfcacheId + // to match. Search params aren't part of the key, so a page whose search + // params changed keeps its existing id. + let bfcacheId: number + if (paramsChange === ParamsChange.PathParam) { + bfcacheId = generateBFCacheId(freshness) + } else { + bfcacheId = oldRenderTree.data.bfcacheId + } + switch (freshness) { + case FreshnessPolicy.Default: + case FreshnessPolicy.Gesture: { + // If the existing data didn't read any of the params that changed, we + // can keep using it. Refreshes always fetch new data, and back/forward + // navigations restore the entry from the BFCache instead. + const oldCacheNode = oldRenderTree.data + if ( + !didReadChangedParam( + oldRenderTree.varyPath, + newRouteTree.varyPath, + oldCacheNode.varyParams + ) + ) { + const cacheNode = createCacheNode( + oldCacheNode.rsc, + oldCacheNode.prefetchRsc, + oldCacheNode.varyParams, + bfcacheId + ) + if (freshness !== FreshnessPolicy.Gesture) { + writeToBFCache( + navigatedAt, + newRouteTree.varyPath, + cacheNode, + seedDynamicStaleAt + ) + } + newRenderTree = createRenderTree(newRouteTree, cacheNode) + needsDynamicRequest = false + break + } + // Intentional fallthrough + } + case FreshnessPolicy.Hydration: + case FreshnessPolicy.HistoryTraversal: + case FreshnessPolicy.RefreshAll: + case FreshnessPolicy.HMRRefresh: { + const result = createRenderTreeForSegment( + navigatedAt, + newRouteTree, + freshness, + seedDynamicStaleAt, + bfcacheId, + map, + restrictToShell + ) + newRenderTree = result.node + needsDynamicRequest = result.needsDynamicRequest + break + } + } + + // A param change mostly acts the same as a refresh, except it does + // trigger a scroll. + if (isLeafSegment) { + accumulateScrollRef(freshness, newRenderTree.data, accumulation) + } + } else if ( + shouldRefreshDynamicData || // During a same-page navigation, we always refetch the page segments - !(isLeafSegment && isSamePageNavigation) && - // A search-param-only change is treated as a refresh of the page segment. - // The internal cache key of the data is different, but the identity of - // the node in the route tree is the same. - segmentMatchKind !== SegmentMatchKind.SearchParamOnlyChange + (isLeafSegment && isSamePageNavigation) ) { - // Reuse the existing CacheNode - const dropPrefetchRsc = false - newCacheNode = reuseSharedCacheNode(dropPrefetchRsc, oldCacheNode) - needsDynamicRequest = false - } else { - // If this is part of a refresh, ignore the existing CacheNode and create a - // new one. - const data = newRouteTree.data - const seedRsc = data !== null ? data.rsc : null - const result = createCacheNodeForSegment( + // This is a refresh of an existing segment. Ignore the existing render + // tree and create a new one. + const result = createRenderTreeForSegment( navigatedAt, newRouteTree, - seedRsc, - newMetadataVaryPath, - seedHead, freshness, seedDynamicStaleAt, - // Carry forward the existing bfcacheId when there's a prior CacheNode: - // even though the data is being refreshed, the state identity of the - // route hasn't changed. Otherwise (no prior node) mint a fresh one. - oldCacheNode !== undefined - ? oldCacheNode.bfcacheId - : generateBFCacheId(freshness), + // Refreshing data preserves the identity of the active segment. + oldRenderTree.data.bfcacheId, map, restrictToShell ) - newCacheNode = result.cacheNode + newRenderTree = result.node needsDynamicRequest = result.needsDynamicRequest - // Scroll handling - if ( - isLeafSegment && - segmentMatchKind === SegmentMatchKind.SearchParamOnlyChange - ) { - // Special case: A search param change mostly acts the same as a - // refresh, except it does trigger a scroll. - accumulateScrollRef(freshness, newCacheNode, accumulation) - } else { - // Normal case: This is a refresh of an existing segment. Carry forward - // the old node's scrollRef. This preserves scroll intent when a prior - // navigation's CacheNode is replaced by a refresh before the scroll - // handler has had a chance to fire — e.g. when router.push() and - // router.refresh() are called in the same startTransition batch. - if (oldCacheNode !== undefined) { - newCacheNode.scrollRef = oldCacheNode.scrollRef - } - } + // Carry forward the old node's scrollRef. This preserves scroll intent + // when a prior navigation's render tree is replaced by a refresh before + // the scroll handler has had a chance to fire — e.g. when router.push() + // and router.refresh() are called in the same startTransition batch. + newRenderTree.data.scrollRef = oldRenderTree.data.scrollRef + } else { + // This segment appears in both the old and new routes. Reuse the existing + // data without triggering a request. + // TODO: Consider adding a fast path where if this segment is unchanged and + // all of its children are unchanged, we return the exact same RenderTree + // object. Reusing the exact previous object gives React more of a chance to + // bail out of rendering. + newRenderTree = createRenderTree(newRouteTree, oldRenderTree.data) + needsDynamicRequest = false } // During a refresh navigation, there's a special case that happens when @@ -446,6 +534,7 @@ function updateCacheNodeOnNavigation( maybeRefreshState : // Inherit the refresh URL from the parent. parentRefreshState + newRenderTree.refreshState = refreshState // If this segment itself needs to fetch new data from the server, then by // definition it is being refreshed. Track its refresh URL so we know which @@ -485,64 +574,47 @@ function updateCacheNodeOnNavigation( [parallelRouteKey: string]: FlightRouterState } = {} - let newCacheNodeSlots: Record | null = null if (newSlots !== null) { - const oldCacheNodeSlots = - oldCacheNode !== undefined ? oldCacheNode.slots : null + const oldRenderTreeSlots = oldRenderTree.slots - newCacheNode.slots = newCacheNodeSlots = {} + const newRenderTreeSlots = new Map>() + newRenderTree.slots = newRenderTreeSlots taskChildren = new Map() for (let [parallelRouteKey, newRouteTreeChild] of newSlots) { - const oldRouterStateChild: FlightRouterState | void = - oldRouterStateChildren[parallelRouteKey] - if (oldRouterStateChild === undefined) { + const oldRenderTreeChild = oldRenderTreeSlots?.get(parallelRouteKey) + if (oldRenderTreeChild === undefined) { // This should never happen, but if it does, it suggests a malformed // server response. Trigger a full-page navigation. return null } - const oldSegmentChild = oldRouterStateChild[0] - let newSegmentChild = createSegmentFromRouteTree(newRouteTreeChild) - let seedHeadChild = seedHead + const oldSegmentChild = oldRenderTreeChild.segment + const newSegmentChild = newRouteTreeChild.segment if ( // Skip this branch during a history traversal. We restore the tree that // was stashed in the history entry as-is. freshness !== FreshnessPolicy.HistoryTraversal && newSegmentChild === DEFAULT_SEGMENT_KEY && - oldSegmentChild !== DEFAULT_SEGMENT_KEY + oldSegmentChild !== DEFAULT_SEGMENT_KEY && + // The active segment was rendered with this layout's params. If a + // path param changed, we can't keep it. Use the default segment from + // the server instead. + paramsChange !== ParamsChange.PathParam ) { // This is a "default" segment. These are never sent by the server during // a soft navigation; instead, the client reuses whatever segment was // already active in that slot on the previous route. newRouteTreeChild = reuseActiveSegmentInDefaultSlot( - newRouteTree, - parallelRouteKey, oldRootRefreshState, - oldRouterStateChild + oldRenderTreeChild ) - newSegmentChild = createSegmentFromRouteTree(newRouteTreeChild) - - // Discard the seed head, which corresponds to the outer route tree, - // not the reused one we're switching to. (Segment data needs no - // equivalent handling: it lives on the route tree nodes themselves, - // and a reused tree's nodes never carry data.) - seedHeadChild = null } - const oldCacheNodeChild = - oldCacheNodeSlots !== null - ? oldCacheNodeSlots[parallelRouteKey] - : undefined - - const taskChild = updateCacheNodeOnNavigation( + const taskChild = updateRenderTreeOnNavigation( navigatedAt, - oldUrl, - oldCacheNodeChild, - oldRouterStateChild, + oldRenderTreeChild, newRouteTreeChild, - newMetadataVaryPath, freshness, - seedHeadChild, seedDynamicStaleAt, isSamePageNavigation, parentNeedsDynamicRequest || needsDynamicRequest, @@ -562,7 +634,7 @@ function updateCacheNodeOnNavigation( // Recursively propagate up the child tasks. taskChildren.set(parallelRouteKey, taskChild) - newCacheNodeSlots[parallelRouteKey] = taskChild.node + newRenderTreeSlots.set(parallelRouteKey, taskChild.node) // The child tree's route state may be different from the prefetched // route sent by the server. We need to clone it as we traverse back up @@ -581,32 +653,27 @@ function updateCacheNodeOnNavigation( } } - const newFlightRouterState: FlightRouterState = [ - createSegmentFromRouteTree(newRouteTree), + const newFlightRouterState = createRouterStateForSegment( + newRouteTree, patchedRouterStateChildren, - refreshState !== null - ? [refreshState.canonicalUrl, refreshState.renderedSearch] - : null, - null, - newRouteTree.prefetchHints, - ] + refreshState + ) - return { - status: needsDynamicRequest + return createNavigationTask( + needsDynamicRequest ? NavigationTaskStatus.Pending : NavigationTaskStatus.Fulfilled, - route: newFlightRouterState, - node: newCacheNode, - dynamicRequestTree: createDynamicRequestTree( + newFlightRouterState, + newRenderTree, + createDynamicRequestTree( newFlightRouterState, dynamicRequestTreeChildren, needsDynamicRequest, childNeedsDynamicRequest, parentNeedsDynamicRequest ), - refreshState, - children: taskChildren, - } + taskChildren + ) } /** @@ -615,16 +682,14 @@ function updateCacheNodeOnNavigation( * navigation share the same ScrollRef — the first segment to scroll * consumes it, preventing others from also scrolling. * - * This is only called inside `createCacheNodeOnNavigation`, which only - * runs when segments diverge from the previous route. So for a refresh - * where the route structure stays the same, segments match, the update - * path is taken, and this function is never called — no scroll ref is - * assigned. A scroll ref is only assigned when the route actually - * changed (e.g. a redirect, or a dynamic condition on the server that - * produces a different route). + * Called for newly entered segments, and for segments whose params changed + * (even if their data was reused). Refreshes keep the existing scroll ref. * * Skipped during hydration (initial render should not scroll) and * history traversal (scroll restoration is handled separately). + * + * The head passes through here as a leaf too; its `scrollRef` is never read + * (only LayoutRouter reads one), and the page leaf sets the same shared ref. */ function accumulateScrollRef( freshness: FreshnessPolicy, @@ -653,12 +718,10 @@ function accumulateScrollRef( } } -function createCacheNodeOnNavigation( +function createRenderTreeOnNavigation( navigatedAt: number, newRouteTree: RouteTree, - newMetadataVaryPath: PageVaryPath | null, freshness: FreshnessPolicy, - seedHead: HeadData | null, seedDynamicStaleAt: number, parentNeedsDynamicRequest: boolean, accumulation: NavigationRequestAccumulation, @@ -667,28 +730,21 @@ function createCacheNodeOnNavigation( // entries. Always false outside the testing API. See navigation-testing-lock. restrictToShell: boolean ): NavigationTask { - // Same traversal as updateCacheNodeNavigation, but simpler. We switch to this + // Same traversal as updateRenderTreeOnNavigation, but simpler. We switch to this // path once we reach the part of the tree that was not in the previous route. // We don't need to diff against the old tree, we just need to create a new // one. We also don't need to worry about any refresh-related logic. // - // For the most part, this is a subset of updateCacheNodeOnNavigation, so any + // For the most part, this is a subset of updateRenderTreeOnNavigation, so any // change that happens in this function likely needs to be applied to that // one, too. However there are some places where the behavior intentionally // diverges, which is why we keep them separate. - const newSegment = createSegmentFromRouteTree(newRouteTree) - const newSlots = newRouteTree.slots - const data = newRouteTree.data - const seedRsc = data !== null ? data.rsc : null - const result = createCacheNodeForSegment( + const result = createRenderTreeForSegment( navigatedAt, newRouteTree, - seedRsc, - newMetadataVaryPath, - seedHead, freshness, seedDynamicStaleAt, // This segment was not part of the previous route, so mint a fresh @@ -697,12 +753,12 @@ function createCacheNodeOnNavigation( map, restrictToShell ) - const newCacheNode = result.cacheNode + const newRenderTree = result.node const needsDynamicRequest = result.needsDynamicRequest const isLeafSegment = newSlots === null if (isLeafSegment) { - accumulateScrollRef(freshness, newCacheNode, accumulation) + accumulateScrollRef(freshness, newRenderTree.data, accumulation) } let patchedRouterStateChildren: { @@ -715,17 +771,15 @@ function createCacheNodeOnNavigation( [parallelRouteKey: string]: FlightRouterState } = {} - let newCacheNodeSlots: Record | null = null if (newSlots !== null) { - newCacheNode.slots = newCacheNodeSlots = {} + const newRenderTreeSlots = new Map>() + newRenderTree.slots = newRenderTreeSlots taskChildren = new Map() for (const [parallelRouteKey, newRouteTreeChild] of newSlots) { - const taskChild = createCacheNodeOnNavigation( + const taskChild = createRenderTreeOnNavigation( navigatedAt, newRouteTreeChild, - newMetadataVaryPath, freshness, - seedHead, seedDynamicStaleAt, parentNeedsDynamicRequest || needsDynamicRequest, accumulation, @@ -734,7 +788,7 @@ function createCacheNodeOnNavigation( ) taskChildren.set(parallelRouteKey, taskChild) - newCacheNodeSlots[parallelRouteKey] = taskChild.node + newRenderTreeSlots.set(parallelRouteKey, taskChild.node) const taskChildRoute = taskChild.route patchedRouterStateChildren[parallelRouteKey] = taskChildRoute @@ -749,63 +803,55 @@ function createCacheNodeOnNavigation( } } - const newFlightRouterState: FlightRouterState = [ - newSegment, + // This route is not part of the current tree, so there's no reason to + // track the refresh URL. + const refreshState = null + const newFlightRouterState = createRouterStateForSegment( + newRouteTree, patchedRouterStateChildren, - null, - null, - newRouteTree.prefetchHints, - ] + refreshState + ) - return { - status: needsDynamicRequest + return createNavigationTask( + needsDynamicRequest ? NavigationTaskStatus.Pending : NavigationTaskStatus.Fulfilled, - route: newFlightRouterState, - node: newCacheNode, - dynamicRequestTree: createDynamicRequestTree( + newFlightRouterState, + newRenderTree, + createDynamicRequestTree( newFlightRouterState, dynamicRequestTreeChildren, needsDynamicRequest, childNeedsDynamicRequest, parentNeedsDynamicRequest ), - // This route is not part of the current tree, so there's no reason to - // track the refresh URL. - refreshState: null, - children: taskChildren, - } + taskChildren + ) } -function createSegmentFromRouteTree( - newRouteTree: RouteTree -): Segment { - if (newRouteTree.isPage) { - // In a dynamic server response, the server embeds the search params into - // the segment key, but in a static one it's omitted. The client handles - // this inconsistency by adding the search params back right at the end. - // - // TODO: The only thing this is used for is to create a cache key for - // ChildSegmentMap. But we already track the `renderedSearch` everywhere as - // part of the varyPath. The plan is get rid of ChildSegmentMap and - // store the page data in a CacheMap using the varyPath, like we do - // for prefetches. Then we can remove it from the segment key. - // - // As an incremental step, we can grab the search params from the varyPath. - const renderedSearch = getRenderedSearchFromVaryPath(newRouteTree.varyPath) - if (renderedSearch === null) { - return PAGE_SEGMENT_KEY +// Converts a route tree node into the router state the client sends back to +// the server. Page nodes carry their rendered search in its own slot. +function createRouterStateForSegment( + routeTree: RouteTree, + children: { [parallelRouteKey: string]: FlightRouterState }, + refreshState: RefreshState | null +): FlightRouterState { + const routerState: FlightRouterState = [ + routeTree.segment, + children, + refreshState !== null + ? [refreshState.canonicalUrl, refreshState.renderedSearch] + : null, + null, + routeTree.prefetchHints, + ] + if (routeTree.segment === PAGE_SEGMENT_KEY) { + const renderedSearch = getRenderedSearchFromVaryPath(routeTree.varyPath) + if (renderedSearch !== null) { + routerState[5] = renderedSearch } - // This is based on equivalent logic in addSearchParamsIfPageSegment, used - // on the server. - const stringifiedQuery = JSON.stringify( - urlSearchParamsToParsedUrlQuery(new URLSearchParams(renderedSearch)) - ) - return stringifiedQuery !== '{}' - ? PAGE_SEGMENT_KEY + '?' + stringifiedQuery - : PAGE_SEGMENT_KEY } - return newRouteTree.segment + return routerState } function patchRouterStateWithNewChildren( @@ -825,6 +871,9 @@ function patchRouterStateWithNewChildren( if (4 in baseRouterState) { clone[4] = baseRouterState[4] } + if (5 in baseRouterState) { + clone[5] = baseRouterState[5] + } return clone } @@ -888,25 +937,23 @@ function accumulateRefreshUrl( } function reuseActiveSegmentInDefaultSlot( - parentRouteTree: RouteTree, - parallelRouteKey: string, oldRootRefreshState: RefreshState, - oldRouterState: FlightRouterState + oldRenderTree: RouteTree ): RouteTree { // This is a "default" segment. These are never sent by the server during a // soft navigation; instead, the client reuses whatever segment was already // active in that slot on the previous route. This means if we later need to // refresh the segment, it will have to be refetched from the previous route's - // URL. We store it in the Flight Router State. + // URL. We store the refresh context on the active render tree. let reusedUrl: string let reusedRenderedSearch: NormalizedSearch - const oldRefreshState = oldRouterState[2] - if (oldRefreshState !== undefined && oldRefreshState !== null) { + const oldRefreshState = oldRenderTree.refreshState + if (oldRefreshState !== null) { // This segment was already reused from an even older route. Keep its // existing URL and refresh state. - reusedUrl = oldRefreshState[0] - reusedRenderedSearch = oldRefreshState[1] as NormalizedSearch + reusedUrl = oldRefreshState.canonicalUrl + reusedRenderedSearch = oldRefreshState.renderedSearch } else { // Since this route didn't already have a refresh state, it must have been // reachable from the root of the old route. So we use the refresh state @@ -915,14 +962,7 @@ function reuseActiveSegmentInDefaultSlot( reusedRenderedSearch = oldRootRefreshState.renderedSearch } - const acc = { metadataVaryPath: null, treeDivergedFromBase: false } - const reusedRouteTree = convertReusedFlightRouterStateToRouteTree( - parentRouteTree, - parallelRouteKey, - oldRouterState, - reusedRenderedSearch, - acc - ) + const reusedRouteTree = rebaseInactiveRouteTree(oldRenderTree) reusedRouteTree.refreshState = { canonicalUrl: reusedUrl, renderedSearch: reusedRenderedSearch, @@ -930,31 +970,27 @@ function reuseActiveSegmentInDefaultSlot( return reusedRouteTree } -function reuseSharedCacheNode( - dropPrefetchRsc: boolean, - existingCacheNode: CacheNode -): CacheNode { - // Clone the CacheNode that was already present in the previous tree. - // Carry forward the scrollRef so scroll intent from a prior navigation - // survives tree rebuilds (e.g. push + refresh in the same batch). - // Carry forward the bfcacheId so shared-layout segments retain stable - // identity across navigations. - return createCacheNode( - existingCacheNode.rsc, - dropPrefetchRsc ? null : existingCacheNode.prefetchRsc, - existingCacheNode.head, - dropPrefetchRsc ? null : existingCacheNode.prefetchHead, - existingCacheNode.bfcacheId, - existingCacheNode.scrollRef - ) +function createRenderTree( + routeTree: RouteTree, + cacheNode: CacheNode +): RouteTree { + return { + requestKey: routeTree.requestKey, + segment: routeTree.segment, + shellVaryPath: routeTree.shellVaryPath, + refreshState: null, + data: cacheNode, + varyPath: routeTree.varyPath, + slots: null, + prefetchHints: routeTree.prefetchHints, + } } -function createCacheNodeForSegment( +function createRenderTreeForSegment( now: number, + // A route tree node, or the one-node metadata tree that stands in for the + // head (see createMetadataRouteTree). tree: RouteTree, - seedRsc: React.ReactNode | null, - metadataVaryPath: PageVaryPath | null, - seedHead: HeadData | null, freshness: FreshnessPolicy, dynamicStaleAt: number, bfcacheId: number, @@ -962,8 +998,8 @@ function createCacheNodeForSegment( // Instant Navigation Testing API only — restricts segment reads to shell // entries. Always false outside the testing API. See navigation-testing-lock. restrictToShell: boolean -): { cacheNode: CacheNode; needsDynamicRequest: boolean } { - // Construct a new CacheNode using data from the BFCache, the client's +): { node: RouteTree; needsDynamicRequest: boolean } { + // Construct an owned render tree using data from the BFCache, the client's // Segment Cache, or seeded from a server response. // // If there's a cache miss, or if we only have a partial hit, we'll render @@ -978,7 +1014,9 @@ function createCacheNodeForSegment( // also be able to use that data without spawning a new request. (This is // referred to as the "seed" data.) - const isPage = tree.isPage + const seedData = tree.data + const seedRsc = seedData !== null ? seedData.rsc : null + const seedVaryParams = seedData !== null ? seedData.varyParams : null // During certain kinds of navigations, we may be able to render from // the BFCache. @@ -998,12 +1036,14 @@ function createCacheNodeForSegment( // BFCacheEntry's id is only restored on history-traversal // navigations. return { - cacheNode: createCacheNode( - bfcacheEntry.rsc, - bfcacheEntry.prefetchRsc, - bfcacheEntry.head, - bfcacheEntry.prefetchHead, - bfcacheId + node: createRenderTree( + tree, + createCacheNode( + bfcacheEntry.rsc, + bfcacheEntry.prefetchRsc, + bfcacheEntry.varyParams, + bfcacheId + ) ), needsDynamicRequest: false, } @@ -1026,39 +1066,16 @@ function createCacheNodeForSegment( // triggers this path. But it does render correctly despite that. That's an // unusual render path so it's not surprising, but we should look into // modeling it in a more consistent way. See also the /_notFound special - // case in updateCacheNodeOnNavigation. - const rsc = seedRsc - const prefetchRsc = null - const head = isPage ? seedHead : null - const prefetchHead = null - writeToBFCache( - now, - tree.varyPath, - rsc, - prefetchRsc, - head, - prefetchHead, - dynamicStaleAt, + // case in updateRenderTreeOnNavigation. + const cacheNode = createCacheNode( + seedRsc, + null, + seedVaryParams, bfcacheId ) - if (isPage && metadataVaryPath !== null) { - writeHeadToBFCache( - now, - metadataVaryPath, - head, - prefetchHead, - dynamicStaleAt, - bfcacheId - ) - } + writeToBFCache(now, tree.varyPath, cacheNode, dynamicStaleAt) return { - cacheNode: createCacheNode( - rsc, - prefetchRsc, - head, - prefetchHead, - bfcacheId - ), + node: createRenderTree(tree, cacheNode), needsDynamicRequest: false, } } @@ -1083,12 +1100,14 @@ function createCacheNodeForSegment( // navigations preserve the original id, regardless of whether // `cacheComponents` Activity preservation is enabled. return { - cacheNode: createCacheNode( - bfcacheEntry.rsc, - dropPrefetchRsc ? null : bfcacheEntry.prefetchRsc, - bfcacheEntry.head, - dropPrefetchRsc ? null : bfcacheEntry.prefetchHead, - bfcacheEntry.bfcacheId + node: createRenderTree( + tree, + createCacheNode( + bfcacheEntry.rsc, + dropPrefetchRsc ? null : bfcacheEntry.prefetchRsc, + bfcacheEntry.varyParams, + bfcacheEntry.bfcacheId + ) ), needsDynamicRequest: false, } @@ -1106,6 +1125,7 @@ function createCacheNodeForSegment( let cachedRsc: React.ReactNode | null = null let isCachedRscPartial: boolean = true + let cachedVaryParams: VaryParams | null = null const segmentEntry = readSegmentCacheEntryForNavigation( now, @@ -1119,6 +1139,7 @@ function createCacheNodeForSegment( // Happy path: a cache hit cachedRsc = segmentEntry.rsc isCachedRscPartial = segmentEntry.isPartial + cachedVaryParams = segmentEntry.varyParams break } case EntryStatus.Pending: { @@ -1129,6 +1150,8 @@ function createCacheNodeForSegment( cachedRsc = promiseForFulfilledEntry.then((entry) => entry !== null ? entry.rsc : null ) + // The entry's data hasn't arrived, and neither has the source of the + // params it depends on; `cachedVaryParams` stays null. // Because the request is still pending, we typically don't know yet // whether the response will be partial. We shouldn't skip this segment // during the dynamic navigation request. Otherwise, we might need to @@ -1153,6 +1176,29 @@ function createCacheNodeForSegment( } } + if ( + process.env.__NEXT_OPTIMISTIC_ROUTING && + tree.segment === HEAD_REQUEST_KEY && + isCachedRscPartial + ) { + // TODO: When optimistic routing is enabled, don't block on waiting for + // the viewport to resolve. This is a temporary workaround until Vary + // Params are tracked when rendering the metadata. We'll fix it before + // this feature is stable. However, it's not a critical issue because 1) + // it will stream in eventually anyway 2) metadata is wrapped in an + // internal Suspense boundary, so is always non-blocking; this only + // affects the viewport node, which is meant to blocking, however... 3) + // before Segment Cache landed this wasn't always the case, anyway, so + // it's unlikely that many people are relying on this behavior. Still, + // will be fixed before stable. It's the very next step in the sequence of + // work on this project. + // + // This line of code works because the App Router treats `null` as + // "no renderable head available", rather than an empty head. React treats + // an empty string as empty. + cachedRsc = '' + } + // Now combine the cached data with the seed data to determine what we can // render immediately, versus what needs to stream in later. @@ -1165,6 +1211,10 @@ function createCacheNodeForSegment( // means the data failed to load; the LayoutRouter will suspend indefinitely // until the router updates again (refer to finishNavigationTask). let rsc: React.ReactNode | null + // The source of the params `rsc` depends on. A server response or a + // fulfilled segment cache entry carries one; a deferred `rsc` gets its + // source when the response arrives (finishPendingCacheNode). + let varyParams: VaryParams | null let doesSegmentNeedDynamicRequest: boolean if (seedRsc !== null) { @@ -1174,6 +1224,7 @@ function createCacheNodeForSegment( // partial cached state in the meantime. prefetchRsc = cachedRsc rsc = seedRsc + varyParams = seedVaryParams } else { // We already have a completely cached segment. Ignore the seed data, // which may still be streaming in. This shouldn't happen in the normal @@ -1181,6 +1232,7 @@ function createCacheNodeForSegment( // already fully cached, and the server will skip rendering them. prefetchRsc = null rsc = cachedRsc + varyParams = cachedVaryParams } doesSegmentNeedDynamicRequest = false } else { @@ -1193,153 +1245,47 @@ function createCacheNodeForSegment( // data arrives from the server. prefetchRsc = cachedRsc rsc = createDeferredRsc() + varyParams = null } else { // The data is fully cached. prefetchRsc = null rsc = cachedRsc + varyParams = cachedVaryParams } doesSegmentNeedDynamicRequest = isCachedRscPartial } - // If this is a page segment, we need to do the same for the head. This - // follows analogous logic to the segment data above. - // TODO: We don't need to store the head on the page segment's CacheNode; we - // can lift it to the main state object. Then we can also delete - // findHeadCache. - - let prefetchHead: HeadData | null = null - let head: React.ReactNode | null = null - let doesHeadNeedDynamicRequest: boolean = isPage - - if (isPage) { - let cachedHead: HeadData | null = null - let isCachedHeadPartial: boolean = true - if (metadataVaryPath !== null) { - const metadataEntry = readSegmentCacheEntryForNavigation( - now, - map, - metadataVaryPath, - restrictToShell - ) - if (metadataEntry !== null) { - switch (metadataEntry.status) { - case EntryStatus.Fulfilled: { - cachedHead = metadataEntry.rsc - isCachedHeadPartial = metadataEntry.isPartial - break - } - case EntryStatus.Pending: { - cachedHead = waitForSegmentCacheEntry(metadataEntry).then( - (entry) => (entry !== null ? entry.rsc : null) - ) - isCachedHeadPartial = metadataEntry.isPartial - break - } - case EntryStatus.Empty: - case EntryStatus.Rejected: { - break - } - default: { - metadataEntry satisfies never - break - } - } - } - } - - if (process.env.__NEXT_OPTIMISTIC_ROUTING && isCachedHeadPartial) { - // TODO: When optimistic routing is enabled, don't block on waiting for - // the viewport to resolve. This is a temporary workaround until Vary - // Params are tracked when rendering the metadata. We'll fix it before - // this feature is stable. However, it's not a critical issue because 1) - // it will stream in eventually anyway 2) metadata is wrapped in an - // internal Suspense boundary, so is always non-blocking; this only - // affects the viewport node, which is meant to blocking, however... 3) - // before Segment Cache landed this wasn't always the case, anyway, so - // it's unlikely that many people are relying on this behavior. Still, - // will be fixed before stable. It's the very next step in the sequence of - // work on this project. - // - // This line of code works because the App Router treats `null` as - // "no renderable head available", rather than an empty head. React treats - // an empty string as empty. - cachedHead = '' - } - - if (seedHead !== null) { - if (isCachedHeadPartial) { - prefetchHead = cachedHead - head = seedHead - } else { - prefetchHead = null - head = cachedHead - } - doesHeadNeedDynamicRequest = false - } else { - if (isCachedHeadPartial) { - prefetchHead = cachedHead - head = createDeferredRsc() - } else { - prefetchHead = null - head = cachedHead - } - doesHeadNeedDynamicRequest = isCachedHeadPartial - } - } - // Now that we're creating a new segment, write its data to the BFCache. A // subsequent back/forward navigation will reuse this same data, until or // unless it's cleared by a refresh/revalidation. // // Skip BFCache writes for optimistic navigations since they are transient // and will be replaced by the canonical navigation. + const cacheNode = createCacheNode(rsc, prefetchRsc, varyParams, bfcacheId) if (freshness !== FreshnessPolicy.Gesture) { - writeToBFCache( - now, - tree.varyPath, - rsc, - prefetchRsc, - head, - prefetchHead, - dynamicStaleAt, - bfcacheId - ) - if (isPage && metadataVaryPath !== null) { - writeHeadToBFCache( - now, - metadataVaryPath, - head, - prefetchHead, - dynamicStaleAt, - bfcacheId - ) - } + writeToBFCache(now, tree.varyPath, cacheNode, dynamicStaleAt) } return { - cacheNode: createCacheNode(rsc, prefetchRsc, head, prefetchHead, bfcacheId), + node: createRenderTree(tree, cacheNode), // TODO: We should store this field on the CacheNode itself. I think we can // probably unify NavigationTask, CacheNode, and DeferredRsc into a // single type. Or at least CacheNode and DeferredRsc. - needsDynamicRequest: - doesSegmentNeedDynamicRequest || doesHeadNeedDynamicRequest, + needsDynamicRequest: doesSegmentNeedDynamicRequest, } } function createCacheNode( rsc: React.ReactNode | null, prefetchRsc: React.ReactNode | null, - head: React.ReactNode | null, - prefetchHead: HeadData | null, + varyParams: VaryParams | null, bfcacheId: number, scrollRef: ScrollRef | null = null ): CacheNode { return { rsc, prefetchRsc, - head, - prefetchHead, - slots: null, + varyParams, scrollRef, bfcacheId, } @@ -1359,45 +1305,13 @@ function generateBFCacheId(freshness: FreshnessPolicy): number { return ++nextBFCacheId } -const enum SegmentMatchKind { - // Two segments are equivalent: the CacheNode can be reused as-is. - Match, - // The segments differ in the parts that determine the route (segment kind, - // dynamic param value, etc.). The CacheNode must be created fresh. - Change, - // Two page segments differ only in their search params. Conceptually this - // is a refresh of the current page rather than a navigation to a new - // route — search params don't contribute to the LayoutRouter state key, - // and they shouldn't change the bfcacheId either. The CacheNode is rebuilt - // (so data refetches) but the bfcacheId carries forward. - SearchParamOnlyChange, -} - -function compareSegments( - newSegment: Segment, - oldSegment: Segment -): SegmentMatchKind { - if (matchSegment(newSegment, oldSegment)) { - return SegmentMatchKind.Match - } - if ( - typeof newSegment === 'string' && - typeof oldSegment === 'string' && - newSegment.startsWith(PAGE_SEGMENT_KEY) && - oldSegment.startsWith(PAGE_SEGMENT_KEY) - ) { - return SegmentMatchKind.SearchParamOnlyChange - } - return SegmentMatchKind.Change -} - // Represents whether the previuos navigation resulted in a route tree mismatch. // A mismatch results in a refresh of the page. If there are two successive // mismatches, we will fall back to an MPA navigation, to prevent a retry loop. let previousNavigationDidMismatch = false // Writes a dynamic server response into the tree created by -// updateCacheNodeOnNavigation. All pending promises that were spawned by the +// updateRenderTreeOnNavigation. All pending promises that were spawned by the // navigation will be resolved, either with dynamic data from the server, or // `null` to indicate that the data is missing. // @@ -1412,7 +1326,7 @@ let previousNavigationDidMismatch = false // This does _not_ create a new tree; it modifies the existing one in place. // Which means it must follow the Suspense rules of cache safety. export function spawnDynamicRequests( - task: NavigationTask, + navigation: RootNavigationTask, primaryUrl: URL, nextUrl: string | null, freshnessPolicy: FreshnessPolicy, @@ -1432,11 +1346,17 @@ export function spawnDynamicRequests( map: CacheMap, signal: AbortSignal | undefined ): void { - const dynamicRequestTree = task.dynamicRequestTree + let dynamicRequestTree = navigation.tree.dynamicRequestTree if (dynamicRequestTree === null) { - // This navigation was fully cached. There are no dynamic requests to spawn. - previousNavigationDidMismatch = false - return + if (navigation.head.status === NavigationTaskStatus.Pending) { + // Every segment is cached, but the head is not. Ask the server for the + // head alone. + dynamicRequestTree = MetadataOnlyRequestTree + } else { + // This navigation was fully cached. There are no dynamic requests to spawn. + previousNavigationDidMismatch = false + return + } } // This is intentionally not an async function to discourage the caller from @@ -1449,7 +1369,8 @@ export function spawnDynamicRequests( // `finishNavigationTask`, can await the promises in any order without // accidentally introducing a network waterfall. const primaryRequestPromise = fetchMissingDynamicData( - task, + navigation.tree, + navigation.head, dynamicRequestTree, primaryUrl, nextUrl, @@ -1499,7 +1420,9 @@ export function spawnDynamicRequests( if (scopedDynamicRequestTree !== null) { refreshRequestPromises.push( fetchMissingDynamicData( - task, + navigation.tree, + // The head belongs to the primary URL. + null, scopedDynamicRequestTree, new URL(refreshUrl, location.origin), // TODO: Just noticed that this should actually the Next-Url at the @@ -1522,7 +1445,7 @@ export function spawnDynamicRequests( // Further async operations are moved into this separate function to // discourage sequential network requests. const voidPromise = finishNavigationTask( - task, + navigation, nextUrl, primaryRequestPromise, refreshRequestPromises, @@ -1535,7 +1458,7 @@ export function spawnDynamicRequests( } async function finishNavigationTask( - task: NavigationTask, + navigation: RootNavigationTask, nextUrl: string | null, primaryRequestPromise: ReturnType, refreshRequestPromises: Array< @@ -1557,7 +1480,19 @@ async function finishNavigationTask( // first phase; it doesn't matter in that case because we're going to refresh // the whole tree regardless. if (exitStatus === NavigationTaskExitStatus.Done) { - exitStatus = abortRemainingPendingTasks(task, null, null) + exitStatus = abortRemainingPendingTasks(navigation.tree, null, null) + // A response without a head is a mismatch, like any missing segment. The + // head's deferred rsc must be resolved to `null` here, never rejected: it + // renders at the app root, so a rejection would hit the root error + // boundary while the retry is in flight. + const headExitStatus = abortRemainingPendingTasks( + navigation.head, + null, + null + ) + if (headExitStatus > exitStatus) { + exitStatus = headExitStatus + } } switch (exitStatus) { @@ -1587,7 +1522,7 @@ async function finishNavigationTask( primaryRequestResult.url, nextUrl, primaryRequestResult.seed, - task.route, + navigation, routeCacheEntry, navigateType, FreshnessPolicy.RefreshAll @@ -1606,7 +1541,7 @@ async function finishNavigationTask( primaryRequestResult.url, nextUrl, primaryRequestResult.seed, - task.route, + navigation, routeCacheEntry, navigateType, FreshnessPolicy.HistoryTraversal @@ -1629,7 +1564,7 @@ async function finishNavigationTask( primaryRequestResult.url, nextUrl, primaryRequestResult.seed, - task.route, + navigation, routeCacheEntry, navigateType, FreshnessPolicy.RefreshAll @@ -1652,7 +1587,7 @@ function waitForRequestsToFinish( // we don't assume that's available. // // Each promise resolves once the server responsds and the data is written - // into the CacheNode tree. Resolve the combined promise once all the + // into the render tree. Resolve the combined promise once all the // requests finish. // // Or, resolve as soon as one of the requests fails, without waiting for the @@ -1696,7 +1631,7 @@ function dispatchRetryDueToTreeMismatch( retryUrl: URL, retryNextUrl: string | null, seed: NavigationSeed | null, - baseTree: FlightRouterState, + navigation: RootNavigationTask, // The route cache entry used for this navigation, if it came from route // prediction. If the navigation results in a mismatch, we mark it as having // a dynamic rewrite so future predictions bail out. @@ -1725,30 +1660,30 @@ function dispatchRetryDueToTreeMismatch( // mark the route as having a dynamic rewrite by traversing the known route // tree. This handles cases where the navigation didn't originate from a // route prediction, but still needs to mark the pattern. - const metadataVaryPath = seed.metadataVaryPath - if (metadataVaryPath !== null) { - const now = Date.now() - discoverKnownRoute( - now, - retryUrl.pathname, - retryUrl.search as NormalizedSearch, - retryNextUrl, - null, - seed.routeTree, - metadataVaryPath, - false, // couldBeIntercepted - doesn't matter, we're just marking hasDynamicRewrite - createHrefFromUrl(retryUrl), - false, // supportsPerSegmentPrefetching - doesn't matter, we're just marking hasDynamicRewrite - true // hasDynamicRewrite - ) - } + const now = Date.now() + discoverKnownRoute( + now, + retryUrl.pathname, + retryUrl.search as NormalizedSearch, + retryNextUrl, + null, + seed.root, + false, // couldBeIntercepted - doesn't matter, we're just marking hasDynamicRewrite + createHrefFromUrl(retryUrl), + seed.renderedSearch, + false, // supportsPerSegmentPrefetching - doesn't matter, we're just marking hasDynamicRewrite + true // hasDynamicRewrite + ) } // Invalidate all route cache entries. If the navigation used a route entry // the server resolved, its tree is what the server just contradicted, so // the retry must re-fetch it rather than navigate with it again. This also // triggers re-prefetching of visible links. - invalidateRouteCacheEntries(retryNextUrl, baseTree) + invalidateRouteCacheEntries(retryNextUrl, { + tree: navigation.tree.node, + head: navigation.head.node, + }) // If this is the second time in a row that a navigation resulted in a // mismatch, fall back to a hard (MPA) refresh. @@ -1769,6 +1704,7 @@ function dispatchRetryDueToTreeMismatch( // not here where the action is constructed. But the current action queue // doesn't provide a natural place for that. Revisit when we refactor the // action queue into a more reactive navigation model. + const baseTree = navigation.tree.route const lastCommitted = getLastCommittedTree() const retryNavigateType: 'push' | 'replace' = lastCommitted !== null && baseTree !== lastCommitted @@ -1789,7 +1725,8 @@ function dispatchRetryDueToTreeMismatch( } async function fetchMissingDynamicData( - task: NavigationTask, + tree: NavigationTask, + head: NavigationTask | null, dynamicRequestTree: FlightRouterState, url: URL, nextUrl: string | null, @@ -1825,18 +1762,17 @@ async function fetchMissingDynamicData( const seed = createNavigationSeed( now, - task.route, + tree.route, result.transportData, // Navigation responses stream in incrementally, so their vary params - // can't be drained here — and nothing consumes them from a navigation - // seed (only segment-cache writes read vary params, and those decode - // their own, buffered, payloads). + // can't be drained here; they decode as null. null, result.isResponsePartial, // Navigation responses always include the param values in the tree, so // there's no pathname to parse them from (nor a need to). null, result.renderedSearch, + null, result.dynamicStaleTime ) @@ -1877,14 +1813,23 @@ async function fetchMissingDynamicData( const dynamicStaleAt = computeDynamicStaleAt(now, result.dynamicStaleTime) const didReceiveUnknownParallelRoute = writeDynamicDataIntoNavigationTask( - task, - seed.routeTree, - seed.head, + tree, + seed.root.tree, dynamicStaleAt, result.debugInfo, result.revealAfter ) + if (head !== null) { + writeDynamicDataIntoNavigationTask( + head, + seed.root.head, + dynamicStaleAt, + result.debugInfo, + result.revealAfter + ) + } + const resolvedUrl = new URL(result.canonicalUrl, location.origin) // Decide whether the navigation needs to be retried. @@ -1950,7 +1895,6 @@ async function fetchMissingDynamicData( function writeDynamicDataIntoNavigationTask( task: NavigationTask, serverRouteTree: RouteTree, - dynamicHead: HeadData, dynamicStaleAt: number, debugInfo: Array | null, revealAfter: Promise | null @@ -1961,20 +1905,20 @@ function writeDynamicDataIntoNavigationTask( const dynamicData = serverRouteTree.data if (task.status === NavigationTaskStatus.Pending && dynamicData !== null) { task.status = NavigationTaskStatus.Fulfilled - finishPendingCacheNode( - task.node, - dynamicData, - dynamicHead, - debugInfo, - revealAfter + const cacheNode = task.node.data + finishPendingCacheNode(cacheNode, dynamicData, debugInfo, revealAfter) + + // The BFCache entry for this segment was written before the response + // arrived. Bring it up to date with what the response filled in: its + // staleAt (the per-page unstable_dynamicStaleTime if set, or the default + // DYNAMIC_STALETIME_MS) and the source of the params its data depends + // on. We only update segments that received dynamic data — static + // segments are unaffected. + updateBFCacheEntryFromDynamicResponse( + serverRouteTree.varyPath, + cacheNode, + dynamicStaleAt ) - - // Update the BFCache entry's staleAt for this segment with the value - // from the dynamic response. This applies the per-page - // unstable_dynamicStaleTime if set, or the default DYNAMIC_STALETIME_MS. - // We only update segments that received dynamic data — static segments - // are unaffected. - updateBFCacheEntryStaleAt(serverRouteTree.varyPath, dynamicStaleAt) } const taskChildren = task.children @@ -2004,18 +1948,21 @@ function writeDynamicDataIntoNavigationTask( // path. But as an extra precaution, we validate in prod, too. didReceiveUnknownParallelRoute = true } else { - const taskSegment = taskChild.route[0] - const serverSegment = createSegmentFromRouteTree(serverRouteTreeChild) + // Check that the response is for the route we expected: same route + // structure and same params, including the page's search params. if ( - matchSegment(serverSegment, taskSegment) && - serverRouteTreeChild.data !== null + doesRouteStructureMatch(taskChild.node, serverRouteTreeChild) && + serverRouteTreeChild.data !== null && + compareParams( + taskChild.node.varyPath, + serverRouteTreeChild.varyPath + ) === ParamsChange.None ) { // Found a match for this task. Keep traversing down the task tree. const childDidReceiveUnknownParallelRoute = writeDynamicDataIntoNavigationTask( taskChild, serverRouteTreeChild, - dynamicHead, dynamicStaleAt, debugInfo, revealAfter @@ -2040,47 +1987,52 @@ function writeDynamicDataIntoNavigationTask( function finishPendingCacheNode( cacheNode: CacheNode, dynamicData: RSCSegmentData, - dynamicHead: HeadData, debugInfo: Array | null, revealAfter: Promise | null ): void { - // Writes a dynamic response into an existing Cache Node tree. This does _not_ + // Writes a dynamic response into an existing render tree. This does _not_ // create a new tree, it updates the existing tree in-place. So it must follow // the Suspense rules of cache safety — it can resolve pending promises, but // it cannot overwrite existing data. It can add segments to the tree (because - // a missing segment will cause the layout router to suspend). - // but it cannot delete them. + // a missing segment will cause the layout router to suspend) but it cannot + // delete them. // // We must resolve every promise in the tree, or else it will suspend // indefinitely. If we did not receive data for a segment, we will resolve its // data promise to `null` to trigger a lazy fetch during render. - // Use the dynamic data from the server to fulfill the deferred RSC promise - // on the Cache Node. + // Use the dynamic data from the server to fulfill the deferred RSC promise. const rsc = cacheNode.rsc const dynamicSegmentData = dynamicData.rsc if (dynamicSegmentData === null) { - // This is an empty CacheNode; this particular server request did not + // This particular server request did not // render this segment. There may be a separate pending request that will, // though, so we won't abort the task until all pending requests finish. return } + // TODO: `varyParams` must always describe the render that produced `rsc`, + // but nothing in the CacheNode type ties the two fields together; this + // function keeps them in lockstep by writing both at once. Eventually the + // whole CacheNode should be a thenable whose fields are populated through + // dedicated helpers that own the state transition. if (rsc === null) { // This is a lazy cache node. We can overwrite it. This is only safe // because we know that the LayoutRouter suspends if `rsc` is `null`. cacheNode.rsc = dynamicSegmentData - } else if (isDeferredRsc(rsc)) { + cacheNode.varyParams = dynamicData.varyParams + } else if (isDeferredRsc(rsc) && rsc.status === 'pending') { // This is a deferred RSC promise. We can fulfill it with the data we just - // received from the server. If it was already resolved by a different - // navigation, then this does nothing because we can't overwrite data. + // received from the server. The source of the params that data depends + // on travels with it. // // In the streaming dev render, defer the fill until `revealAfter` settles, // so React doesn't render the boundary's children before their row has been // decoded (otherwise it suspends on the still-pending children and commits // a premature fallback). Outside that render `revealAfter` is null and we // resolve immediately. + cacheNode.varyParams = dynamicData.varyParams if (revealAfter !== null) { const resolveRsc = () => rsc.resolve(dynamicSegmentData, debugInfo) // Use the same callback for both outcomes: we don't expect `revealAfter` @@ -2091,16 +2043,9 @@ function finishPendingCacheNode( rsc.resolve(dynamicSegmentData, debugInfo) } } else { - // This is not a deferred RSC promise, nor is it empty, so it must have - // been populated by a different navigation. We must not overwrite it. - } - - // Check if this is a leaf segment. If so, it will have a `head` property with - // a pending promise that needs to be resolved with the dynamic head from - // the server. - const head = cacheNode.head - if (isDeferredRsc(head)) { - head.resolve(dynamicHead, debugInfo) + // This is not a deferred RSC promise that's still pending, nor is it + // empty, so it must have been populated by a different navigation. We + // must not overwrite it (nor its dependency source). } } @@ -2113,7 +2058,7 @@ function abortRemainingPendingTasks( if (task.status === NavigationTaskStatus.Pending) { // The data for this segment is still missing. task.status = NavigationTaskStatus.Rejected - abortPendingCacheNode(task.node, error, debugInfo) + abortPendingCacheNode(task.node.data, error, debugInfo) // If the server failed to fulfill the data for this segment, it implies // that the route tree received from the server mismatched the tree that @@ -2127,7 +2072,7 @@ function abortRemainingPendingTasks( // // When this happens, we treat this the same as a refresh(). The entire // tree will be re-rendered from the root. - if (task.refreshState === null) { + if (task.node.refreshState === null) { // Trigger a "soft" refresh. Essentially the same as calling `refresh()` // in a Server Action. exitStatus = NavigationTaskExitStatus.SoftRetry @@ -2180,15 +2125,6 @@ function abortPendingCacheNode( rsc.reject(error, debugInfo) } } - - // Check if this is a leaf segment. If so, it will have a `head` property with - // a pending promise that needs to be resolved. If an error was provided, we - // will not resolve it with an error, since this is rendered at the root of - // the app. We want the segment to error, not the entire app. - const head = cacheNode.head - if (isDeferredRsc(head)) { - head.resolve(null, debugInfo) - } } const DEFERRED = Symbol() diff --git a/packages/next/src/client/components/router-reducer/compute-changed-path.ts b/packages/next/src/client/components/router-reducer/compute-changed-path.ts index d64d9aa1347c..8bbbd88fbcff 100644 --- a/packages/next/src/client/components/router-reducer/compute-changed-path.ts +++ b/packages/next/src/client/components/router-reducer/compute-changed-path.ts @@ -9,7 +9,6 @@ import { DEFAULT_SEGMENT_KEY, PAGE_SEGMENT_KEY, } from '../../../shared/lib/segment' -import { matchSegment } from '../match-segments' const removeLeadingSlash = (segment: string): string => { return segment[0] === '/' ? segment.slice(1) : segment @@ -30,7 +29,7 @@ const segmentToPathname = (segment: Segment): string => { export const segmentToSourcePagePathname = (segment: Segment): string => { if (typeof segment === 'string') { if (segment === 'children') return '' - if (segment.startsWith(PAGE_SEGMENT_KEY)) return 'page' + if (segment === PAGE_SEGMENT_KEY) return 'page' return segment } @@ -91,7 +90,7 @@ export function extractPathFromFlightRouterState( ) return undefined - if (segment.startsWith(PAGE_SEGMENT_KEY)) return '' + if (segment === PAGE_SEGMENT_KEY) return '' const segments = [segmentToPathname(segment)] const parallelRoutes = flightRouterState[1] ?? {} @@ -185,7 +184,22 @@ function computeChangedPathImpl( return '' } - if (!matchSegment(segmentA, segmentB)) { + // Param types and static sibling hints don't affect the resulting pathname, + // so only compare the segment name and param value. + // TODO: computeChangedPath is only used to compute the Next-Url header. + // We should refactor this data structure to be lower cardinality. For example + // it doesn't need to include any of the concrete param values, just the + // route structure. + const didPathChange = + typeof segmentA === 'string' || typeof segmentB === 'string' + ? segmentA !== segmentB + : segmentA[0] !== segmentB[0] || segmentA[1] !== segmentB[1] + if ( + didPathChange || + // A change to the page's search params counts, too. (They used to be part + // of the page segment string.) + (segmentA === PAGE_SEGMENT_KEY && treeA[5] !== treeB[5]) + ) { // once we find where the tree changed, we compute the rest of the path by traversing the tree return extractPathFromFlightRouterState(treeB) ?? '' } @@ -232,7 +246,7 @@ export function getSelectedParams( const segment = parallelRoute[0] const isDynamicParameter = Array.isArray(segment) const segmentValue = isDynamicParameter ? segment[1] : segment - if (!segmentValue || segmentValue.startsWith(PAGE_SEGMENT_KEY)) continue + if (!segmentValue || segmentValue === PAGE_SEGMENT_KEY) continue // Ensure catchAll and optional catchall are turned into an array const isCatchAll = diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts index 8c8744aca1d1..ace762cd9c9d 100644 --- a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts +++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts @@ -5,17 +5,15 @@ import { extractPathFromFlightRouterState } from './compute-changed-path' import type { AppRouterState } from './router-reducer-types' import { transportNodeToFlightRouterState } from '../../../shared/lib/rsc-transport' -import { createInitialCacheNodeForHydration } from '../render-tree' +import { createInitialRenderTreeForHydration } from '../render-tree' import { writeRuntimePrefetchStreamIntoCache, spawnStaticStageCacheWrite, segmentCacheMap, + createRootRouteTree, } from '../segment-cache/cache' -import { decodeTransportTreeIntoRouteTree } from '../segment-cache/decode-server-response' -import { - UnknownDynamicStaleTime, - computeDynamicStaleAt, -} from '../segment-cache/bfcache' +import { createNavigationSeed } from '../segment-cache/decode-server-response' +import { UnknownDynamicStaleTime } from '../segment-cache/bfcache' import { decodeStageUntilBoundary } from './fetch-server-response' import { discoverKnownRoute } from '../segment-cache/optimistic-routes' import type { NormalizedSearch } from '../segment-cache/cache-key' @@ -51,12 +49,11 @@ export function createInitialRouterState({ // as a URL that should be crawled. const initialCanonicalUrl = initialCanonicalUrlParts.join('/') - const initialHead = initialTransportData.h.r - - // The initial router state tree, derived from the transport tree. Page - // segments keep their search params, which travel inside the segment - // string. - const initialTree = transportNodeToFlightRouterState(initialTransportData.t) + // The initial router state tree, derived from the transport tree. + const initialTree = transportNodeToFlightRouterState( + initialTransportData.t, + initialRenderedSearch + ) const canonicalUrl = // location.href is read as the initial value for canonicalUrl in the browser @@ -66,13 +63,10 @@ export function createInitialRouterState({ createHrefFromUrl(location) : initialCanonicalUrl - // Decode the initial transport tree into the RouteTree type, with the - // payload's render output embedded on each node. (discoverKnownRoute below - // stores this tree in the route cache, which strips the data on write — - // see stripDataFromRouteTree.) - // NOTE: The metadataVaryPath isn't used for anything currently because the - // head is embedded into the CacheNode tree, but eventually we'll lift it out - // and store it on the top-level state object. + // Decode the initial transport data into the RouteTree type, with the + // payload's render output embedded on each node and the head as its own + // one-node tree. (discoverKnownRoute below stores the route tree in the + // route cache, which strips the data on write — see stripDataFromRouteTree.) // // For statically-generated-at-build-time HTML pages, the tree baked into // the initial RSC payload won't have the correct segment inlining hints @@ -80,16 +74,16 @@ export function createInitialRouterState({ // trees with InliningHintsStale, which causes the route cache entry to be // immediately expired. The next prefetch will re-fetch the tree with // correct hints from the /_tree response. - const acc = { metadataVaryPath: null, treeDivergedFromBase: false } - const initialRouteTree = decodeTransportTreeIntoRouteTree( - initialTransportData.t, + const initialSeed = createNavigationSeed( + navigatedAt, // There's no base tree to overlay onto; the initial payload is a full // render from the root. null, + initialTransportData, // The initial payload may still be streaming in while we hydrate, so its - // vary params can't be drained here — and nothing consumes them from - // this tree. The segment-cache write below re-decodes the transport data - // with the payload's root params once the stale time has resolved. + // vary params can't be drained here; they decode as null. The + // segment-cache write below re-decodes the transport data with the + // payload's root params once the stale time has resolved. null, // Same for partiality: only segment-cache writes consume it, and the // write below re-decodes with the payload's actual response-level value. @@ -100,23 +94,20 @@ export function createInitialRouterState({ // see createInitialRSCPayloadFromFallbackPrerender), so there's no // pathname to parse them from. null, - initialRenderedSearch as NormalizedSearch, - acc + initialRenderedSearch, + null, + initialDynamicStaleTimeSeconds ?? UnknownDynamicStaleTime ) - const metadataVaryPath = acc.metadataVaryPath - const initialTask = createInitialCacheNodeForHydration( + const initialRoot = initialSeed.root + const initialNavigation = createInitialRenderTreeForHydration( navigatedAt, - initialRouteTree, - initialHead, - computeDynamicStaleAt( - navigatedAt, - initialDynamicStaleTimeSeconds ?? UnknownDynamicStaleTime - ) + initialRoot, + initialSeed.dynamicStaleAt ) // The following only applies in the browser (location !== null) since neither // route learning nor segment cache state persists from SSR to client. - if (location !== null && metadataVaryPath !== null) { + if (location !== null) { // Learn the route pattern so we can predict it for future navigations. discoverKnownRoute( Date.now(), @@ -124,10 +115,10 @@ export function createInitialRouterState({ location.search as NormalizedSearch, null, // nextUrl — initial render is never an interception null, // No pending entry - initialRouteTree, - metadataVaryPath, + initialRoot, initialCouldBeIntercepted, canonicalUrl, + initialSeed.renderedSearch, initialSupportsPerSegmentPrefetching, false // hasDynamicRewrite ) @@ -245,8 +236,11 @@ export function createInitialRouterState({ // complete tree.) const initialState = { - tree: initialTask.route, - cache: initialTask.node, + tree: initialNavigation.tree.route, + root: createRootRouteTree( + initialNavigation.tree.node, + initialNavigation.head.node + ), pushRef: { pendingPush: false, mpaNavigation: false, diff --git a/packages/next/src/client/components/router-reducer/create-router-cache-key.ts b/packages/next/src/client/components/router-reducer/create-router-cache-key.ts index 70377e0ea5fa..b38611bbe8a2 100644 --- a/packages/next/src/client/components/router-reducer/create-router-cache-key.ts +++ b/packages/next/src/client/components/router-reducer/create-router-cache-key.ts @@ -1,21 +1,11 @@ import type { Segment } from '../../../shared/lib/app-router-types' -import { PAGE_SEGMENT_KEY } from '../../../shared/lib/segment' -export function createRouterCacheKey( - segment: Segment, - withoutSearchParameters: boolean = false -) { +export function createRouterCacheKey(segment: Segment) { // if the segment is an array, it means it's a dynamic segment // for example, ['lang', 'en', 'd']. We need to convert it to a string to store it as a cache node key. if (Array.isArray(segment)) { return `${segment[0]}|${segment[1]}|${segment[2]}` } - // Page segments might have search parameters, ie __PAGE__?foo=bar - // When `withoutSearchParameters` is true, we only want to return the page segment - if (withoutSearchParameters && segment.startsWith(PAGE_SEGMENT_KEY)) { - return PAGE_SEGMENT_KEY - } - return segment } diff --git a/packages/next/src/client/components/router-reducer/create-segment-key.browser.ts b/packages/next/src/client/components/router-reducer/create-segment-key.browser.ts index d8fb04c7c25f..85521147b972 100644 --- a/packages/next/src/client/components/router-reducer/create-segment-key.browser.ts +++ b/packages/next/src/client/components/router-reducer/create-segment-key.browser.ts @@ -1,3 +1,24 @@ +import type { VaryPath, VaryPathNode } from '../segment-cache/vary-path' + // In the browser, React keys include concrete params so navigation preserves // or resets component state according to the segment's actual identity. export { createRouterCacheKey as createSegmentKey } from './create-router-cache-key' + +// The head's key also includes the search params. Otherwise, inside a +// transition, `useDeferredValue` returns the new (still pending) head instead +// of the prefetched one, and the whole navigation suspends. +export function createHeadKey(varyPath: VaryPath): string { + // Encode the parts as a JSON array rather than joining them. A catch-all + // value and a search string can both contain `/`, so joined strings from + // different params could be the same. + const parts: Array = [varyPath.value] + let params: VaryPathNode | null = varyPath.parent + while (params !== null) { + const value = params.value + if (typeof value === 'string') { + parts.push(value) + } + params = params.parent + } + return JSON.stringify(parts) +} diff --git a/packages/next/src/client/components/router-reducer/create-segment-key.test.ts b/packages/next/src/client/components/router-reducer/create-segment-key.test.ts deleted file mode 100644 index 21258e220fc7..000000000000 --- a/packages/next/src/client/components/router-reducer/create-segment-key.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { createRouterCacheKey } from './create-router-cache-key' -import { createSegmentKey } from './create-segment-key' -import { createSegmentKey as createBrowserSegmentKey } from './create-segment-key.browser' - -describe('createSegmentKey on the server', () => { - it.each(['d', 'c', 'oc', 'di(.)', 'ci(.)'] as const)( - 'keeps %s keys stable between prerendering and resuming', - (paramType) => { - const fallbackKey = createSegmentKey([ - 'slug', - '%%drp:slug:abc123%%', - paramType, - null, - ]) - - expect(fallbackKey).toBe(`slug|${paramType}`) - expect(createSegmentKey(['slug', 'first', paramType, null])).toBe( - fallbackKey - ) - expect(createSegmentKey(['slug', 'second/third', paramType, null])).toBe( - fallbackKey - ) - } - ) - - it('distinguishes param names and segment types', () => { - const keys = [ - createSegmentKey(['slug', 'value', 'd', null]), - createSegmentKey(['other', 'value', 'd', null]), - createSegmentKey(['slug', 'value', 'c', null]), - createSegmentKey(['slug', 'value', 'oc', null]), - createSegmentKey(['slug', 'value', 'di(.)', null]), - createSegmentKey(['slug', 'value', 'ci(.)', null]), - ] - - expect(new Set(keys).size).toBe(keys.length) - }) - - it.each(['', 'catalog', '__DEFAULT__', '__PAGE__'])( - 'preserves the static segment %j', - (segment) => { - expect(createSegmentKey(segment)).toBe(segment) - } - ) - - it.each([undefined, false, true])( - 'always omits search params, even with withoutSearchParameters=%s', - (withoutSearchParameters) => { - expect( - createSegmentKey('__PAGE__?{"q":"first"}', withoutSearchParameters) - ).toBe('__PAGE__') - expect( - createSegmentKey('__PAGE__?{"q":"second"}', withoutSearchParameters) - ).toBe('__PAGE__') - } - ) -}) - -describe('createSegmentKey in the browser', () => { - it('re-exports the router cache key implementation', () => { - expect(createBrowserSegmentKey).toBe(createRouterCacheKey) - }) - - it.each(['d', 'c', 'oc', 'di(.)', 'ci(.)'] as const)( - 'includes concrete param values in %s keys', - (paramType) => { - expect(createBrowserSegmentKey(['slug', 'first', paramType, null])).toBe( - `slug|first|${paramType}` - ) - expect( - createBrowserSegmentKey(['slug', 'second/third', paramType, null]) - ).toBe(`slug|second/third|${paramType}`) - } - ) - - it.each([undefined, false, true])( - 'respects withoutSearchParameters=%s', - (withoutSearchParameters) => { - const segment = '__PAGE__?{"q":"first"}' - expect(createBrowserSegmentKey(segment, withoutSearchParameters)).toBe( - withoutSearchParameters ? '__PAGE__' : segment - ) - } - ) -}) diff --git a/packages/next/src/client/components/router-reducer/create-segment-key.ts b/packages/next/src/client/components/router-reducer/create-segment-key.ts index 540f1ea7a11a..e86d15c9f31c 100644 --- a/packages/next/src/client/components/router-reducer/create-segment-key.ts +++ b/packages/next/src/client/components/router-reducer/create-segment-key.ts @@ -1,4 +1,5 @@ import type { Segment } from '../../../shared/lib/app-router-types' +import type { VaryPath } from '../segment-cache/vary-path' import { createRouterCacheKey } from './create-router-cache-key' // React uses these keys to find suspended subtrees when resuming HTML. They @@ -6,14 +7,20 @@ import { createRouterCacheKey } from './create-router-cache-key' // The browser uses the concrete keys instead, so navigation still resets or // preserves state as appropriate. Its bundle uses create-segment-key.browser.ts. // React keys are not embedded in the HTML. -export function createSegmentKey( - segment: Segment, - // Server keys always omit search params, regardless of this browser option. - _withoutSearchParameters?: boolean -): string { +export function createSegmentKey(segment: Segment): string { if (Array.isArray(segment)) { return `${segment[0]}|${segment[2]}` } - return createRouterCacheKey(segment, true) + return createRouterCacheKey(segment) +} + +// TODO: To model this more accurately, we should use React.optimisticKey +// instead. Perhaps a separate Fragment that wraps around the Head: +// where headKey is React.optimisticKey during +// SSR. We should do this for all fallback param values. +export function createHeadKey(varyPath: VaryPath): string { + // The head's vary path starts with its request key: the route structure, + // with param names but no values. + return varyPath.value } diff --git a/packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts b/packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts index b2f575fab110..aef8fb55aa4e 100644 --- a/packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts +++ b/packages/next/src/client/components/router-reducer/is-navigating-to-new-root-layout.ts @@ -1,10 +1,9 @@ -import type { FlightRouterState } from '../../../shared/lib/app-router-types' import { PrefetchHint } from '../../../shared/lib/app-router-types' -import type { RouteTree, RSCSegmentData } from '../segment-cache/cache' +import type { RouteTree } from '../segment-cache/cache' -export function isNavigatingToNewRootLayout( - currentTree: FlightRouterState, - nextTree: RouteTree +export function isNavigatingToNewRootLayout( + currentTree: RouteTree, + nextTree: RouteTree ): boolean { // Decides whether navigating from currentTree to nextTree crosses into a // different root layout, which requires a full-page (MPA-style) navigation. @@ -16,7 +15,7 @@ export function isNavigatingToNewRootLayout( // dynamic param *values*) for the same depth. So we walk the prefix in // lockstep and report a change as soon as the prefixes diverge. const currentInPrefix = - ((currentTree[4] ?? 0) & PrefetchHint.IsRootLayoutOrAbove) !== 0 + (currentTree.prefetchHints & PrefetchHint.IsRootLayoutOrAbove) !== 0 const nextInPrefix = (nextTree.prefetchHints & PrefetchHint.IsRootLayoutOrAbove) !== 0 @@ -38,7 +37,7 @@ export function isNavigatingToNewRootLayout( // (e.g. /[name] for slug1 vs slug2) still resolve to the same /[name]/layout. // E.g. /same/(group1)/layout.js -> /same/(group2)/layout.js: (group1) changed // to (group2) inside the prefix, so the root layout changed. - const currentTreeSegment = currentTree[0] + const currentTreeSegment = currentTree.segment const nextTreeSegment = nextTree.segment if (Array.isArray(currentTreeSegment) && Array.isArray(nextTreeSegment)) { if ( @@ -54,10 +53,10 @@ export function isNavigatingToNewRootLayout( // Keep walking the prefix. (Above the root layout there is only a `children` // slot, but we traverse all slots defensively.) const slots = nextTree.slots - const currentTreeChildren = currentTree[1] + const currentTreeChildren = currentTree.slots if (slots !== null) { for (const [slot, nextTreeChild] of slots) { - const currentTreeChild = currentTreeChildren[slot] + const currentTreeChild = currentTreeChildren?.get(slot) if ( currentTreeChild === undefined || isNavigatingToNewRootLayout(currentTreeChild, nextTreeChild) diff --git a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts deleted file mode 100644 index f6e8f0a7bea8..000000000000 --- a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { - FlightRouterState, - CacheNode, -} from '../../../../shared/lib/app-router-types' -import { DEFAULT_SEGMENT_KEY } from '../../../../shared/lib/segment' -import { createSegmentKey } from '../create-segment-key' - -export function findHeadInCache( - cache: CacheNode, - parallelRoutes: FlightRouterState[1] -): [CacheNode, string] | null { - return findHeadInCacheImpl(cache, parallelRoutes, '') -} - -function findHeadInCacheImpl( - cache: CacheNode, - parallelRoutes: FlightRouterState[1], - keyPrefix: string -): [CacheNode, string] | null { - const isLastItem = Object.keys(parallelRoutes).length === 0 - if (isLastItem) { - // Returns the entire Cache Node of the segment whose head we will render. - return [cache, keyPrefix] - } - - // First try the 'children' parallel route if it exists - // when starting from the "root", this corresponds with the main page component - const parallelRoutesKeys = Object.keys(parallelRoutes).filter( - (key) => key !== 'children' - ) - - // if we are at the root, we need to check the children slot first - if ('children' in parallelRoutes) { - parallelRoutesKeys.unshift('children') - } - - const slots = cache.slots - if (slots !== null) { - for (const key of parallelRoutesKeys) { - const [segment, childParallelRoutes] = parallelRoutes[key] - // If the parallel is not matched and using the default segment, - // skip searching the head from it. - if (segment === DEFAULT_SEGMENT_KEY) { - continue - } - - const childCacheNode = slots[key] - if (!childCacheNode) { - continue - } - - // This key identifies the Head component, not a cache entry. On the - // server it must match even when resuming with previously unknown params. - const segmentKey = createSegmentKey(segment) - - const item = findHeadInCacheImpl( - childCacheNode, - childParallelRoutes, - keyPrefix + '/' + segmentKey - ) - - if (item) { - return item - } - } - } - - return null -} diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index a2666d7c5595..bf20d216cf05 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -46,7 +46,7 @@ export function navigateReducer( url, currentUrl, currentRenderedSearch, - state.cache, + state.root, state.tree, state.nextUrl, FreshnessPolicy.Default, diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts index bfbfd9931576..8de4cae419da 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts @@ -33,8 +33,8 @@ export function refreshReducer( process.env.__NEXT_EXPOSE_TESTING_API && action.bypassCacheInvalidation if (!bypassCacheInvalidation) { const currentNextUrl = state.nextUrl - const currentRouterState = state.tree - invalidateSegmentCacheEntries(currentNextUrl, currentRouterState) + const currentRoot = state.root + invalidateSegmentCacheEntries(currentNextUrl, currentRoot) } // A full refresh has no HMR generation to cancel. return refreshDynamicData(state, FreshnessPolicy.RefreshAll, undefined) @@ -83,6 +83,7 @@ export function refreshDynamicData( true, null, currentRenderedSearch, + null, UnknownDynamicStaleTime ) @@ -99,8 +100,7 @@ export function refreshDynamicData( refreshSeed, currentUrl, currentRenderedSearch, - state.cache, - currentFlightRouterState, + state.root, freshnessPolicy, nextUrlForRefresh, scrollBehavior, diff --git a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts index f6192e5b43c6..1b744a453f36 100644 --- a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts @@ -63,18 +63,16 @@ export function restoreReducer( true, null, renderedSearch, + null, UnknownDynamicStaleTime ) - const task = startPPRNavigation( + const navigation = startPPRNavigation( now, currentUrl, state.renderedSearch, - state.cache, - state.tree, - restoreSeed.routeTree, - restoreSeed.metadataVaryPath, + state.root, + restoreSeed.root, FreshnessPolicy.HistoryTraversal, - null, restoreSeed.dynamicStaleAt, false, accumulation, @@ -84,11 +82,11 @@ export function restoreReducer( false ) - if (task === null) { + if (navigation === null) { return completeHardNavigation(state, restoredUrl, 'replace') } spawnDynamicRequests( - task, + navigation, restoredUrl, restoredNextUrl, FreshnessPolicy.HistoryTraversal, @@ -118,8 +116,7 @@ export function restoreReducer( state, restoredUrl, renderedSearch, - task.node, - task.route, + navigation, restoredNextUrl ) } diff --git a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts index 902d1feaa0b4..764906a376be 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts @@ -365,7 +365,7 @@ export function serverActionReducer( // invalidate both caches until we have a way to detect cookie // mutations on the client. if (revalidationKind === ActionDidRevalidateStaticAndDynamic) { - invalidateEntirePrefetchCache(nextUrl, state.tree) + invalidateEntirePrefetchCache(nextUrl, state.root) } // Start a cooldown before re-prefetching to allow CDN cache @@ -494,26 +494,24 @@ export function serverActionReducer( // tree, so there's no pathname to parse them from (nor a need to). null, flightDataRenderedSearch, + null, UnknownDynamicStaleTime ) // Learn the route pattern so we can predict it for future navigations. - const metadataVaryPath = redirectSeed.metadataVaryPath - if (metadataVaryPath !== null) { - discoverKnownRoute( - now, - redirectUrl.pathname, - redirectUrl.search as NormalizedSearch, - nextUrl, - null, // No pending entry - redirectSeed.routeTree, - metadataVaryPath, - couldBeIntercepted, - redirectCanonicalUrl, - isPrerender, - false // hasDynamicRewrite - ) - } + discoverKnownRoute( + now, + redirectUrl.pathname, + redirectUrl.search as NormalizedSearch, + nextUrl, + null, // No pending entry + redirectSeed.root, + couldBeIntercepted, + redirectCanonicalUrl, + redirectSeed.renderedSearch, + isPrerender, + false // hasDynamicRewrite + ) const navigationLock = getCurrentNavigationLock() return navigateToKnownRoute( @@ -524,8 +522,7 @@ export function serverActionReducer( redirectSeed, currentUrl, currentRenderedSearch, - state.cache, - currentFlightRouterState, + state.root, freshnessPolicy, nextUrl, scrollBehavior, @@ -551,7 +548,7 @@ export function serverActionReducer( redirectUrl, currentUrl, currentRenderedSearch, - state.cache, + state.root, currentFlightRouterState, nextUrl, freshnessPolicy, diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts index a401e3bd4c2f..fbd29207ca11 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts @@ -59,8 +59,7 @@ export function serverPatchReducer( retrySeed, currentUrl, currentRenderedSearch, - state.cache, - state.tree, + state.root, action.freshnessPolicy, retryNextUrl, scrollBehavior, diff --git a/packages/next/src/client/components/router-reducer/router-reducer-types.ts b/packages/next/src/client/components/router-reducer/router-reducer-types.ts index d4937bba7d66..0081ec8a9f2a 100644 --- a/packages/next/src/client/components/router-reducer/router-reducer-types.ts +++ b/packages/next/src/client/components/router-reducer/router-reducer-types.ts @@ -1,3 +1,4 @@ +import type { RootRouteTree } from '../segment-cache/cache' import type { CacheNode, ScrollRef } from '../../../shared/lib/app-router-types' import type { FlightRouterState } from '../../../shared/lib/app-router-types' import type { NavigationSeed } from '../segment-cache/decode-server-response' @@ -185,7 +186,7 @@ export type ScrollHandlerRef = { * When true, the scroll handler uses the navigation-level `scrollRef` * for every segment regardless of per-node state. Used for hash-only * navigations where every segment should be treated as a scroll - * target. When false, the handler checks `cacheNode.scrollRef` + * target. When false, the handler checks `renderTree.data.scrollRef` * instead (per-node), so only segments that actually navigated scroll. */ forceScroll: boolean @@ -210,10 +211,15 @@ export type AppRouterState = { */ tree: FlightRouterState /** - * The cache holds React nodes for every segment that is shown on screen as well as previously shown segments. - * It also holds in-progress data requests. + * The render tree and the document head (see RootRouteTree). The tree holds + * React nodes for every segment that is shown on screen as well as + * previously shown segments, and in-progress data requests. The head is a + * one-node render tree keyed at the metadata vary path. + * + * One object per committed navigation; the prefetch scheduler compares it by + * identity (see PrefetchTask.renderTreeAtTimeOfPrefetch). */ - cache: CacheNode + root: RootRouteTree /** * Decides if the update should create a new history entry and if the navigation has to trigger a browser navigation. */ diff --git a/packages/next/src/client/components/segment-cache/bfcache.ts b/packages/next/src/client/components/segment-cache/bfcache.ts index d2def0b16b46..ff33719fcbb5 100644 --- a/packages/next/src/client/components/segment-cache/bfcache.ts +++ b/packages/next/src/client/components/segment-cache/bfcache.ts @@ -1,5 +1,7 @@ import { DYNAMIC_STALETIME_MS } from '../router-reducer/reducers/navigate-reducer' -import type { SegmentVaryPath } from './vary-path' +import type { CacheNode } from '../../../shared/lib/app-router-types' +import type { VaryParams } from '../../../shared/lib/segment-cache/vary-params-decoding' +import type { VaryPath } from './vary-path' /** * Sentinel value indicating that no per-page dynamic stale time was provided. @@ -32,8 +34,11 @@ import { export type BFCacheEntry = { rsc: React.ReactNode | null prefetchRsc: React.ReactNode | null - head: React.ReactNode | null - prefetchHead: React.ReactNode | null + + // The source of the params `rsc` depends on, copied from the CacheNode that + // wrote this entry (see CacheNode.varyParams). A restored node reads it to + // decide whether a later navigation can keep its data. + varyParams: VaryParams | null // The bfcacheId of the CacheNode that wrote this entry. Restored on // history-traversal navigations so that `useRouter().bfcacheId` is stable @@ -69,28 +74,21 @@ export function invalidateBfCache(): void { export function writeToBFCache( now: number, - varyPath: SegmentVaryPath, - rsc: React.ReactNode, - prefetchRsc: React.ReactNode, - head: React.ReactNode, - prefetchHead: React.ReactNode, - dynamicStaleAt: number, - bfcacheId: number + varyPath: VaryPath, + cacheNode: CacheNode, + dynamicStaleAt: number ): void { if (typeof window === 'undefined') { return } const entry: BFCacheEntry = { - rsc, - prefetchRsc, + rsc: cacheNode.rsc, + prefetchRsc: cacheNode.prefetchRsc, - // TODO: These fields will be removed from both BFCacheEntry and - // SegmentCacheEntry. The head has its own separate cache entry. - head, - prefetchHead, + varyParams: cacheNode.varyParams, - bfcacheId, + bfcacheId: cacheNode.bfcacheId, ref: null, // TODO: This is just a heuristic. Getting the actual size of the segment @@ -113,35 +111,27 @@ export function writeToBFCache( setInCacheMap(bfcacheMap, varyPath, entry, isRevalidation) } -export function writeHeadToBFCache( - now: number, - varyPath: SegmentVaryPath, - head: React.ReactNode, - prefetchHead: React.ReactNode, - dynamicStaleAt: number, - bfcacheId: number -): void { - // Read the special "segment" that represents the head data. - writeToBFCache( - now, - varyPath, - head, - prefetchHead, - null, - null, - dynamicStaleAt, - bfcacheId - ) -} - /** - * Update the staleAt of an existing BFCache entry. Used after a dynamic - * response arrives with a per-page stale time from `unstable_dynamicStaleTime`. - * The per-page value is authoritative — it overrides whatever staleAt was set - * by the default DYNAMIC_STALETIME_MS. + * Patches the entry written for a segment before its dynamic response + * arrived, with what the response filled in on the segment's CacheNode: the + * per-page stale time from `unstable_dynamicStaleTime` (authoritative over + * the default DYNAMIC_STALETIME_MS the entry was written with) and the + * source of the params its data depends on. The entry shares the node's + * deferred `rsc` promise, which the response resolves in place. Only the entry + * that shares the node's `rsc` is updated; a refresh may have replaced the + * entry at the same vary path, and that entry belongs to the newer node. + * + * TODO: This function exists because the entry gets `rsc` when it is written + * but the stale time and vary params only later, through a second write that + * has to find the entry again. The response should fill in all three as one + * unit: make the pending CacheNode itself the thenable (like DeferredRsc, but + * for the whole node) with an explicit pending → fulfilled/rejected + * transition, so the entry holds the node and observes its resolution + * directly, with nothing to look up or patch afterwards. */ -export function updateBFCacheEntryStaleAt( - varyPath: SegmentVaryPath, +export function updateBFCacheEntryFromDynamicResponse( + varyPath: VaryPath, + cacheNode: CacheNode, newStaleAt: number ): void { if (typeof window === 'undefined') { @@ -157,14 +147,13 @@ export function updateBFCacheEntryStaleAt( isRevalidation, false ) - if (entry !== null) { + if (entry !== null && entry.rsc === cacheNode.rsc) { entry.staleAt = newStaleAt + entry.varyParams = cacheNode.varyParams } } -export function readFromBFCache( - varyPath: SegmentVaryPath -): BFCacheEntry | null { +export function readFromBFCache(varyPath: VaryPath): BFCacheEntry | null { if (typeof window === 'undefined') { return null } @@ -184,7 +173,7 @@ export function readFromBFCache( export function readFromBFCacheDuringRegularNavigation( now: number, - varyPath: SegmentVaryPath + varyPath: VaryPath ): BFCacheEntry | null { if (typeof window === 'undefined') { return null diff --git a/packages/next/src/client/components/segment-cache/cache-map.ts b/packages/next/src/client/components/segment-cache/cache-map.ts index eebc1581aa04..0787cde68ac8 100644 --- a/packages/next/src/client/components/segment-cache/cache-map.ts +++ b/packages/next/src/client/components/segment-cache/cache-map.ts @@ -1,4 +1,4 @@ -import type { VaryPath } from './vary-path' +import type { VaryPathNode } from './vary-path' import { lruPut, updateLruSize, deleteFromLru } from './lru' /** @@ -160,7 +160,7 @@ export function createCacheMap(): CacheMap { function getOrInitialize( cacheMap: CacheMap, - keys: VaryPath, + keys: VaryPathNode, isRevalidation: boolean ): MapEntry { // Go through each level of keys until we find the entry that matches, or @@ -170,7 +170,7 @@ function getOrInitialize( // Unlike getWithFallback, it will not access fallback entries unless it's // explicitly part of the keypath. let entry = cacheMap - let remainingKeys: VaryPath | null = keys + let remainingKeys: VaryPathNode | null = keys let key: unknown | null = null while (true) { const previousKey = key @@ -230,7 +230,7 @@ export function getFromCacheMap( now: number, currentCacheVersion: number, rootEntry: CacheMap, - keys: VaryPath, + keys: VaryPathNode, isRevalidation: boolean, // When true, terminal entries whose status is not Fulfilled are skipped, so // the lookup falls through to a less-specific Fallback entry. Use this @@ -301,7 +301,7 @@ function getEntryWithFallbackImpl( now: number, currentCacheVersion: number, entry: MapEntry, - keys: VaryPath | null, + keys: VaryPathNode | null, isRevalidation: boolean, previousKey: unknown | null, onlyMatchFulfilled: boolean @@ -317,7 +317,7 @@ function getEntryWithFallbackImpl( // are treated as non-matches, so the recursion will continue searching for // a Fallback match. See getFromCacheMap for the rationale. let key - let remainingKeys: VaryPath | null + let remainingKeys: VaryPathNode | null if (keys !== null) { key = keys.value remainingKeys = keys.parent @@ -373,7 +373,7 @@ function getEntryWithFallbackImpl( export function setInCacheMap( cacheMap: CacheMap, - keys: VaryPath, + keys: VaryPathNode, value: V, isRevalidation: boolean ): void { diff --git a/packages/next/src/client/components/segment-cache/cache.ts b/packages/next/src/client/components/segment-cache/cache.ts index 2cd2bad5f876..4cfc9325bfd5 100644 --- a/packages/next/src/client/components/segment-cache/cache.ts +++ b/packages/next/src/client/components/segment-cache/cache.ts @@ -1,10 +1,15 @@ +import type { CacheNode, Segment } from '../../../shared/lib/app-router-types' import type React from 'react' -import type { Segment as FlightRouterStateSegment } from '../../../shared/lib/app-router-types' import { PrefetchHint, StaticAttemptHints, } from '../../../shared/lib/app-router-types' -import type { VaryParams } from '../../../shared/lib/segment-cache/vary-params-decoding' +import { + createVaryParams, + readVaryParams, + SEARCH_PARAMS_VARY_ID, + type VaryParams, +} from '../../../shared/lib/segment-cache/vary-params-decoding' import { readFulfilledValue } from '../../../shared/lib/rsc-transport' import { NEXT_DID_POSTPONE_HEADER, @@ -33,19 +38,15 @@ import { } from './scheduler' import { type RouteVaryPath, - type SegmentVaryPath, - type PartialSegmentVaryPath, + type VaryPath, + type PartialVaryPath, getRouteVaryPath, getFulfilledRouteVaryPath, getFulfilledSegmentVaryPath, getSegmentVaryPathForRequest, getShellSegmentVaryPath, - clonePageVaryPathWithNewSearchParams, - type PageVaryPath, - type LayoutVaryPath, - getPartialPageVaryPath, - getPartialLayoutVaryPath, - getRenderedSearchFromVaryPath, + cloneVaryPathWithNewSearchParams, + getPartialVaryPath, } from './vary-path' import { createHrefFromUrl } from '../router-reducer/create-href-from-url' import type { @@ -59,6 +60,7 @@ import { getPathnameFromRequestURL, getRenderedPathname, getRenderedSearch, + normalizeRenderedSearch, } from '../../route-params' import { createCacheMap, @@ -185,10 +187,9 @@ export type RSCSegmentData = { */ isPartial: boolean /** - * The params this segment's output depends on (root params already - * unioned in), drained from the response's wire iterables at decode. Null - * means unknown — tracking wasn't enabled, or the decode had no root - * params to union in — so consumers key on all params. + * The source of the params this segment's output depends on (root params + * included). Null means unknown — tracking wasn't enabled, or the decode + * had no root params to union in — so consumers key on all params. */ varyParams: VaryParams | null /** @@ -199,26 +200,17 @@ export type RSCSegmentData = { staleTimeSeconds: number | null } -type RouteTreeShared = { +export type RouteTree = { requestKey: SegmentRequestKey - // TODO: Remove the `segment` field, now that it can be reconstructed - // from `param`. - segment: FlightRouterStateSegment + segment: Segment + varyPath: VaryPath // The vary path used for shell-scoped keying of this segment: the // segment's vary path with every non-root param replaced with Fallback // (see getShellSegmentVaryPath), so one shell-tier entry serves all param // values below the root. Precomputed once during tree construction so we // don't have to recompute it on every shell request. - shellVaryPath: SegmentVaryPath + shellVaryPath: VaryPath refreshState: RefreshState | null - // Render output for this segment, when the tree was created from a server - // response that rendered it. The type parameter encodes a lifecycle - // invariant: trees stored long-term in the route cache - // (RouteCacheEntry.tree / .metadata) are RouteTree — structure only — - // so RSC payloads can never be pinned in memory outside the segment - // cache's eviction control. Trees that carry data must be transient: - // created for a navigation or cache-write, then dropped once the data is - // transferred into CacheNodes / SegmentCacheEntries. data: TData // Keyed by parallel route slot name. Stored as a Map rather than a plain // object because slot names are app-defined; with a plain object, every @@ -236,17 +228,38 @@ export type RefreshState = { renderedSearch: NormalizedSearch } -type LayoutRouteTree = RouteTreeShared & { - isPage: false - varyPath: LayoutVaryPath +// A route's complete render structure. The head is fetched, cached, and +// rendered like a page segment, but it has no position in the route tree, so +// it sits beside the tree as its own one-node tree (see +// createMetadataRouteTree). This is the shape a server response decodes to, +// the router state holds, and a navigation produces. +export type RootRouteTree = { + tree: RouteTree + head: RouteTree } -type PageRouteTree = RouteTreeShared & { - isPage: true - varyPath: PageVaryPath +export function createRootRouteTree( + tree: RouteTree, + head: RouteTree +): RootRouteTree { + return { tree, head } } -export type RouteTree = LayoutRouteTree | PageRouteTree +export function doesRouteStructureMatch( + currentTree: RouteTree, + nextTree: RouteTree +): boolean { + // The request key includes the param names and types, but not the values. + if (currentTree.requestKey !== nextTree.requestKey) { + return false + } + // The root request key is always empty (even for global Not Found), so we + // have to compare the root segment directly. + return ( + nextTree.requestKey !== ROOT_SEGMENT_REQUEST_KEY || + currentTree.segment === nextTree.segment + ) +} type RouteCacheEntryShared = { // This is false only if we're certain the route cannot be intercepted. It's @@ -278,8 +291,7 @@ export type PendingRouteCacheEntry = RouteCacheEntryShared & { blockedTasks: Set | null canonicalUrl: null renderedSearch: null - tree: null - metadata: null + root: null supportsPerSegmentPrefetching: false } @@ -288,8 +300,7 @@ type RejectedRouteCacheEntry = RouteCacheEntryShared & { blockedTasks: Set | null canonicalUrl: null renderedSearch: null - tree: null - metadata: null + root: null supportsPerSegmentPrefetching: boolean } @@ -298,8 +309,7 @@ export type FulfilledRouteCacheEntry = RouteCacheEntryShared & { blockedTasks: null canonicalUrl: string renderedSearch: NormalizedSearch - tree: RouteTree - metadata: RouteTree + root: RootRouteTree supportsPerSegmentPrefetching: boolean } @@ -382,6 +392,12 @@ export type FulfilledSegmentCacheEntry = SegmentCacheEntryShared & { blockedTasks: null rsc: React.ReactNode | null isPartial: boolean + // The source of the params `rsc` depends on, recorded under exactly the + // condition the entry's key trusts it (see the re-key derivation in + // writeSegmentDataIntoCache). Null means unknown: consumers assume the + // output depends on every param. A navigation that renders this entry's + // `rsc` as its final data carries it onto the CacheNode. + varyParams: VaryParams | null promise: null } @@ -465,13 +481,13 @@ export function getCurrentSegmentCacheVersion(): number { */ export function invalidateEntirePrefetchCache( nextUrl: string | null, - tree: FlightRouterState + root: RootRouteTree ): void { currentRouteCacheVersion++ currentSegmentCacheVersion++ - pingVisibleLinks(nextUrl, tree) - pingInvalidationListeners(nextUrl, tree) + pingVisibleLinks(nextUrl, root) + pingInvalidationListeners(nextUrl, root) } /** @@ -483,12 +499,12 @@ export function invalidateEntirePrefetchCache( */ export function invalidateRouteCacheEntries( nextUrl: string | null, - tree: FlightRouterState + root: RootRouteTree ): void { currentRouteCacheVersion++ - pingVisibleLinks(nextUrl, tree) - pingInvalidationListeners(nextUrl, tree) + pingVisibleLinks(nextUrl, root) + pingInvalidationListeners(nextUrl, root) } /** @@ -500,12 +516,12 @@ export function invalidateRouteCacheEntries( */ export function invalidateSegmentCacheEntries( nextUrl: string | null, - tree: FlightRouterState + root: RootRouteTree ): void { currentSegmentCacheVersion++ - pingVisibleLinks(nextUrl, tree) - pingInvalidationListeners(nextUrl, tree) + pingVisibleLinks(nextUrl, root) + pingInvalidationListeners(nextUrl, root) } function attachInvalidationListener(task: PrefetchTask): void { @@ -545,7 +561,7 @@ function notifyInvalidationListener(task: PrefetchTask): void { export function pingInvalidationListeners( nextUrl: string | null, - tree: FlightRouterState + root: RootRouteTree ): void { // The rough equivalent of pingVisibleLinks, but for onInvalidate callbacks. // This is called when the Next-Url or the base tree changes, since those @@ -555,7 +571,7 @@ export function pingInvalidationListeners( const tasks = invalidationListeners invalidationListeners = null for (const task of tasks) { - if (isPrefetchTaskDirty(task, nextUrl, tree)) { + if (isPrefetchTaskDirty(task, nextUrl, root)) { notifyInvalidationListener(task) } } @@ -612,7 +628,7 @@ export function readSegmentCacheEntryForNavigation( // The map the navigation is bound to: a locked navigation's driving-task // map, or the shared map otherwise. map: CacheMap, - varyPath: SegmentVaryPath, + varyPath: VaryPath, restrictToShell: boolean = false ): SegmentCacheEntry | null { const isRevalidation = false @@ -653,7 +669,7 @@ export function readSegmentCacheEntryForNavigation( function readRevalidatingSegmentCacheEntry( now: number, map: CacheMap, - varyPath: SegmentVaryPath + varyPath: VaryPath ): SegmentCacheEntry | null { const isRevalidation = true return getFromCacheMap( @@ -686,8 +702,7 @@ function createDetachedRouteCacheEntry(): PendingRouteCacheEntry { canonicalUrl: null, status: EntryStatus.Empty, blockedTasks: null, - tree: null, - metadata: null, + root: null, // This is initialized to true because we don't know yet whether the route // could be intercepted. It's only set to false once we receive a response // from the server. @@ -815,7 +830,7 @@ export function deprecated_requestOptimisticRouteCacheEntry( routeWithNoSearchParams.renderedSearch !== '' ? // Base route was rewritten. Reuse the same rewritten search string. routeWithNoSearchParams.renderedSearch - : requestedSearch + : normalizeRenderedSearch(requestedSearch) const optimisticUrl = new URL( routeWithNoSearchParams.canonicalUrl, @@ -825,12 +840,17 @@ export function deprecated_requestOptimisticRouteCacheEntry( const optimisticCanonicalUrl = createHrefFromUrl(optimisticUrl) const optimisticRouteTree = deprecated_createOptimisticRouteTree( - routeWithNoSearchParams.tree, + routeWithNoSearchParams.root.tree, optimisticRenderedSearch ) - const optimisticMetadataTree = deprecated_createOptimisticRouteTree( - routeWithNoSearchParams.metadata, - optimisticRenderedSearch + const baseMetadataTree = routeWithNoSearchParams.root.head + const optimisticMetadataTree = createMetadataRouteTree( + cloneVaryPathWithNewSearchParams( + baseMetadataTree.varyPath, + optimisticRenderedSearch + ), + baseMetadataTree.prefetchHints, + null ) // Clone the base route tree, and override the relevant fields with our @@ -841,8 +861,7 @@ export function deprecated_requestOptimisticRouteCacheEntry( status: EntryStatus.Fulfilled, // This isn't cloned because it's instance-specific blockedTasks: null, - tree: optimisticRouteTree, - metadata: optimisticMetadataTree, + root: createRootRouteTree(optimisticRouteTree, optimisticMetadataTree), couldBeIntercepted: routeWithNoSearchParams.couldBeIntercepted, supportsPerSegmentPrefetching: routeWithNoSearchParams.supportsPerSegmentPrefetching, @@ -882,37 +901,20 @@ function deprecated_createOptimisticRouteTree( } } - // We only need to clone the vary path if the route is a page. - if (tree.isPage) { - // The shell vary path Fallbacks search params, so it's unaffected by the - // new rendered search and can be reused as-is. - return { - requestKey: tree.requestKey, - segment: tree.segment, - shellVaryPath: tree.shellVaryPath, - refreshState: tree.refreshState, - // Optimistic trees are structure-only. (The input tree comes from the - // route cache, which never carries render output.) - data: null, - varyPath: clonePageVaryPathWithNewSearchParams( - tree.varyPath, - newRenderedSearch - ), - isPage: true, - slots: clonedSlots, - - prefetchHints: tree.prefetchHints, - } - } - + // The shell vary path Fallbacks search params, so it's unaffected by the + // new rendered search and can be reused as-is. return { requestKey: tree.requestKey, segment: tree.segment, shellVaryPath: tree.shellVaryPath, refreshState: tree.refreshState, + // Optimistic trees are structure-only. (The input tree comes from the + // route cache, which never carries render output.) data: null, - varyPath: tree.varyPath, - isPage: false, + varyPath: cloneVaryPathWithNewSearchParams( + tree.varyPath, + newRenderedSearch + ), slots: clonedSlots, prefetchHints: tree.prefetchHints, } @@ -1082,7 +1084,7 @@ export function upsertSegmentEntry( // testing-lock scope boundary still writes into the map its entries // live in. map: CacheMap, - varyPath: SegmentVaryPath, + varyPath: VaryPath, candidateEntry: SegmentCacheEntry, // The fully concrete vary path a read for this segment position resolves // against (all concrete param values, i.e. `tree.varyPath`) — the most @@ -1091,7 +1093,7 @@ export function upsertSegmentEntry( // Used to detect and evict stale entries at more specific keypaths that // would otherwise shadow the candidate. Pass null when there's no request // context; the shadow check is skipped. - lookupVaryPath: SegmentVaryPath | null + lookupVaryPath: VaryPath | null ): SegmentCacheEntry | null { // We have a new entry that has not yet been inserted into the cache. Before // we do so, we need to confirm whether it takes precedence over the existing @@ -1208,7 +1210,7 @@ export function upsertSegmentEntry( function evictShadowingSegmentEntries( now: number, map: CacheMap, - lookupVaryPath: SegmentVaryPath, + lookupVaryPath: VaryPath, candidateEntry: SegmentCacheEntry ): void { // There can in principle be multiple shadowing entries at successively less @@ -1352,6 +1354,7 @@ export function attemptToFulfillDynamicSegmentFromBFCache( bfcacheEntry.rsc, dynamicPrefetchStaleAt, isPartial, + bfcacheEntry.varyParams, // bfcache data is concrete, never an ISR fallback. false, FetchStrategy.Full @@ -1390,6 +1393,7 @@ export function attemptToUpgradeSegmentFromBFCache( bfcacheEntry.rsc, dynamicPrefetchStaleAt, isPartial, + bfcacheEntry.varyParams, // bfcache data is concrete, never an ISR fallback. false, FetchStrategy.Full @@ -1427,28 +1431,50 @@ function pingBlockedTasks(entry: { } } -export function createMetadataRouteTree( - metadataVaryPath: PageVaryPath, +/** + * The head's request key on the client. The server's own key for the head, + * HEAD_REQUEST_KEY, carries no path information: there is only one head per + * URL, so the server has no need to distinguish parallel pages. On the client + * the request key is the head's cache identity and what doesRouteStructureMatch + * compares, so the head takes its page's request key with HEAD_REQUEST_KEY + * appended — the key the server would have assigned had the head been a + * segment below the page — and two pages' heads never match. The head varies + * on the same params as its page, so the rest of its vary path is the page's. + * The page must be the route's own: a page in a slot retained from another + * URL (one with a refresh state) belongs to that URL's head. When a route has + * multiple parallel pages of its own, the first one is used; the keys only + * differ in route groups and slot names, so any of them works as long as it + * is always the same one. + */ +export function getHeadRequestKey( + pageRequestKey: SegmentRequestKey +): SegmentRequestKey { + return (pageRequestKey + HEAD_REQUEST_KEY) as SegmentRequestKey +} + +export function createMetadataRouteTree( + metadataVaryPath: VaryPath, // The route root's prefetch hints. The head has no node of its own on the - // wire, so route-level hints are read from the root on its behalf — the - // same convention as pingStaticHead in scheduler.ts. - rootPrefetchHints: number -): RouteTree { - // The Head is not actually part of the route tree, but other than that, it's - // fetched and cached like a segment. Some functions expect a RouteTree - // object, so rather than fork the logic in all those places, we use this - // "fake" one. - const metadata: RouteTree = { - requestKey: HEAD_REQUEST_KEY, + // wire, so the route-level hint that applies to it is copied from the root. + rootPrefetchHints: number, + // The head's payload, with the same lifetimes as a segment node's `data` + // (see RouteTree): null in the route cache, the response's decoded + // head on a navigation seed, a CacheNode on the router state. + data: TData +): RouteTree { + // The head is a one-node tree beside the route tree (see RootRouteTree). It + // has no position in the route tree, but it's fetched, cached, compared, and + // rendered like a segment, so it is a RouteTree node like any other. + const metadata: RouteTree = { + // The first entry of the head's vary path (see getHeadRequestKey). The + // server knows nothing of this key; it is always asked for + // HEAD_REQUEST_KEY, which is why the segment stays the bare marker. + requestKey: metadataVaryPath.value, segment: HEAD_REQUEST_KEY, shellVaryPath: getShellSegmentVaryPath(metadataVaryPath), refreshState: null, - data: null, + data, varyPath: metadataVaryPath, - // The metadata isn't really a "page" (though it isn't really a "segment" - // either) but for the purposes of how this field is used, it behaves like - // one. If this logic ever gets more complex we can change this to an enum. - isPage: true, slots: null, // Only the static-attempt bits apply to the head: it's a route-level // fact ("static per-segment responses may exist for this route"), and @@ -1466,7 +1492,7 @@ export function createMetadataRouteTree( * the subtrees that carry data. Called when a tree is stored in the route * cache: route cache entries live indefinitely, so retaining render output * there would pin RSC payloads in memory outside the segment cache's eviction - * control. See the lifecycle note on RouteTreeShared. + * control. See the lifecycle note on RouteTree. */ function stripDataFromRouteTree( tree: RouteTree @@ -1499,19 +1525,6 @@ function stripDataFromRouteTree( string, RouteTree > | null - if (tree.isPage) { - return { - requestKey: tree.requestKey, - segment: tree.segment, - shellVaryPath: tree.shellVaryPath, - refreshState: tree.refreshState, - data: null, - varyPath: tree.varyPath, - isPage: true, - slots: strippedSlots, - prefetchHints: tree.prefetchHints, - } - } return { requestKey: tree.requestKey, segment: tree.segment, @@ -1519,7 +1532,6 @@ function stripDataFromRouteTree( refreshState: tree.refreshState, data: null, varyPath: tree.varyPath, - isPage: false, slots: strippedSlots, prefetchHints: tree.prefetchHints, } @@ -1528,21 +1540,18 @@ function stripDataFromRouteTree( export function fulfillRouteCacheEntry( now: number, entry: PendingRouteCacheEntry, - tree: RouteTree, - metadataVaryPath: PageVaryPath, + root: RootRouteTree, couldBeIntercepted: boolean, canonicalUrl: string, + renderedSearch: NormalizedSearch, supportsPerSegmentPrefetching: boolean ): FulfilledRouteCacheEntry { - // Get the rendered search from the vary path - const renderedSearch = - getRenderedSearchFromVaryPath(metadataVaryPath) ?? ('' as NormalizedSearch) + const tree = root.tree const fulfilledEntry: FulfilledRouteCacheEntry = entry as any fulfilledEntry.status = EntryStatus.Fulfilled - fulfilledEntry.tree = stripDataFromRouteTree(tree) - fulfilledEntry.metadata = createMetadataRouteTree( - metadataVaryPath, - tree.prefetchHints + fulfilledEntry.root = createRootRouteTree( + stripDataFromRouteTree(tree), + stripDataFromRouteTree(root.head) ) // Route structure is essentially static — it only changes on deploy. // Always use the static stale time. @@ -1572,20 +1581,20 @@ export function writeRouteIntoCache( pathname: NormalizedPathname, search: NormalizedSearch, nextUrl: string | null, - tree: RouteTree, - metadataVaryPath: PageVaryPath, + root: RootRouteTree, couldBeIntercepted: boolean, canonicalUrl: string, + renderedSearch: NormalizedSearch, supportsPerSegmentPrefetching: boolean ): FulfilledRouteCacheEntry { const pendingEntry = createDetachedRouteCacheEntry() const fulfilledEntry = fulfillRouteCacheEntry( now, pendingEntry, - tree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) const varyPath = getFulfilledRouteVaryPath( @@ -1604,6 +1613,7 @@ function fulfillSegmentCacheEntry( rsc: React.ReactNode, staleAt: number, isPartial: boolean, + varyParams: VaryParams | null, // Only static (per-segment PPR) responses can be ISR fallbacks; all other // callers pass false. Always assigned (even when false) so that re-fulfilling // a previously-fallback entry with a concrete response clears the flag and @@ -1626,6 +1636,7 @@ function fulfillSegmentCacheEntry( fulfilledEntry.rsc = rsc fulfilledEntry.staleAt = staleAt fulfilledEntry.isPartial = isPartial + fulfilledEntry.varyParams = varyParams fulfilledEntry.isUpgradeableISRFallback = isUpgradeableISRFallback fulfilledEntry.fetchStrategy = fetchStrategy // Resolve any listeners that were waiting for this data. @@ -1665,7 +1676,7 @@ function rejectSegmentCacheEntry( } export type RouteTreeAccumulator = { - metadataVaryPath: PageVaryPath | null + metadataVaryPath: VaryPath | null // Whether the decoded tree's segment identities diverged from the base // tree it was overlaid onto. See NavigationSeed.treeDivergedFromBase. treeDivergedFromBase: boolean @@ -1685,44 +1696,37 @@ export function convertRootFlightRouterStateToRouteTree( ) } -export function convertReusedFlightRouterStateToRouteTree( - parentRouteTree: RouteTree, - parallelRouteKey: string, - flightRouterState: FlightRouterState, - renderedSearch: NormalizedSearch, - acc: RouteTreeAccumulator -) { - // Create a RouteTree for a FlightRouterState that was reused from an older - // route. This happens during a navigation when a parallel route slot does not - // match the target route; we reuse whatever slot was already active. - - // Unlike a FlightRouterState, the RouteTree type contains backreferences to - // the parent segments. Append the vary path to the parent's vary path. - const parentPartialVaryPath = parentRouteTree.isPage - ? getPartialPageVaryPath(parentRouteTree.varyPath) - : getPartialLayoutVaryPath(parentRouteTree.varyPath) - const segment = flightRouterState[0] - // And the request key. - const parentRequestKey = parentRouteTree.requestKey - const requestKeyPart = createSegmentRequestKeyPart(segment) - const requestKey = appendSegmentRequestKeyPart( - parentRequestKey, - parallelRouteKey, - requestKeyPart - ) - return convertFlightRouterStateToRouteTree( - flightRouterState, - requestKey, - parentPartialVaryPath, - renderedSearch, - acc - ) +export function rebaseInactiveRouteTree( + treeToRebase: RouteTree +): RouteTree { + // A parallel route slot that the target route doesn't provide keeps the + // slot already active on the current route, under the new parent. The slot + // sits at the same route position, so its request keys and vary paths are + // unchanged: copy the structure and drop the payloads, which belong to the + // previous render and are reused separately by render-tree. + let slots: Map> | null = null + if (treeToRebase.slots !== null) { + slots = new Map() + for (const [parallelRouteKey, child] of treeToRebase.slots) { + slots.set(parallelRouteKey, rebaseInactiveRouteTree(child)) + } + } + return { + requestKey: treeToRebase.requestKey, + segment: treeToRebase.segment, + varyPath: treeToRebase.varyPath, + shellVaryPath: treeToRebase.shellVaryPath, + refreshState: treeToRebase.refreshState, + data: null, + slots, + prefetchHints: treeToRebase.prefetchHints, + } } export function convertFlightRouterStateToRouteTree( flightRouterState: FlightRouterState, requestKey: SegmentRequestKey, - parentPartialVaryPath: PartialSegmentVaryPath | null, + parentPartialVaryPath: PartialVaryPath | null, parentRenderedSearch: NormalizedSearch, acc: RouteTreeAccumulator ): RouteTree { @@ -1745,6 +1749,9 @@ export function convertFlightRouterStateToRouteTree( renderedSearch: compressedRefreshState[1] as NormalizedSearch, } : null + // Use the incoming search params, even if the response has no new data for + // this segment. The base tree may still have the previous URL's. History + // restores pass in their saved search params instead. const renderedSearch = refreshState !== null ? refreshState.renderedSearch : parentRenderedSearch @@ -1754,12 +1761,10 @@ export function convertFlightRouterStateToRouteTree( requestKey, parentPartialVaryPath, renderedSearch, + refreshState, acc ) - tree.refreshState = refreshState - const partialVaryPath = tree.isPage - ? getPartialPageVaryPath(tree.varyPath) - : getPartialLayoutVaryPath(tree.varyPath) + const partialVaryPath = getPartialVaryPath(tree.varyPath) let slots: Map> | null = null @@ -1794,8 +1799,8 @@ export function convertFlightRouterStateToRouteTree( return tree } -export function convertRouteTreeToFlightRouterState( - routeTree: RouteTree +export function convertRouteTreeToFlightRouterState( + routeTree: RouteTree ): FlightRouterState { const parallelRoutes: Record = {} const slots = routeTree.slots @@ -2027,10 +2032,17 @@ export async function fetchRouteOnCacheMiss( search, nextUrl, entry, - routeTree, - metadataVaryPath, + { + tree: routeTree, + head: createMetadataRouteTree( + metadataVaryPath, + routeTree.prefetchHints, + null + ), + }, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching, false // hasDynamicRewrite ) @@ -2191,17 +2203,23 @@ async function fetchAndWritePerSegmentPrefetchResponse( const url = new URL(route.canonicalUrl, location.origin) const nextUrl = routeKey.nextUrl - const requestKey = tree.requestKey - const normalizedRequestKey = - requestKey === ROOT_SEGMENT_REQUEST_KEY - ? // The root segment is a special case. To simplify the server-side - // handling of these requests, we encode the root segment path as - // `_index` instead of as an empty string. This should be treated as - // an implementation detail and not as a stable part of the protocol. - // It just needs to match the equivalent logic that happens when - // prerendering the responses. It should not leak outside of Next.js. - ('/_index' as SegmentRequestKey) - : requestKey + let normalizedRequestKey: SegmentRequestKey + if (tree.segment === HEAD_REQUEST_KEY) { + // The head's request key is its client cache identity; the server only + // knows the head by its bare marker, which is also the head node's + // segment (see createMetadataRouteTree). + normalizedRequestKey = HEAD_REQUEST_KEY + } else if (tree.requestKey === ROOT_SEGMENT_REQUEST_KEY) { + // The root segment is a special case. To simplify the server-side + // handling of these requests, we encode the root segment path as + // `_index` instead of as an empty string. This should be treated as + // an implementation detail and not as a stable part of the protocol. + // It just needs to match the equivalent logic that happens when + // prerendering the responses. It should not leak outside of Next.js. + normalizedRequestKey = '/_index' as SegmentRequestKey + } else { + normalizedRequestKey = tree.requestKey + } const headers: RequestHeaders = { [RSC_HEADER]: '1', @@ -2367,12 +2385,12 @@ async function fetchAndWritePerSegmentPrefetchResponse( // response-level value — so the conservative value (true) is passed. // - The head is keyed at the route's own metadata vary path: the head has // no tree position, so the decode could only derive a vary path for it - // from a page node in the payload's own tree, which a standalone head - // response (a bare root identity) doesn't have. - // (createMetadataRouteTree stores a PageVaryPath in `varyPath`, so the - // cast is sound.) + // from a page node in the payload's own tree. A per-segment response + // carries only the spine from the root to the requested segment, so any + // response whose terminal isn't a page has no page node to key the head + // from; the standalone head response is one such case. const now = Date.now() - const metadataVaryPath = route.metadata.varyPath as PageVaryPath + const metadataVaryPath = route.root.head.varyPath writeResponsePayloadsIntoCache( now, fetchStrategy, @@ -2518,23 +2536,33 @@ export async function fetchSegmentPrefetchesUsingRuntimeRequest( | FetchStrategy.PPRRuntime | FetchStrategy.RuntimeShell | FetchStrategy.Full, - dynamicRequestTree: FlightRouterState, + requestTree: FlightRouterState, spawnedEntries: Map ): Promise | null> { const key = task.key const url = new URL(route.canonicalUrl, location.origin) const nextUrl = key.nextUrl + // When the request tree was derived from a predicted route entry, pass the + // node it was predicted from to the write path so the prediction can be + // disabled if the server's rendered tree diverges from it. For an entry + // the server resolved this is null: a divergence from it says nothing + // about route prediction, and its unfulfilled entries take the usual + // backoff. + let dynamicRequestTree: FlightRouterState + let predictedFrom: KnownRoutePart | null if ( spawnedEntries.size === 1 && - spawnedEntries.has(route.metadata.requestKey) + spawnedEntries.has(route.root.head.requestKey) ) { - // The only thing pending is the head. Instruct the server to - // skip over everything else. - // TODO: Lift this logic into the caller. Or perhaps unify the - // "request tree" and the spawnedEntries into the same type so they are - // guaranteed to always been in sync. + // Only the head is pending, so ask the server for metadata only: it skips + // the segments and renders just the head. The stub is not derived from + // the route entry, so divergence from it carries no signal. dynamicRequestTree = MetadataOnlyRequestTree + predictedFrom = null + } else { + dynamicRequestTree = requestTree + predictedFrom = route.predictedFrom } const headers: RequestHeaders = { @@ -2654,21 +2682,6 @@ export async function fetchSegmentPrefetchesUsingRuntimeRequest( const buildId = response.headers.get(NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? serverData.b - // When the request tree was derived from a predicted route entry, pass the - // node it was predicted from to the write path so the prediction can be - // disabled if the server's rendered tree diverges from it. For an entry - // the server resolved this is null: a divergence from it says nothing - // about route prediction, and its unfulfilled entries take the usual - // backoff. A head-only request uses the MetadataOnlyRequestTree stub - // rather than a tree derived from the route entry, so divergence from it - // carries no signal. - // TODO: This special case goes away once the response is diffed against - // the base RouteTree (route.tree) instead of the request tree. - const predictedFrom = - dynamicRequestTree !== MetadataOnlyRequestTree - ? route.predictedFrom - : null - // Extract the response's shell-stage payload, when it carries one. No // shell can be extracted without cache metadata (only present when // Cached Navigations is enabled); for responses without a distinct @@ -2776,7 +2789,7 @@ function writeResponsePayloadsIntoCache( // (Per-segment payloads encode partiality per node and ignore the // response-level value entirely.) isFullResponsePartial: boolean, - metadataVaryPath: PageVaryPath | null, + metadataVaryPath: VaryPath | null, // The pending entries this response fulfills. Null when the caller owns // none (the embedded runtime prefetch stream), in which case every write // is a detached upsert. @@ -3096,10 +3109,8 @@ function writeServerResponseIntoCache( // partiality per node, so their writes pass the conservative value // (true), which is never read. isResponsePartial: boolean, - // Where to key the head. Null derives it from the decoded tree's first - // page node; per-segment payloads pass the route's own metadata vary path - // instead, since a standalone head response's tree has no page node. - metadataVaryPath: PageVaryPath | null, + // Where to key the head; see createNavigationSeed. + metadataVaryPath: VaryPath | null, spawnedEntries: Map | null, // The strategy tier describing the CONTENT of the payload being written, // when it differs from `fetchStrategy` (which drives matching and @@ -3162,12 +3173,13 @@ function writeServerResponseIntoCache( isResponsePartial, renderedPathname, renderedSearch, + metadataVaryPath, // Only navigations consume the seed's dynamicStaleAt; cache writes pass // unknown to use the default. UnknownDynamicStaleTime ) const requiresRuntimeCompleteness = - (navigationSeed.routeTree.prefetchHints & + (navigationSeed.root.tree.prefetchHints & PrefetchHint.SubtreeHasPartialPrefetching) !== 0 @@ -3201,17 +3213,7 @@ function writeServerResponseIntoCache( ? readFulfilledValue(response.u, false, /* rejectedValue */ true) : null - const routeTree = navigationSeed.routeTree - if (metadataVaryPath === null) { - metadataVaryPath = navigationSeed.metadataVaryPath - } - const metadataTree = - metadataVaryPath !== null - ? createMetadataRouteTree( - metadataVaryPath, - navigationSeed.routeTree.prefetchHints - ) - : null + const routeTree = navigationSeed.root.tree // The route tree carries the render output of every segment the response // included, so a single traversal from the root writes all of it into @@ -3231,17 +3233,18 @@ function writeServerResponseIntoCache( writtenEntries ) - const head = navigationSeed.head - if (head !== null && metadataTree !== null) { - // The head carries its own staleTime in per-segment prefetch responses; - // everywhere else the response-level staleness governs it. + const metadataTree = navigationSeed.root.head + const headData = metadataTree.data + if (headData !== null && headData.rsc !== null) { + // The head follows the same stale-time rules as a segment. const headStaleAt = - navigationSeed.headStaleTimeSeconds !== null - ? now + getStaleTimeMs(navigationSeed.headStaleTimeSeconds) + headData.staleTimeSeconds !== null + ? now + getStaleTimeMs(headData.staleTimeSeconds) : staleAt - // A head has no loading boundary. Match pingRuntimeHead, which spawns - // LoadingBoundary head entries using the concrete Full strategy. + // A head has no loading boundary. Match the scheduler, which spawns + // LoadingBoundary head entries using the concrete Full strategy (see the + // head's runtime fetch in pingRootRouteTree). const headFetchStrategy = fetchStrategy === FetchStrategy.LoadingBoundary ? FetchStrategy.Full @@ -3250,13 +3253,13 @@ function writeServerResponseIntoCache( now, map, headFetchStrategy, - head, + headData.rsc, // The decode already resolved the head's partiality from the wire // form and the response-level value — see the head read in // createNavigationSeed. - navigationSeed.isHeadPartial, + headData.isPartial, headStaleAt, - navigationSeed.headVaryParams, + headData.varyParams, metadataTree, spawnedEntries, contentFetchStrategy, @@ -3384,7 +3387,7 @@ function writeSegmentDataIntoCache( rsc: React.ReactNode, isPartial: boolean, staleAt: number, - segmentVaryParams: Set | null, + segmentVaryParams: VaryParams | null, tree: RouteTree, spawnedEntries: Map | null, // The strategy tier describing the CONTENT of the payload this write came @@ -3496,43 +3499,60 @@ function writeSegmentDataIntoCache( // is good for any value of them (its request path below IS the shell // vary path). const payloadStrategy = contentFetchStrategy ?? fetchStrategy - let fulfilledVaryPath: SegmentVaryPath | null = null + let fulfilledVaryPath: VaryPath | null = null + // The dependency source the entry records, so a navigation that renders + // its content can tell which params that content read. It is the same + // evidence the key derivation below trusts, under the same condition, with + // the same correction; otherwise null, and consumers assume every param. + let recordedVaryParams: VaryParams | null = null if ( process.env.__NEXT_VARY_PARAMS && payloadStrategy !== FetchStrategy.Full && segmentVaryParams !== null ) { - let varyParams = segmentVaryParams - if (payloadStrategy === FetchStrategy.RuntimeShell && varyParams.has('?')) { - // SPECIAL CASE: for a RuntimeShell payload, the search params entry - // ('?') is dropped from the server's vary evidence before deriving the - // key, so the search component of the resulting path is marked as the - // fallback. This exists ONLY because of a known compromise in how the - // server reports search params: accessing `searchParams` records a - // dependency on '?' at access time, even when the render suspends on - // that access and cuts the content at the param fallback. A shell - // render's page and head segments therefore report '?' while the - // emitted bytes contain no search-dependent content. Trusting that - // report would key shell-grade content at a concrete search value, - // where shell-restricted reads (which generalize every non-root - // param — see getShellSegmentVaryPath) can never find it. A - // RuntimeShell payload's search-dependent content is reduced to - // fallbacks by construction, so its key must not vary on search - // regardless of the over-reported evidence. Every other component of - // the evidence is still honored as-is. - // - // Nothing else should rely on this branch; for every other payload - // grade — and every other param — the server's evidence - // is authoritative. - // - // TODO: Reconsider special-casing this on the server instead: don't - // report a param access that never resolved past the fallback cut in - // the emitted stage. A shell payload's evidence would then be - // accurate, and this branch could be deleted. - varyParams = new Set(varyParams) - varyParams.delete('?') + // Read the reported set now, when the key is chosen. The payload is fully + // buffered by the time it's written, so the source has settled; a read of + // null means the report is unavailable and every param varies. + let varyParams = readVaryParams(segmentVaryParams) + if (varyParams !== null) { + if ( + payloadStrategy === FetchStrategy.RuntimeShell && + varyParams.has(SEARCH_PARAMS_VARY_ID) + ) { + // SPECIAL CASE: for a RuntimeShell payload, the search params entry + // is dropped from the server's vary evidence before deriving the + // key, so the search component of the resulting path is marked as + // the fallback. This exists ONLY because of a known compromise in + // how the server reports search params: accessing `searchParams` + // records a dependency on them at access time, even when the render + // suspends on that access and cuts the content at the param + // fallback. A shell render's page and head segments therefore report + // the search params while the emitted bytes contain no + // search-dependent content. + // Trusting that report would key shell-grade content at a concrete + // search value, where shell-restricted reads (which generalize every + // non-root param — see getShellSegmentVaryPath) can never find it. A + // RuntimeShell payload's search-dependent content is reduced to + // fallbacks by construction, so its key must not vary on search + // regardless of the over-reported evidence. Every other component of + // the evidence is still honored as-is. + // + // Nothing else should rely on this branch; for every other payload + // grade — and every other param — the server's evidence + // is authoritative. + // + // TODO: Reconsider special-casing this on the server instead: don't + // report a param access that never resolved past the fallback cut in + // the emitted stage. A shell payload's evidence would then be + // accurate, and this branch could be deleted. + varyParams = new Set(varyParams) + varyParams.delete(SEARCH_PARAMS_VARY_ID) + recordedVaryParams = createVaryParams(varyParams) + } else { + recordedVaryParams = segmentVaryParams + } + fulfilledVaryPath = getFulfilledSegmentVaryPath(tree.varyPath, varyParams) } - fulfilledVaryPath = getFulfilledSegmentVaryPath(tree.varyPath, varyParams) } // The canonical path to (re-)key the entry at. When the derivation above @@ -3563,7 +3583,7 @@ function writeSegmentDataIntoCache( const isOwned = ownedEntry !== undefined && ownedEntry.status === EntryStatus.Pending let fulfilledEntry: FulfilledSegmentCacheEntry - let insertVaryPath: SegmentVaryPath | null + let insertVaryPath: VaryPath | null if (isOwned) { // We own this entry — fulfill it directly. fulfilledEntry = fulfillSegmentCacheEntry( @@ -3571,6 +3591,7 @@ function writeSegmentDataIntoCache( rsc, staleAt, isPartial, + recordedVaryParams, isUpgradeableISRFallback, recordedFetchStrategy ) @@ -3590,6 +3611,7 @@ function writeSegmentDataIntoCache( rsc, staleAt, isPartial, + recordedVaryParams, isUpgradeableISRFallback, recordedFetchStrategy ) diff --git a/packages/next/src/client/components/segment-cache/decode-server-response.ts b/packages/next/src/client/components/segment-cache/decode-server-response.ts index d54978986345..baa93f9392de 100644 --- a/packages/next/src/client/components/segment-cache/decode-server-response.ts +++ b/packages/next/src/client/components/segment-cache/decode-server-response.ts @@ -7,7 +7,6 @@ import type { FlightRouterState, - HeadData, Segment as FlightRouterStateSegment, } from '../../../shared/lib/app-router-types' import { @@ -21,11 +20,8 @@ import type { TransportSegment, } from '../../../shared/lib/rsc-transport' import { readFulfilledValue } from '../../../shared/lib/rsc-transport' -import type { - VaryParams, - VaryParamsIterable, -} from '../../../shared/lib/segment-cache/vary-params-decoding' -import { readVaryParams } from '../../../shared/lib/segment-cache/vary-params-decoding' +import type { VaryParamsIterable } from '../../../shared/lib/segment-cache/vary-params-decoding' +import { decodeVaryParams } from '../../../shared/lib/segment-cache/vary-params-decoding' import { type SegmentRequestKey, ROOT_SEGMENT_REQUEST_KEY, @@ -36,7 +32,6 @@ import { DEFAULT_SEGMENT_KEY, PAGE_SEGMENT_KEY, } from '../../../shared/lib/segment' -import { matchSegment } from '../match-segments' import { InvariantError } from '../../../shared/lib/invariant-error' import { doesStaticSegmentAppearInURL, @@ -45,49 +40,34 @@ import { } from '../../route-params' import type { NormalizedSearch } from './cache-key' import { splitPathnameIntoParts } from './cache-key' -import type { - PageVaryPath, - PartialSegmentVaryPath, - SegmentVaryPath, -} from './vary-path' +import type { PartialVaryPath, VaryPath } from './vary-path' import { appendLayoutVaryPath, - finalizeLayoutVaryPath, - finalizeMetadataVaryPath, - finalizePageVaryPath, - getPartialLayoutVaryPath, - getPartialPageVaryPath, + finalizeVaryPath, + getPartialVaryPath, getShellSegmentVaryPath, } from './vary-path' import { type RouteTree, + type RootRouteTree, type RSCSegmentData, type RefreshState, type RouteTreeAccumulator, convertFlightRouterStateToRouteTree, convertRootFlightRouterStateToRouteTree, + createMetadataRouteTree, + createRootRouteTree, + getHeadRequestKey, } from './cache' import { computeDynamicStaleAt } from './bfcache' export type NavigationSeed = { - renderedSearch: string - routeTree: RouteTree - metadataVaryPath: PageVaryPath | null - head: HeadData | null - isHeadPartial: boolean + renderedSearch: NormalizedSearch /** - * The params the head's output depends on (root params already unioned - * in), drained from the response's wire iterables at decode. Null means - * unknown — tracking wasn't enabled, or the decode had no root params to - * union in — so consumers key on all params. + * The decoded response. The head's `data` is decoded exactly like a segment + * node's: null when the response carries no head. */ - headVaryParams: VaryParams | null - /** - * The head's own staleTime in seconds, when the response carries one - * (per-segment prefetch responses only — see TransportSegmentData['s']). - * Null means the response-level staleness governs the head. - */ - headStaleTimeSeconds: number | null + root: RootRouteTree dynamicStaleAt: number // Whether the response rendered a segment whose identity differs from the // base tree's at the same position (inactive parallel route branches are @@ -124,13 +104,11 @@ export function createNavigationSeed( // The response's root vary params (its `r` field): the root params // accessed anywhere in the response, emitted once at the response level // and unioned into the head's and every segment's own drained set here at - // the decode boundary. Pass null when vary params are unavailable or - // unwanted: navigation and reducer flows, whose responses stream in - // incrementally (the wire iterables can only be drained completely from a - // fully-buffered response) and whose seeds' vary params nothing consumes — - // only segment-cache writes read them, and those decode their own, - // buffered, payloads. Null decodes every set as null ("unknown; key on - // all params") without touching the wire iterables. + // the decode boundary. Pass null when the response streams in + // incrementally (navigation and reducer flows): the wire iterables can + // only be drained completely from a fully-buffered response, so their sets + // decode as null ("unknown; key on all params") without touching the wire + // iterables. rootVaryParams: VaryParamsIterable | null, // Whether anything in the response is not fully resolved: dynamic holes, runtime holes, anything suspended. // Boolean-form nodes resolve their partiality to this value (their wire @@ -146,18 +124,23 @@ export function createNavigationSeed( // decodeTransportTreeIntoRouteTree. Callers whose responses always carry // concrete values (navigation responses) may pass null. renderedPathname: string | null, + // Already normalized by the response reader (see getRenderedSearch); the + // router state stores it as a plain string, so it is re-branded here. renderedSearch: string, + // Where to key the head. Null derives it from the route's own first page + // node (see createRouteTreeNode). Per-segment prefetch payloads pass the + // route's own metadata vary path instead: a standalone head response's tree + // is a bare root identity with no page node. + metadataVaryPath: VaryPath | null, dynamicStaleTimeSeconds: number ): NavigationSeed { + const normalizedRenderedSearch = renderedSearch as NormalizedSearch const acc: RouteTreeAccumulator = { metadataVaryPath: null, treeDivergedFromBase: false, } let routeTree: RouteTree - let head: HeadData | null = null - let isHeadPartial = true - let headVaryParams: VaryParams | null = null - let headStaleTimeSeconds: number | null = null + let headData: RSCSegmentData | null = null if (transportData !== null) { routeTree = decodeTransportTreeIntoRouteTree( transportData.t, @@ -165,12 +148,11 @@ export function createNavigationSeed( rootVaryParams, isResponsePartial, renderedPathname, - renderedSearch as NormalizedSearch, + normalizedRenderedSearch, acc ) const transportHead = transportData.h if (transportHead !== undefined) { - head = transportHead.r // The wire form of `p` determines which signal is authoritative for // the head's partiality, mirroring the per-node rule in // decodeTransportNode: @@ -192,17 +174,20 @@ export function createNavigationSeed( // carries a complete head; a partial (postponed) one does not. // Without Cache Components, the server sends the correct // isHeadPartial, so the wire boolean is used as-is. - isHeadPartial = - typeof transportHead.p === 'boolean' - ? process.env.__NEXT_CACHE_COMPONENTS - ? isResponsePartial - : transportHead.p - : readFulfilledIsPartial(transportHead.p) - headVaryParams = readVaryParams(transportHead.v, rootVaryParams) - headStaleTimeSeconds = - transportHead.s !== undefined - ? readFulfilledStaleTimeSeconds(transportHead.s) - : null + headData = { + rsc: transportHead.r, + isPartial: + typeof transportHead.p === 'boolean' + ? process.env.__NEXT_CACHE_COMPONENTS + ? isResponsePartial + : transportHead.p + : readFulfilledIsPartial(transportHead.p), + varyParams: decodeVaryParams(transportHead.v, rootVaryParams), + staleTimeSeconds: + transportHead.s !== undefined + ? readFulfilledStaleTimeSeconds(transportHead.s) + : null, + } } } else { if (currentTree === null) { @@ -213,19 +198,33 @@ export function createNavigationSeed( } routeTree = convertRootFlightRouterStateToRouteTree( currentTree, - renderedSearch as NormalizedSearch, + normalizedRenderedSearch, acc ) } + if (metadataVaryPath === null) { + metadataVaryPath = acc.metadataVaryPath + if (metadataVaryPath === null) { + // Every route renders a page, so a rendered tree always has a node to + // key the head under. + throw new InvariantError( + 'Cannot key the head of a server response: its tree has no page ' + + 'segment.' + ) + } + } + return { - routeTree, - metadataVaryPath: acc.metadataVaryPath, - renderedSearch, - head, - isHeadPartial, - headVaryParams, - headStaleTimeSeconds, + root: createRootRouteTree( + routeTree, + createMetadataRouteTree( + metadataVaryPath, + routeTree.prefetchHints, + headData + ) + ), + renderedSearch: normalizedRenderedSearch, dynamicStaleAt: computeDynamicStaleAt(now, dynamicStaleTimeSeconds), treeDivergedFromBase: acc.treeDivergedFromBase, } @@ -233,27 +232,25 @@ export function createNavigationSeed( /** * Creates a RouteTree node for a segment, with its identity and cache-key - * information (vary paths, page-ness, the normalized segment value) + * information (vary paths, the normalized segment value, the refresh state) * initialized, and the remaining fields set to their defaults. The caller * finishes initializing those in place after recursing into the children. - * Shared by the FlightRouterState converter and the transport decoder so the - * two cannot drift, and so every node they produce has the same property - * order (one hidden class). + * Shared by FlightRouterState conversion, transport decoding, and subtree + * rebasing so their routing identity stays consistent. */ export function createRouteTreeNode( originalSegment: FlightRouterStateSegment, isRootParam: boolean, requestKey: SegmentRequestKey, - parentPartialVaryPath: PartialSegmentVaryPath | null, + parentPartialVaryPath: PartialVaryPath | null, renderedSearch: NormalizedSearch, + refreshState: RefreshState | null, acc: RouteTreeAccumulator ): RouteTree { let segment: FlightRouterStateSegment - let partialVaryPath: PartialSegmentVaryPath | null - let isPage: boolean - let varyPath: SegmentVaryPath + let partialVaryPath: PartialVaryPath | null + let varyPath: VaryPath if (Array.isArray(originalSegment)) { - isPage = false const paramCacheKey = originalSegment[1] const paramName = originalSegment[0] partialVaryPath = appendLayoutVaryPath( @@ -262,7 +259,7 @@ export function createRouteTreeNode( paramName, isRootParam ) - varyPath = finalizeLayoutVaryPath(requestKey, partialVaryPath) + varyPath = finalizeVaryPath(requestKey, null, partialVaryPath) segment = originalSegment } else { // This segment does not have a param. Inherit the partial vary path of @@ -270,54 +267,31 @@ export function createRouteTreeNode( partialVaryPath = parentPartialVaryPath if (requestKey.endsWith(PAGE_SEGMENT_KEY)) { // This is a page segment. - isPage = true - - // The navigation implementation expects the search params to be included - // in the segment. However, in the case of a static response, the search - // params are omitted. So the client needs to add them back in when reading - // from the Segment Cache. - // - // For consistency, we'll do this for live-render responses, too. - // - // TODO: We should move search params out of FlightRouterState and handle - // them entirely on the client, similar to our plan for dynamic params. segment = PAGE_SEGMENT_KEY - varyPath = finalizePageVaryPath( - requestKey, - renderedSearch, - partialVaryPath - ) - // The metadata "segment" is not part the route tree, but it has the same - // conceptual params as a page segment. Write the vary path into the - // accumulator object. If there are multiple parallel pages, we use the - // first one. Which page we choose is arbitrary as long as it's - // consistently the same one every time every time. See - // finalizeMetadataVaryPath for more details. - if (acc.metadataVaryPath === null) { - acc.metadataVaryPath = finalizeMetadataVaryPath( - requestKey, + varyPath = finalizeVaryPath(requestKey, renderedSearch, partialVaryPath) + // The head is keyed under the route's own first page and varies on the + // same params as that page (see getHeadRequestKey). A page reused from + // another URL carries a refresh state and never keys it. + if (refreshState === null && acc.metadataVaryPath === null) { + acc.metadataVaryPath = finalizeVaryPath( + getHeadRequestKey(requestKey), renderedSearch, partialVaryPath ) } } else { // This is a layout segment. - isPage = false segment = originalSegment - varyPath = finalizeLayoutVaryPath(requestKey, partialVaryPath) + varyPath = finalizeVaryPath(requestKey, null, partialVaryPath) } } return { requestKey, segment, shellVaryPath: getShellSegmentVaryPath(varyPath), - refreshState: null, + refreshState, data: null, - // TODO: Cheating the type system here a bit because TypeScript can't tell - // that the type of isPage and varyPath are consistent. If isPage were - // wrong it would break the behavior and we'd catch it quickly. - varyPath: varyPath as any, - isPage: isPage as boolean as any, + varyPath, slots: null, prefetchHints: 0, } @@ -341,7 +315,7 @@ export function createRouteTreeNode( * * TODO: The base is a FlightRouterState only because that's the * representation the client router currently renders from (the router - * reducer's `state.tree`, which the CacheNode tree and layout-router are + * reducer's `state.tree`, which the render tree and layout-router are * keyed against). Once the rendering path is updated to use RouteTree as its * source of truth, the base tree here can be a RouteTree, and the base-only * conversion path (convertFlightRouterStateToRouteTree) goes away with it. @@ -420,17 +394,33 @@ function resolveTransportSegment( pathnameParts, pathnamePartsIndex ) - // TODO: We're intentionally not adding the search param to page segments - // here; it's tracked separately and added back during a read from the - // Segment Cache. return [ transportSegment.n, - getCacheKeyForDynamicParam(paramValue, '' as NormalizedSearch), + getCacheKeyForDynamicParam(paramValue), transportSegment.t, transportSegment.s, ] } +function doSegmentsMatch( + baseSegment: FlightRouterStateSegment, + segment: FlightRouterStateSegment +): boolean { + if (typeof baseSegment === 'string' || typeof segment === 'string') { + // Static segments have to match exactly. + return baseSegment === segment + } + // Both segments are dynamic. The static sibling hints aren't part of the + // segment's identity, so only compare the param name, type, and value. + const [baseParamName, baseParamValue, baseParamType] = baseSegment + const [paramName, paramValue, paramType] = segment + return ( + baseParamName === paramName && + baseParamType === paramType && + baseParamValue === paramValue + ) +} + function decodeTransportNode( node: PartialTransportNode, // The node's identity, already resolved by the caller (the parent's child @@ -446,7 +436,7 @@ function decodeTransportNode( rootVaryParams: VaryParamsIterable | null, isResponsePartial: boolean, requestKey: SegmentRequestKey, - parentPartialVaryPath: PartialSegmentVaryPath | null, + parentPartialVaryPath: PartialVaryPath | null, parentRenderedSearch: NormalizedSearch, pathnameParts: Array | null, // The URL position this node's children read from. @@ -468,18 +458,10 @@ function decodeTransportNode( // are still checked. } else { const baseSegment = compareBase[0] - if ( - typeof originalSegment === 'string' && - typeof baseSegment === 'string' && - originalSegment.startsWith(PAGE_SEGMENT_KEY) && - baseSegment.startsWith(PAGE_SEGMENT_KEY) - ) { - // Page segments match modulo embedded search params, which are - // validated separately (see getRenderedSearch). - } else if (originalSegment === DEFAULT_SEGMENT_KEY) { + if (originalSegment === DEFAULT_SEGMENT_KEY) { // A default filled in by the server is not a claim about the // position's identity. - } else if (!matchSegment(baseSegment, originalSegment)) { + } else if (!doSegmentsMatch(baseSegment, originalSegment)) { acc.treeDivergedFromBase = true } } @@ -514,12 +496,10 @@ function decodeTransportNode( requestKey, parentPartialVaryPath, renderedSearch, + refreshState, acc ) - tree.refreshState = refreshState - const partialVaryPath = tree.isPage - ? getPartialPageVaryPath(tree.varyPath) - : getPartialLayoutVaryPath(tree.varyPath) + const partialVaryPath = getPartialVaryPath(tree.varyPath) let slots: Map> | null = null const transportChildren = node.c @@ -657,11 +637,12 @@ function decodeTransportNode( typeof nodeData.p === 'boolean' ? isResponsePartial : readFulfilledIsPartial(nodeData.p), - // Drain the segment's wire iterable into a plain set, unioning in the - // response-level root params. Same buffered-read reasoning as `p` - // above; skipped entirely (decoded as null, "unknown") when the caller - // passed no root params — see createNavigationSeed. - varyParams: readVaryParams(nodeData.v, rootVaryParams), + // The source of the params this segment's output depends on: the + // segment's wire iterable, drained here, unioning in the response-level + // root params (same buffered-read reasoning as `p` above), or decoded + // as null ("unknown") when the caller passed no root params — see + // createNavigationSeed. + varyParams: decodeVaryParams(nodeData.v, rootVaryParams), // Per-node staleTime, only present in per-segment prefetch responses // (same buffered-read reasoning as `p` above). staleTimeSeconds: diff --git a/packages/next/src/client/components/segment-cache/optimistic-routes.ts b/packages/next/src/client/components/segment-cache/optimistic-routes.ts index 7dd849c8ee08..a75ce9f675a4 100644 --- a/packages/next/src/client/components/segment-cache/optimistic-routes.ts +++ b/packages/next/src/client/components/segment-cache/optimistic-routes.ts @@ -45,8 +45,10 @@ import type { DynamicParamTypesShort } from '../../../shared/lib/app-router-types' import { PrefetchHint } from '../../../shared/lib/app-router-types' +import { PAGE_SEGMENT_KEY } from '../../../shared/lib/segment' import type { RouteTree, + RootRouteTree, RSCSegmentData, FulfilledRouteCacheEntry, } from './cache' @@ -57,22 +59,23 @@ import { getCurrentRouteCacheVersion, type PendingRouteCacheEntry, createMetadataRouteTree, + createRootRouteTree, + getHeadRequestKey, } from './cache' import { isValueExpired } from './cache-map' import { canonicalizeURLPart, + normalizeRenderedSearch, doesStaticSegmentAppearInURL, } from '../../route-params' import type { NormalizedPathname, NormalizedSearch } from './cache-key' import { splitPathnameIntoParts } from './cache-key' import { appendLayoutVaryPath, - finalizeLayoutVaryPath, - finalizePageVaryPath, - finalizeMetadataVaryPath, + finalizeVaryPath, getShellSegmentVaryPath, - type PartialSegmentVaryPath, - type PageVaryPath, + type PartialVaryPath, + type VaryPath, } from './vary-path' /** @@ -238,14 +241,14 @@ export function discoverKnownRoute( search: NormalizedSearch, nextUrl: string | null, pendingEntry: PendingRouteCacheEntry | null, - routeTree: RouteTree, - metadataVaryPath: PageVaryPath, + root: RootRouteTree, couldBeIntercepted: boolean, canonicalUrl: string, + renderedSearch: NormalizedSearch, supportsPerSegmentPrefetching: boolean, hasDynamicRewrite: boolean ): FulfilledRouteCacheEntry { - const tree = routeTree + const tree = root.tree const pathnameParts = splitPathnameIntoParts(pathname) @@ -254,10 +257,10 @@ export function discoverKnownRoute( const fulfilledEntry = fulfillRouteCacheEntry( now, pendingEntry, - tree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) // Populate the known route tree (handles rewrite detection internally). @@ -273,10 +276,10 @@ export function discoverKnownRoute( pathname, search, nextUrl, - tree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching, hasDynamicRewrite ) @@ -295,10 +298,10 @@ export function discoverKnownRoute( pathname, search, nextUrl, - tree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching, hasDynamicRewrite ) @@ -316,10 +319,10 @@ function handleMismatchDueToRewrite( pathname: string, search: NormalizedSearch, nextUrl: string | null, - fullTree: RouteTree, - metadataVaryPath: PageVaryPath, + root: RootRouteTree, couldBeIntercepted: boolean, canonicalUrl: string, + renderedSearch: NormalizedSearch, supportsPerSegmentPrefetching: boolean ): FulfilledRouteCacheEntry { if (existingEntry !== null) { @@ -330,10 +333,10 @@ function handleMismatchDueToRewrite( pathname as NormalizedPathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -388,10 +391,10 @@ function discoverKnownRoutePart( pathname: string, search: NormalizedSearch, nextUrl: string | null, - fullTree: RouteTree, - metadataVaryPath: PageVaryPath, + root: RootRouteTree, couldBeIntercepted: boolean, canonicalUrl: string, + renderedSearch: NormalizedSearch, supportsPerSegmentPrefetching: boolean, hasDynamicRewrite: boolean ): FulfilledRouteCacheEntry { @@ -415,10 +418,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -456,10 +459,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -477,10 +480,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -506,10 +509,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -532,10 +535,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -573,10 +576,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -640,10 +643,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching, hasDynamicRewrite ) @@ -662,10 +665,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -680,10 +683,10 @@ function discoverKnownRoutePart( pathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -713,10 +716,10 @@ function discoverKnownRoutePart( pathname as NormalizedPathname, search, nextUrl, - fullTree, - metadataVaryPath, + root, couldBeIntercepted, canonicalUrl, + renderedSearch, supportsPerSegmentPrefetching ) } @@ -774,7 +777,7 @@ export function matchKnownRoute( // components read them. Resolve the target URL on the server instead of // combining a predicted tree with UI cached for an allowed parameter value. if ( - pattern.tree.prefetchHints & + pattern.root.tree.prefetchHints & (PrefetchHint.IsClosedParam | PrefetchHint.SubtreeHasClosedParams) ) { return null @@ -783,11 +786,12 @@ export function matchKnownRoute( // "Reify" the pattern: clone the template tree with concrete param values. // This substitutes resolved params (e.g., slug: "hello") into dynamic // segments and recomputes vary paths for correct segment cache keying. + const renderedSearch = normalizeRenderedSearch(search) const acc: ReifyAccumulator = { metadataVaryPath: null } const reifiedTree = reifyRouteTree( - pattern.tree, + pattern.root.tree, resolvedParams, - search, + renderedSearch, null, // Start with null partial vary path at the root acc ) @@ -802,7 +806,8 @@ export function matchKnownRoute( } const reifiedMetadata = createMetadataRouteTree( metadataVaryPath, - reifiedTree.prefetchHints + reifiedTree.prefetchHints, + null ) // Create a synthetic (predicted) entry. It's not inserted into the route @@ -815,12 +820,11 @@ export function matchKnownRoute( canonicalUrl: pathname + search, status: EntryStatus.Fulfilled, blockedTasks: null, - tree: reifiedTree, - metadata: reifiedMetadata, + root: createRootRouteTree(reifiedTree, reifiedMetadata), couldBeIntercepted: pattern.couldBeIntercepted, supportsPerSegmentPrefetching: pattern.supportsPerSegmentPrefetching, predictedFrom: matchedPart, - renderedSearch: search, + renderedSearch, ref: null, size: pattern.size, staleAt: pattern.staleAt, @@ -1010,7 +1014,7 @@ function matchKnownRoutePart( * (parallel routes may have multiple pages, but metadata uses the first). */ type ReifyAccumulator = { - metadataVaryPath: PageVaryPath | null + metadataVaryPath: VaryPath | null } /** @@ -1028,7 +1032,7 @@ function reifyRouteTree( pattern: RouteTree, resolvedParams: ResolvedParams, search: NormalizedSearch, - parentPartialVaryPath: PartialSegmentVaryPath | null, + parentPartialVaryPath: PartialVaryPath | null, acc: ReifyAccumulator ): RouteTree { const originalSegment = pattern.segment @@ -1039,7 +1043,7 @@ function reifyRouteTree( (pattern.prefetchHints & PrefetchHint.IsRootLayoutOrAbove) !== 0 let newSegment = originalSegment - let partialVaryPath: PartialSegmentVaryPath | null + let partialVaryPath: PartialVaryPath | null if (typeof originalSegment !== 'string') { // Dynamic segment: compute new cache key and append to partial vary path @@ -1087,51 +1091,33 @@ function reifyRouteTree( } } - if (pattern.isPage) { + let newVaryPath: VaryPath + if (originalSegment === PAGE_SEGMENT_KEY) { // Page segment: finalize with search params - const newVaryPath = finalizePageVaryPath( - pattern.requestKey, - search, - partialVaryPath - ) - // Collect metadata vary path (first page wins, same as original algorithm) + newVaryPath = finalizeVaryPath(pattern.requestKey, search, partialVaryPath) + // Collect metadata vary path (first page wins; see getHeadRequestKey) if (acc.metadataVaryPath === null) { - acc.metadataVaryPath = finalizeMetadataVaryPath( - pattern.requestKey, + acc.metadataVaryPath = finalizeVaryPath( + getHeadRequestKey(pattern.requestKey), search, partialVaryPath ) } - return { - requestKey: pattern.requestKey, - segment: newSegment, - shellVaryPath: getShellSegmentVaryPath(newVaryPath), - refreshState: pattern.refreshState, - // Route cache patterns never carry seed data (see - // stripDataFromRouteTree), so neither do trees reified from them. - data: null, - varyPath: newVaryPath, - isPage: true, - slots: newSlots, - prefetchHints: pattern.prefetchHints, - } } else { // Layout segment: finalize without search params - const newVaryPath = finalizeLayoutVaryPath( - pattern.requestKey, - partialVaryPath - ) - return { - requestKey: pattern.requestKey, - segment: newSegment, - shellVaryPath: getShellSegmentVaryPath(newVaryPath), - refreshState: pattern.refreshState, - data: null, - varyPath: newVaryPath, - isPage: false, - slots: newSlots, - prefetchHints: pattern.prefetchHints, - } + newVaryPath = finalizeVaryPath(pattern.requestKey, null, partialVaryPath) + } + return { + requestKey: pattern.requestKey, + segment: newSegment, + shellVaryPath: getShellSegmentVaryPath(newVaryPath), + refreshState: pattern.refreshState, + // Route cache patterns never carry seed data (see + // stripDataFromRouteTree), so neither do trees reified from them. + data: null, + varyPath: newVaryPath, + slots: newSlots, + prefetchHints: pattern.prefetchHints, } } diff --git a/packages/next/src/client/components/segment-cache/scheduler.ts b/packages/next/src/client/components/segment-cache/scheduler.ts index 3b204484befc..8ff82ba25449 100644 --- a/packages/next/src/client/components/segment-cache/scheduler.ts +++ b/packages/next/src/client/components/segment-cache/scheduler.ts @@ -1,13 +1,12 @@ +import { compareParams, ParamsChange } from './vary-path' import type { FlightRouterState, - Segment as FlightRouterStateSegment, - Segment, + CacheNode, } from '../../../shared/lib/app-router-types' import { PrefetchHint, StaticPrefetchDisabled, } from '../../../shared/lib/app-router-types' -import { matchSegment } from '../match-segments' import { readOrCreateRouteCacheEntry, readRouteCacheEntry, @@ -18,10 +17,12 @@ import { type FulfilledRouteCacheEntry, type RouteCacheEntry, type RouteTree, + type RootRouteTree, fetchSegmentPrefetchesUsingRuntimeRequest, type PendingSegmentCacheEntry, type SegmentCacheEntry, convertRouteTreeToFlightRouterState, + doesRouteStructureMatch, readOrCreateRevalidatingSegmentEntry, upgradeToPendingSegment, overwriteRevalidatingSegmentCacheEntry, @@ -31,7 +32,6 @@ import { } from './cache' import type { RouteCacheKey } from './cache-key' import { createCacheKey } from './cache-key' -import { urlSearchParamsToParsedUrlQuery } from '../../route-params' import { FetchStrategy, type PrefetchTaskFetchStrategy, @@ -44,10 +44,6 @@ import { } from './cache' import type { CacheMap } from './cache-map' import type { NavigationLockPrefetch } from './navigation-testing-lock' -import { - addSearchParamsIfPageSegment, - PAGE_SEGMENT_KEY, -} from '../../../shared/lib/segment' import type { SegmentRequestKey } from '../../../shared/lib/segment-cache/segment-value-encoding' import { cleanup } from './lru' @@ -66,12 +62,12 @@ const scheduleMicrotask = export type PrefetchTask = { key: RouteCacheKey - /** - * The FlightRouterState at the time the task was initiated. This is needed - * when falling back to the non-PPR behavior, which only prefetches up to - * the first loading boundary. - */ - treeAtTimeOfPrefetch: FlightRouterState + // The active render tree and head when this task was scheduled. The walks + // compare the target route against these to decide which segments the + // navigation would keep and which it would fetch. Compared by identity in + // isPrefetchTaskDirty, which relies on the router state holding one + // RootRouteTree object per committed navigation (see AppRouterState.root). + renderTreeAtTimeOfPrefetch: RootRouteTree /** * The cache versions at the time the task was initiated. Used to determine @@ -308,7 +304,8 @@ export type IncludeDynamicData = null | 'full' | 'dynamic' * expected to be validated and normalized. * * @param key The RouteCacheKey to prefetch. - * @param treeAtTimeOfPrefetch The app's current FlightRouterState + * @param renderTreeAtTimeOfPrefetch The active render tree and head, and + * their vary paths * @param fetchStrategy Whether to prefetch dynamic data, in addition to * static data. This is used by ``. * @param navigationLockPrefetch Testing API only. Non-null when this prefetch @@ -317,7 +314,7 @@ export type IncludeDynamicData = null | 'full' | 'dynamic' */ export function schedulePrefetchTask( key: RouteCacheKey, - treeAtTimeOfPrefetch: FlightRouterState, + renderTreeAtTimeOfPrefetch: RootRouteTree, fetchStrategy: PrefetchTaskFetchStrategy, priority: PrefetchPriority, onInvalidate: null | (() => void), @@ -341,7 +338,7 @@ export function schedulePrefetchTask( // Spawn a new prefetch task const task: PrefetchTask = { key, - treeAtTimeOfPrefetch, + renderTreeAtTimeOfPrefetch, routeCacheVersion: getCurrentRouteCacheVersion(), segmentCacheVersion: getCurrentSegmentCacheVersion(), segmentCacheMap: taskSegmentCacheMap, @@ -391,7 +388,7 @@ export function cancelPrefetchTask(task: PrefetchTask): void { export function reschedulePrefetchTask( task: PrefetchTask, - treeAtTimeOfPrefetch: FlightRouterState, + renderTreeAtTimeOfPrefetch: RootRouteTree, fetchStrategy: PrefetchTaskFetchStrategy, priority: PrefetchPriority ): void { @@ -419,7 +416,7 @@ export function reschedulePrefetchTask( // Intent priority, even if the rescheduled priority is lower. task === mostRecentlyHoveredLink ? PrefetchPriority.Intent : priority - task.treeAtTimeOfPrefetch = treeAtTimeOfPrefetch + task.renderTreeAtTimeOfPrefetch = renderTreeAtTimeOfPrefetch task.fetchStrategy = fetchStrategy trackMostRecentlyHoveredLink(task) @@ -436,7 +433,7 @@ export function reschedulePrefetchTask( export function isPrefetchTaskDirty( task: PrefetchTask, nextUrl: string | null, - tree: FlightRouterState + root: RootRouteTree ): boolean { // This is used to quickly bail out of a prefetch task if the result is // guaranteed to not have changed since the task was initiated. This is @@ -446,7 +443,7 @@ export function isPrefetchTaskDirty( return ( task.routeCacheVersion !== getCurrentRouteCacheVersion() || task.segmentCacheVersion !== getCurrentSegmentCacheVersion() || - task.treeAtTimeOfPrefetch !== tree || + task.renderTreeAtTimeOfPrefetch !== root || task.key.nextUrl !== nextUrl ) } @@ -634,7 +631,7 @@ function processQueueInMicrotask() { const routeHasPartialPrefetching = route !== null && route.status === EntryStatus.Fulfilled && - (route.tree.prefetchHints & + (route.root.tree.prefetchHints & PrefetchHint.SubtreeHasPartialPrefetching) !== 0 task.phase = routeHasPartialPrefetching @@ -854,7 +851,7 @@ function pingRootRouteTree( // Stop prefetching segments until there's more bandwidth. return PrefetchTaskExitStatus.InProgress } - const tree = route.tree + const tree = route.root.tree // A task's fetch strategy gets set to `PPR` for any "auto" prefetch. // If it turned out that the route isn't PPR-enabled, we need to use `LoadingBoundary` instead. @@ -911,19 +908,48 @@ function pingRootRouteTree( staticWalkStrategy === FetchStrategy.PPR && !needsSpeculativePrefetch( task.fetchStrategy, - route.tree.prefetchHints + route.root.tree.prefetchHints ) ) { return PrefetchTaskExitStatus.Done } - pingStaticHead(now, task, route, staticWalkStrategy) + // The head is a one-node tree beside the route tree (see + // createMetadataRouteTree in cache.ts); it takes the same walk as + // a segment the current page doesn't have. If the head was inlined + // into a page's bundle (HeadOutlined is NOT set on the root), skip + // the standalone walk — the head data will arrive as part of that + // page's response, and its runtime-completeness signal is carried + // by that page's own entries. + const head = route.root.head + if ( + !process.env.__NEXT_PREFETCH_INLINING || + (route.root.tree.prefetchHints & PrefetchHint.HeadOutlined) !== 0 || + // An inlined head that can't attempt a static fetch still deopts + // to the runtime request (the first check of the decision point + // in pingSegmentInCacheComponentsTree); only the static fetch + // itself is skipped for an inlined head. + (walkCanUseRuntimeRequests(staticWalkStrategy, route) && + !shouldSegmentAttemptStaticRequest(staticWalkStrategy, head)) + ) { + const headExitStatus = pingNewPartOfCacheComponentsTree( + now, + task, + route, + head, + null, + staticWalkStrategy + ) + if (headExitStatus === PrefetchTaskExitStatus.InProgress) { + return PrefetchTaskExitStatus.InProgress + } + } const exitStatus = pingSharedPartOfCacheComponentsTree( now, task, route, - task.treeAtTimeOfPrefetch, + task.renderTreeAtTimeOfPrefetch.tree, tree, null, staticWalkStrategy @@ -933,7 +959,7 @@ function pingRootRouteTree( return PrefetchTaskExitStatus.InProgress } - // `pingNewPartOfCacheComponentsTree` may have determined that + // `pingSegmentInCacheComponentsTree` may have determined that // we need to do a runtime prefetch for one or more segments. // Bail out early if runtime prefetches are not permitted for this route. if (walkCanUseRuntimeRequests(staticWalkStrategy, route)) { @@ -944,19 +970,32 @@ function pingRootRouteTree( // spawnedRuntimePrefetches was populated during the traversal // above: every subtree in the new part of the tree that needs a - // runtime prefetch — plus, during the Shell phase, the head, if - // its static attempt was insufficient (see above). + // runtime prefetch, the head included — it registers under its + // own request key, like any segment, when its own static attempt + // was insufficient or never happened. // // If it's null, nothing in the new part of the tree is a candidate // for runtime prefetching, and we don't fetch the head, either — - // the head is runtime prefetched only if one of the segments is. + // the head is runtime prefetched only if something is. const spawnedRuntimePrefetches = task.spawnedRuntimePrefetches if (spawnedRuntimePrefetches !== null) { const spawnedEntries = new Map< SegmentRequestKey, PendingSegmentCacheEntry >() - pingRuntimeHead(now, task, route, spawnedEntries, runtimeStrategy) + // The head has no position in the request tree — a runtime + // response carries it beside the segments (see + // writeServerResponseIntoCache in cache.ts) — so the head's + // own request tree is discarded. + pingRouteTreeAndIncludeDynamicData( + now, + task, + route, + head, + false, + spawnedEntries, + runtimeStrategy + ) const requestTree = pingRuntimePrefetches( now, task, @@ -1002,12 +1041,29 @@ function pingRootRouteTree( SegmentRequestKey, PendingSegmentCacheEntry >() - pingRuntimeHead(now, task, route, spawnedEntries, fetchStrategy) + // The head has no position in the request tree — a runtime response + // carries it beside the segments (see writeServerResponseIntoCache + // in cache.ts) — so the head's own request tree is discarded. + const head = route.root.head + pingRouteTreeAndIncludeDynamicData( + now, + task, + route, + head, + false, + spawnedEntries, + // When prefetching the head, there's no difference between Full + // and LoadingBoundary: the head has no loading boundary, so a + // LoadingBoundary request would skip it. + fetchStrategy === FetchStrategy.LoadingBoundary + ? FetchStrategy.Full + : fetchStrategy + ) const dynamicRequestTree = diffRouteTreeAgainstCurrent( now, task, route, - task.treeAtTimeOfPrefetch, + task.renderTreeAtTimeOfPrefetch.tree, tree, spawnedEntries, fetchStrategy @@ -1059,84 +1115,6 @@ type SegmentBundle = { parent: SegmentBundle | null } -/** - * Prefetches the Head data for a page (metadata, viewport). The Head is not - * really a route segment, in the sense that it doesn't appear in the route - * tree, but we store it in the cache as if it were, using a special key. - * - * Symmetric with the per-segment decision point in - * pingNewPartOfCacheComponentsTree: the head deopts to the runtime prefetch - * path either when it requires runtime completeness and no static attempt is - * happening, or when a fulfilled static head entry reported that a runtime - * request would return more content than the entry contains. Deopting - * registers the head under its metadata request key, which makes the runtime - * gate in pingRootRouteTree fire even when every tree segment was - * sufficient; pingRuntimeHead performs the actual head work. - */ -function pingStaticHead( - now: number, - task: PrefetchTask, - route: FulfilledRouteCacheEntry, - // The per-pass static walk strategy; see pingRootRouteTree where - // it's derived. - fetchStrategy: FetchStrategy.PPR | FetchStrategy.StaticShell -): void { - const headCanUseRuntimeRequests = walkCanUseRuntimeRequests( - fetchStrategy, - route - ) - if ( - headCanUseRuntimeRequests && - // The head is not a tree node — it hangs off the route root — so the - // static-attempt hints are read from the root's node. (Segments read the - // hints from their own node; see `pingNewPartOfCacheComponentsTree.`) - !shouldSegmentAttemptStaticRequest(fetchStrategy, route.tree) - ) { - // No static attempt: the head arrives via the runtime request instead. - addSpawnedRuntimePrefetch(task, route.metadata.requestKey) - return - } - - if ( - // If the head was inlined into a page's bundle (HeadOutlined is NOT set - // on the root), skip the standalone fetch — the head data will arrive - // as part of that page's response, and its runtime-completeness signal - // is carried by that page's own entries. - process.env.__NEXT_PREFETCH_INLINING && - !(route.tree.prefetchHints & PrefetchHint.HeadOutlined) - ) { - return - } - - const segments: SegmentBundle = { - tree: route.metadata, - entry: readOrCreateSegmentCacheEntry( - now, - task.segmentCacheMap, - fetchStrategy, - route.metadata - ), - parent: null, - } - const needsRuntimeRequest = pingSegmentBundle( - now, - task, - route, - task.key, - route.metadata, - segments, - fetchStrategy, - true - ) - if (headCanUseRuntimeRequests && needsRuntimeRequest) { - // The static attempt was insufficient for the head. Deopt to a - // runtime prefetch. (Outside of runtime-completeness contexts the - // head's signal is unused — a partial static head is filled in by the - // navigation-time request, as with any other static segment.) - addSpawnedRuntimePrefetch(task, route.metadata.requestKey) - } -} - /** * Whether the task can use runtime requests to prefetch the content. * @@ -1163,7 +1141,9 @@ function walkCanUseRuntimeRequests( } // `FetchStrategy.PPR` can only use runtime requests if PPF is enabled on the route. return ( - (route.tree.prefetchHints & PrefetchHint.SubtreeHasPartialPrefetching) !== 0 + (route.root.tree.prefetchHints & + PrefetchHint.SubtreeHasPartialPrefetching) !== + 0 ) } @@ -1278,8 +1258,9 @@ function isShellEntryEligibleForStaticAttempt( } /** - * Register a subtree root (or the head's metadata key) for the batched - * runtime request issued by the gate at the end of pingRootRouteTree. + * Register a subtree root (the head is one, under its own request key) for + * the batched runtime request issued by the gate at the end of + * pingRootRouteTree. */ function addSpawnedRuntimePrefetch( task: PrefetchTask, @@ -1292,62 +1273,85 @@ function addSpawnedRuntimePrefetch( } } -function pingRuntimeHead( - now: number, - task: PrefetchTask, - route: FulfilledRouteCacheEntry, - spawnedEntries: Map, - fetchStrategy: - | FetchStrategy.Full - | FetchStrategy.PPRRuntime - | FetchStrategy.RuntimeShell - | FetchStrategy.LoadingBoundary -): void { - pingRouteTreeAndIncludeDynamicData( - now, - task, - route, - route.metadata, - false, - spawnedEntries, - // When prefetching the head, there's no difference between Full - // and LoadingBoundary - fetchStrategy === FetchStrategy.LoadingBoundary - ? FetchStrategy.Full - : fetchStrategy - ) -} - // TODO: Rename dynamic -> runtime throughout this module +/** + * The static walk over the part of the target route that also exists on the + * current page: the current page's node and the target route's node at the + * same position in the tree. It mirrors the navigation's traversal order (see + * updateRenderTreeOnNavigation in render-tree.ts): first whether the route + * position still matches — if not, this node begins the new part of the route + * (pingNewPartOfCacheComponentsTree) — and then whether any of the node's + * param values changed — if so, the node and everything below it begin the + * new part of the route too. The walk does not consult which params a + * segment read, so it prefetches more than the navigation replaces: the + * navigation keeps data whose read params did not change and decides each + * descendant on its own. A node the walk keeps is prefetched at the ordinary + * static tier. + * Its children continue here wherever the current page has a child in the + * same slot; a child in a slot the current page doesn't have enters the new + * part of the route directly. + * + * Bundle chains must not cross the strategy boundary: a kept node walks at + * PPR while a Shell-phase new part walks at StaticShell, and a chain spanning + * both would fulfill the kept node's concrete-path entry with shell-variant + * data. Nor may the chain be finished by fetching the new-part node at PPR + * — that would prefetch new-part segments at the concrete tier during the + * Shell phase, which only the Speculative phase is allowed to do. So the + * chain is dropped wherever the walk hands off to the new part during a + * StaticShell walk, exactly like the drop sites in + * pingSegmentInCacheComponentsTree: nothing in a dropped chain was upgraded + * to Pending, so no entry is stranded, and the inlined kept data is fetched + * by the Speculative pass whenever its walk of the new part permits the + * child fetch. + */ function pingSharedPartOfCacheComponentsTree( now: number, task: PrefetchTask, route: FulfilledRouteCacheEntry, - oldTree: FlightRouterState, + currentTree: RouteTree, newTree: RouteTree, parentBundle: SegmentBundle | null, // The per-pass static walk strategy; see pingRootRouteTree where // it's derived. fetchStrategy: FetchStrategy.PPR | FetchStrategy.StaticShell ): PrefetchTaskExitStatus.InProgress | PrefetchTaskExitStatus.Done { - // When Cache Components is enabled (or PPR, or a fully static route when PPR - // is disabled; those cases are treated equivalently to Cache Components), we - // start by prefetching each segment individually. Once we reach the "new" - // part of the tree — the part that doesn't exist on the current page — we - // may choose to switch to a runtime prefetch instead, based on the - // information sent by the server in the route tree. - // - // The traversal starts in the "shared" part of the tree. Once we reach the - // "new" part of the tree, we switch to a different traversal, - // pingNewPartOfCacheComponentsTree. - - // The shared part of the tree always performs the ordinary static (PPR) - // prefetch, regardless of phase. Phase-specific strategies — the runtime - // shell request and the Shell phase's StaticShell walk — apply only to the - // new part of the tree, so the per-pass walk strategy is irrelevant here. - // (The needs-runtime signal is ignored: shared segments are already - // rendered on the current page, so a runtime prefetch has nothing to add.) + if (!doesRouteStructureMatch(currentTree, newTree)) { + // We're entering the part of the target route that doesn't exist on the + // current page. + return pingNewPartOfCacheComponentsTree( + now, + task, + route, + newTree, + fetchStrategy === FetchStrategy.StaticShell ? null : parentBundle, + fetchStrategy + ) + } + if ( + compareParams(currentTree.varyPath, newTree.varyPath) !== ParamsChange.None + ) { + // A param changed. The navigation replaces only the data that read it and + // decides each descendant on its own (see updateRenderTreeOnNavigation in + // render-tree.ts); this walk doesn't know what each segment read, so it + // prefetches the whole subtree. + return pingNewPartOfCacheComponentsTree( + now, + task, + route, + newTree, + fetchStrategy === FetchStrategy.StaticShell ? null : parentBundle, + fetchStrategy + ) + } + + // The navigation keeps this segment's current data. A kept segment always + // performs the ordinary static (PPR) prefetch, regardless of phase. + // Phase-specific strategies — the runtime shell request and the Shell + // phase's StaticShell walk — apply only to the new part of the tree, so the + // per-pass walk strategy is irrelevant here. (The needs-runtime signal is + // ignored: kept segments are already rendered on the current page, so a + // runtime prefetch has nothing to add.) const bundleInProgress = accumulateSegmentBundle( now, task, @@ -1358,20 +1362,16 @@ function pingSharedPartOfCacheComponentsTree( true ).bundle - // Recursively ping the children. - const oldTreeChildren = oldTree[1] + // Recursively ping the children, continuing in lockstep with the current + // page wherever it has a child in the same slot. const newTreeChildren = newTree.slots if (newTreeChildren !== null) { + const currentSlots = currentTree.slots for (const [parallelRouteKey, newTreeChild] of newTreeChildren) { if (!hasNetworkBandwidth(task)) { // Stop prefetching segments until there's more bandwidth. return PrefetchTaskExitStatus.InProgress } - const newTreeChildSegment = newTreeChild.segment - const oldTreeChild: FlightRouterState | void = - oldTreeChildren[parallelRouteKey] - const oldTreeChildSegment: FlightRouterStateSegment | void = - oldTreeChild?.[0] // Only pass the bundle to the child that accepts it. A parent is // only ever bundled into one child. const bundleForChild = @@ -1380,50 +1380,30 @@ function pingSharedPartOfCacheComponentsTree( newTreeChild.prefetchHints & PrefetchHint.ParentInlinedIntoSelf ? bundleInProgress : null - let childExitStatus - if ( - oldTreeChildSegment !== undefined && - doesCurrentSegmentMatchCachedSegment( - route, - newTreeChildSegment, - oldTreeChildSegment - ) - ) { - // We're still in the "shared" part of the tree. + let currentTreeChild: RouteTree | undefined = undefined + if (currentSlots !== null) { + currentTreeChild = currentSlots.get(parallelRouteKey) + } + let childExitStatus: + | PrefetchTaskExitStatus.InProgress + | PrefetchTaskExitStatus.Done + if (currentTreeChild !== undefined) { childExitStatus = pingSharedPartOfCacheComponentsTree( now, task, route, - oldTreeChild, + currentTreeChild, newTreeChild, bundleForChild, fetchStrategy ) } else { - // We've entered the "new" part of the tree. Switch - // traversal functions. - // - // Bundle chains must not cross the strategy boundary: the shared - // part walks at PPR while a Shell-phase new part walks at - // StaticShell, and a chain spanning both would fulfill the shared - // parent's concrete-path entry with shell-variant data. Nor may we - // finish the chain by fetching the new-part child at PPR here — - // that would prefetch new-part segments at the concrete tier - // during the Shell phase, which only the Speculative phase is - // allowed to do. So drop the bundle instead, exactly like the - // Speculative walk's subtree bail does when a chain crosses into a - // subtree it skips: nothing in a dropped chain was upgraded to - // Pending, so no entry is stranded, and the inlined shared data is - // fetched by the Speculative pass whenever its walk of the new - // part permits the child fetch. - const bundleForNewPart = - fetchStrategy === FetchStrategy.StaticShell ? null : bundleForChild childExitStatus = pingNewPartOfCacheComponentsTree( now, task, route, newTreeChild, - bundleForNewPart, + fetchStrategy === FetchStrategy.StaticShell ? null : bundleForChild, fetchStrategy ) } @@ -1434,9 +1414,18 @@ function pingSharedPartOfCacheComponentsTree( } } + // The static attempt was sufficient for this segment (each child is its + // own decision point) — or parts of it are still in flight, in which case + // the task is blocked and the decision re-runs against the received + // responses. return PrefetchTaskExitStatus.Done } +/** + * The static walk over the part of the target route that doesn't exist on + * the current page. Nothing here is compared against the current tree: every + * segment goes through the per-segment decision point. + */ function pingNewPartOfCacheComponentsTree( now: number, task: PrefetchTask, @@ -1447,33 +1436,104 @@ function pingNewPartOfCacheComponentsTree( // it's derived. fetchStrategy: FetchStrategy.PPR | FetchStrategy.StaticShell ): PrefetchTaskExitStatus.InProgress | PrefetchTaskExitStatus.Done { - // We're now prefetching in the "new" part of the tree, the part that - // doesn't exist on the current page. (In other words, we're deeper than - // the shared layouts.) Segments in here default to being prefetched - // statically, at the per-pass strategy derived in pingRootRouteTree. - // - // This is where we decide whether we should use runtime requests, if the walk - // is allowed to do so (see `walkCanUseRuntimeRequests`). - // - // If runtime requests are allowed, but the segment's node has one of the - // `ShouldAttemptStatic{Shell,Prefetch}` hints set (either because the build-time - // prerender accessed no runtime data, or because of `ensureStatic`), then its - // subtree should be prefetched statically first. - // However, the hint may be stale after a revalidation, so we'll also check the - // `needsRuntimeRequest` promise on the static response, and will follow up with - // a runtime request if needed. - // Pending responses block the task, so the attempt is serial, never raced: - // static attempt → observe → runtime (if needed). - // - // The static hints and `needsRuntimeRequest` have no effect if runtime requests - // are not allowed (i.e. outside of Partial Prefetching). + const accumulation = pingSegmentInCacheComponentsTree( + now, + task, + route, + tree, + parentBundle, + fetchStrategy + ) + if (accumulation === null) { + return PrefetchTaskExitStatus.Done + } + const bundleInProgress = accumulation.bundle + + // Recursively ping the children. + const treeChildren = tree.slots + if (treeChildren !== null) { + for (const treeChild of treeChildren.values()) { + if (!hasNetworkBandwidth(task)) { + // Stop prefetching segments until there's more bandwidth. + return PrefetchTaskExitStatus.InProgress + } + // Only pass the bundle to the child that accepts it. A parent is + // only ever bundled into one child. + const bundleForChild = + process.env.__NEXT_PREFETCH_INLINING && + bundleInProgress !== null && + treeChild.prefetchHints & PrefetchHint.ParentInlinedIntoSelf + ? bundleInProgress + : null + const childExitStatus = pingNewPartOfCacheComponentsTree( + now, + task, + route, + treeChild, + bundleForChild, + fetchStrategy + ) + if (childExitStatus === PrefetchTaskExitStatus.InProgress) { + // Child yielded without finishing. + return PrefetchTaskExitStatus.InProgress + } + } + } + + // The static attempt was sufficient for this segment (each child is its + // own decision point) — or parts of it are still in flight, in which case + // the task is blocked and the decision re-runs against the received + // responses. + return PrefetchTaskExitStatus.Done +} +/** + * The per-segment decision point of the static walk: the one place that + * decides how a segment in the new part of the route — one the navigation + * won't keep — is prefetched (pingNewPartOfCacheComponentsTree). + * + * When Cache Components is enabled (or PPR, or a fully static route when PPR + * is disabled; those cases are treated equivalently to Cache Components), we + * prefetch each segment individually, statically, at the per-pass strategy + * derived in pingRootRouteTree. + * + * This is where we decide whether we should use runtime requests, if the walk + * is allowed to do so (see `walkCanUseRuntimeRequests`). + * + * If runtime requests are allowed, but the segment's node has one of the + * `ShouldAttemptStatic{Shell,Prefetch}` hints set (either because the build-time + * prerender accessed no runtime data, or because of `ensureStatic`), then its + * subtree should be prefetched statically first. + * However, the hint may be stale after a revalidation, so we'll also check the + * `needsRuntimeRequest` promise on the static response, and will follow up with + * a runtime request if needed. + * Pending responses block the task, so the attempt is serial, never raced: + * static attempt → observe → runtime (if needed). + * + * The static hints and `needsRuntimeRequest` have no effect if runtime requests + * are not allowed (i.e. outside of Partial Prefetching). + * + * Returns the segment's bundle accumulation when the walk should continue + * into its children, and null when the walk stops at this segment: the link + * needs no speculative prefetch, or the segment deopted and the batched + * runtime request covers the whole subtree. + */ +function pingSegmentInCacheComponentsTree( + now: number, + task: PrefetchTask, + route: FulfilledRouteCacheEntry, + tree: RouteTree, + parentBundle: SegmentBundle | null, + // The per-pass static walk strategy; see pingRootRouteTree where + // it's derived. + fetchStrategy: FetchStrategy.PPR | FetchStrategy.StaticShell +): { bundle: SegmentBundle | null; needsRuntimeRequest: boolean } | null { // In PPF, links may skip speculative prefetching if they only need a shell. if ( fetchStrategy === FetchStrategy.PPR && - !needsSpeculativePrefetch(task.fetchStrategy, route.tree.prefetchHints) + !needsSpeculativePrefetch(task.fetchStrategy, route.root.tree.prefetchHints) ) { - return PrefetchTaskExitStatus.Done + return null } // Constant for the whole pass; recomputed here only because the walk is @@ -1504,7 +1564,7 @@ function pingNewPartOfCacheComponentsTree( fetchStrategy ) } - return PrefetchTaskExitStatus.Done + return null } // Prefetch this segment and its subtree statically, using the normal @@ -1518,7 +1578,6 @@ function pingNewPartOfCacheComponentsTree( fetchStrategy, true ) - const bundleInProgress = accumulation.bundle if (canUseRuntimeRequests && accumulation.needsRuntimeRequest) { // The static attempt for this segment was insufficient. Stop the walk @@ -1529,51 +1588,17 @@ function pingNewPartOfCacheComponentsTree( // upgraded to Pending, so no entry is stranded blocking the task, and // Empty entries in the dropped chain are re-fetched by a later pass.) addSpawnedRuntimePrefetch(task, tree.requestKey) - return PrefetchTaskExitStatus.Done - } - - if (tree.slots !== null) { - if (!hasNetworkBandwidth(task)) { - // Stop prefetching segments until there's more bandwidth. - return PrefetchTaskExitStatus.InProgress - } - // Recursively ping the children. - for (const childTree of tree.slots.values()) { - // Only pass the bundle to the child that accepts it. A parent is - // only ever bundled into one child. - const bundleForChild = - process.env.__NEXT_PREFETCH_INLINING && - bundleInProgress !== null && - childTree.prefetchHints & PrefetchHint.ParentInlinedIntoSelf - ? bundleInProgress - : null - const childResult = pingNewPartOfCacheComponentsTree( - now, - task, - route, - childTree, - bundleForChild, - fetchStrategy - ) - if (childResult === PrefetchTaskExitStatus.InProgress) { - // Child yielded without finishing. - return PrefetchTaskExitStatus.InProgress - } - } + return null } - // The static attempt was sufficient for this segment (each child is its - // own decision point) — or parts of it are still in flight, in which case - // the task is blocked and the decision re-runs against the received - // responses. - return PrefetchTaskExitStatus.Done + return accumulation } function diffRouteTreeAgainstCurrent( now: number, task: PrefetchTask, route: FulfilledRouteCacheEntry, - oldTree: FlightRouterState, + oldTree: RouteTree, newTree: RouteTree, spawnedEntries: Map, fetchStrategy: @@ -1582,129 +1607,30 @@ function diffRouteTreeAgainstCurrent( | FetchStrategy.LoadingBoundary ): FlightRouterState { // This is a single recursive traversal that does multiple things: - // - Finds the parts of the target route (newTree) that are not part of - // of the current page (oldTree) by diffing them, using the same algorithm - // as a real navigation. + // - Finds the segments that differ from the current route, comparing each + // segment the same way the navigation will (see + // updateRenderTreeOnNavigation in render-tree.ts): its route position, + // then whether any of its param values changed. // - Constructs a request tree (FlightRouterState) that describes which // segments need to be prefetched and which ones are already cached. // - Creates a set of pending cache entries for the segments that need to // be prefetched, so that a subsequent prefetch task does not request the // same segments again. - const oldTreeChildren = oldTree[1] + const oldSlots = oldTree.slots const newTreeChildren = newTree.slots let requestTreeChildren: Record = {} if (newTreeChildren !== null) { for (const [parallelRouteKey, newTreeChild] of newTreeChildren) { - const newTreeChildSegment = newTreeChild.segment - const oldTreeChild: FlightRouterState | void = - oldTreeChildren[parallelRouteKey] - const oldTreeChildSegment: FlightRouterStateSegment | void = - oldTreeChild?.[0] - if ( - oldTreeChildSegment !== undefined && - doesCurrentSegmentMatchCachedSegment( - route, - newTreeChildSegment, - oldTreeChildSegment - ) - ) { - // This segment is already part of the current route. Keep traversing. - const requestTreeChild = diffRouteTreeAgainstCurrent( - now, - task, - route, - oldTreeChild, - newTreeChild, - spawnedEntries, - fetchStrategy - ) - requestTreeChildren[parallelRouteKey] = requestTreeChild - } else { - // This segment is not part of the current route. We're entering a - // part of the tree that we need to prefetch (unless everything is - // already cached). - switch (fetchStrategy) { - case FetchStrategy.LoadingBoundary: { - // When PPR is disabled, we can't prefetch per segment. We must - // fallback to the old prefetch behavior and send a runtime request. - // Only routes that include a loading boundary can be prefetched in - // this way. - // - // This is simlar to a "full" prefetch, but we're much more - // conservative about which segments to include in the request. - // - // The server will only render up to the first loading boundary - // inside new part of the tree. If there's no loading boundary - // anywhere in the tree, the server will never return any data, so - // we can skip the request. - const subtreeHasLoadingBoundary = - (newTreeChild.prefetchHints & - (PrefetchHint.SegmentHasLoadingBoundary | - PrefetchHint.SubtreeHasLoadingBoundary)) !== - 0 - const requestTreeChild = subtreeHasLoadingBoundary - ? pingPPRDisabledRouteTreeUpToLoadingBoundary( - now, - task, - route, - newTreeChild, - null, - spawnedEntries - ) - : // There's no loading boundary within this tree. Bail out. - convertRouteTreeToFlightRouterState(newTreeChild) - requestTreeChildren[parallelRouteKey] = requestTreeChild - break - } - case FetchStrategy.PPRRuntime: { - // This is a runtime prefetch. Fetch all cacheable data in the tree, - // not just the static PPR shell. - const requestTreeChild = pingRouteTreeAndIncludeDynamicData( - now, - task, - route, - newTreeChild, - false, - spawnedEntries, - fetchStrategy - ) - requestTreeChildren[parallelRouteKey] = requestTreeChild - break - } - case FetchStrategy.Full: { - // This is a "full" prefetch. Fetch all the data in the tree, both - // static and dynamic. We issue roughly the same request that we - // would during a real navigation. The goal is that once the - // navigation occurs, the router should not have to fetch any - // additional data. - // - // Although the response will include dynamic data, opting into a - // Full prefetch — via — implicitly - // instructs the cache to treat the response as "static", or non- - // dynamic, since the whole point is to cache it for - // future navigations. - // - // Construct a tree (currently a FlightRouterState) that represents - // which segments need to be prefetched and which ones are already - // cached. If the tree is empty, then we can exit. Otherwise, we'll - // send the request tree to the server and use the response to - // populate the segment cache. - const requestTreeChild = pingRouteTreeAndIncludeDynamicData( - now, - task, - route, - newTreeChild, - false, - spawnedEntries, - fetchStrategy - ) - requestTreeChildren[parallelRouteKey] = requestTreeChild - break - } - default: - fetchStrategy satisfies never - } - } + const oldTreeChild = oldSlots?.get(parallelRouteKey) + requestTreeChildren[parallelRouteKey] = diffSegmentAgainstCurrent( + now, + task, + route, + oldTreeChild, + newTreeChild, + spawnedEntries, + fetchStrategy + ) } } const requestTree: FlightRouterState = [ @@ -1719,6 +1645,124 @@ function diffRouteTreeAgainstCurrent( return requestTree } +/** + * The per-segment decision of a runtime request's tree walk + * (diffRouteTreeAgainstCurrent): the segment at this position of the target + * route, and the current page's segment at the same position, if it has one. + * A segment the navigation keeps — same route position, and none of its param + * values changed — is omitted from the request and the walk continues into + * its children. Otherwise this segment begins a part of the tree that needs + * to be prefetched (unless everything is already cached), requested according + * to the strategy. + */ +function diffSegmentAgainstCurrent( + now: number, + task: PrefetchTask, + route: FulfilledRouteCacheEntry, + oldTree: RouteTree | undefined, + newTree: RouteTree, + spawnedEntries: Map, + fetchStrategy: + | FetchStrategy.Full + | FetchStrategy.PPRRuntime + | FetchStrategy.LoadingBoundary +): FlightRouterState { + if (oldTree !== undefined && doesRouteStructureMatch(oldTree, newTree)) { + // This segment is already part of the current route. + if ( + compareParams(oldTree.varyPath, newTree.varyPath) === ParamsChange.None + ) { + // The navigation keeps its data. Keep traversing. + return diffRouteTreeAgainstCurrent( + now, + task, + route, + oldTree, + newTree, + spawnedEntries, + fetchStrategy + ) + } + } + // This segment is not part of the current route, or the navigation + // replaces its data. We're entering a part of the tree that we need to + // prefetch (unless everything is already cached). + switch (fetchStrategy) { + case FetchStrategy.LoadingBoundary: { + // When PPR is disabled, we can't prefetch per segment. We must + // fallback to the old prefetch behavior and send a runtime request. + // Only routes that include a loading boundary can be prefetched in + // this way. + // + // This is simlar to a "full" prefetch, but we're much more + // conservative about which segments to include in the request. + // + // The server will only render up to the first loading boundary + // inside new part of the tree. If there's no loading boundary + // anywhere in the tree, the server will never return any data, so + // we can skip the request. + const subtreeHasLoadingBoundary = + (newTree.prefetchHints & + (PrefetchHint.SegmentHasLoadingBoundary | + PrefetchHint.SubtreeHasLoadingBoundary)) !== + 0 + if (subtreeHasLoadingBoundary) { + return pingPPRDisabledRouteTreeUpToLoadingBoundary( + now, + task, + route, + newTree, + null, + spawnedEntries + ) + } + // There's no loading boundary within this tree. Bail out. + return convertRouteTreeToFlightRouterState(newTree) + } + case FetchStrategy.PPRRuntime: { + // This is a runtime prefetch. Fetch all cacheable data in the tree, + // not just the static PPR shell. + return pingRouteTreeAndIncludeDynamicData( + now, + task, + route, + newTree, + false, + spawnedEntries, + fetchStrategy + ) + } + case FetchStrategy.Full: { + // This is a "full" prefetch. Fetch all the data in the tree, both + // static and dynamic. We issue roughly the same request that we + // would during a real navigation. The goal is that once the + // navigation occurs, the router should not have to fetch any + // additional data. + // + // Although the response will include dynamic data, opting into a + // Full prefetch — via — implicitly + // instructs the cache to treat the response as "static", or non- + // dynamic, since the whole point is to cache it for + // future navigations. + // + // Construct a tree (currently a FlightRouterState) that represents + // which segments need to be prefetched and which ones are already + // cached. If the tree is empty, then we can exit. Otherwise, we'll + // send the request tree to the server and use the response to + // populate the segment cache. + return pingRouteTreeAndIncludeDynamicData( + now, + task, + route, + newTree, + false, + spawnedEntries, + fetchStrategy + ) + } + } +} + function pingPPRDisabledRouteTreeUpToLoadingBoundary( now: number, task: PrefetchTask, @@ -2112,11 +2156,11 @@ function pingRuntimePrefetches( * request would return more content than the entry contains * (needsRuntimeRequest, derived at write time from the response that * produced the entry). The callers surface this signal to the per-segment - * decision point in pingNewPartOfCacheComponentsTree (and its analog for - * the head in pingStaticHead), which uses it during a static attempt to - * decide whether to fall back to a runtime prefetch. One exception withholds - * the signal: a shell-tier entry whose segment carries the static-attempt - * hint spawns a concrete static attempt first — see the Fulfilled case. + * decision point in pingSegmentInCacheComponentsTree, which uses it during + * a static attempt to decide whether to fall back to a runtime prefetch. One + * exception withholds the signal: a shell-tier entry whose segment carries + * the static-attempt hint spawns a concrete static attempt first — see the + * Fulfilled case. */ function pingSegmentBundle( now: number, @@ -2437,7 +2481,7 @@ function accumulateSegmentBundle( ) { if (segment.status === EntryStatus.Pending) { // The chain this entry joins may be dropped before it's ever pinged - // (see the drop sites in pingNewPartOfCacheComponentsTree), and only + // (see the drop sites in pingSegmentInCacheComponentsTree), and only // the ping blocks on Pending entries. Register on the in-flight // response at read time instead, so the pass observes it before the // phase can complete even if the chain is dropped. When the chain does @@ -2478,12 +2522,12 @@ function accumulateSegmentBundle( tree.prefetchHints & PrefetchHint.HeadInlinedIntoSelf ) { effectiveParent = { - tree: route.metadata, + tree: route.root.head, entry: readOrCreateSegmentCacheEntry( now, task.segmentCacheMap, fetchStrategy, - route.metadata + route.root.head ), parent: parentBundle, } @@ -2624,36 +2668,6 @@ function pingFullSegmentRevalidation( } } -function doesCurrentSegmentMatchCachedSegment( - route: FulfilledRouteCacheEntry, - currentSegment: Segment, - cachedSegment: Segment -): boolean { - if (cachedSegment === PAGE_SEGMENT_KEY) { - // In the FlightRouterState stored by the router, the page segment has the - // rendered search params appended to the name of the segment. In the - // prefetch cache, however, this is stored separately. So, when comparing - // the router's current FlightRouterState to the cached FlightRouterState, - // we need to make sure we compare both parts of the segment. - // TODO: This is not modeled clearly. We use the same type, - // FlightRouterState, for both the CacheNode tree _and_ the prefetch cache - // _and_ the server response format, when conceptually those are three - // different things and treated in different ways. We should encode more of - // this information into the type design so mistakes are less likely. - return ( - currentSegment === - addSearchParamsIfPageSegment( - PAGE_SEGMENT_KEY, - urlSearchParamsToParsedUrlQuery( - new URLSearchParams(route.renderedSearch) - ) - ) - ) - } - // Non-page segments are compared using the same function as the server - return matchSegment(cachedSegment, currentSegment) -} - /** * Decides whether to speculatively prefetch a subtree. Under Partial * Prefetching we only do this if the Link's prefetch prop is set to true — diff --git a/packages/next/src/client/components/segment-cache/vary-path.ts b/packages/next/src/client/components/segment-cache/vary-path.ts index 958970068b7d..f6bc56f77f99 100644 --- a/packages/next/src/client/components/segment-cache/vary-path.ts +++ b/packages/next/src/client/components/segment-cache/vary-path.ts @@ -4,9 +4,15 @@ import type { NormalizedSearch, NormalizedNextUrl, } from './cache-key' -import type { RouteTree, RSCSegmentData } from './cache' +import type { RouteTree } from './cache' import { Fallback, type FallbackType } from './cache-map' -import { HEAD_REQUEST_KEY } from '../../../shared/lib/segment-cache/segment-value-encoding' +import type { SegmentRequestKey } from '../../../shared/lib/segment-cache/segment-value-encoding' +import { + readVaryParams, + SEARCH_PARAMS_VARY_ID, + type VaryParamId, + type VaryParams, +} from '../../../shared/lib/segment-cache/vary-params-decoding' type Opaque = T & { __brand: K } @@ -25,17 +31,17 @@ type Opaque = T & { __brand: K } * A route's vary path is simpler: it's comprised of the pathname, search * string, and Next-URL header. */ -export type VaryPath = { +export type VaryPathNode = { /** * Identifies which param this vary path node corresponds to. Used by * getFulfilledSegmentVaryPath to determine which params to replace with * Fallback based on the varyParams set from the server. * * - For path params: the param name (e.g., 'slug') - * - For search params: '?' + * - For search params: SEARCH_PARAMS_VARY_ID * - For non-param nodes (request keys, etc.): null */ - id: string | null + id: VaryParamId | null value: string | null | FallbackType /** * Whether this node corresponds to a root param — a path param at or above @@ -44,11 +50,11 @@ export type VaryPath = { * Fallback. See getShellSegmentVaryPath. Only ever true on path param nodes; * false for structural and search param nodes. * - * Always a boolean (never undefined) so that every VaryPath node shares a + * Always a boolean (never undefined) so that every VaryPathNode shares a * single hidden class, keeping the cache hot paths monomorphic. */ isRootParam: boolean - parent: VaryPath | null + parent: VaryPathNode | null } // Because it's so important for vary paths to line up across cache accesses, @@ -62,7 +68,7 @@ export type RouteVaryPath = Opaque< value: NormalizedPathname isRootParam: false parent: { - id: '?' + id: typeof SEARCH_PARAMS_VARY_ID value: NormalizedSearch isRootParam: false parent: { @@ -76,38 +82,25 @@ export type RouteVaryPath = Opaque< 'RouteVaryPath' > -// requestKey -> pathParams -export type LayoutVaryPath = Opaque< +// requestKey -> [searchParams] -> pathParams +// +// The first entry is the request key (id: null). It is followed by the search +// params entry (id: SEARCH_PARAMS_VARY_ID) when the segment varies on search +// params, and then by the path params (id: param name), one entry per param, +// nearest first. +export type VaryPath = Opaque< { id: null - value: string + value: SegmentRequestKey isRootParam: false - parent: PartialSegmentVaryPath | null + parent: VaryPathNode | null }, - 'LayoutVaryPath' + 'VaryPath' > -// requestKey -> searchParams -> pathParams -export type PageVaryPath = Opaque< - { - id: null - value: string - isRootParam: false - parent: { - id: '?' - value: NormalizedSearch | FallbackType - isRootParam: false - parent: PartialSegmentVaryPath | null - } - }, - 'PageVaryPath' -> - -export type SegmentVaryPath = LayoutVaryPath | PageVaryPath - // Intermediate type used when building a vary path during a recursive traversal // of the route tree. -export type PartialSegmentVaryPath = Opaque +export type PartialVaryPath = Opaque export function getRouteVaryPath( pathname: NormalizedPathname, @@ -115,12 +108,12 @@ export function getRouteVaryPath( nextUrl: NormalizedNextUrl | null ): RouteVaryPath { // requestKey -> searchParams -> nextUrl - const varyPath: VaryPath = { + const varyPath: VaryPathNode = { id: null, value: pathname, isRootParam: false, parent: { - id: '?', + id: SEARCH_PARAMS_VARY_ID, value: search, isRootParam: false, parent: { @@ -143,12 +136,12 @@ export function getFulfilledRouteVaryPath( // This is called when a route's data is fulfilled. The cache entry will be // re-keyed based on which inputs the response varies by. // requestKey -> searchParams -> nextUrl - const varyPath: VaryPath = { + const varyPath: VaryPathNode = { id: null, value: pathname, isRootParam: false, parent: { - id: '?', + id: SEARCH_PARAMS_VARY_ID, value: search, isRootParam: false, parent: { @@ -163,119 +156,63 @@ export function getFulfilledRouteVaryPath( } export function appendLayoutVaryPath( - parentPath: PartialSegmentVaryPath | null, + parentPath: PartialVaryPath | null, cacheKey: string, paramName: string, isRootParam: boolean -): PartialSegmentVaryPath { - const varyPathPart: VaryPath = { +): PartialVaryPath { + const varyPathPart: VaryPathNode = { id: paramName, value: cacheKey, isRootParam, parent: parentPath, } - return varyPathPart as PartialSegmentVaryPath + return varyPathPart as PartialVaryPath } -export function finalizeLayoutVaryPath( - requestKey: string, - varyPath: PartialSegmentVaryPath | null -): LayoutVaryPath { - const layoutVaryPath: VaryPath = { - id: null, - value: requestKey, - isRootParam: false, - parent: varyPath, +export function finalizeVaryPath( + requestKey: SegmentRequestKey, + // Non-null when the segment varies on search params: the search entry is + // spliced in between the request key and the path params. Fallback keys an + // entry that is reusable across all search strings. + searchParams: NormalizedSearch | FallbackType | null, + partialVaryPath: PartialVaryPath | null +): VaryPath { + // requestKey -> [searchParams] -> pathParams + let parent: VaryPathNode | null = partialVaryPath + if (searchParams !== null) { + parent = { + id: SEARCH_PARAMS_VARY_ID, + value: searchParams, + isRootParam: false, + parent: partialVaryPath, + } } - return layoutVaryPath as LayoutVaryPath -} - -export function getPartialLayoutVaryPath( - finalizedVaryPath: LayoutVaryPath -): PartialSegmentVaryPath | null { - // This is the inverse of finalizeLayoutVaryPath. - return finalizedVaryPath.parent -} - -export function finalizePageVaryPath( - requestKey: string, - renderedSearch: NormalizedSearch, - varyPath: PartialSegmentVaryPath | null -): PageVaryPath { - // Unlike layouts, a page segment's vary path also includes the search string. - // requestKey -> searchParams -> pathParams - const pageVaryPath: VaryPath = { + const varyPath: VaryPathNode = { id: null, value: requestKey, isRootParam: false, - parent: { - id: '?', - value: renderedSearch, - isRootParam: false, - parent: varyPath, - }, + parent, } - return pageVaryPath as PageVaryPath -} - -export function getPartialPageVaryPath( - finalizedVaryPath: PageVaryPath -): PartialSegmentVaryPath | null { - // This is the inverse of finalizePageVaryPath. - return finalizedVaryPath.parent.parent + return varyPath as VaryPath } -export function finalizeMetadataVaryPath( - pageRequestKey: string, - renderedSearch: NormalizedSearch, - varyPath: PartialSegmentVaryPath | null -): PageVaryPath { - // The metadata "segment" is not a real segment because it doesn't exist in - // the normal structure of the route tree, but in terms of caching, it - // behaves like a page segment because it varies by all the same params as - // a page. - // - // To keep the protocol for querying the server simple, the request key for - // the metadata does not include any path information. It's unnecessary from - // the server's perspective, because unlike page segments, there's only one - // metadata response per URL, i.e. there's no need to distinguish multiple - // parallel pages. - // - // However, this means the metadata request key is insufficient for - // caching the the metadata in the client cache, because on the client we - // use the request key to distinguish the metadata entry from all other - // page's metadata entries. - // - // So instead we create a simulated request key based on the page segment. - // Conceptually this is equivalent to the request key the server would have - // assigned the metadata segment if it treated it as part of the actual - // route structure. - - // If there are multiple parallel pages, we use whichever is the first one. - // This is fine because the only difference between request keys for - // different parallel pages are things like route groups and parallel - // route slots. As long as it's always the same one, it doesn't matter. - const pageVaryPath: VaryPath = { - id: null, - // Append the actual metadata request key to the page request key. Note - // that we're not using a separate vary path part; it's unnecessary because - // these are not conceptually separate inputs. - value: pageRequestKey + HEAD_REQUEST_KEY, - isRootParam: false, - parent: { - id: '?', - value: renderedSearch, - isRootParam: false, - parent: varyPath, - }, +export function getPartialVaryPath( + finalizedVaryPath: VaryPath +): PartialVaryPath | null { + // This is the inverse of finalizeVaryPath: strip the request key, and the + // search params entry if there is one. + const parent = finalizedVaryPath.parent + if (parent !== null && parent.id === SEARCH_PARAMS_VARY_ID) { + return parent.parent as PartialVaryPath | null } - return pageVaryPath as PageVaryPath + return parent as PartialVaryPath | null } -export function getSegmentVaryPathForRequest( +export function getSegmentVaryPathForRequest( fetchStrategy: FetchStrategy, - tree: RouteTree -): SegmentVaryPath { + tree: RouteTree +): VaryPath { // This is used for storing pending requests in the cache. We want to choose // the most generic vary path based on the strategy used to fetch it, i.e. // static/PPR versus runtime prefetching, so that it can be reused as much @@ -313,10 +250,13 @@ export function getSegmentVaryPathForRequest( return tree.shellVaryPath } - // Only page segments (and the special "metadata" segment, which is treated - // like a page segment for the purposes of caching) may contain search - // params. There's no reason to include them in the vary path otherwise. - if (tree.isPage) { + // The vary path includes a search params entry only when the segment varies + // on search params. + const searchParamsVaryPath = originalVaryPath.parent + if ( + searchParamsVaryPath !== null && + searchParamsVaryPath.id === SEARCH_PARAMS_VARY_ID + ) { // Only a runtime prefetch will include search params in the vary path. // Static prefetches never include search params, so they can be reused // across all possible search param values. @@ -325,67 +265,153 @@ export function getSegmentVaryPathForRequest( fetchStrategy === FetchStrategy.PPRRuntime if (!doesVaryOnSearchParams) { - // The response from the the server will not vary on search params. Clone - // the end of the original vary path to replace the search params - // with Fallback. + // The response from the the server will not vary on search params. + // Rebuild the vary path with the search params replaced by Fallback. // // requestKey -> searchParams -> pathParams // ^ This part gets replaced with Fallback - const searchParamsVaryPath = (originalVaryPath as PageVaryPath).parent - const pathParamsVaryPath = searchParamsVaryPath.parent - const patchedVaryPath: VaryPath = { - id: null, - value: originalVaryPath.value, - isRootParam: false, - parent: { - id: '?', - value: Fallback, - isRootParam: false, - parent: pathParamsVaryPath, - }, - } - return patchedVaryPath as SegmentVaryPath + return finalizeVaryPath( + originalVaryPath.value, + Fallback, + getPartialVaryPath(originalVaryPath) + ) } } // The request does vary on search params. We don't need to modify anything. - return originalVaryPath as SegmentVaryPath + return originalVaryPath } -export function clonePageVaryPathWithNewSearchParams( - originalVaryPath: PageVaryPath, +export function cloneVaryPathWithNewSearchParams( + originalVaryPath: VaryPath, newSearch: NormalizedSearch -): PageVaryPath { +): VaryPath { // requestKey -> searchParams -> pathParams // ^ This part gets replaced with newSearch const searchParamsVaryPath = originalVaryPath.parent - const clonedVaryPath: VaryPath = { - id: null, - value: originalVaryPath.value, - isRootParam: false, - parent: { - id: '?', - value: newSearch, - isRootParam: false, - parent: searchParamsVaryPath.parent, - }, + if ( + searchParamsVaryPath === null || + searchParamsVaryPath.id !== SEARCH_PARAMS_VARY_ID + ) { + // No search params entry; nothing to replace. + return originalVaryPath } - return clonedVaryPath as PageVaryPath + return finalizeVaryPath( + originalVaryPath.value, + newSearch, + getPartialVaryPath(originalVaryPath) + ) } +/** + * Returns the rendered value of the vary path's search params entry when the + * vary path has one with a concrete value, null otherwise. Only a segment that + * varies on search params carries the entry; on every other vary path, and on + * one whose search params entry is Fallback, this is null. + */ export function getRenderedSearchFromVaryPath( - varyPath: PageVaryPath + varyPath: VaryPath ): NormalizedSearch | null { - const searchParams = varyPath.parent.value - return typeof searchParams === 'string' - ? (searchParams as NormalizedSearch) - : null + let node: VaryPathNode | null = varyPath + while (node !== null) { + if (node.id === SEARCH_PARAMS_VARY_ID) { + const search = node.value + if (typeof search === 'string') { + return search as NormalizedSearch + } + return null + } + node = node.parent + } + return null +} + +/** + * The kind of param change between two vary paths for the same segment. A path + * param change takes precedence, because path params are part of + * LayoutRouter's React key: the segment remounts either way. + */ +export const enum ParamsChange { + None, + SearchParams, + PathParam, +} + +export function compareParams( + currentVaryPath: VaryPath, + nextVaryPath: VaryPath +): ParamsChange { + // Both vary paths are for the same segment, so they list the same params in + // the same order. Walk them together. This includes params inherited from + // parent layouts, since those may have changed, too. + let current: VaryPathNode | null = currentVaryPath + let next: VaryPathNode | null = nextVaryPath + let change = ParamsChange.None + while (current !== null && next !== null) { + if (current.value !== next.value) { + const id = current.id + if (id === null) { + // The request key. Callers check that the route structure matches + // first, so it's always the same. + } else if (id === SEARCH_PARAMS_VARY_ID) { + change = ParamsChange.SearchParams + } else { + return ParamsChange.PathParam + } + } + current = current.parent + next = next.parent + } + return change +} + +export function didReadChangedParam( + currentVaryPath: VaryPath, + nextVaryPath: VaryPath, + varyParams: VaryParams | null +): boolean { + // Returns true if the output rendered with `currentVaryPath` read a param + // whose value is different in `nextVaryPath`. `varyParams` is the set of + // params the output read, or null if we don't know. + // + // Same traversal as compareParams. Only read the set if a param changed. + let current: VaryPathNode | null = currentVaryPath + let next: VaryPathNode | null = nextVaryPath + let total: Set | null = null + while (current !== null && next !== null) { + if (current.value !== next.value) { + const id = current.id + if (id === null) { + // The request key. Callers check that the route structure matches + // first, so it's always the same. + } else { + if (total === null) { + if (varyParams === null) { + // We don't know. Assume it read the param. + return true + } + total = readVaryParams(varyParams) + if (total === null) { + // The render hasn't finished, or it aborted. Assume it read the + // param. + return true + } + } + if (total.has(id)) { + return true + } + } + } + current = current.parent + next = next.parent + } + return false } export function getFulfilledSegmentVaryPath( - original: VaryPath, - varyParams: Set -): SegmentVaryPath { + original: VaryPathNode, + varyParams: Set +): VaryPath { // Re-keys a segment's vary path based on which params the segment actually // depends on. Params that are NOT in the varyParams set are replaced with // Fallback, allowing the cache entry to be reused across different values of @@ -394,7 +420,7 @@ export function getFulfilledSegmentVaryPath( // This is called when a segment is fulfilled with data from the server. The // varyParams set comes from the server and indicates which params were // accessed during rendering. - const clone: VaryPath = { + const clone: VaryPathNode = { id: original.id, // If the id is null, this node is not a param (e.g., it's a request key). // If the id is in the varyParams set, keep the original value. @@ -409,10 +435,10 @@ export function getFulfilledSegmentVaryPath( ? null : getFulfilledSegmentVaryPath(original.parent, varyParams), } - return clone as SegmentVaryPath + return clone as VaryPath } -export function getShellSegmentVaryPath(original: VaryPath): SegmentVaryPath { +export function getShellSegmentVaryPath(original: VaryPathNode): VaryPath { // Re-keys a segment's vary path to identify the "App Shell" entry for this // segment position — a reusable loading state that can be served for any // concrete navigation to this segment. The shell is rendered with params @@ -421,7 +447,7 @@ export function getShellSegmentVaryPath(original: VaryPath): SegmentVaryPath { // them. Accordingly, we keep the concrete value of structural nodes (request // keys, etc.) and root param nodes, and replace every other param node (non- // root path params and search params) with Fallback. - const clone: VaryPath = { + const clone: VaryPathNode = { id: original.id, value: original.id === null || original.isRootParam === true @@ -433,5 +459,5 @@ export function getShellSegmentVaryPath(original: VaryPath): SegmentVaryPath { ? null : getShellSegmentVaryPath(original.parent), } - return clone as SegmentVaryPath + return clone as VaryPath } diff --git a/packages/next/src/client/flight-data-helpers.test.ts b/packages/next/src/client/flight-data-helpers.test.ts index 2c3901a4b5cf..a9dbf98a2ca3 100644 --- a/packages/next/src/client/flight-data-helpers.test.ts +++ b/packages/next/src/client/flight-data-helpers.test.ts @@ -8,11 +8,12 @@ describe('prepareFlightRouterStateForRequest', () => { describe('HMR refresh handling', () => { it('should preserve complete state for HMR refresh requests', () => { const flightRouterState: FlightRouterState = [ - '__PAGE__?{"sensitive":"data"}', + '__PAGE__', {}, ['/some/url', ''], 'refetch', PrefetchHint.IsRootLayoutOrAbove | 1, + '?sensitive=data', ] const result = prepareFlightRouterStateForRequest(flightRouterState, true) @@ -22,19 +23,28 @@ describe('prepareFlightRouterStateForRequest', () => { }) }) - describe('__PAGE__ segment handling', () => { - it('should strip search params from __PAGE__ segments', () => { + describe('page search handling', () => { + it('should strip the page search (index 5)', () => { const flightRouterState: FlightRouterState = [ - '__PAGE__?{"param":"value","foo":"bar"}', + '__PAGE__', {}, + ['/some/url', ''], + 'refetch', + PrefetchHint.IsRootLayoutOrAbove | 1, + '?sensitive=data', ] - const result = prepareFlightRouterStateForRequest(flightRouterState) + const result = prepareFlightRouterStateForRequest( + flightRouterState, + false + ) const decoded = JSON.parse(decodeURIComponent(result)) - expect(decoded[0]).toBe('__PAGE__') + expect(decoded[5]).toBeUndefined() }) + }) + describe('__PAGE__ segment handling', () => { it('should preserve non-page segments', () => { const flightRouterState: FlightRouterState = ['regular-segment', {}] @@ -174,7 +184,7 @@ describe('prepareFlightRouterStateForRequest', () => { const flightRouterState: FlightRouterState = [ 'parent', { - children: ['__PAGE__?{"nested":"param"}', {}, ['/nested/url', '']], + children: ['__PAGE__', {}, ['/nested/url', '']], modal: ['modal-segment', {}, ['/modal/url', ''], 'refetch'], }, ['/parent/url', ''], @@ -188,7 +198,7 @@ describe('prepareFlightRouterStateForRequest', () => { 'parent', { children: [ - '__PAGE__', // search params stripped + '__PAGE__', {}, // URL stripped // 'refresh' marker stripped @@ -212,12 +222,7 @@ describe('prepareFlightRouterStateForRequest', () => { children: [ 'level1', { - children: [ - '__PAGE__?{"deep":"nesting"}', - {}, - ['/deep/url', ''], - 'refetch', - ], + children: ['__PAGE__', {}, ['/deep/url', ''], 'refetch'], }, ], }, @@ -235,13 +240,13 @@ describe('prepareFlightRouterStateForRequest', () => { describe('real-world scenarios', () => { it('should handle complex FlightRouterState with all features', () => { const complexState: FlightRouterState = [ - '__PAGE__?{"userId":"123"}', + '__PAGE__', { children: [ 'dashboard', { modal: [ - '__PAGE__?{"modalParam":"data"}', + '__PAGE__', {}, ['/modal/path', ''], null, @@ -270,7 +275,7 @@ describe('prepareFlightRouterStateForRequest', () => { const decoded = JSON.parse(decodeURIComponent(result)) // Root level checks - expect(decoded[0]).toBe('__PAGE__') // search params stripped + expect(decoded[0]).toBe('__PAGE__') expect(decoded[2]).toBeNull() // URL stripped expect(decoded[3]).toBe('inside-shared-layout') // server marker preserved expect(decoded[4]).toBe( @@ -289,7 +294,7 @@ describe('prepareFlightRouterStateForRequest', () => { // Modal route checks const modalRoute = childrenRoute[1].modal - expect(modalRoute[0]).toBe('__PAGE__') // search params stripped + expect(modalRoute[0]).toBe('__PAGE__') expect(modalRoute[2]).toBeNull() // URL stripped expect(modalRoute[3]).toBeNull() // 'refresh' marker stripped expect(modalRoute[4]).toBe(PrefetchHint.SegmentHasLoadingBoundary) // prefetchHints preserved diff --git a/packages/next/src/client/flight-data-helpers.ts b/packages/next/src/client/flight-data-helpers.ts index e1c32f2dfb15..3214d8bea777 100644 --- a/packages/next/src/client/flight-data-helpers.ts +++ b/packages/next/src/client/flight-data-helpers.ts @@ -8,8 +8,6 @@ import type { FullTransportNode, TransportSegment, } from '../shared/lib/rsc-transport' -import { PAGE_SEGMENT_KEY } from '../shared/lib/segment' -import type { NormalizedSearch } from './components/segment-cache/cache-key' import { getCacheKeyForDynamicParam, parseDynamicParamFromURLPart, @@ -57,8 +55,7 @@ export function createInitialRSCPayloadFromFallbackPrerender( t: fillInFallbackTransportTree( fallbackTransportData.t, renderedPathname.split('/').filter((part) => part !== ''), - 0, - renderedSearch as NormalizedSearch + 0 ), h: fallbackTransportData.h, }, @@ -80,8 +77,7 @@ export function createInitialRSCPayloadFromFallbackPrerender( function fillInFallbackTransportTree( node: FullTransportNode, pathnameParts: Array, - pathnamePartsIndex: number, - renderedSearch: NormalizedSearch + pathnamePartsIndex: number ): FullTransportNode { const originalSegment = node.s let newSegment: TransportSegment @@ -98,7 +94,7 @@ function fillInFallbackTransportTree( newSegment = { n: originalSegment.n, t: originalSegment.t, - k: getCacheKeyForDynamicParam(paramValue, renderedSearch), + k: getCacheKeyForDynamicParam(paramValue), s: originalSegment.s, } doesAppearInURL = true @@ -120,8 +116,7 @@ function fillInFallbackTransportTree( fillInFallbackTransportTree( childNode, pathnameParts, - childPathnamePartsIndex, - renderedSearch + childPathnamePartsIndex ) ) } @@ -206,22 +201,18 @@ function stripClientOnlyDataFromFlightRouterState( result[4] = prefetchHints } - // Everything else is used only by the client and is not needed for requests. + // Leave out the page's search params. They're only used by the client, and + // request headers may be forwarded through redirects. return result } /** * Strips client-only data from segments: - * - Search parameters from __PAGE__ segments * - staticSiblings from dynamic segment tuples (only needed for client-side * prefetch reuse decisions) */ function stripClientOnlyDataFromSegment(segment: Segment): Segment { if (typeof segment === 'string') { - // Strip search params from __PAGE__ segments - if (segment.startsWith(PAGE_SEGMENT_KEY + '?')) { - return PAGE_SEGMENT_KEY - } return segment } // Dynamic segment tuple: [paramName, paramCacheKey, paramType, staticSiblings] diff --git a/packages/next/src/client/route-params.ts b/packages/next/src/client/route-params.ts index 90a6d75a93a5..5762196a49f3 100644 --- a/packages/next/src/client/route-params.ts +++ b/packages/next/src/client/route-params.ts @@ -1,9 +1,5 @@ import type { DynamicParamTypesShort } from '../shared/lib/app-router-types' -import { - addSearchParamsIfPageSegment, - DEFAULT_SEGMENT_KEY, - PAGE_SEGMENT_KEY, -} from '../shared/lib/segment' +import { DEFAULT_SEGMENT_KEY, PAGE_SEGMENT_KEY } from '../shared/lib/segment' import { ROOT_SEGMENT_REQUEST_KEY } from '../shared/lib/segment-cache/segment-value-encoding' import { NEXT_REWRITTEN_PATH_HEADER, @@ -19,9 +15,19 @@ import type { } from './components/segment-cache/cache-key' import type { RSCResponse } from './components/router-reducer/fetch-server-response' import type { ParsedUrlQuery } from 'querystring' +import { getRenderedSearch as getRenderedSearchFromQuery } from '../shared/lib/router/utils/querystring' export type RouteParamValue = string | Array | null +export function normalizeRenderedSearch(search: string): NormalizedSearch { + // The same search params should produce the same string whether they came + // from the URL, a rewrite header, or the response (e.g. '+' vs '%20'). Only + // used for the rendered search. Request URLs keep their original form, + // since middleware and rewrites may depend on it. + const query = urlSearchParamsToParsedUrlQuery(new URLSearchParams(search)) + return getRenderedSearchFromQuery(query) as NormalizedSearch +} + export function getRenderedSearch( response: RSCResponse | Response ): NormalizedSearch { @@ -30,14 +36,13 @@ export function getRenderedSearch( // the response will include a header that gives the rewritten search query. const rewrittenQuery = response.headers.get(NEXT_REWRITTEN_QUERY_HEADER) if (rewrittenQuery !== null) { - return ( - rewrittenQuery === '' ? '' : '?' + rewrittenQuery - ) as NormalizedSearch + return normalizeRenderedSearch(rewrittenQuery) } // If the header is not present, there was no rewrite, so we use the search // query of the response URL. - return urlToUrlWithoutFlightMarker(new URL(response.url)) - .search as NormalizedSearch + return normalizeRenderedSearch( + urlToUrlWithoutFlightMarker(new URL(response.url)).search + ) } export function getRenderedPathname( @@ -181,7 +186,7 @@ export function doesStaticSegmentAppearInURL(segment: string): boolean { // Otherwise, we wouldn't need this special case because pages are // always leaf nodes. // TODO: Investigate why the loader produces these fake page segments. - segment.startsWith(PAGE_SEGMENT_KEY) || + segment === PAGE_SEGMENT_KEY || // Route groups. (segment[0] === '(' && segment.endsWith(')')) || segment === DEFAULT_SEGMENT_KEY || @@ -195,21 +200,13 @@ export function doesStaticSegmentAppearInURL(segment: string): boolean { } export function getCacheKeyForDynamicParam( - paramValue: RouteParamValue, - renderedSearch: NormalizedSearch + paramValue: RouteParamValue ): string { // This needs to match the logic in get-dynamic-param.ts, until we're able to // unify the various implementations so that these are always computed on // the client. if (typeof paramValue === 'string') { - // TODO: Refactor or remove this helper function to accept a string rather - // than the whole segment type. Also we can probably just append the - // search string instead of turning it into JSON. - const pageSegmentWithSearchParams = addSearchParamsIfPageSegment( - paramValue, - urlSearchParamsToParsedUrlQuery(new URLSearchParams(renderedSearch)) - ) as string - return pageSegmentWithSearchParams + return paramValue } else if (paramValue === null) { return '' } else { diff --git a/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts b/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts index a455b619ad4c..0e5cb58d7083 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts +++ b/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts @@ -50,7 +50,7 @@ export function formatRoutePattern(tree: FlightRouterState): string { // and route groups (parenthesized segments like "(marketing)") if ( segment !== '' && - !segment.startsWith('__PAGE__') && + segment !== '__PAGE__' && segment !== '__DEFAULT__' && !(segment.startsWith('(') && segment.endsWith(')')) ) { diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 854e9a7c6cfc..3062e321bcb5 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -73,6 +73,7 @@ import type { AnyStream } from './stream-ops' import { createRenderInBrowserAbortSignal } from './render-in-browser' import { getInstantTestBootstrapScriptContent } from './instant-test-bootstrap' import { stripInternalQueries } from '../internal-utils' +import { getRenderedSearch } from '../../shared/lib/router/utils/querystring' import { NEXT_HMR_REFRESH_HEADER, NEXT_ROUTER_PREFETCH_HEADER, @@ -2157,39 +2158,6 @@ function prepareInitialCanonicalUrl(url: RequestStore['url']) { return (url.pathname + url.search).split('/') } -function getRenderedSearch(query: NextParsedUrlQuery): string { - // Inlined implementation of querystring.encode, which is not available in - // the Edge runtime. - const pairs = [] - for (const key in query) { - const value = query[key] - if (value == null) continue - if (Array.isArray(value)) { - for (const v of value) { - pairs.push( - `${encodeURIComponent(key)}=${encodeURIComponent(String(v))}` - ) - } - } else { - pairs.push( - `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}` - ) - } - } - - // The result should match the format of a web URL's `search` property, since - // this is the format that's stored in the App Router state. - // TODO: We're a bit inconsistent about this. The x-nextjs-rewritten-query - // header omits the leading question mark. Should refactor to always do - // that instead. - if (pairs.length === 0) { - // If the search string is empty, return an empty string. - return '' - } - // Prepend '?' to the search params string. - return '?' + pairs.join('&') -} - // This is the data necessary to render when no SSR errors are encountered async function getRSCPayload( tree: LoaderTree, @@ -2456,7 +2424,6 @@ async function getErrorRSCPayload( ctx.missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - query, ctx.renderOpts.notFoundParams ) // Attach the error shell as the root's render output. Vary params are not @@ -7818,7 +7785,6 @@ async function validateInstantConfigs( cache, loaderTree, ctx.getDynamicParamFromSegment, - ctx.query, depth, groupDepthForValidation, extraChunksSignal, diff --git a/packages/next/src/server/app-render/create-component-tree.tsx b/packages/next/src/server/app-render/create-component-tree.tsx index c7134cc5f1c2..8b535880257d 100644 --- a/packages/next/src/server/app-render/create-component-tree.tsx +++ b/packages/next/src/server/app-render/create-component-tree.tsx @@ -47,10 +47,7 @@ import type { UseCacheLayoutProps, UseCachePageProps, } from '../use-cache/use-cache-wrapper' -import { - addSearchParamsIfPageSegment, - DEFAULT_SEGMENT_KEY, -} from '../../shared/lib/segment' +import { DEFAULT_SEGMENT_KEY } from '../../shared/lib/segment' import { BOUNDARY_PREFIX, BOUNDARY_SUFFIX, @@ -470,10 +467,7 @@ async function createComponentTreeInternal( // The segment's identity on the wire. const transportSegment = segmentToTransportSegment( - addSearchParamsIfPageSegment( - segmentParam ? segmentParam.treeSegment : segment, - query - ) + segmentParam ? segmentParam.treeSegment : segment ) // Create object holding the parent params and current params @@ -600,7 +594,6 @@ async function createComponentTreeInternal( ctx.missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - query, ctx.renderOpts.notFoundParams, rootLayoutIncludedAtThisLevelOrAbove ) diff --git a/packages/next/src/server/app-render/create-transport-tree-from-loader-tree.ts b/packages/next/src/server/app-render/create-transport-tree-from-loader-tree.ts index 0769a0bd41f5..9dbe805ac13b 100644 --- a/packages/next/src/server/app-render/create-transport-tree-from-loader-tree.ts +++ b/packages/next/src/server/app-render/create-transport-tree-from-loader-tree.ts @@ -13,7 +13,6 @@ import { segmentToTransportSegment, } from '../../shared/lib/rsc-transport' import type { GetDynamicParamFromSegment } from './app-render' -import { addSearchParamsIfPageSegment } from '../../shared/lib/segment' import type { AppSegmentConfig } from '../../build/segment-config/app/app-segment-config' import { getSegmentParam } from '../../shared/lib/router/utils/get-segment-param' @@ -168,7 +167,6 @@ async function createTransportTreeFromLoaderTreeImpl( missingPrefetchHintPolicy: MissingPrefetchHintPolicy, partialPrefetching: boolean, getDynamicParamFromSegment: GetDynamicParamFromSegment, - searchParams: any, didFindRootLayout: boolean, notFoundParams: readonly string[] | undefined ): Promise { @@ -205,7 +203,6 @@ async function createTransportTreeFromLoaderTreeImpl( missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - searchParams, didFindRootLayout, notFoundParams ) @@ -220,9 +217,7 @@ async function createTransportTreeFromLoaderTreeImpl( } const node: PartialTransportNode = { - s: segmentToTransportSegment( - addSearchParamsIfPageSegment(treeSegment, searchParams) - ), + s: segmentToTransportSegment(treeSegment), } if (prefetchHints !== 0) { node.h = prefetchHints @@ -249,7 +244,6 @@ export async function createTransportTreeFromLoaderTree( missingPrefetchHintPolicy: MissingPrefetchHintPolicy, partialPrefetching: boolean, getDynamicParamFromSegment: GetDynamicParamFromSegment, - searchParams: any, notFoundParams: readonly string[] | undefined, // Whether a root layout was already found above this loader tree slice, so a // slice that starts below the root layout doesn't mark a sub-layout as the @@ -264,7 +258,6 @@ export async function createTransportTreeFromLoaderTree( missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - searchParams, didFindRootLayout, notFoundParams ) @@ -282,7 +275,6 @@ export async function createFullTransportTreeFromLoaderTree( missingPrefetchHintPolicy: MissingPrefetchHintPolicy, partialPrefetching: boolean, getDynamicParamFromSegment: GetDynamicParamFromSegment, - searchParams: any, notFoundParams: readonly string[] | undefined ): Promise { // With emitSkippedData, every node carries data, which is what @@ -296,7 +288,6 @@ export async function createFullTransportTreeFromLoaderTree( missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - searchParams, false, notFoundParams ) as Promise @@ -317,10 +308,6 @@ export async function createRouteTreePrefetch( // See note on createTransportTreeFromLoaderTree's didFindRootLayout. didFindRootLayout: boolean = false ): Promise { - // Search params should not be added to page segment's cache key during a - // route tree prefetch request, because they do not affect the structure of - // the route. The client cache has its own logic to handle search params. - const searchParams = {} return createTransportTreeFromLoaderTreeImpl( loaderTree, false, @@ -329,7 +316,6 @@ export async function createRouteTreePrefetch( missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - searchParams, didFindRootLayout, notFoundParams ) diff --git a/packages/next/src/server/app-render/instant-validation/instant-config.tsx b/packages/next/src/server/app-render/instant-validation/instant-config.tsx index aec3dd4b06c5..a246dce24b3e 100644 --- a/packages/next/src/server/app-render/instant-validation/instant-config.tsx +++ b/packages/next/src/server/app-render/instant-validation/instant-config.tsx @@ -27,11 +27,7 @@ import { InvariantError } from '../../../shared/lib/invariant-error' */ export function isImplicitValidationSegment(segment: Segment): boolean { const key = typeof segment === 'string' ? segment : segment[0] - return ( - key === PAGE_SEGMENT_KEY || - key.startsWith(PAGE_SEGMENT_KEY) || - key === DEFAULT_SEGMENT_KEY - ) + return key === PAGE_SEGMENT_KEY || key === DEFAULT_SEGMENT_KEY } /** diff --git a/packages/next/src/server/app-render/instant-validation/instant-validation.tsx b/packages/next/src/server/app-render/instant-validation/instant-validation.tsx index 1b06f00062a6..fdf073c52934 100644 --- a/packages/next/src/server/app-render/instant-validation/instant-validation.tsx +++ b/packages/next/src/server/app-render/instant-validation/instant-validation.tsx @@ -54,7 +54,6 @@ import type { FlightComponentMod } from '../stream-ops' // eslint-disable-next-line import/no-extraneous-dependencies import { createFromNodeStream } from 'react-server-dom-webpack/client' import { - addSearchParamsIfPageSegment, isGroupSegment, PAGE_SEGMENT_KEY, DEFAULT_SEGMENT_KEY, @@ -64,7 +63,6 @@ import { isFrameworkErrorRoute, isImplicitValidationSegment, } from './instant-config' -import type { NextParsedUrlQuery } from '../../request-meta' const filterStackFrame = process.env.NODE_ENV !== 'production' @@ -134,8 +132,6 @@ function traverseTransportNodeSegments( return } for (const [parallelRouteKey, childNode] of children) { - // NOTE: if this is a __PAGE__ segment, it might have search params appended. - // Whoever reads from the cache needs to append them as well. const childPath = createChildSegmentPath( path, parallelRouteKey, @@ -893,7 +889,7 @@ function segmentConsumesURLDepth(segment: Segment): boolean { if (typeof segment !== 'string') return true // Route groups, pages, defaults, and not-found don't consume a depth. if ( - segment.startsWith(PAGE_SEGMENT_KEY) || + segment === PAGE_SEGMENT_KEY || isGroupSegment(segment) || segment === DEFAULT_SEGMENT_KEY || segment === NOT_FOUND_SEGMENT_KEY @@ -1021,7 +1017,6 @@ export async function createCombinedPayloadAtDepth( cache: SegmentCache, initialLoaderTree: LoaderTree, getDynamicParamFromSegment: GetDynamicParamFromSegment, - query: NextParsedUrlQuery | null, depth: number, groupDepth: number, releaseSignal: AbortSignal, @@ -1074,8 +1069,7 @@ export async function createCombinedPayloadAtDepth( if (dynamicParam) { return dynamicParam.treeSegment } - const segment = loaderTree[0] - return query ? addSearchParamsIfPageSegment(segment, query) : segment + return loaderTree[0] } async function buildSharedTransportTree( diff --git a/packages/next/src/server/app-render/types.ts b/packages/next/src/server/app-render/types.ts index 90ebb79bcc36..ecffc3476415 100644 --- a/packages/next/src/server/app-render/types.ts +++ b/packages/next/src/server/app-render/types.ts @@ -79,7 +79,8 @@ export const flightRouterStateSchema: s.Describe = s.tuple([ ]) ) ), - s.optional(s.number()), + s.optional(s.nullable(s.number())), + s.optional(s.string()), ]) export type ServerOnInstrumentationRequestError = ( diff --git a/packages/next/src/server/app-render/vary-params.ts b/packages/next/src/server/app-render/vary-params.ts index 165136f29ac2..3b697e9a3a7f 100644 --- a/packages/next/src/server/app-render/vary-params.ts +++ b/packages/next/src/server/app-render/vary-params.ts @@ -4,11 +4,15 @@ import { getVaryParamsAccumulator, workUnitAsyncStorage, } from './work-unit-async-storage.external' +import { + SEARCH_PARAMS_VARY_ID, + type VaryParamId, +} from '../../shared/lib/segment-cache/vary-params-decoding' /** * Accumulates vary params for a single segment (or for metadata/rootParams). * - * A VaryParamsAccumulator is an `AsyncIterable` that can be serialized + * A VaryParamsAccumulator is an `AsyncIterable` that can be serialized * by React Flight. As params are accessed during render, each newly-seen param * name is `add`ed, which yields it into the Flight stream immediately. After * rendering, call `close()` (via `finishAccumulatingVaryParams`) to end the @@ -29,28 +33,29 @@ import { * only instance referenced by more than one segment, and it only ever yields * "done", so concurrent iteration of it is safe. */ -export class VaryParamsAccumulator implements AsyncIterable { - private _resolve: ((result: IteratorResult) => void) | null = null +export class VaryParamsAccumulator implements AsyncIterable { + private _resolve: ((result: IteratorResult) => void) | null = + null private _done = false - private _buffer: string[] = [] + private _buffer: VaryParamId[] = [] // The set of param names already yielded. Doubles as the dedupe guard so the // same name is never emitted twice. - private _seen: Set = new Set() + private _seen: Set = new Set() /** * Records that a param was accessed. Yields the name into the stream the * first time it's seen; subsequent accesses of the same name are no-ops. */ - add(paramName: string): void { - if (this._done || this._seen.has(paramName)) { + add(id: VaryParamId): void { + if (this._done || this._seen.has(id)) { return } - this._seen.add(paramName) + this._seen.add(id) if (this._resolve !== null) { - this._resolve({ value: paramName, done: false }) + this._resolve({ value: id, done: false }) this._resolve = null } else { - this._buffer.push(paramName) + this._buffer.push(id) } } @@ -67,7 +72,7 @@ export class VaryParamsAccumulator implements AsyncIterable { } } - [Symbol.asyncIterator](): AsyncIterator { + [Symbol.asyncIterator](): AsyncIterator { return { next: () => { if (this._buffer.length > 0) { @@ -76,7 +81,7 @@ export class VaryParamsAccumulator implements AsyncIterable { if (this._done) { return Promise.resolve({ value: undefined, done: true }) } - return new Promise>((resolve) => { + return new Promise>((resolve) => { this._resolve = resolve }) }, @@ -182,9 +187,9 @@ export function getRootParamsVaryParamsAccumulator(): VaryParamsAccumulator | nu */ export function accumulateVaryParam( accumulator: VaryParamsAccumulator, - paramName: string + id: VaryParamId ): void { - accumulator.add(paramName) + accumulator.add(id) } /** @@ -261,23 +266,23 @@ export function createVaryingSearchParams( // checks, or enumeration — must register as varying. A Proxy is required // (rather than per-property getters) so that enumeration of an empty // searchParams object still triggers a vary. All accesses bucket into the - // single sentinel '?'; the segment is keyed by the whole query string. + // single search params id; the segment is keyed by the whole query string. // TODO: Split into per-param tracking if the cache key evolves. return new Proxy(originalSearchParamsObject, { get(target, prop, receiver) { if (typeof prop === 'string') { - accumulateVaryParam(accumulator, '?') + accumulateVaryParam(accumulator, SEARCH_PARAMS_VARY_ID) } return Reflect.get(target, prop, receiver) }, has(target, prop) { if (typeof prop === 'string') { - accumulateVaryParam(accumulator, '?') + accumulateVaryParam(accumulator, SEARCH_PARAMS_VARY_ID) } return Reflect.has(target, prop) }, ownKeys(target) { - accumulateVaryParam(accumulator, '?') + accumulateVaryParam(accumulator, SEARCH_PARAMS_VARY_ID) return Reflect.ownKeys(target) }, }) diff --git a/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx b/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx index 9b10ff0450ff..b056daafe669 100644 --- a/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx +++ b/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx @@ -1,3 +1,5 @@ +import { PAGE_SEGMENT_KEY } from '../../shared/lib/segment' +import { getRenderedSearch } from '../../shared/lib/router/utils/querystring' import type { FlightRouterState, PrefetchHints, @@ -10,7 +12,6 @@ import { segmentToTransportSegment, } from '../../shared/lib/rsc-transport' import type { PreloadCallbacks } from './types' -import { matchSegment } from '../../client/components/match-segments' import type { LoaderTree } from '../lib/app-dir-module' import { getLinkAndScriptTags } from './get-css-inlined-link-tags' import { getPreloadableFonts } from './get-preloadable-fonts' @@ -21,7 +22,6 @@ import { } from './create-transport-tree-from-loader-tree' import type { AppRenderContext } from './app-render' import { hasLoadingComponentInTree } from './has-loading-component-in-tree' -import { addSearchParamsIfPageSegment } from '../../shared/lib/segment' import { createComponentTree } from './create-component-tree' /** @@ -37,6 +37,28 @@ export type NavigationResponseTree = { isHeadPartial: boolean } +function didRouteOrPathParamChange( + actualSegment: Segment, + requestedSegment: Segment +): boolean { + // The caller handles the page's search params separately. + if ( + typeof actualSegment === 'string' || + typeof requestedSegment === 'string' + ) { + // Static segments have to match exactly. + return actualSegment !== requestedSegment + } + + // Both segments are dynamic. Compare the param name, type, and value. The + // static sibling hints (index 3) aren't part of the segment's identity. + return ( + actualSegment[0] !== requestedSegment[0] || + actualSegment[2] !== requestedSegment[2] || + actualSegment[1] !== requestedSegment[1] + ) +} + /** * Use router state to decide at what common layout to render the page. * This can either be the common layout between two pages or a specific place to start rendering from using the "refetch" marker in the tree. @@ -111,10 +133,9 @@ export async function walkTreeWithFlightRouterState({ [segmentParam.param]: segmentParam.value, } : parentParams - const actualSegment: Segment = addSearchParamsIfPageSegment( - segmentParam ? segmentParam.treeSegment : segment, - query - ) + const actualSegment: Segment = segmentParam + ? segmentParam.treeSegment + : segment /** * Decide if the current segment is where rendering has to start. @@ -122,8 +143,13 @@ export async function walkTreeWithFlightRouterState({ const renderComponentsOnThisLevel = // No further router state available !flightRouterState || - // Segment in router state does not match current segment - !matchSegment(actualSegment, flightRouterState[0]) || + // Route structure or path param changed + didRouteOrPathParamChange(actualSegment, flightRouterState[0]) || + // Normal requests leave out the page's search params, so treat a missing + // value as empty. HMR requests include them. + (actualSegment === PAGE_SEGMENT_KEY && + getRenderedSearch(query) !== + (flightRouterState[5] === undefined ? '' : flightRouterState[5])) || // Explicit refresh flightRouterState[3] === 'refetch' @@ -182,7 +208,6 @@ export async function walkTreeWithFlightRouterState({ ctx.missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - query, ctx.renderOpts.notFoundParams, rootLayoutIncluded ) @@ -214,7 +239,6 @@ export async function walkTreeWithFlightRouterState({ ctx.missingPrefetchHintPolicy, partialPrefetching, getDynamicParamFromSegment, - query, ctx.renderOpts.notFoundParams, rootLayoutIncluded ) diff --git a/packages/next/src/server/dev/on-demand-entry-handler.ts b/packages/next/src/server/dev/on-demand-entry-handler.ts index a7a4d1167c6d..1e5cb1e49fe0 100644 --- a/packages/next/src/server/dev/on-demand-entry-handler.ts +++ b/packages/next/src/server/dev/on-demand-entry-handler.ts @@ -147,7 +147,7 @@ function getEntrypointsFromTree( ? convertDynamicParamTypeToSyntax(segment[2], segment[0]) : segment - const isPageSegment = currentSegment.startsWith(PAGE_SEGMENT_KEY) + const isPageSegment = currentSegment === PAGE_SEGMENT_KEY const currentPath = [...parentPath, isPageSegment ? '' : currentSegment] diff --git a/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.test.ts b/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.test.ts index a2f8a8931892..3fc2e1ad87e9 100644 --- a/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.test.ts +++ b/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.test.ts @@ -145,6 +145,28 @@ describe('PrerenderManifestMatcher', () => { route: genericRootRoute, }) }) + + it('should match a pathname that was already decoded for manifest lookup', () => { + const route = createMockDynamicRoute({ + fallbackSourceRoute: '/open/[...slug]', + }) + + const manifest = createMockPrerenderManifest({ + '/open/[...slug]': route, + }) + + const matcher = new PrerenderManifestMatcher( + '/open/[...slug]', + manifest + ) + + expect( + matcher.match('/open/docs/space here/with%2Fslash/100%') + ).toEqual({ + source: '/open/[...slug]', + route, + }) + }) }) describe('no match scenarios', () => { diff --git a/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.ts b/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.ts index 959a52250735..b99ee573e130 100644 --- a/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.ts +++ b/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.ts @@ -3,10 +3,6 @@ import type { PrerenderManifest, } from '../../../../build' import type { DeepReadonly } from '../../../../shared/lib/deep-readonly' -import { - getRouteMatcher, - type RouteMatchFn, -} from '../../../../shared/lib/router/utils/route-matcher' import { getRouteRegex } from '../../../../shared/lib/router/utils/route-regex' /** @@ -17,7 +13,7 @@ type Matcher = { * The matcher for the dynamic route. This is lazily created when the matcher * is first used. */ - matcher?: RouteMatchFn + matcher?: RegExp /** * The source of the dynamic route. @@ -69,11 +65,13 @@ export class PrerenderManifestMatcher { for (const matcher of this.matchers) { // Lazily create the matcher, this is only done once per matcher. if (!matcher.matcher) { - matcher.matcher = getRouteMatcher(getRouteRegex(matcher.source)) + // The pathname has already been decoded for manifest lookup. We only + // need to check its structure here; extracting captures would decode + // route parameters again and reject literal percent signs. + matcher.matcher = getRouteRegex(matcher.source).re } - const match = matcher.matcher(pathname) - if (match) { + if (matcher.matcher.test(pathname)) { return { source: matcher.source, route: matcher.route, diff --git a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts index a1a83495aef2..b77ef9627a28 100644 --- a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts +++ b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts @@ -1,5 +1,7 @@ 'use client' +import type { RouteTree } from '../../client/components/segment-cache/cache' + import type { ScrollHandlerRef, PrefetchKind, @@ -94,7 +96,7 @@ export const AppRouterContext = React.createContext( ) export const LayoutRouterContext = React.createContext<{ parentTree: FlightRouterState - parentCacheNode: CacheNode + parentRenderTree: RouteTree parentSegmentPath: FlightSegmentPath | null parentParams: Params parentLoadingData: LoadingModuleData | null diff --git a/packages/next/src/shared/lib/app-router-types.ts b/packages/next/src/shared/lib/app-router-types.ts index f59b446d4ab8..2a8950ff3f40 100644 --- a/packages/next/src/shared/lib/app-router-types.ts +++ b/packages/next/src/shared/lib/app-router-types.ts @@ -11,16 +11,19 @@ export type LoadingModuleData = | [React.JSX.Element, React.ReactNode, React.ReactNode] | null -import type { VaryParamsIterable } from './segment-cache/vary-params-decoding' +import type { + VaryParams, + VaryParamsIterable, +} from './segment-cache/vary-params-decoding' import type { FullTransportData, PartialTransportData } from './rsc-transport' /** viewport metadata node */ export type HeadData = React.ReactNode /** - * Cache node used in app-router / layout-router. + * Render state for a segment. Reuse this object while its data is unchanged; + * create a new one when a navigation replaces the segment's data. */ - export type CacheNode = { /** * When rsc is not null, it represents the RSC data for the @@ -45,11 +48,14 @@ export type CacheNode = { */ prefetchRsc: React.ReactNode - prefetchHead: HeadData | null - - head: HeadData - - slots: Record | null + /** + * The source of the params `rsc` depends on, from the response that + * produced it. Null when unknown: the data came from the segment cache, or + * from a render that didn't track params, or `rsc` is still pending — it + * is set alongside `rsc` when the response arrives. A navigation that only + * changes params this output did not depend on can keep rendering it. + */ + varyParams: VaryParams | null /** * A shared mutable ref that tracks whether this segment should be scrolled @@ -164,9 +170,16 @@ export type FlightRouterState = [ /** * Bitmask of PrefetchHint flags. Encodes route structure metadata: * root layout, loading boundaries, instant configs, and prefetch strategy - * hints. Only set when non-zero. + * hints. Only set when non-zero. JSON encodes an omitted slot as null when + * a later slot is present. + */ + prefetchHints?: number | null, + /** + * The rendered search params of a page segment. An empty string means there + * are none. Only set on page nodes, and left out of request headers. + * TODO: Revisit this as part of the larger FlightRouterState refactor. */ - prefetchHints?: number, + renderedSearch?: string, ] /** @@ -371,7 +384,7 @@ export function propagateSubtreeBits( /** * A path through the segment tree: a repeating sequence of segment and * parallel route key. Used by the client to address positions in the - * CacheNode tree (see layout-router). + * render tree (see layout-router). */ export type FlightSegmentPath = // Uses `any` as repeating pattern can't be typed. diff --git a/packages/next/src/shared/lib/router/utils/querystring.ts b/packages/next/src/shared/lib/router/utils/querystring.ts index f4e9a036e071..a96ab0daa9f4 100644 --- a/packages/next/src/shared/lib/router/utils/querystring.ts +++ b/packages/next/src/shared/lib/router/utils/querystring.ts @@ -1,5 +1,38 @@ import type { ParsedUrlQuery } from 'querystring' +export function getRenderedSearch(query: ParsedUrlQuery): string { + // Inlined implementation of querystring.encode, which is not available in + // the Edge runtime. + const pairs = [] + for (const key in query) { + const value = query[key] + if (value == null) continue + if (Array.isArray(value)) { + for (const v of value) { + pairs.push( + `${encodeURIComponent(key)}=${encodeURIComponent(String(v))}` + ) + } + } else { + pairs.push( + `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}` + ) + } + } + + // The result should match the format of a web URL's `search` property, since + // this is the format that's stored in the App Router state. + // TODO: We're a bit inconsistent about this. The x-nextjs-rewritten-query + // header omits the leading question mark. Should refactor to always do + // that instead. + if (pairs.length === 0) { + // If the search string is empty, return an empty string. + return '' + } + // Prepend '?' to the search params string. + return '?' + pairs.join('&') +} + export function searchParamsToUrlQuery( searchParams: URLSearchParams ): ParsedUrlQuery { diff --git a/packages/next/src/shared/lib/rsc-transport.ts b/packages/next/src/shared/lib/rsc-transport.ts index ed541106f4aa..ade64df921af 100644 --- a/packages/next/src/shared/lib/rsc-transport.ts +++ b/packages/next/src/shared/lib/rsc-transport.ts @@ -1,3 +1,4 @@ +import { PAGE_SEGMENT_KEY } from './segment' /** * The transport format for RSC responses. * @@ -26,11 +27,8 @@ import type { VaryParamsIterable } from './segment-cache/vary-params-decoding' * Segment identity on the wire. A string for static segments; an object for * dynamic (parameterized) segments. * - * TODO: Page segments currently smuggle search params inside the string - * (`__PAGE__?{...}`, see addSearchParamsIfPageSegment). This convention is - * carried over as-is for now. Consider giving search params a dedicated - * field (or removing them from the response entirely, since the client - * already knows the rendered search from the response-level `q`). + * Page segments are always `__PAGE__`. The search params are sent separately, + * in the response's `q` field. */ export type TransportSegment = string | TransportDynamicSegment @@ -300,18 +298,20 @@ export function transportSegmentToSegment( /** * Derives a FlightRouterState from a transport tree. Used where the client * needs a router-state representation of a full response (e.g. the initial - * hydration payload). Render output (`d`) is not carried over; page segments - * keep their search params, which travel inside the segment string. + * hydration payload). Render output (`d`) is not carried over. */ export function transportNodeToFlightRouterState( - node: TransportNode + node: TransportNode, + renderedSearch: string ): FlightRouterState { const parallelRoutes: Record = {} const children = node.c if (children !== undefined) { for (const [parallelRouteKey, childNode] of children) { - parallelRoutes[parallelRouteKey] = - transportNodeToFlightRouterState(childNode) + parallelRoutes[parallelRouteKey] = transportNodeToFlightRouterState( + childNode, + renderedSearch + ) } } const flightRouterState: FlightRouterState = [ @@ -321,5 +321,8 @@ export function transportNodeToFlightRouterState( if (node.h !== undefined) { flightRouterState[4] = node.h } + if (flightRouterState[0] === PAGE_SEGMENT_KEY) { + flightRouterState[5] = renderedSearch + } return flightRouterState } diff --git a/packages/next/src/shared/lib/segment-cache/segment-value-encoding.ts b/packages/next/src/shared/lib/segment-cache/segment-value-encoding.ts index 54e0ba95f0c6..bc73f2274341 100644 --- a/packages/next/src/shared/lib/segment-cache/segment-value-encoding.ts +++ b/packages/next/src/shared/lib/segment-cache/segment-value-encoding.ts @@ -1,4 +1,3 @@ -import { PAGE_SEGMENT_KEY } from '../segment' import type { Segment as FlightRouterStateSegment } from '../app-router-types' // TypeScript trick to simulate opaque types, like in Flow. @@ -15,18 +14,6 @@ export function createSegmentRequestKeyPart( segment: FlightRouterStateSegment ): SegmentRequestKeyPart { if (typeof segment === 'string') { - if (segment.startsWith(PAGE_SEGMENT_KEY)) { - // The Flight Router State type sometimes includes the search params in - // the page segment. However, the Segment Cache tracks this as a separate - // key. So, we strip the search params here, and then add them back when - // the cache entry is turned back into a FlightRouterState. This is an - // unfortunate consequence of the FlightRouteState being used both as a - // transport type and as a cache key; we'll address this once more of the - // Segment Cache implementation has settled. - // TODO: We should hoist the search params out of the FlightRouterState - // type entirely, This is our plan for dynamic route params, too. - return PAGE_SEGMENT_KEY as SegmentRequestKeyPart - } const safeName = // TODO: FlightRouterState encodes Not Found routes as "/_not-found". // But params typically don't include the leading slash. We should use diff --git a/packages/next/src/shared/lib/segment-cache/vary-params-decoding.ts b/packages/next/src/shared/lib/segment-cache/vary-params-decoding.ts index 6179e65e2067..24f9ed67fc5f 100644 --- a/packages/next/src/shared/lib/segment-cache/vary-params-decoding.ts +++ b/packages/next/src/shared/lib/segment-cache/vary-params-decoding.ts @@ -6,11 +6,34 @@ import { readFulfilledValue } from '../rsc-transport' -export type VaryParams = Set +/** + * The vary path id for search params. Path params are identified by their + * name. Search params don't have a fixed set of names, so any access to them + * is reported under this one id, and the segment is keyed by the whole search + * string (see createVaryingSearchParams in app-render/vary-params.ts). + * + * It's a number so it can't collide with a param name. `app/[?]/page.tsx` is a + * valid route. + */ +export const SEARCH_PARAMS_VARY_ID = 0 + +// Path param names, and SEARCH_PARAMS_VARY_ID for the search params. +export type VaryParamId = string | number + +/** + * The params a piece of rendered output depends on — the ids of the vary path + * nodes it read: path param names, and SEARCH_PARAMS_VARY_ID for the search + * params — as the source that reports them rather than a snapshot of it. + * + * The wire iterables can only be drained from a fully-buffered response; they + * are drained once at decode into an already-settled thenable, and read at the + * point a decision needs the set (readVaryParams). + */ +export type VaryParams = PromiseLike> /** * Vary params are serialized into the Flight stream as an - * `AsyncIterable` that yields each accessed param name exactly once + * `AsyncIterable` that yields each accessed param id exactly once * (the server dedupes before emitting). Because each access is flushed into the * stream as it happens, there's no step at the end of the render that has to * run for the client to read anything. If a prerender is aborted by sync I/O, @@ -19,11 +42,11 @@ export type VaryParams = Set * * Root params are NOT included in a segment's own iterable. They're emitted * once at the top level of the response (as a separate iterable) and unioned in - * by `readVaryParams`, because root params can be accessed at any point during - * the render — folding them into every segment would otherwise require a merge - * once the whole render is complete. + * by `decodeVaryParams`, because root params can be accessed at any point + * during the render — folding them into every segment would otherwise require + * a merge once the whole render is complete. */ -export type VaryParamsIterable = AsyncIterable +export type VaryParamsIterable = AsyncIterable /** * Synchronously drains a vary params `AsyncIterable`, adding each yielded name @@ -45,7 +68,7 @@ export type VaryParamsIterable = AsyncIterable */ function drainVaryParams( iterable: VaryParamsIterable, - target: VaryParams + target: Set ): void { const iterator = iterable[Symbol.asyncIterator]() while (true) { @@ -60,13 +83,13 @@ function drainVaryParams( } /** - * Reads a segment's (or the head's) vary params, unioning in the response-level - * root params. + * Converts a segment's (or the head's) vary params off the wire, at the + * decode boundary, unioning in the response-level root params. * * Root params are emitted once at the top level rather than folded into every - * segment by the server, so every read recombines them here — building the - * merge into the read means a caller can't forget it, and it's done in a single - * pass with no intermediate set. + * segment by the server, so every decode recombines them here — building the + * merge into the decode means a caller can't forget it, and it's done in a + * single pass with no intermediate set. * * Returns null ("unknown", key on all params) unless BOTH iterables are * present. A null/absent `iterable` means the segment's own tracking wasn't @@ -80,7 +103,7 @@ function drainVaryParams( * set — a tracked segment that read no params, with no root params accessed, * can be shared across all param values. */ -export function readVaryParams( +export function decodeVaryParams( iterable: VaryParamsIterable | null | undefined, rootIterable: VaryParamsIterable | null | undefined ): VaryParams | null { @@ -92,8 +115,34 @@ export function readVaryParams( ) { return null } - const varyParams: VaryParams = new Set() - drainVaryParams(iterable, varyParams) - drainVaryParams(rootIterable, varyParams) - return varyParams + const total: Set = new Set() + drainVaryParams(iterable, total) + drainVaryParams(rootIterable, total) + return createVaryParams(total) +} + +/** + * Wraps an already-known set as a vary params source. Shaped like a settled + * Flight promise so readVaryParams can read it off the thenable's status. + */ +export function createVaryParams(total: Set): VaryParams { + // TODO: Don't need to use a native promise. Just inline a thenable that + // immediately calls its listener. + const settled = Promise.resolve(total) as Promise> & { + status: 'fulfilled' + value: Set + } + settled.status = 'fulfilled' + settled.value = total + return settled +} + +/** + * Reads the set from a vary params source. Null when it is not available; + * the reader assumes every param varies. + */ +export function readVaryParams( + varyParams: VaryParams +): Set | null { + return readFulfilledValue(varyParams, null) } diff --git a/packages/next/src/shared/lib/segment.ts b/packages/next/src/shared/lib/segment.ts index 8d8117f598e6..4421892e3ba2 100644 --- a/packages/next/src/shared/lib/segment.ts +++ b/packages/next/src/shared/lib/segment.ts @@ -13,22 +13,6 @@ export function isParallelRouteSegment(segment: string) { return segment.startsWith('@') && segment !== '@children' } -export function addSearchParamsIfPageSegment( - segment: Segment, - searchParams: Record -) { - const isPageSegment = segment.includes(PAGE_SEGMENT_KEY) - - if (isPageSegment) { - const stringifiedQuery = JSON.stringify(searchParams) - return stringifiedQuery !== '{}' - ? PAGE_SEGMENT_KEY + '?' + stringifiedQuery - : PAGE_SEGMENT_KEY - } - - return segment -} - export function computeSelectedLayoutSegment( segments: string[] | null, parallelRouteKey: string @@ -70,7 +54,7 @@ export function getSelectedLayoutSegmentPath( let segmentValue = getSegmentValue(segment) - if (!segmentValue || segmentValue.startsWith(PAGE_SEGMENT_KEY)) { + if (!segmentValue || segmentValue === PAGE_SEGMENT_KEY) { return segmentPath } diff --git a/test/development/browser-logs/browser-logs.test.ts b/test/development/browser-logs/browser-logs.test.ts index 9de931a2b3ca..68955dce2176 100644 --- a/test/development/browser-logs/browser-logs.test.ts +++ b/test/development/browser-logs/browser-logs.test.ts @@ -377,14 +377,14 @@ describe(`Terminal Logging (${bundlerName})`, () => { https://react.dev/link/hydration-mismatch ... - - + + - + diff --git a/test/e2e/app-dir/app-static/app-static.test.ts b/test/e2e/app-dir/app-static/app-static.test.ts index c3eb319849d1..64ff0729a2ad 100644 --- a/test/e2e/app-dir/app-static/app-static.test.ts +++ b/test/e2e/app-dir/app-static/app-static.test.ts @@ -785,10 +785,8 @@ describe('app-dir static/dynamic handling', () => { ? await next.readFile('.next/server/app/blog/seb.prefetch.rsc') : await next.readFile('.next/server/app/blog/seb.rsc') - // During SSG, pages that correspond with dynamic routes shouldn't have any search - // parameters in the `__PAGE__` segment string. The only time we expect to see - // search parameters in the `__PAGE__` segment string is when the RSC data is - // requested from the client with search parameters. + // Page segments contain only structural identity. Search params are + // carried separately, including in responses for dynamic routes. expect(data).not.toContain('__PAGE__?') expect(data).toContain('__PAGE__') }) diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/@side/default.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/@side/default.tsx new file mode 100644 index 000000000000..86b9e9a38812 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/@side/default.tsx @@ -0,0 +1,3 @@ +export default function Default() { + return null +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/@side/one/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/@side/one/page.tsx new file mode 100644 index 000000000000..f7d103e9ed7a --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/@side/one/page.tsx @@ -0,0 +1,16 @@ +import { connection } from 'next/server' + +export default async function Side({ + searchParams, +}: { + searchParams: Promise<{ value?: string }> +}) { + await connection() + const { value } = await searchParams + return ( + <> +

{value}

+

{crypto.randomUUID()}

+ + ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/layout.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/layout.tsx new file mode 100644 index 000000000000..893d72c2f6f3 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/layout.tsx @@ -0,0 +1,25 @@ +import Link from 'next/link' +import { Suspense } from 'react' +import { RefreshButton } from '../components/RefreshButton' + +export default function Layout({ + children, + side, +}: { + children: React.ReactNode + side: React.ReactNode +}) { + return ( + <> + + Two + + + Three + + + {children} + {side} + + ) +} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/one/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/one/page.tsx new file mode 100644 index 000000000000..7397dc3abb9f --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/one/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return

one

+} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/three/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/three/page.tsx new file mode 100644 index 000000000000..02458ac83b6b --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/three/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return

three

+} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/two/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/two/page.tsx new file mode 100644 index 000000000000..add603d1a517 --- /dev/null +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/retained-search/two/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return

two

+} diff --git a/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts b/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts index 6105af962902..23a0e2866474 100644 --- a/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts +++ b/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts @@ -1,5 +1,7 @@ import { nextTestSetup } from 'e2e-utils' import { check, retry } from 'next-test-utils' +import { createRouterAct } from 'router-act' +import type * as Playwright from 'playwright' describe('parallel-routes-revalidation', () => { const { next, isNextDev, isNextStart, isNextDeploy } = nextTestSetup({ @@ -43,6 +45,43 @@ describe('parallel-routes-revalidation', () => { }) } + it('refreshes a retained slot using its original URL after multiple navigations', async () => { + let act: ReturnType + const browser = await next.browser('/retained-search/one?value=first', { + beforePageLoad(p: Playwright.Page) { + act = createRouterAct(p) + }, + }) + const originalRender = await browser.elementById('retained-render').text() + expect(await browser.elementById('retained-value').text()).toBe('first') + + for (const [page, value] of [ + ['two', 'second'], + ['three', 'third'], + ]) { + await act(async () => { + await browser + .elementByCss(`a[href="/retained-search/${page}?value=${value}"]`) + .click() + }) + expect(await browser.elementById('active-page').text()).toBe(page) + expect(await browser.elementById('retained-value').text()).toBe('first') + expect(await browser.elementById('retained-render').text()).toBe( + originalRender + ) + } + + await act(async () => { + await browser.elementById('refresh-button').click() + }) + expect(await browser.elementById('retained-render').text()).not.toBe( + originalRender + ) + expect(await browser.elementById('retained-value').text()).toBe('first') + expect(await browser.elementById('active-page').text()).toBe('three') + expect(new URL(await browser.url()).search).toBe('?value=third') + }) + it('should handle router.refresh() when called in a slot', async () => { const browser = await next.browser('/') await check(() => browser.hasElementByCssSelector('#refresh-router'), false) diff --git a/test/e2e/app-dir/prerender-encoding/app/closed/[...slug]/page.tsx b/test/e2e/app-dir/prerender-encoding/app/closed/[...slug]/page.tsx new file mode 100644 index 000000000000..84bc2f1b407d --- /dev/null +++ b/test/e2e/app-dir/prerender-encoding/app/closed/[...slug]/page.tsx @@ -0,0 +1,18 @@ +export const dynamicParams = false + +export function generateStaticParams() { + return [ + { slug: ['docs', 'space here', '100%'] }, + { slug: ['docs', 'space here', 'with/slash', '100%'] }, + ] +} + +export default async function Page({ + params, +}: { + params: Promise<{ slug: string[] }> +}) { + const { slug } = await params + + return
params.slug is {slug.join('/')}
+} diff --git a/test/e2e/app-dir/prerender-encoding/app/open/[...slug]/page.tsx b/test/e2e/app-dir/prerender-encoding/app/open/[...slug]/page.tsx new file mode 100644 index 000000000000..214249c254b6 --- /dev/null +++ b/test/e2e/app-dir/prerender-encoding/app/open/[...slug]/page.tsx @@ -0,0 +1,16 @@ +export function generateStaticParams() { + return [ + { slug: ['docs', 'space here', '100%'] }, + { slug: ['docs', 'space here', 'with/slash', '100%'] }, + ] +} + +export default async function Page({ + params, +}: { + params: Promise<{ slug: string[] }> +}) { + const { slug } = await params + + return
params.slug is {slug.join('/')}
+} diff --git a/test/e2e/app-dir/prerender-encoding/prerender-encoding.test.ts b/test/e2e/app-dir/prerender-encoding/prerender-encoding.test.ts index 57dd4bb74117..967e61293c78 100644 --- a/test/e2e/app-dir/prerender-encoding/prerender-encoding.test.ts +++ b/test/e2e/app-dir/prerender-encoding/prerender-encoding.test.ts @@ -1,3 +1,4 @@ +import { load } from 'cheerio' import { nextTestSetup } from 'e2e-utils' describe('prerender-encoding', () => { @@ -9,4 +10,52 @@ describe('prerender-encoding', () => { const $ = await next.render$('/sticks%20%26%20stones') expect($('div').text()).toBe('params.id is sticks%20%26%20stones') }) + + it('should serve an exact closed catch-all path containing a literal percent', async () => { + const response = await next.fetch('/closed/docs/space%20here/100%25') + expect(response.status).toBe(200) + const $ = load(await response.text()) + expect($('div').text()).toBe('params.slug is docs/space%20here/100%25') + }) + + it('should serve an open catch-all fallback containing a literal percent', async () => { + const pathname = '/open/docs/space%20here/fallback%25' + for (let i = 0; i < 2; i++) { + const response = await next.fetch(pathname) + expect(response.status).toBe(200) + const $ = load(await response.text()) + expect($('div').text()).toBe( + 'params.slug is docs/space%20here/fallback%25' + ) + } + }) + + // Vercel tries the raw URL and the fully decoded path for filesystem lookup. + // Neither matches this output's decoded space/percent and still-encoded slash. + // @gate !deploy + it('should preserve an encoded slash in an exact closed catch-all path', async () => { + const response = await next.fetch( + '/closed/docs/space%20here/with%2Fslash/100%25' + ) + expect(response.status).toBe(200) + const $ = load(await response.text()) + expect($('div').text()).toBe( + 'params.slug is docs/space%20here/with%2Fslash/100%25' + ) + }) + + // Vercel decodes the captured slash before splitting catch-all values, whereas + // next start preserves it within a single value. + // @gate !deploy + it('should preserve an encoded slash in an open catch-all fallback', async () => { + const pathname = '/open/docs/space%20here/with%2Fslash/fallback%25' + for (let i = 0; i < 2; i++) { + const response = await next.fetch(pathname) + expect(response.status).toBe(200) + const $ = load(await response.text()) + expect($('div').text()).toBe( + 'params.slug is docs/space%20here/with%2Fslash/fallback%25' + ) + } + }) }) diff --git a/test/e2e/app-dir/segment-cache/metadata/app/page-with-per-tenant-head/[locale]/[tenant]/page.tsx b/test/e2e/app-dir/segment-cache/metadata/app/page-with-per-tenant-head/[locale]/[tenant]/page.tsx new file mode 100644 index 000000000000..e42e2cbd1305 --- /dev/null +++ b/test/e2e/app-dir/segment-cache/metadata/app/page-with-per-tenant-head/[locale]/[tenant]/page.tsx @@ -0,0 +1,30 @@ +import { Metadata } from 'next' + +type Params = { locale: string; tenant: string } + +// Only one tenant is prerendered. Any other tenant is rendered on demand, so +// its head is missing from the cache while the body, which reads only the +// locale, can be reused from the prerendered one. +export function generateStaticParams(): Params[] { + return [{ locale: 'en', tenant: 'acme' }] +} + +export async function generateMetadata({ + params, +}: { + params: Promise +}): Promise { + const { tenant } = await params + return { + title: `Tenant: ${tenant}`, + } +} + +export default async function PageWithPerTenantHead({ + params, +}: { + params: Promise +}) { + const { locale } = await params + return
{`Locale: ${locale}`}
+} diff --git a/test/e2e/app-dir/segment-cache/metadata/app/page.tsx b/test/e2e/app-dir/segment-cache/metadata/app/page.tsx index 9841c98fcd7d..57533bd984dd 100644 --- a/test/e2e/app-dir/segment-cache/metadata/app/page.tsx +++ b/test/e2e/app-dir/segment-cache/metadata/app/page.tsx @@ -37,6 +37,22 @@ export default function Page() { +
+
    +
  • + + Page with per-tenant head (en/acme) + +
  • +
  • + + Page with per-tenant head (en/initech, prefetch=false) + +
  • +
) } diff --git a/test/e2e/app-dir/segment-cache/metadata/segment-cache-metadata.test.ts b/test/e2e/app-dir/segment-cache/metadata/segment-cache-metadata.test.ts index 8d34b1a05001..d4973acbca7f 100644 --- a/test/e2e/app-dir/segment-cache/metadata/segment-cache-metadata.test.ts +++ b/test/e2e/app-dir/segment-cache/metadata/segment-cache-metadata.test.ts @@ -1,5 +1,19 @@ import { nextTestSetup } from 'e2e-utils' +import { retry } from 'next-test-utils' import { createRouterAct } from 'router-act' +import type * as Playwright from 'playwright' + +const NEXT_ROUTER_STATE_TREE_HEADER = 'next-router-state-tree' +const NEXT_ROUTER_PREFETCH_HEADER = 'next-router-prefetch' + +// The request tree the client sends when every segment is cached and only the +// head is missing. Mirrors MetadataOnlyRequestTree in the segment cache. +const METADATA_ONLY_REQUEST_TREE = JSON.stringify([ + '', + {}, + null, + 'metadata-only', +]) describe('segment cache (metadata)', () => { const { next, isNextDev } = nextTestSetup({ @@ -129,4 +143,72 @@ describe('segment cache (metadata)', () => { }, 'no-requests') }) }) + + // On the platform a cold ISR path is served from a completed static + // prerender that cannot vary on the request tree, so the page body is sent + // along with the head. + // @gate !deploy + it('requests only the head when the page segments are cached', async () => { + let act: ReturnType + // The request tree of every navigation request (RSC requests that are + // not prefetches), decoded from its header. + const navigationRequestTrees: Array = [] + const browser = await next.browser('/', { + beforePageLoad(p: Playwright.Page) { + act = createRouterAct(p) + p.on('request', (request) => { + const headers = request.headers() + if ( + headers['rsc'] === '1' && + headers[NEXT_ROUTER_PREFETCH_HEADER] === undefined + ) { + const encoded = headers[NEXT_ROUTER_STATE_TREE_HEADER] + navigationRequestTrees.push( + encoded === undefined ? null : decodeURIComponent(encoded) + ) + } + }) + }, + }) + + // Prefetch the prerendered tenant. The body reads only the locale, so + // this caches it for every tenant in "en"; the head reads the tenant, so + // only acme's head is cached. + await act(async () => { + const checkbox = await browser.elementByCss( + 'input[data-link-accordion="/page-with-per-tenant-head/en/acme"]' + ) + await checkbox.click() + }, [{ includes: 'Locale: en' }, { includes: 'Tenant: acme' }]) + + // Reveal a link to a tenant that is not prerendered, without prefetching + // it. Its head is the only thing missing from the cache. + await act(async () => { + const checkbox = await browser.elementByCss( + 'input[data-link-accordion="/page-with-per-tenant-head/en/initech"]' + ) + await checkbox.click() + }, 'no-requests') + + // The navigation asks the server for the head alone. The response carries + // the new tenant's title and no page body. + await act(async () => { + const link = await browser.elementByCss( + 'a[href="/page-with-per-tenant-head/en/initech"]' + ) + await link.click() + }, [ + { includes: 'Tenant: initech' }, + { includes: 'Locale: en', block: 'reject' }, + ]) + expect(navigationRequestTrees).toEqual([METADATA_ONLY_REQUEST_TREE]) + + // The body is the cached one, rendered under the new tenant's title. + const pageContent = await browser.elementById('target-page') + expect(await pageContent.text()).toBe('Locale: en') + await retry(async () => { + const title = await browser.eval(() => document.title) + expect(title).toBe('Tenant: initech') + }) + }) }) diff --git a/test/e2e/app-dir/segment-cache/search-params/app/search-params/target-page/page.tsx b/test/e2e/app-dir/segment-cache/search-params/app/search-params/target-page/page.tsx index b8829770a640..4d32ba27d8aa 100644 --- a/test/e2e/app-dir/segment-cache/search-params/app/search-params/target-page/page.tsx +++ b/test/e2e/app-dir/segment-cache/search-params/app/search-params/target-page/page.tsx @@ -1,4 +1,5 @@ import { Suspense } from 'react' +import { LinkAccordion } from '../../../components/link-accordion' async function Content({ searchParams }) { const { searchParam } = await searchParams @@ -11,6 +12,15 @@ export default async function Target({ searchParams }) {
+ + Change search params + + + Navigate with encoded and repeated search params + ) } diff --git a/test/e2e/app-dir/segment-cache/search-params/segment-cache-question-mark-param.test.ts b/test/e2e/app-dir/segment-cache/search-params/segment-cache-question-mark-param.test.ts new file mode 100644 index 000000000000..239830ccf14a --- /dev/null +++ b/test/e2e/app-dir/segment-cache/search-params/segment-cache-question-mark-param.test.ts @@ -0,0 +1,85 @@ +import { nextTestSetup, FileRef } from 'e2e-utils' +import { join } from 'path' +import { retry } from '../../../../lib/next-test-utils' + +// `[?]` is a valid param name, so a vary path node whose id is `?` must not +// be taken for the search params entry. The `[?]` route is written at +// runtime rather than checked in: git refuses a path containing `?` on +// Windows, and webpack's `next build` rejects the emitted filename as a +// query string, so the fixture is turbopack-only. +// @force-gate turbopack && !deploy +describe('segment cache (param named "?")', () => { + const { next } = nextTestSetup({ + files: { + 'app/layout.tsx': new FileRef(join(__dirname, 'app/layout.tsx')), + 'next.config.js': new FileRef(join(__dirname, 'next.config.js')), + 'app/[?]/layout.tsx': ` + export default function QuestionMarkLayout({ + children, + }: { + children: React.ReactNode + }) { + return ( +
+

Layout under [?]

+ {children} +
+ ) + } + `, + 'app/[?]/page.tsx': ` + import Link from 'next/link' + import { Suspense } from 'react' + + async function Param({ params }: { params: Promise<{ '?': string }> }) { + const { '?': value } = await params + return
Param: {value}
+ } + + export default async function QuestionMarkPage({ + params, + }: { + params: Promise<{ '?': string }> + }) { + return ( + <> + + + + + param-a + + + param-b + + + ) + } + `, + }, + }) + + // The layout matters: the page's vary path derives from the layout's, which + // ends at the `?` param node. + it('keys segments under a param literally named "?" by its value', async () => { + const browser = await next.browser('/param-a') + expect(await browser.elementById('question-mark-param').text()).toBe( + 'Param: param-a' + ) + + await browser.elementById('link-param-b').click() + await retry(async () => { + expect(await browser.elementById('question-mark-param').text()).toBe( + 'Param: param-b' + ) + expect(await browser.url()).toMatch(/\/param-b$/) + }) + + await browser.elementById('link-param-a').click() + await retry(async () => { + expect(await browser.elementById('question-mark-param').text()).toBe( + 'Param: param-a' + ) + }) + }) +}) diff --git a/test/e2e/app-dir/segment-cache/search-params/segment-cache-search-params.test.ts b/test/e2e/app-dir/segment-cache/search-params/segment-cache-search-params.test.ts index 420c024f3c95..57d15597eb08 100644 --- a/test/e2e/app-dir/segment-cache/search-params/segment-cache-search-params.test.ts +++ b/test/e2e/app-dir/segment-cache/search-params/segment-cache-search-params.test.ts @@ -11,6 +11,79 @@ describe('segment cache (search params)', () => { return } + it('prefetches new vary data for a query-only navigation from the active page', async () => { + let act: ReturnType + const browser = await next.browser( + '/search-params/target-page?searchParam=initial', + { + beforePageLoad(page) { + act = createRouterAct(page) + }, + } + ) + expect( + await browser.elementById('target-page-with-search-param').text() + ).toContain('Search param: initial') + await act( + async () => { + await browser + .elementByCss( + 'input[data-link-accordion="/search-params/target-page?searchParam=query_only"]' + ) + .click() + }, + { includes: 'Search param: query_only' } + ) + await act(async () => { + await browser + .elementByCss( + 'a[href="/search-params/target-page?searchParam=query_only"]' + ) + .click() + }, 'no-requests') + expect( + await browser.elementById('target-page-with-search-param').text() + ).toContain('Search param: query_only') + }) + + it('navigates to a prefetched page with encoded and repeated search params', async () => { + let act: ReturnType + const browser = await next.browser( + '/search-params/target-page?searchParam=initial', + { + beforePageLoad(page) { + act = createRouterAct(page) + }, + } + ) + + // Prefetch the shell before navigating. This URL uses both space encodings, + // unescaped punctuation, a bare key, and interleaved repeated values. + await act(async () => { + await browser + .elementByCss( + 'input[data-link-accordion="/search-params/target-page?searchParam=hello+world,/x&unused&searchParam=another%20value"]' + ) + .click() + }) + await act( + async () => { + await browser + .elementByCss( + 'a[href="/search-params/target-page?searchParam=hello+world,/x&unused&searchParam=another%20value"]' + ) + .click() + }, + { includes: 'Search param: hello world,/x,another value' } + ) + expect( + await browser.elementById('target-page-with-search-param').text() + ).toBe('Search param: hello world,/x,another value') + expect(new URL(await browser.url()).search).toBe( + '?searchParam=hello+world,/x&unused&searchParam=another%20value' + ) + }) + it('when fetching with PPR, does not include search params in the cache key', async () => { let act: ReturnType const browser = await next.browser('/search-params', { diff --git a/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/layout.tsx b/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/layout.tsx new file mode 100644 index 000000000000..ce7eff5165aa --- /dev/null +++ b/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/layout.tsx @@ -0,0 +1,21 @@ +import { LinkAccordion } from '../../../../components/link-accordion' + +/** + * Navigation reuse: the page does not read searchParams but its metadata + * does. A query-only navigation must still fetch the head, because it depends + * on the query. + */ +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> +
    +
  • + + x=2 + +
  • +
+ {children} + + ) +} diff --git a/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/loading.tsx b/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/loading.tsx new file mode 100644 index 000000000000..c443db0168cf --- /dev/null +++ b/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return
Loading metadata-query page...
+} diff --git a/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/page.tsx b/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/page.tsx new file mode 100644 index 000000000000..cd732fffecdf --- /dev/null +++ b/test/e2e/app-dir/segment-cache/vary-params/app/(main)/navigation-reuse/metadata-query/page.tsx @@ -0,0 +1,17 @@ +import { connection } from 'next/server' + +type SearchParams = { x?: string } + +export async function generateMetadata({ + searchParams, +}: { + searchParams: Promise +}) { + const { x } = await searchParams + return { title: `Query title: ${x}` } +} + +export default async function Page() { + await connection() + return

{`Server token: ${Math.random()}`}

+} diff --git a/test/e2e/app-dir/segment-cache/vary-params/vary-params.test.ts b/test/e2e/app-dir/segment-cache/vary-params/vary-params.test.ts index 12416c179a6a..2323cbf1648f 100644 --- a/test/e2e/app-dir/segment-cache/vary-params/vary-params.test.ts +++ b/test/e2e/app-dir/segment-cache/vary-params/vary-params.test.ts @@ -888,4 +888,39 @@ describe('segment cache - vary params', () => { ) } ) + + it('still fetches the head on a query-only navigation when the metadata read searchParams', async () => { + let act: ReturnType + const browser = await next.browser('/navigation-reuse/metadata-query?x=1', { + beforePageLoad(page: Playwright.Page) { + act = createRouterAct(page) + }, + }) + expect(await browser.eval('document.title')).toBe('Query title: 1') + const initialToken = await browser.elementById('server-token').text() + + await act(async () => { + await browser + .elementByCss( + 'input[data-link-accordion="/navigation-reuse/metadata-query?x=2"]' + ) + .click() + }, 'no-requests') + + // The head read the query, so it's fetched again. + await act( + async () => { + await browser + .elementByCss('a[href="/navigation-reuse/metadata-query?x=2"]') + .click() + }, + { includes: 'Query title: 2' } + ) + expect(await browser.eval('document.title')).toBe('Query title: 2') + // A dynamic render reports no dependency information, so the page is + // re-rendered along with the head. + expect(await browser.elementById('server-token').text()).not.toBe( + initialToken + ) + }) }) diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/gc.rs b/turbopack/crates/turbo-tasks-backend/src/backend/gc.rs index dc4bb3bc1e0c..9911036ae9ca 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/gc.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/gc.rs @@ -31,8 +31,8 @@ use crate::{ backend::{ AnyOperation, TurboTasksBackend, operation::{ - AggregationUpdateQueue, CleanupOldEdgesOperation, ExecuteContext, ExecuteContextImpl, - TaskGuard, capture_all_outgoing_edges, + AggregationUpdateJob, AggregationUpdateQueue, CleanupOldEdgesOperation, ExecuteContext, + ExecuteContextImpl, TaskGuard, capture_all_edges, }, snapshot_coordinator::SnapshotPhase, storage::{SpecificTaskDataCategory, TaskDataCategory}, @@ -129,6 +129,9 @@ pub struct GcPassResult { deleted_roots: Vec, /// Aggregation rebalance requests that were deferred from the main GC loop. deferred_balance_edges: FxHashSet<(TaskId, TaskId)>, + /// Dependents of collected tasks whose forward edge was scrubbed, deferred from the main GC + /// loop. Dirtying propagates through the aggregation graph, so it must not race the cascade. + deferred_dirty_dependents: FxHashSet, /// The gc loop was interrupted by competing work. pub interrupted: bool, } @@ -176,6 +179,15 @@ impl GcPassResult { } self.deferred_balance_edges .extend(other.deferred_balance_edges); + // merge into the larger set and keep that one + if other.deferred_dirty_dependents.len() > self.deferred_dirty_dependents.len() { + std::mem::swap( + &mut self.deferred_dirty_dependents, + &mut other.deferred_dirty_dependents, + ); + } + self.deferred_dirty_dependents + .extend(other.deferred_dirty_dependents); self.interrupted |= other.interrupted; self } @@ -251,7 +263,7 @@ impl TurboTasksBackend { }; let collector = |child_id| spawner.spawn(GcJob::Collect(child_id)); let mut ctx = ExecuteContextImpl::new_for_gc(self, turbo_tasks, phase, &collector); - // `All` restores Data so `capture_all_outgoing_edges` below can read the + // `All` restores Data so `capture_all_edges` below can read the // Data-category dependency sets. The recheck itself only needs Meta. let mut task = ctx.task(task_id, TaskDataCategory::All); // Recheck under the guard: the shard scan saw this task without holding it, and a @@ -261,7 +273,7 @@ impl TurboTasksBackend { return ControlFlow::Continue(()); } - let old_edges = capture_all_outgoing_edges(&task); + let old_edges = capture_all_edges(&task); // Clear `immutable` defensively so `resurrect_deleted` can mark the task dirty if // it needs to task.set_immutable(false); @@ -287,9 +299,12 @@ impl TurboTasksBackend { // Delete outgoing edges but don't update the aggregation graph yet. // To avoid accidentally rebalancing on deleted tasks due to racing deletions, // we defer all rebalancing to the end - result.deferred_balance_edges.extend( - CleanupOldEdgesOperation::run_edge_deletions_only(task_id, old_edges, &mut ctx), - ); + let deferred = + CleanupOldEdgesOperation::run_edge_deletions_only(task_id, old_edges, &mut ctx); + result.deferred_balance_edges.extend(deferred.balance_edges); + result + .deferred_dirty_dependents + .extend(deferred.dirty_dependents); ControlFlow::Continue(()) }, |(stats, result), (other_stats, other_result)| { @@ -313,6 +328,21 @@ impl TurboTasksBackend { while !queue.process(&mut ctx) {} } + // Dirty the dependents whose edges were scrubbed. After the rebalance above so the + // aggregation graph is settled, and before the root scan below because dirtying can change + // activeness and therefore rootness. + let dirty_dependents = std::mem::take(&mut result.deferred_dirty_dependents); + if !dirty_dependents.is_empty() { + let noop_collector = |_task_id| {}; + let mut ctx = ExecuteContextImpl::new_for_gc(self, turbo_tasks, phase, &noop_collector); + let mut queue = AggregationUpdateQueue::new(); + // A dependent collected by this same pass is skipped: the job is weak by construction. + queue.push(AggregationUpdateJob::InvalidateDueToDependencyTornDown { + task_ids: dirty_dependents.into_iter().collect(), + }); + while !queue.process(&mut ctx) {} + } + // Collect all active roots // We don't do this in the GC pass because a task detected as a root 'early' might become a // non-root later due to other operations (e.g. it might get promoted to a live aggregation diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs index 99e7ff326a13..24c80a309666 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs @@ -68,7 +68,7 @@ use crate::{ AggregationUpdateJob, AggregationUpdateQueue, ChildExecuteContext, CleanupOldEdgesOperation, ConnectChildOperation, ExecuteContext, ExecuteContextImpl, LeafDistanceUpdateQueue, Operation, OutdatedEdge, TaskGuard, TaskType, TaskTypeRef, - capture_all_outgoing_edges, connect_children, get_aggregation_number, get_uppers, + capture_all_edges, connect_children, get_aggregation_number, get_uppers, make_task_dirty_internal, prepare_new_children, }, snapshot_coordinator::{OperationGuard, SnapshotCoordinator}, @@ -435,12 +435,10 @@ impl TurboTasksBackend { } } - /// The number of persistent (non-transient) tasks resident in the map. Test-only hook; see - /// [`Storage::resident_persistent_task_count_for_testing`] for why the metric excludes - /// transient tasks. + /// The number oftasks resident in the map. #[doc(hidden)] - pub fn resident_persistent_task_count_for_testing(&self) -> usize { - self.storage.resident_persistent_task_count_for_testing() + pub fn resident_task_count_for_testing(&self) -> usize { + self.storage.resident_task_count_for_testing() } /// The persistent `parent_count` of a resident task (0 if absent or not resident). Test-only @@ -1140,7 +1138,7 @@ impl TurboTasksBackend { parent_span: Option, reason: SnapshotReason, turbo_tasks: &TurboTasks, - ) -> Result<(Instant, bool, Option<(GcStats, GcPassResult)>), anyhow::Error> { + ) -> Result<(Instant, bool, Option<(GcStats, GcPassResult)>)> { let snapshot_span = tracing::trace_span!(parent: parent_span.clone(), "snapshot", reason = reason.as_str()) .entered(); @@ -1160,7 +1158,6 @@ impl TurboTasksBackend { let mut snapshot_phase = self.snapshot_coord.begin_snapshot(); let (gc_elapsed, gc_roots_to_persist, gc_outcome) = if self.gc_enabled { let gc_span = tracing::info_span!( - parent: parent_span.clone(), "gc", stats = tracing::field::Empty, interrupted = tracing::field::Empty @@ -1417,7 +1414,7 @@ impl TurboTasksBackend { }; } else { debug_assert!( - !inner.gc_maybe_collectible(), + !inner.gc_collectible(), "tasks scheduled for persistent must not be collectible, this implies a \ missed task during GC" ); @@ -3445,7 +3442,7 @@ impl TurboTasksBackend { activeness_state.all_clean_event.notify(usize::MAX); } // Remove all the outgoing edges of this task. - let old_edges = capture_all_outgoing_edges(&task); + let old_edges = capture_all_edges(&task); drop(task); if !old_edges.is_empty() { diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs index 50a37a30ca42..f992b69cdb4e 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/aggregation_update.rs @@ -42,8 +42,9 @@ use crate::{ backend::{ TaskDataCategory, operation::{ - ExecuteContext, Operation, TaskGuard, connect_child::resurrect_deleted, - invalidate::make_task_dirty, + ExecuteContext, Operation, TaskGuard, + connect_child::resurrect_deleted, + invalidate::{make_task_dirty, try_make_task_dirty}, }, storage_schema::TaskStorageAccessors, }, @@ -306,6 +307,11 @@ pub enum AggregationUpdateJob { }, /// Notifies an upper task about changed data from an inner task. AggregatedDataUpdate(Box), + /// Mark these tasks dirty because they have a dependency on a task being deleted by GC. + /// + /// The id references are weak by construction: a dependent that was itself collected is + /// skipped. + InvalidateDueToDependencyTornDown { task_ids: TaskIdVec }, /// Invalidates tasks that are dependent on a collectible type. InvalidateDueToCollectiblesChange { task_ids: TaskIdVec, @@ -1535,6 +1541,18 @@ impl AggregationUpdateQueue { } self.aggregated_data_update(upper_ids, ctx, update); } + AggregationUpdateJob::InvalidateDueToDependencyTornDown { task_ids } => { + for task_id in task_ids { + // `try_*`: the dependent may itself have been collected in this cascade. + try_make_task_dirty( + task_id, + #[cfg(feature = "task_dirty_cause")] + TaskDirtyCause::DependencyTornDown, + self, + ctx, + ); + } + } AggregationUpdateJob::InvalidateDueToCollectiblesChange { task_ids, #[cfg(feature = "task_dirty_cause")] diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/cleanup_old_edges.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/cleanup_old_edges.rs index 7f217c78f0dd..88837f53b046 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/cleanup_old_edges.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/cleanup_old_edges.rs @@ -1,7 +1,8 @@ use std::mem::take; +use auto_hash_map::AutoSet; use bincode::{Decode, Encode}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxBuildHasher, FxHashSet}; use smallvec::SmallVec; use turbo_tasks::TaskId; @@ -52,10 +53,16 @@ pub enum OutdatedEdge { HashedCellDependency(CellRef, u64), OutputDependency(TaskId), CollectiblesDependency(CollectiblesRef), + /// Reverse cell and output edges: some other task reads a cell of the task being deleted by GC. + /// Not modeled as a reversed `CellDependency` since the cleanup is assymetric, one side is + /// being deleted so we just tear down the other side. + CellDependentOfDeleted(CellRef), + HashedCellDependentOfDeleted(CellRef, u64), + OutputDependentOfDeleted(TaskId), } -/// Captures *all* of a task's outgoing edges as [`OutdatedEdge`]s -pub fn capture_all_outgoing_edges(task: &impl TaskStorageAccessors) -> Vec { +/// Captures *every* edge incident to a task -- both directions -- as [`OutdatedEdge`]s. +pub fn capture_all_edges(task: &impl TaskStorageAccessors) -> Vec { let mut old_edges: Vec = Vec::new(); old_edges.extend(task.iter_children().map(OutdatedEdge::Child)); old_edges.extend( @@ -74,24 +81,38 @@ pub fn capture_all_outgoing_edges(task: &impl TaskStorageAccessors) -> Vec>(ctx: &C) -> TaskDataCategory { - if ctx.collects_gc_candidates() { - // Under GC we need to query meta fields so be sure to recover Meta also - TaskDataCategory::All - } else { - TaskDataCategory::Data - } -} - #[cfg(feature = "trace_aggregation_update_stats")] type Stats = super::aggregation_update::AggregationUpdateQueueStats; #[cfg(not(feature = "trace_aggregation_update_stats"))] type Stats = (); +/// Work a GC-phase edge teardown produced but deliberately did not run, because it is unsafe while +/// other collect workers are still running. The caller replays it once the pass is quiescent. +#[derive(Default)] +pub struct DeferredCleanup { + /// Rebalance jobs. `balance_edge` *adds* aggregation edges, which must not happen mid-cascade. + pub balance_edges: Vec<(TaskId, TaskId)>, + /// Dependents whose forward edge to the torn-down task was scrubbed. They must be dirtied: the + /// value they were derived from no longer exists, so their cached result cannot be trusted. + pub dirty_dependents: AutoSet, +} + impl CleanupOldEdgesOperation { pub fn run( task_id: TaskId, @@ -109,24 +130,23 @@ impl CleanupOldEdgesOperation { /// GC variant: tears down `outdated`, running only the edge deletions. /// - /// Returns the balance jobs that the deletions produced, for the caller to replay once the - /// parallel collect is quiescent. Deletion is safe to run concurrently, but `balance_edge` - /// *adds* edges, which is not while other workers are still collecting. + /// Returns the work the deletions produced but did not run, for the caller to replay once the + /// parallel collect is quiescent. Deletion is safe to run concurrently; the deferred work is + /// not. `balance_edge` *adds* edges, and dirtying propagates through the aggregation graph -- + /// neither is safe while other workers are still collecting. pub fn run_edge_deletions_only<'a, C: ExecuteContext<'a>>( task_id: TaskId, outdated: Vec, ctx: &mut C, - ) -> impl Iterator + use { + ) -> DeferredCleanup { let op = CleanupOldEdgesOperation::RemoveEdges { task_id, outdated, queue: AggregationUpdateQueue::new_without_optimizations(), }; - op.execute_inner(ctx, true) - .1 - .map(|mut queue| queue.take_deferred_balance_edges()) - .into_iter() - .flatten() + let (_, stopped) = op.execute_inner(ctx, true); + // the option must be Some when stop_when_only_rebalance + stopped.unwrap() } fn execute_with_stats(self, ctx: &mut impl ExecuteContext<'_>) -> Stats { @@ -137,7 +157,8 @@ impl CleanupOldEdgesOperation { mut self, ctx: &mut impl ExecuteContext<'_>, stop_when_only_rebalance_remains: bool, - ) -> (Stats, Option) { + ) -> (Stats, Option) { + let mut dirty_dependents = AutoSet::default(); loop { ctx.operation_suspend_point(&self); match self { @@ -160,27 +181,36 @@ impl CleanupOldEdgesOperation { }); let mut task = ctx.task(task_id, TaskDataCategory::All); - let mut removed_persistent_children = - SmallVec::<[TaskId; 4]>::new(); - for child_id in children.iter() { - if task.remove_children(child_id) && !child_id.is_transient() { - removed_persistent_children.push(*child_id); - } - } - // Each removed persistent child loses a parent. - if !removed_persistent_children.is_empty() { - let job = if task_id.is_transient() { - AggregationUpdateJob::AdjustTransientRefCount { - task_ids: removed_persistent_children, - delta: -1, + // Mirror `ConnectChildrenOperation`'s split exactly: an edge + // counted as durable is released from `parent_count`, everything + // else from `transient_ref_count`. Getting this wrong either + // strands a task forever or underflows the count. + let parent_is_transient = task_id.is_transient(); + let mut removed_durable = SmallVec::<[TaskId; 4]>::new(); + let mut removed_transient = SmallVec::<[TaskId; 4]>::new(); + children.retain(|child_id| { + if task.remove_children(child_id) { + if parent_is_transient || child_id.is_transient() { + removed_transient.push(*child_id); + } else { + removed_durable.push(*child_id); } + true } else { - AggregationUpdateJob::AdjustParentCount { - task_ids: removed_persistent_children, - delta: -1, - } - }; - queue.push(job); + false + } + }); + if !removed_durable.is_empty() { + queue.push(AggregationUpdateJob::AdjustParentCount { + task_ids: removed_durable, + delta: -1, + }); + } + if !removed_transient.is_empty() { + queue.push(AggregationUpdateJob::AdjustTransientRefCount { + task_ids: removed_transient, + delta: -1, + }); } if is_aggregating_node(get_aggregation_number(&task)) { drop(task); @@ -257,15 +287,11 @@ impl CleanupOldEdgesOperation { cell, } = forward; { - let category = dependent_scrub_category(ctx); - let mut task = ctx.task(cell_task_id, category); - let removed = task.remove_cell_dependents(&CellRef { + let mut task = ctx.task(cell_task_id, TaskDataCategory::Data); + task.remove_cell_dependents(&CellRef { task: task_id, cell, }); - if removed && task.is_cell_dependents_empty() { - ctx.note_maybe_collectible(&task); - } } { let mut task = ctx.task(task_id, TaskDataCategory::Data); @@ -279,19 +305,14 @@ impl CleanupOldEdgesOperation { cell, } = forward; { - let category = dependent_scrub_category(ctx); - let mut task = ctx.task(cell_task_id, category); - let removed = task.remove_cell_dependents_hashed(&( + let mut task = ctx.task(cell_task_id, TaskDataCategory::Data); + task.remove_cell_dependents_hashed(&( CellRef { task: task_id, cell, }, key, )); - - if removed && task.is_cell_dependents_hashed_empty() { - ctx.note_maybe_collectible(&task); - } } { let mut task = ctx.task(task_id, TaskDataCategory::Data); @@ -307,12 +328,8 @@ impl CleanupOldEdgesOperation { ) .entered(); { - let category = dependent_scrub_category(ctx); - let mut task = ctx.task(output_task_id, category); - let removed = task.remove_output_dependent(&task_id); - if removed && task.is_output_dependent_empty() { - ctx.note_maybe_collectible(&task); - } + let mut task = ctx.task(output_task_id, TaskDataCategory::Data); + task.remove_output_dependent(&task_id); } { let mut task = ctx.task(task_id, TaskDataCategory::Data); @@ -324,15 +341,12 @@ impl CleanupOldEdgesOperation { task: dependent_task_id, }) => { { - let category = dependent_scrub_category(ctx); - let mut task = ctx.task(dependent_task_id, category); - let removed = task.remove_collectibles_dependents(&( + let mut task = + ctx.task(dependent_task_id, TaskDataCategory::Meta); + task.remove_collectibles_dependents(&( collectible_type, task_id, )); - if removed && task.collectibles_dependents_len() == 0 { - ctx.note_maybe_collectible(&task); - } } { let mut task = ctx.task(task_id, TaskDataCategory::Data); @@ -342,9 +356,61 @@ impl CleanupOldEdgesOperation { }); } } + // Handle reverse edges, we remove these when `task_id` is being + // deleted. The reverse dependents must exist and we dirty them as we + // go. We also don't bother removing cell_dependents from `task_id` + // since that task is being deleted. + OutdatedEdge::CellDependentOfDeleted(CellRef { + task: dependent_task_id, + cell, + }) => { + // Orientation flip: the stored entry names the dependent, the + // forward entry we remove names `task_id`. + let forward = CellRef { + task: task_id, + cell, + }; + let mut task = ctx.task(dependent_task_id, TaskDataCategory::Data); + task.remove_cell_dependencies(&forward); + task.remove_outdated_cell_dependencies(&forward); + drop(task); + dirty_dependents.insert(dependent_task_id); + } + OutdatedEdge::HashedCellDependentOfDeleted( + CellRef { + task: dependent_task_id, + cell, + }, + key, + ) => { + let forward = CellRef { + task: task_id, + cell, + }; + let mut task = ctx.task(dependent_task_id, TaskDataCategory::Data); + task.remove_cell_dependencies_hashed(&(forward, key)); + task.remove_outdated_cell_dependencies_hashed(&(forward, key)); + drop(task); + dirty_dependents.insert(dependent_task_id); + } + OutdatedEdge::OutputDependentOfDeleted(dependent_task_id) => { + let mut task = ctx.task(dependent_task_id, TaskDataCategory::Data); + task.remove_output_dependencies(&task_id); + task.remove_outdated_output_dependencies(&task_id); + drop(task); + dirty_dependents.insert(dependent_task_id); + } } } + // If we accumulated any dirty_dependents flush them to the aggregation update + // queue before suspending. + if !stop_when_only_rebalance_remains && !dirty_dependents.is_empty() { + queue.push(AggregationUpdateJob::InvalidateDueToDependencyTornDown { + task_ids: take(&mut dirty_dependents).into_iter().collect(), + }); + } + if outdated.is_empty() { self = CleanupOldEdgesOperation::AggregationUpdate { queue: take(queue) }; } @@ -354,7 +420,13 @@ impl CleanupOldEdgesOperation { // Edge removal is done; hand the rebalance back to the caller. Any // other pending work would be dropped here, so `only_rebalance_remains` // asserts that nothing else is left. - return (Default::default(), Some(take(queue))); + return ( + Default::default(), + Some(DeferredCleanup { + balance_edges: queue.take_deferred_balance_edges().collect(), + dirty_dependents, + }), + ); } if queue.process(ctx) { self = CleanupOldEdgesOperation::Done { diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_child.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_child.rs index ca04e1c9304f..f959b239f051 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_child.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_child.rs @@ -87,6 +87,14 @@ impl ConnectChildOperation { release_construction_ref: bool, mut ctx: impl ExecuteContext<'_>, ) { + if parent_task_id.is_none() { + // All parentless tasks receive a transient ref when connected: their lifetime cannot be + // constrained by turbo-tasks and needs to be managed by the caller. If the caller + // doesn't manage it, the GC root TTL handles it in a later session. + let mut child_task = + ctx.open_or_create_task_storage(child_task_id, TaskDataCategory::Meta); + child_task.update_and_get_transient_ref_count(1); + } if let Some(parent_task_id) = parent_task_id { let mut parent_task = ctx.task(parent_task_id, TaskDataCategory::Meta); let Some(InProgressState::InProgress(InProgressStateInner { new_children, .. })) = diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_children.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_children.rs index 7198a2dd4435..f7873f4af42c 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_children.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_children.rs @@ -71,12 +71,16 @@ pub fn connect_children( "connect_children parent_count + dirty", |mut child, ctx| { // Bump before `make_task_dirty_internal`, which consumes the guard. - if !child.id().is_transient() { - if parent_is_transient { - child.update_and_get_transient_ref_count(1); - } else { - child.update_and_get_parent_count(1); - } + // + // `parent_count` is the *durable* count, so it may only track an edge that will + // itself be persisted: a persistent parent holding a persistent child. Every + // other combination is session-only and belongs in `transient_ref_count` -- + // including a transient child, whose incoming edges can never outlive the + // session no matter what kind of parent holds them. + if parent_is_transient || child.id().is_transient() { + child.update_and_get_transient_ref_count(1); + } else { + child.update_and_get_parent_count(1); } if !child.has_output() { make_task_dirty_internal( diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs index d5d40991fd90..5cf7f79bb9db 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/invalidate.rs @@ -60,7 +60,7 @@ impl Operation for InvalidateOperation { } => { let mut queue = AggregationUpdateQueue::new(); for task_id in task_ids { - make_task_dirty( + try_make_task_dirty( task_id, #[cfg(feature = "task_dirty_cause")] cause.clone(), @@ -88,6 +88,7 @@ impl Operation for InvalidateOperation { } } +/// Marks a task dirty. The task must exist. pub fn make_task_dirty( task_id: TaskId, #[cfg(feature = "task_dirty_cause")] cause: TaskDirtyCause, @@ -105,6 +106,28 @@ pub fn make_task_dirty( ); } +/// Marks a task dirty, doing nothing if it no longer exists. +/// +/// Intended for invalidation usecases. +pub fn try_make_task_dirty( + task_id: TaskId, + #[cfg(feature = "task_dirty_cause")] cause: TaskDirtyCause, + queue: &mut AggregationUpdateQueue, + ctx: &mut impl ExecuteContext<'_>, +) { + let Some(mut task) = ctx.try_get_task(task_id, TaskDataCategory::All) else { + return; + }; + make_task_dirty_internal( + &mut task, + true, + #[cfg(feature = "task_dirty_cause")] + cause, + queue, + ctx, + ); +} + /// Requires the guard to be allocated with [TaskDataCategory::All] pub fn make_task_dirty_internal<'e, E: ExecuteContext<'e>>( task: &mut E::TaskGuardImpl, diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs index 55a80d392014..e0e4c2ee87b9 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs @@ -9,6 +9,7 @@ mod update_cell; mod update_collectible; use std::{ fmt::{Debug, Display, Formatter}, + ops::{Deref, DerefMut}, sync::Arc, }; @@ -30,7 +31,7 @@ use crate::{ EventDescription, TaskDataCategory, TurboTasksBackend, cell_data::CellData, snapshot_coordinator::{OperationGuard, SnapshotPhase}, - storage::{SpecificTaskDataCategory, StorageWriteGuard, TrackOutcome}, + storage::{SpecificTaskDataCategory, StorageWriteGuard, TaskEntryGuard, TrackOutcome}, storage_schema::{TaskStorage, TaskStorageAccessors}, }, data::{ActivenessState, CollectibleRef, Dirtyness, InProgressState, TransientTask}, @@ -40,9 +41,44 @@ pub trait Operation: Encode + Decode<()> + Default + TryFrom); } -/// Whether an [`ExecuteContext`] task open may create the task or requires it to already exist. -/// A private impl detail behind the two public methods ([`ExecuteContext::task`] = `MustExist`, -/// [`ExecuteContext::open_or_create_task_storage`] = `MaybeCreate`). +/// The task storage `open_task` is working with, which may or may not still own its map entry. +enum OpenedTask<'a> { + Owned(TaskEntryGuard<'a>), + Restored(StorageWriteGuard<'a>), +} + +impl<'a> OpenedTask<'a> { + fn into_write_guard(self) -> StorageWriteGuard<'a> { + match self { + OpenedTask::Owned(g) => g.into_write_guard(), + OpenedTask::Restored(g) => g, + } + } +} + +impl Deref for OpenedTask<'_> { + type Target = TaskStorage; + fn deref(&self) -> &Self::Target { + match self { + OpenedTask::Owned(g) => g, + OpenedTask::Restored(g) => g, + } + } +} + +impl DerefMut for OpenedTask<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + OpenedTask::Owned(g) => g, + OpenedTask::Restored(g) => g, + } + } +} + +/// Whether an [`ExecuteContext`] task open may create the task, requires it to already exist, or +/// tolerates its absence. A private impl detail behind the three public methods +/// ([`ExecuteContext::task`] = `MustExist`, [`ExecuteContext::open_or_create_task_storage`] = +/// `MaybeCreate`, [`ExecuteContext::try_get_task`] = `AllowMissing`). #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum TaskAccess { /// Open the task, creating it if it does not exist: `access_mut` inserts a blank entry, then @@ -52,8 +88,10 @@ enum TaskAccess { /// task that exists in neither memory nor persistent storage is a bug — a stale reference to an /// already-collected or never-created task — and this refuses to fabricate a blank for it. /// - /// This is very much expression a 'foreign key constraint' on the database. + /// This is very much expressing a 'foreign key constraint' on the database. MustExist, + /// Open a task that may legitimately be gone or `deleted`. + AllowMissing, } // TODO: consider removing this trait (and `TaskGuard`) in favor of the concrete types. Each has @@ -73,6 +111,15 @@ pub trait ExecuteContext<'e>: Sized { /// The check applies only to persistent tasks; a `MustExist` open of a transient id falls /// through to create. See `ExecuteContextImpl::open_task`. fn task(&mut self, task_id: TaskId, category: TaskDataCategory) -> Self::TaskGuardImpl; + /// Opens a task that may legitimately be gone, returning `None` if it is. + /// + /// Gone covers both a task that exists nowhere and one that is soft-deleted: the caller cannot + /// tell those apart, since only the timing of the next eviction separates them. + fn try_get_task( + &mut self, + task_id: TaskId, + category: TaskDataCategory, + ) -> Option; /// Opens a task, materializing an in-memory storage entry for it if one is not resident yet /// (inserting a blank, then restoring `category` from disk if present). Use only where the /// task's storage may not be resident: the first connect of a freshly-minted child (threads can @@ -144,11 +191,6 @@ pub trait ExecuteContext<'e>: Sized { /// /// Only effective in a gc context see [`Self::collects_gc_candidates`]. fn note_maybe_collectible(&mut self, task: &impl TaskGuard); - /// Whether [`Self::note_maybe_collectible`] does anything, i.e. this is a GC context. - /// - /// Lets a caller skip work that only exists to feed the collector — in particular opening a - /// task with a wider [`TaskDataCategory`] than it would otherwise need. - fn collects_gc_candidates(&self) -> bool; fn should_track_dependencies(&self) -> bool; fn should_track_activeness(&self) -> bool; fn turbo_tasks(&self) -> Arc; @@ -299,26 +341,24 @@ impl<'e> ExecuteContextImpl<'e> { task_id: TaskId, category: TaskDataCategory, access: TaskAccess, - ) -> TaskGuardImpl<'e> { + ) -> Option> { self.task_lock_counter.acquire(); - // A resident entry always corresponds to a task that exists (only a `MaybeCreate` open ever - // inserts a blank, and only for a task being created). A `MustExist` open therefore only - // needs to prove existence when the entry looks like a fresh blank: nothing restored, not a - // new task. (A fully-evicted resident task also matches this shape, but it is on disk, so - // the `found_on_disk` check below clears it — the panic fires only when the task is in - // neither memory nor disk.) - let mut task = self.backend.storage.access_mut(task_id); - // The `MustExist` non-fabrication check applies only to **persistent** tasks: they have - // disk backing and are the subject of the stale-reference/GC concern. A transient task has - // no disk copy and is materialized lazily in memory (a strongly-consistent read can open a - // transient root through the aggregation graph before its storage entry exists), so a - // `MustExist` open of a transient id is a no-op that falls through to create. - let maybe_fabricated = access == TaskAccess::MustExist - && !task_id.is_transient() - && !task.flags.is_restored(TaskDataCategory::Meta) - && !task.flags.is_restored(TaskDataCategory::Data) - && !task.flags.new_task(); + let mut task = OpenedTask::Owned(self.backend.storage.access_entry_mut(task_id)); + // Treat deleted tasks under Allowmissing as missing + if access == TaskAccess::AllowMissing && task.flags.deleted() { + self.task_lock_counter.release(); + return None; + } + + // IF the caller cares about existence (either to panic or return None), check if this is an + // effectively blank task + let needs_existence_check = + matches!(access, TaskAccess::MustExist | TaskAccess::AllowMissing) + && !task_id.is_transient() + && !task.flags.is_restored(TaskDataCategory::Meta) + && !task.flags.is_restored(TaskDataCategory::Data) + && !task.flags.new_task(); if !task.flags.is_restored(category) { if task_id.is_transient() { task.flags.set_restored(TaskDataCategory::All); @@ -343,103 +383,144 @@ impl<'e> ExecuteContextImpl<'e> { task.flags.set_meta_restoring(true); } - if do_data || do_meta || data_restoring || meta_restoring { - let waiting_for_restore = data_restoring || meta_restoring; - if waiting_for_restore { - // The caller holds the task id outside the graph while waiting, so pin it - // against GC until the restored guard reaches the use boundary. Eviction is - // still allowed; the wait loop restores the category again if needed. - task.update_and_get_transient_ref_count(1); - } - // Drop lock while doing I/O (our I/O can overlap with the other thread). - drop(task); - - // Perform I/O for categories we claimed. - let storage_data = do_data - .then(|| self.restore_task_data(task_id, SpecificTaskDataCategory::Data)); - let storage_meta = do_meta - .then(|| self.restore_task_data(task_id, SpecificTaskDataCategory::Meta)); - - // Whether our own I/O found the task on disk (in any restored category). - // Another thread restoring it concurrently (`*_restoring`) - // also proves existence: it only sets the restoring bit - // after finding the task. - let found_on_disk = restored_from_disk(&storage_data) - || restored_from_disk(&storage_meta) - || data_restoring - || meta_restoring; - - // Wait for categories claimed by another thread (after our I/O). - // Reuse the returned write guard to avoid a second lock acquisition. - task = if let Some(cat) = wait_category(data_restoring, meta_restoring) { - self.wait_for_restore_or_panic(task_id, cat) - } else { - self.backend.storage.access_mut(task_id) - }; - if waiting_for_restore { - // This caller owns the pin and releases it only after acquiring the task - // guard it is about to use. - task.update_and_get_transient_ref_count(-1); - } + // `!is_restored(category)` above already implies this: for a single category + // it is the same predicate, and for `All` both are the disjunction + // `!data_restored || !meta_restored`. Asserted so that a future change to + // `is_restored` or `TaskDataCategory` surfaces here instead of silently + // skipping the restore. + debug_assert!( + needs_data || needs_meta, + "task({task_id}, {category:?}): not restored, yet neither category needs \ + restoring" + ); - // Apply results and clear restoring bits. - if let Some(result) = storage_data - && let Err(e) = - apply_restore_result(&mut task, result, SpecificTaskDataCategory::Data) - { - drop(task); - self.backend.storage.restored.notify(usize::MAX); - panic!("Failed to restore data for task {task_id}: {e:?}"); - } - if let Some(result) = storage_meta - && let Err(e) = - apply_restore_result(&mut task, result, SpecificTaskDataCategory::Meta) - { - drop(task); - self.backend.storage.restored.notify(usize::MAX); - panic!("Failed to restore meta for task {task_id}: {e:?}"); - } + let waiting_for_restore = data_restoring || meta_restoring; + if waiting_for_restore { + // The caller holds the task id outside the graph while waiting, so pin it + // against GC until the restored guard reaches the use boundary. Eviction is + // still allowed; the wait loop restores the category again if needed. + task.update_and_get_transient_ref_count(1); + } + // Drop lock while doing I/O (our I/O can overlap with the other thread). + drop(task); - if do_data || do_meta { - // Keep the guard through return. Once the restoring bit is clear, eviction - // may otherwise drop the category before this caller can use it. - self.backend.storage.restored.notify(usize::MAX); - } + // Perform I/O for categories we claimed. + let storage_data = do_data + .then(|| self.restore_task_data(task_id, SpecificTaskDataCategory::Data)); + let storage_meta = do_meta + .then(|| self.restore_task_data(task_id, SpecificTaskDataCategory::Meta)); - // The caller asserted this task exists (`MustExist`), but it looked like a - // fresh blank and restore found nothing on disk (and no one - // else was restoring it): it exists nowhere. Fail loudly - // rather than hand back a fabricated task, which - // would silently corrupt the graph. (The leftover blank entry is inert; the - // panic tears the process down.) - // - // This also fires if the on-disk cache is corrupt or truncated. That is the - // intended behavior: there is no recovery path for reading a cell on a task - // that is missing from disk, and the panic is self-healing — the cache is - // discarded and rebuilt on the next run. - assert!( - !(maybe_fabricated && !found_on_disk), - "task({task_id}, MustExist): task exists in neither memory nor persistent \ - storage — a stale reference to an already-collected or never-created task" - ); + // Whether our own I/O found the task on disk (in any restored category). + // + // A category another thread claimed (`*_restoring`) counts as found. The bit is + // set before that thread's I/O, so on its own it proves only that someone is + // looking — but this thread waits for that restore below, and a peer that comes up + // empty clears the category's `restored` bit before returning, so the blank cannot + // be mistaken for a real task. + let found_on_disk = restored_from_disk(&storage_data) + || restored_from_disk(&storage_meta) + || data_restoring + || meta_restoring; + + // Wait for categories claimed by another thread (after our I/O). + // Reuse the returned write guard to avoid a second lock acquisition. + // Reuse the guard the waiter already holds; only our own-I/O path can come + // up empty and need to discard the entry. + task = if let Some(cat) = wait_category(data_restoring, meta_restoring) { + OpenedTask::Restored(self.wait_for_restore_or_panic(task_id, cat)) } else { - // Nothing to restore (no categories claimed, none in progress) yet the entry - // looked like a fresh blank for a task asserted to exist: it does not exist. - assert!( - !maybe_fabricated, + OpenedTask::Owned(self.backend.storage.access_entry_mut(task_id)) + }; + if waiting_for_restore { + // This caller owns the pin and releases it only after acquiring the task + // guard it is about to use. + task.update_and_get_transient_ref_count(-1); + } + + // Apply results and clear restoring bits. + if let Some(result) = storage_data + && let Err(e) = + apply_restore_result(&mut task, result, SpecificTaskDataCategory::Data) + { + drop(task); + self.backend.storage.restored.notify(usize::MAX); + panic!("Failed to restore data for task {task_id}: {e:?}"); + } + if let Some(result) = storage_meta + && let Err(e) = + apply_restore_result(&mut task, result, SpecificTaskDataCategory::Meta) + { + drop(task); + self.backend.storage.restored.notify(usize::MAX); + panic!("Failed to restore meta for task {task_id}: {e:?}"); + } + + if do_data || do_meta { + // Keep the guard through return. Once the restoring bit is clear, eviction + // may otherwise drop the category before this caller can use it. + self.backend.storage.restored.notify(usize::MAX); + } + + // It looked like a fresh blank and restore found nothing on disk (and no one + // else was restoring it): it exists nowhere. An `AllowMissing` open reports + // that; a `MustExist` open fails loudly rather than hand + // back a fabricated task, which would silently corrupt the + // graph. (The leftover blank entry is inert; the + // panic tears the process down.) + if needs_existence_check && !found_on_disk { + if access == TaskAccess::AllowMissing { + // If the count is non-zero that means another thread is mid-restore or + // otherwise connecting to it. If they are waiting with AllowMissing they + // will perform the discard and if not then they want the blank and that is + // also fine. + if task.gc_transient_ref_count() == 0 { + match task { + OpenedTask::Owned(g) => g.discard(), + OpenedTask::Restored(_) => { + unreachable!( + "a task restored by another thread exists and is never \ + discarded" + ) + } + }; + } else { + // A waiter pins the entry, so it cannot be removed here. Reset the + // categories we read back to "never looked" instead so the other reader + // re-reads and also observes absence + // + // Only the categories this thread claimed are ours to clear. One a + // peer restored is the peer's to report, and it reaches this same + // code to clear its own. + debug_assert!( + !(do_data && task.flags.data_restoring()) + && !(do_meta && task.flags.meta_restoring()), + "task({task_id}): apply_restore_result should have cleared the \ + restoring bits for the categories we claimed" + ); + if do_data { + task.flags.set_data_restored(false); + } + if do_meta { + task.flags.set_meta_restored(false); + } + } + self.task_lock_counter.release(); + return None; + } + panic!( "task({task_id}, MustExist): task exists in neither memory nor persistent \ storage — a stale reference to an already-collected or never-created task" ); } } } - TaskGuardImpl { - task, + Some(TaskGuardImpl { + task: task.into_write_guard(), task_id, #[cfg(debug_assertions)] category, task_lock_counter: self.task_lock_counter.clone(), - } + }) } /// Restores one category for a task from persistent storage. `None` means the task was **not @@ -974,7 +1055,7 @@ fn wait_category(wait_data: bool, wait_meta: bool) -> Option { /// the restored flag. On error, returns the error so the caller can drop the task lock, /// notify waiters, and panic. fn apply_restore_result( - task: &mut StorageWriteGuard<'_>, + task: &mut (impl DerefMut + ?Sized), result: Result>, category: SpecificTaskDataCategory, ) -> Result<()> { @@ -1018,6 +1099,15 @@ impl<'e> ExecuteContext<'e> for ExecuteContextImpl<'e> { fn task(&mut self, task_id: TaskId, category: TaskDataCategory) -> Self::TaskGuardImpl { self.open_task(task_id, category, TaskAccess::MustExist) + .expect("a MustExist open either yields a task or panics") + } + + fn try_get_task( + &mut self, + task_id: TaskId, + category: TaskDataCategory, + ) -> Option { + self.open_task(task_id, category, TaskAccess::AllowMissing) } fn open_or_create_task_storage( @@ -1026,6 +1116,7 @@ impl<'e> ExecuteContext<'e> for ExecuteContextImpl<'e> { category: TaskDataCategory, ) -> Self::TaskGuardImpl { self.open_task(task_id, category, TaskAccess::MaybeCreate) + .expect("a MaybeCreate open always yields a task") } fn prepare_tasks( @@ -1080,11 +1171,11 @@ impl<'e> ExecuteContext<'e> for ExecuteContextImpl<'e> { // in memory and has no disk copy): a task that looks like a freshly-inserted blank (nothing // restored, not a new task) and that restore does not find on disk exists nowhere — a stale // reference. See `TaskAccess::MustExist`. - let maybe_fabricated1 = !task_id1.is_transient() + let needs_existence_check1 = !task_id1.is_transient() && !task1.flags.is_restored(TaskDataCategory::Meta) && !task1.flags.is_restored(TaskDataCategory::Data) && !task1.flags.new_task(); - let maybe_fabricated2 = !task_id2.is_transient() + let needs_existence_check2 = !task_id2.is_transient() && !task2.flags.is_restored(TaskDataCategory::Meta) && !task2.flags.is_restored(TaskDataCategory::Data) && !task2.flags.new_task(); @@ -1232,15 +1323,15 @@ impl<'e> ExecuteContext<'e> for ExecuteContextImpl<'e> { // A `MustExist` pair open must not fabricate: a task that looked like a fresh blank and // was not found on disk exists nowhere (a stale reference). See // `TaskAccess::MustExist`. Only reachable in the restore branch — a task - // already resident/restored (the else path) has `maybe_fabricated == + // already resident/restored (the else path) has `needs_existence_check == // false`. assert!( - !(maybe_fabricated1 && !found_on_disk1), + !(needs_existence_check1 && !found_on_disk1), "task_pair({task_id1}, .., MustExist): task exists in neither memory nor \ persistent storage — a stale reference to a never-created task" ); assert!( - !(maybe_fabricated2 && !found_on_disk2), + !(needs_existence_check2 && !found_on_disk2), "task_pair(.., {task_id2}, MustExist): task exists in neither memory nor \ persistent storage — a stale reference to a never-created task" ); @@ -1287,11 +1378,6 @@ impl<'e> ExecuteContext<'e> for ExecuteContextImpl<'e> { collector(task.id()); } } - - fn collects_gc_candidates(&self) -> bool { - matches!(self.phase, ExecutePhase::Gc(_)) - } - fn should_track_dependencies(&self) -> bool { self.backend.should_track_dependencies() } @@ -1486,16 +1572,10 @@ pub trait TaskGuard: Debug + TaskStorageAccessors { new_value } - /// Whether a GC pass may collect this task: it is non-transient and nothing references it. - /// - /// How much this proves depends on the guard's category — with only `Meta` open it is a sound - /// pre-filter that cannot see dependency edges, and with `All` open it is authoritative. See - /// [`TaskStorage::gc_maybe_collectible`] for the full contract. + /// Whether a GC pass may collect this task: nothing references it. fn is_gc_collectible(&self) -> bool { - // Transient-ness is a property of the id, not the storage; transient tasks are never - // collected. self.check_access(SpecificTaskDataCategory::Meta); - !self.id().is_transient() && self.typed().gc_maybe_collectible() + self.typed().gc_collectible() } fn invalidate_serialization(&mut self); @@ -2009,7 +2089,7 @@ pub use self::{ AggregatedDataUpdate, AggregationUpdateJob, get_aggregation_number, get_uppers, is_aggregating_node, is_root_node, }, - cleanup_old_edges::{OutdatedEdge, capture_all_outgoing_edges}, + cleanup_old_edges::{OutdatedEdge, capture_all_edges}, connect_children::connect_children, invalidate::make_task_dirty_internal, prepare_new_children::prepare_new_children, diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/storage.rs b/turbopack/crates/turbo-tasks-backend/src/backend/storage.rs index 370ac1984b59..921f2e801846 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/storage.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/storage.rs @@ -16,8 +16,6 @@ use tracing::span::Id; use turbo_bincode::TurboBincodeBuffer; use turbo_tasks::{FxDashMap, TaskId, backend::CachedTaskTypeArc, event::Event, parallel}; -#[cfg(debug_assertions)] -use crate::backend::storage_schema::GcRootHolders; use crate::{ backend::storage_schema::{ DropPartialOutcome, KeyEvictability, TaskStorage, UnevictableReason, ValueEvictability, @@ -522,6 +520,20 @@ impl Storage { } } + /// Like [`Self::access_mut`], but keeps the map entry so the caller can still remove it. + pub fn access_entry_mut(&self, key: TaskId) -> TaskEntryGuard<'_> { + let entry = match self.map.entry(key) { + dashmap::mapref::entry::Entry::Occupied(e) => e, + dashmap::mapref::entry::Entry::Vacant(e) => { + e.insert_entry(Box::new(TaskStorage::new())) + } + }; + TaskEntryGuard { + storage: self, + entry, + } + } + /// Read-only access to an already resident task. Returns `None` if the task isnt in memory /// resident. The closure runs while a shard read lock is held, so it must be cheap and must /// not re-enter the map. @@ -530,21 +542,10 @@ impl Storage { Some(f(task.value())) } - /// The number of **persistent** (non-transient) tasks resident in the map. Use this to assert - /// GC returns to a flat baseline across re-rooting: GC never collects transient tasks (e.g. - /// `run_once`/Once roots), so their count is not expected to settle. + /// The number of tasks resident in the map. #[doc(hidden)] - pub fn resident_persistent_task_count_for_testing(&self) -> usize { - let mut persistent = 0; - for shard in self.map.shards() { - let shard = shard.read(); - for (task_id, _) in shard.iter() { - if !task_id.is_transient() { - persistent += 1; - } - } - } - persistent + pub fn resident_task_count_for_testing(&self) -> usize { + self.map.len() } /// The number of shards in the resident map. GC seeds one `ScanShard` job per index; the slice @@ -554,96 +555,31 @@ impl Storage { self.map.shards().len() } - /// Iterates the non-transient tasks of a **single** shard of the resident map by index, under - /// that shard's read lock. - fn for_each_resident_persistent_in_shard( - &self, - index: usize, - mut f: impl FnMut(TaskId, &TaskStorage), - ) { + /// Scans a **single** shard by index, invoking `on_candidate` for each resident task whose + /// storage passes [`TaskStorage::gc_collectible`]. + pub fn gc_scan_shard(&self, index: usize, mut on_candidate: impl FnMut(TaskId)) { let shard = self.map.shards()[index].read(); for (task_id, task) in shard.iter() { - if task_id.is_transient() { - continue; + if task.gc_collectible() { + on_candidate(*task_id); } - f(*task_id, task); } } - /// Scans a **single** shard by index, invoking `on_candidate` for each resident, non-transient - /// task whose storage passes the cheap [`TaskStorage::gc_maybe_collectible`] pre-filter. - pub fn gc_scan_shard(&self, index: usize, mut on_candidate: impl FnMut(TaskId)) { - self.for_each_resident_persistent_in_shard(index, |task_id, storage| { - if storage.gc_maybe_collectible() { - on_candidate(task_id); - } - }); - } - /// Return the set of all known live roots. pub fn gc_scan_roots(&self) -> impl Iterator { - // Roots that failed the "held by a transient pin" expectation, with the referencing tasks - // that kept them un-collectible. Collected during the scan and reported *after* it: naming - // a dependent means reading its storage, and the shard locks are held inside the closure. - #[cfg(debug_assertions)] - let unexpected = std::sync::Mutex::new(Vec::<(TaskId, GcRootHolders)>::new()); - let per_shard: Vec> = parallel::map_collect(&(0..self.shard_count()).collect::>(), |&index| { let mut roots = Vec::new(); - self.for_each_resident_persistent_in_shard(index, |task_id, storage| { - if storage.gc_is_root() { - // The `is_root` criteria is conservative, in debug assert that we aren't - // marking things as roots for surprising reasons - #[cfg(debug_assertions)] - if !storage.gc_is_held_by_transient_pin() { - unexpected - .lock() - .unwrap() - .push((task_id, storage.gc_root_holders())); - } - roots.push(task_id); + let shard = self.map.shards()[index].read(); + for (task_id, task) in shard.iter() { + if !task_id.is_transient() && task.gc_is_root() { + roots.push(*task_id); } - }); + } roots }); - #[cfg(debug_assertions)] - { - use std::fmt::Write as _; - let unexpected = unexpected.into_inner().unwrap(); - if !unexpected.is_empty() { - let mut report = String::new(); - fn describe_task(storage: &Storage, task_id: TaskId) -> String { - storage - .access_mut(task_id) - .get_persistent_task_type() - // `NativeFunction`'s `Debug` is the public view of its name fields. - .map(|t| format!("{:?}", t.native_fn)) - .unwrap_or_else(|| "".to_string()) - } - for (task_id, holders) in &unexpected { - let _ = writeln!( - report, - " {} ({task_id:?}) is held by:", - describe_task(self, *task_id) - ); - for (kind, holder) in holders.iter() { - let _ = writeln!( - report, - " via {kind}: {} ({holder:?})", - describe_task(self, *holder) - ); - } - } - panic!( - "{} GC root(s) held by a non-transient pin.\nBeing held by another kind of \ - reference implies a bug in GC or the aggregation graph.\n{report}", - unexpected.len() - ); - } - } - per_shard.into_iter().flatten() } @@ -729,20 +665,22 @@ impl Storage { } }; shard.retain(|(task_id, task)| { - if task_id.is_transient() { + // Transient tasks can not be evicted at all, unless they are fully + // delete by the GC. + if task_id.is_transient() && !task.flags.deleted() { evicted.unevictable_reasons[UnevictableReason::Transient.index()] += 1; return true; } - // GC'd tasks were tombstoned during the snapshot so we can drop them fully now. + // All GC'd tasks were tombstoned during the snapshot (or are not persisted) so we + // can drop them fully now. if task.flags.deleted() { - let task_type = task - .get_persistent_task_type() - .expect("GC deleted tasks must have a task type"); - remove_from_task_cache( - &mut evicted, - &mut deferred_task_cache_removals, - task_type, - ); + if let Some(task_type) = task.get_persistent_task_type() { + remove_from_task_cache( + &mut evicted, + &mut deferred_task_cache_removals, + task_type, + ); + } evicted.full += 1; return false; } @@ -831,6 +769,44 @@ impl Storage { } } +/// A write guard that still owns its map entry, so the task can be removed under the lock that is +/// already held. +/// +/// Use [`Storage::access_entry_mut`] to obtain one. Convert it with [`Self::into_write_guard`] once +/// removal is no longer a possibility, or call [`Self::discard`] to drop the entry outright. +pub struct TaskEntryGuard<'a> { + storage: &'a Storage, + entry: dashmap::mapref::entry::OccupiedEntry<'a, TaskId, Box>, +} + +impl<'a> TaskEntryGuard<'a> { + /// Removes this task's entry. + pub fn discard(self) { + self.entry.remove(); + } + + /// Gives up the ability to remove the entry, yielding an ordinary write guard. + pub fn into_write_guard(self) -> StorageWriteGuard<'a> { + StorageWriteGuard { + storage: self.storage, + inner: self.entry.into_ref().into(), + } + } +} + +impl Deref for TaskEntryGuard<'_> { + type Target = TaskStorage; + fn deref(&self) -> &Self::Target { + self.entry.get() + } +} + +impl DerefMut for TaskEntryGuard<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.entry.get_mut() + } +} + pub struct StorageWriteGuard<'a> { storage: &'a Storage, inner: RefMut<'a, TaskId, Box>, @@ -1215,7 +1191,7 @@ mod tests { let task = storage.access_mut(task_id); assert_eq!(task.gc_transient_ref_count(), 1); - assert!(!task.gc_maybe_collectible()); + assert!(!task.gc_collectible()); } /// A process fn that returns a non-empty SnapshotItem so the iterator doesn't diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs b/turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs index 36582f84e8ba..cfea1bffc6f9 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs @@ -867,110 +867,88 @@ impl TaskStorage { new_value } - /// Whether a GC pass may collect this task: nothing references it, via parents, transient - /// pins, aggregation edges, or dependency edges. + /// Whether a GC pass can collect this task: nothing references it, via parents, transient + /// pins, or aggregation edges. /// - /// Precision depends on what the caller restored. Meta alone cannot see the three Data-category - /// dependent sets, so the answer is a sound *pre-filter*: a `false` is definitive, a `true` may - /// still have dependents. With Meta + Data it is the full predicate. That one-directional - /// conservatism lets the cheap Meta-only shard scan and the authoritative under-guard recheck - /// (which opens `TaskDataCategory::All`, and is what actually gates collection) share this - /// single predicate. - pub fn gc_maybe_collectible(&self) -> bool { + /// Only reads `Meta`, so the shard scan and the under-guard recheck get the same answer -- it + /// is exact in both, not a pre-filter. + /// + /// The `Data`-category dependent sets are deliberately not consulted: + /// + /// - `cell_dependents` / `cell_dependents_hashed` are redundant with ancestry. A cell dependent + /// is either a child, whose child edge already orders the teardown, or a sibling reached by + /// passing a `ResolvedVc` laterally, which needs a common ancestor that collects both in the + /// same pass. Counting them deadlocked the caller/callee cycle `NftJsonAsset::content` -> + /// `all_assets_from_entries_filtered`, whose tasks could then never be collected. + /// - `output_dependent` is redundant with `parent_count`. It records a read of a task's + /// *output*, which is the `OperationVc` representation, and those reads go through + /// `connect()` -- so the reader is already a child. (A `ResolvedVc` read, the one that + /// travels laterally as an argument, lands in `cell_dependents` instead.) + /// + /// Removing the cell sets exposed a race in the GC cascade -- rebalancing running while other + /// workers were still collecting -- which `gc_collect` now avoids by deferring all rebalance + /// work until the parallel phase is quiescent. + pub fn gc_collectible(&self) -> bool { + self.gc_unreferenced(ReferenceScope::All) + } + + /// Whether nothing in `scope` refers to this task. + fn gc_unreferenced(&self, scope: ReferenceScope) -> bool { // None of the predicates below are correct without this. - self.flags.is_restored(TaskDataCategory::Meta) + if !self.flags.is_restored(TaskDataCategory::Meta) // Already collected this session (soft-deleted, awaiting tombstone + hard-delete): // don't re-select it, or a second pass would collect it again while it is still // resident. - && !self.flags.deleted() - && self.gc_parent_count() == 0 - && self.gc_transient_ref_count() == 0 - && self.get_activeness().is_none() - && self.get_in_progress().is_none() - // It is rare for an upper to be present when the ref counts are 0, but it - // happens transiently during a concurrent GC pass as uppers move around in the cascade. - && self.upper().is_empty() - // `collectibles_dependents` is Meta, so it is always checkable here. - && self - .collectibles_dependents() - .is_none_or(|d| d.is_empty()) - // The remaining dependent sets are Data; skipped (leaving this a pre-filter) when Data - // is not restored. - && (!self.flags.is_restored(TaskDataCategory::Data) - || (self.output_dependent().is_empty() - && self.cell_dependents().is_none_or(|d| d.is_empty()) - && self - .cell_dependents_hashed() - .is_none_or(|d| d.is_empty()))) - } - - /// Whether this task is a GC **root**: parent-less, but pinned for some reason - /// - /// NOTE: this is a conservative classification. The typical reason is that there is a - /// [`TaskStorage::transient_ref`] live, but this will return true if there is merely an - /// `upper`. - pub fn gc_is_root(&self) -> bool { - self.flags.is_restored(TaskDataCategory::Meta) - && !self.flags.deleted() - && self.gc_parent_count() == 0 - && !self.gc_maybe_collectible() - } - - /// Whether this task is held by a pin that eviction cannot drop, which is what a task - /// classified by [`TaskStorage::gc_is_root`] is expected to be held by. - #[cfg(debug_assertions)] - pub fn gc_is_held_by_transient_pin(&self) -> bool { - self.gc_transient_ref_count() > 0 - || self.get_in_progress().is_some() - || self.get_activeness().is_some() - } - - /// The concrete references keeping this task un-collectible, as `(edge kind, holder task)` - /// pairs. - #[cfg(debug_assertions)] - pub fn gc_root_holders(&self) -> GcRootHolders { - let mut holders = GcRootHolders::default(); - for (&upper, _) in self.upper().iter() { - holders.push("upper", upper); - } - if let Some(deps) = self.collectibles_dependents() { - for &(_, task) in deps.iter() { - holders.push("collectibles_dependent", task); - } - } - for &task in self.output_dependent().iter() { - holders.push("output_dependent", task); + || self.flags.deleted() + || self.gc_parent_count() != 0 + { + return false; } - if let Some(deps) = self.cell_dependents() { - // In a `cell_dependents` entry `CellRef.task` is the DEPENDENT's id, not this task's. - for cell_ref in deps.iter() { - holders.push("cell_dependent", cell_ref.task); + match scope { + ReferenceScope::All => { + // It is rare for an upper to be present when the ref counts are 0, but it happens + // transiently during a concurrent GC pass as uppers move around in the cascade. + self.upper().is_empty() + // It would be rare for a collectibles dependent to be the only thing holding a + // task, but the invalidation that disconnected the task may not have bubbled + // all the way up yet. + && self.collectibles_dependents().is_none_or(|d| d.is_empty()) + && self.gc_transient_ref_count() == 0 + && self.get_in_progress().is_none() + && self.get_activeness().is_none() } - } - if let Some(deps) = self.cell_dependents_hashed() { - for (cell_ref, _) in deps.iter() { - holders.push("cell_dependent_hashed", cell_ref.task); + // Same two edge sets, minus the entries that die with the session. The pins above are + // skipped entirely: they are `category = "transient"` and never reach disk. + ReferenceScope::Persistent => { + self.upper().iter().all(|(u, _)| u.is_transient()) + && self + .collectibles_dependents() + .is_none_or(|d| d.iter().all(|(_, t)| t.is_transient())) } } - holders } -} - -/// `(edge kind, holder task)` pairs explaining why a task is not collectible. -/// See [`TaskStorage::gc_root_holders`]. -#[cfg(debug_assertions)] -#[derive(Default, Debug)] -pub struct GcRootHolders(Vec<(&'static str, TaskId)>); -#[cfg(debug_assertions)] -impl GcRootHolders { - fn push(&mut self, kind: &'static str, task: TaskId) { - self.0.push((kind, task)); + /// Whether this task is a GC **root**: nothing *persistent* refers to it, so only this session + /// is keeping it alive -- a `transient_ref` pin, an in-progress execution, activeness, or an + /// `upper` / collectibles edge from a transient task. + pub fn gc_is_root(&self) -> bool { + self.gc_unreferenced(ReferenceScope::Persistent) && !self.gc_collectible() } +} - pub fn iter(&self) -> impl Iterator { - self.0.iter() - } +/// Which references [`TaskStorage::gc_unreferenced`] counts. +/// +/// A task can be held by references that outlive the session and by references that do not -- the +/// transient entries of `upper` / `collectibles_dependents`, and the `transient_ref_count`, +/// `in_progress` and `activeness` pins. The two GC predicates care about different subsets: +/// collectibility about everything currently holding the task, rootness about only what would +/// survive a restart. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum ReferenceScope { + /// Every referrer, transient ones included. + All, + /// Only referrers that outlive the session. + Persistent, } /// Counts for aggregation tree and collectibles fields. diff --git a/turbopack/crates/turbo-tasks-backend/tests/gc_collection.rs b/turbopack/crates/turbo-tasks-backend/tests/gc_collection.rs index 593196d5858d..bcfaf36709ac 100644 --- a/turbopack/crates/turbo-tasks-backend/tests/gc_collection.rs +++ b/turbopack/crates/turbo-tasks-backend/tests/gc_collection.rs @@ -11,7 +11,7 @@ use anyhow::Result; use turbo_tasks::{ResolvedVc, TaskId, Vc, prevent_gc}; use crate::{ - gc_fixture::{Selector, create_selector}, + gc_fixture::{Constant, Selector, create_constant, create_selector}, util::create_tt, }; @@ -47,6 +47,29 @@ async fn branch_b() -> Result> { Ok(Vc::cell(2 + *leaf(20).await?)) } +/// Reads `observed` so the task registers an invalidator on it, then goes out of the live graph +/// when the selector flips. Whether that invalidator outlives the task is the point of +/// `mutating_a_state_read_by_a_collected_task_does_not_panic`. +#[turbo_tasks::function] +async fn state_reader(observed: ResolvedVc) -> Result> { + Ok(Vc::cell(*observed.await?.get())) +} + +/// Reads `state_reader` only while the selector is false, so flipping it disconnects the reader. +#[turbo_tasks::function(operation, root)] +async fn select_state_reader( + selector: ResolvedVc, + observed: ResolvedVc, +) -> Result> { + let use_b = *selector.await?.get(); + let value = if use_b { + *branch_b().await? + } else { + *state_reader(*observed).await? + }; + Ok(Vc::cell(value)) +} + /// A task that pins itself against GC while executing. Once pinned it must survive collection even /// after it is disconnected. #[turbo_tasks::function] @@ -114,7 +137,6 @@ async fn gc_collects_disconnected_subtree() { ); // Flipping back must recompute branch_a fresh, since it was collected. - let tt3 = tt.clone(); let result = turbo_tasks::run_once(tt.clone(), async move { let selector_op = create_selector(true); let selector_vc = selector_op.resolve().strongly_consistent().await?; @@ -123,7 +145,6 @@ async fn gc_collects_disconnected_subtree() { assert_eq!(*output.read_strongly_consistent().await?, 22); selector.set(false); assert_eq!(*output.read_strongly_consistent().await?, 11); - let _ = &tt3; anyhow::Ok(()) }) .await; @@ -238,7 +259,16 @@ async fn dispose_root_task_releases_anchored_subgraph() { turbo_tasks::run_once(tt.clone(), async move { anyhow::Ok(()) }) .await .unwrap(); - assert_eq!(tt.backend().gc_for_testing(&tt), 1); + // Two: the leaf, and the disposed root_task` itself. The `root_task` is a transient task, and + // transient tasks are collectible, so releasing the last reference to the subgraph reclaims + // both. + assert_eq!( + tt.backend() + .snapshot_and_evict_for_testing(&tt) + .gc_stats() + .collected, + 2 + ); // Disposal after the backend has stopped (the whole task map is dropped by `stop`), as a // `RootTask` finalized during Node worker teardown would be. @@ -266,3 +296,58 @@ async fn unpin_after_stop_does_not_panic() { tt.unpin_task_for_gc(leaf_id); } + +/// A `State` keeps an `Invalidator` for every task that read it, and those entries are plain task +/// ids with nothing keeping the task alive. Collecting a reader therefore leaves a dangling +/// invalidator behind, this test ensures that that doesn't cause a panic. +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn mutating_a_state_read_by_a_collected_task_does_not_panic() { + let (tt, _persistence_dir) = + create_tt("mutating_a_state_read_by_a_collected_task_does_not_panic"); + let tt2 = tt.clone(); + + let result = turbo_tasks::run_once(tt.clone(), async move { + let selector_op = create_selector(false); + let selector_vc = selector_op.resolve().strongly_consistent().await?; + let selector = selector_op.read_strongly_consistent().await?; + let observed_vc = create_constant().resolve().strongly_consistent().await?; + + // `state_reader` reads `observed`, registering an invalidator on that State. + let output = select_state_reader(selector_vc, observed_vc); + output.read_strongly_consistent().await?; + + // Flip so `state_reader` leaves the live graph; its invalidator stays on `observed`. + selector.set(true); + output.read_strongly_consistent().await?; + + anyhow::Ok(()) + }) + .await; + result.unwrap(); + + let collected = tt2.backend().gc_for_testing(&tt2); + assert!( + collected > 0, + "the disconnected state reader should have been collected" + ); + + // Required, and not just for realism: the evict is what selects the code path under test. + // GC only soft-deletes, leaving the task resident, and a weak open of a resident-but-deleted + // task returns early on the `deleted` flag. Evicting drops it from memory (and tombstones it on + // disk), so the open below instead reaches the exists-nowhere case in + // `ExecuteContextImpl::open_task` -- nothing restored, nothing found on disk -- which is the + // one that would panic under `MustExist`. Drop this line and the test still passes, but it + // stops covering that path. + tt2.backend().snapshot_and_evict_for_testing(&tt2); + + // Mutating the State now walks its invalidator list, which still names the collected reader. + let result = turbo_tasks::run_once(tt.clone(), async move { + let observed = create_constant().read_strongly_consistent().await?; + observed.set(1); + anyhow::Ok(()) + }) + .await; + result.unwrap(); + + tt.stop_and_wait().await; +} diff --git a/turbopack/crates/turbo-tasks-backend/tests/gc_cross_session.rs b/turbopack/crates/turbo-tasks-backend/tests/gc_cross_session.rs index 8efa2c136afa..d55dff109039 100644 --- a/turbopack/crates/turbo-tasks-backend/tests/gc_cross_session.rs +++ b/turbopack/crates/turbo-tasks-backend/tests/gc_cross_session.rs @@ -15,11 +15,11 @@ use std::{ }; use anyhow::Result; -use turbo_tasks::{GcRoot, TurboTasks, Vc}; +use turbo_tasks::{GcRoot, ResolvedVc, TurboTasks, Vc}; use turbo_tasks_backend::TurboTasksBackend; use crate::{ - gc_fixture::{create_constant, diamond_root_op}, + gc_fixture::{Constant, Selector, create_constant, create_selector, diamond_root_op}, util::{create_persistence_dir, reopen_tt_with_gc, reopen_tt_with_gc_ttl}, }; @@ -219,3 +219,135 @@ async fn gc_collect_scrubs_disk_only_forward_dep_target() { result.unwrap(); } } + +// Regression test for a bug where a second cell reader owned by a different root task would panic +// when the root eventually ages out. +// +// This test merely asserts that such dependencies don't cause panics. +// +// `shared_target` is *called* by the owning side only, so that side is its sole parent. +// `borrowing_root` receives the target already resolved and only reads it through `shared_reader`: +// a forward cell-dependency, no child edge. So the target's `cell_dependents` holds reader 2 while +// its `parent_count` comes entirely from the owning side. +// +// Session 1 flips a selector to drop the owning side cleanly -- no invalidation, so the target +// keeps its edges -- and collects it. The shared target cascades with it, because +// `cell_dependents` do not keep a task alive (the documented `gc_collectible` heuristic). Reader 2 +// survives under the pinned borrowing root holding a forward dependency on a task that is now +// gone, and that state is persisted. +// +// Session 2 ages the borrowing root out. Tearing down reader 2 scrubs its forward dependency on +// the target collected back in session 1: a `MustExist` open of an already-collected task. The +// session does not need to execute anything -- the tasks only have to exist on disk for GC to +// reach them. + +#[turbo_tasks::function] +async fn shared_target(constant: ResolvedVc) -> Result> { + Ok(Vc::cell(*constant.await?.get() + 41)) +} + +/// Reads the shared target via an already-resolved `Vc`: a forward cell-dependency, no child edge. +#[turbo_tasks::function] +async fn shared_reader(target: ResolvedVc, n: u32) -> Result> { + Ok(Vc::cell(n + *target.await?)) +} + +/// Calls the shared target -- becoming its only parent -- and reads it through reader 1. +#[turbo_tasks::function] +async fn owning_subtree(constant: ResolvedVc) -> Result> { + let target = shared_target(*constant).to_resolved().await?; + assert_eq!(*shared_reader(*target, 1).await?, 42); + // Return the target's own `Vc`, so a caller that resolves this reaches `shared_target`. + Ok(*target) +} + +/// Selector-gated root over [`owning_subtree`]: flipping the selector to `true` drops the owning +/// subtree cleanly, without invalidating it, so it keeps its edges and becomes collectible. +#[turbo_tasks::function(operation, root)] +async fn select_owning( + selector: ResolvedVc, + constant: ResolvedVc, +) -> Result> { + // Pass the subtree's `Vc` through unchanged rather than re-celling it, so resolving this op + // names `shared_target` itself -- that is the task the borrowing side must depend on. + if !*selector.await?.get() { + Ok(owning_subtree(*constant)) + } else { + Ok(Vc::cell(0)) + } +} + +/// Reaches reader 2 through an **already-resolved** target, so it never calls the target and never +/// becomes its parent. Reader 2 keeps its forward cell-dependency on the target after the owning +/// subtree -- the target's only parent -- is collected. +#[turbo_tasks::function(operation, root)] +async fn borrowing_root(target: ResolvedVc) -> Result> { + Ok(Vc::cell(*shared_reader(*target, 2).await?)) +} + +/// A surviving task must not be left holding a forward cell-dependency on a collected task. +/// +/// Two readers share a cell target, but only one side *owns* it (is its parent). The owning side is +/// collected while the borrowing root is still pinned, so the target is destroyed with reader 2 +/// still depending on it -- and reader 2 is only torn down in the next session. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn shared_cell_target_collected_before_its_second_reader() { + let dir = create_persistence_dir("shared_cell_target_collected_before_its_second_reader"); + + // Session 1: build both sides over one shared target, drop the owning side, and collect it. + // That leaves reader 2 holding a dependency on the collected target, and persists it. + { + let tt = reopen_tt_with_gc(&dir); + let borrowing_op = turbo_tasks::run_once(tt.clone(), async move { + let selector_op = create_selector(false); + let selector_vc = selector_op.resolve().strongly_consistent().await?; + let selector = selector_op.read_strongly_consistent().await?; + + let constant_op = create_constant(); + let constant_vc = constant_op.resolve().strongly_consistent().await?; + + // The owning side parents the target and hands it back. + let owning = select_owning(selector_vc, constant_vc); + let target = owning.resolve().strongly_consistent().await?; + assert_eq!(*target.await?, 41); + + let borrowing = borrowing_root(target); + assert_eq!(*borrowing.read_strongly_consistent().await?, 43); + + // Drop the owning subtree cleanly: no invalidation, so the target keeps its edges. + selector.set(true); + assert_eq!(*owning.read_strongly_consistent().await?, 0); + anyhow::Ok(borrowing) + }) + .await + .unwrap(); + + // Pin the borrowing root so only the owning side is collectible. + let borrowing_pin = GcRoot::pin(tt.clone(), borrowing_op); + let collected = gc_until_collected(&tt, 3).await; + assert!( + collected >= 3, + "the owning subtree, reader 1 and the shared target should be collected (got {collected})" + ); + + tt.backend().snapshot_and_evict_for_testing(&tt); + drop(borrowing_pin); + + tt.stop_and_wait().await; + } + + // Session 2: nothing is pinned, so the borrowing root ages out. Tearing down reader 2 scrubs + // its forward dependency on the target collected in session 1 -- the dangling half-edge. + // Surviving these passes without a panic is the assertion. + { + let tt = reopen_tt_with_gc_ttl(&dir, Duration::ZERO); + let tt2 = tt.clone(); + turbo_tasks::run_once(tt.clone(), async move { + gc_until_collected(&tt2, 2).await; + anyhow::Ok(()) + }) + .await + .unwrap(); + tt.stop_and_wait().await; + } +} diff --git a/turbopack/crates/turbo-tasks-backend/tests/gc_resurrection.rs b/turbopack/crates/turbo-tasks-backend/tests/gc_resurrection.rs index 3b4f5ba10256..1531d36bbb47 100644 --- a/turbopack/crates/turbo-tasks-backend/tests/gc_resurrection.rs +++ b/turbopack/crates/turbo-tasks-backend/tests/gc_resurrection.rs @@ -87,7 +87,7 @@ async fn select_diamond( /// The **aggregation-graph rebalance** in GC: when the `reader` subtree is disconnected cleanly and /// collected, GC must remove `reader` from each `sd_leaf`'s `upper` set so the leaves — now /// parentless *and* upper-less — cascade-collect in the same pass. Without the rebalance a leaf -/// keeps a dangling `upper` edge to the deleted `reader`, fails `gc_maybe_collectible`, and leaks +/// keeps a dangling `upper` edge to the deleted `reader`, fails `gc_collectible`, and leaks /// until eviction hides it. #[tokio::test(flavor = "multi_thread", worker_threads = 8)] async fn gc_rebalances_aggregation_and_cascades_in_one_pass() { @@ -115,11 +115,11 @@ async fn gc_rebalances_aggregation_and_cascades_in_one_pass() { result.unwrap(); // Baseline resident count with the reader subtree disconnected but not yet collected. - let baseline = tt2.backend().resident_persistent_task_count_for_testing(); + let baseline = tt2.backend().resident_task_count_for_testing(); let collected = tt2.backend().gc_for_testing(&tt2); tt2.backend().snapshot_and_evict_for_testing(&tt2); - let after = tt2.backend().resident_persistent_task_count_for_testing(); + let after = tt2.backend().resident_task_count_for_testing(); assert_eq!( collected, @@ -132,10 +132,10 @@ async fn gc_rebalances_aggregation_and_cascades_in_one_pass() { "resident count must drop by exactly the collected subtree" ); - // Only the three top-level `(operation, root)` tasks may be tracked as roots. A leaf that - // reached the post-drain scan still holding a dangling `upper` edge to the deleted `reader` - // would land in the map as `MostRecent`, which never ages out. If this fires it is a finding - // about the aggregation graph, not a reason to narrow `gc_is_root`. + // Only the three top-level `(operation, root)` tasks may be tracked as roots -- they are the + // ones held by a transient pin. A leaf still holding a dangling `upper` edge to the deleted + // `reader` is not a root (it fails `gc_unreferenced`), so it silently leaks rather than being + // tracked; the resident-count assertions above are what catch that. let roots = tt2.backend().persisted_gc_roots_for_testing(); assert_eq!(roots.len(), 3, "unexpected roots tracked: {roots:?}"); @@ -172,11 +172,11 @@ async fn gc_diamond_forward_dep_no_resurrection() { .await; result.unwrap(); - let baseline = tt2.backend().resident_persistent_task_count_for_testing(); + let baseline = tt2.backend().resident_task_count_for_testing(); let collected = tt2.backend().gc_for_testing(&tt2); tt2.backend().snapshot_and_evict_for_testing(&tt2); - let after = tt2.backend().resident_persistent_task_count_for_testing(); + let after = tt2.backend().resident_task_count_for_testing(); assert_eq!( collected, @@ -230,7 +230,6 @@ async fn gc_resurrect_on_reconnect() { // Reconnect the subtree (selector back to false) BEFORE any snapshot. Reading `reader` again // connects it, which must resurrect it (and its leaves, as it re-reads them). - let tt3 = tt.clone(); let result = turbo_tasks::run_once(tt.clone(), async move { let selector_op = create_selector(false); let selector_vc = selector_op.resolve().strongly_consistent().await?; @@ -244,7 +243,6 @@ async fn gc_resurrect_on_reconnect() { expected, "resurrected reader must recompute the correct value" ); - let _ = &tt3; anyhow::Ok(()) }) .await; @@ -252,7 +250,6 @@ async fn gc_resurrect_on_reconnect() { // A snapshot+evict now must NOT have tombstoned/hard-deleted the resurrected subtree. tt2.backend().snapshot_and_evict_for_testing(&tt2); - let tt4 = tt.clone(); let result = turbo_tasks::run_once(tt.clone(), async move { let selector_op = create_selector(false); let selector_vc = selector_op.resolve().strongly_consistent().await?; @@ -260,7 +257,6 @@ async fn gc_resurrect_on_reconnect() { let constant_vc = constant_op.resolve().strongly_consistent().await?; let output = select_reader(selector_vc, constant_vc); assert_eq!(*output.read_strongly_consistent().await?, expected); - let _ = &tt4; anyhow::Ok(()) }) .await; @@ -319,7 +315,6 @@ async fn select_imm_reader(selector: ResolvedVc) -> Result> { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn gc_resurrect_immutable_recomputes() { let (tt, _persistence_dir) = create_tt("gc_resurrect_immutable_recomputes"); - let tt2 = tt.clone(); let expected: u32 = (0..IMM_FANOUT).fold(0u32, |a, b| a.wrapping_add(b * 3)); let result = turbo_tasks::run_once(tt.clone(), async move { @@ -349,7 +344,7 @@ async fn gc_resurrect_immutable_recomputes() { // Collect the disconnected subtree. The entries stay resident (no snapshot yet), so the tasks // are soft-deleted rather than gone. - let collected = tt2.backend().gc_for_testing(&tt2); + let collected = tt.backend().gc_for_testing(&tt); assert_eq!( collected, IMM_FANOUT as usize + 1, @@ -360,10 +355,8 @@ async fn gc_resurrect_immutable_recomputes() { // This must recompute it rather than serve a stale value. Done before the reconnect below, // while the subtree is still collected — afterwards the leaves are live again and a read would // legitimately hit a fresh cell, proving nothing. - let tt_direct = tt.clone(); let result = turbo_tasks::run_once(tt.clone(), async move { assert_eq!(*read_imm_leaf(0).read_strongly_consistent().await?, 0); - let _ = &tt_direct; anyhow::Ok(()) }) .await; @@ -377,7 +370,6 @@ async fn gc_resurrect_immutable_recomputes() { // Reconnect BEFORE any snapshot. These tasks were never persisted, so there is nothing on disk // to restore — the only way back to a correct value is re-execution. - let tt3 = tt.clone(); let result = turbo_tasks::run_once(tt.clone(), async move { let selector_op = create_selector(false); let selector_vc = selector_op.resolve().strongly_consistent().await?; @@ -389,7 +381,7 @@ async fn gc_resurrect_immutable_recomputes() { expected, "a resurrected immutable task must recompute the correct value" ); - let _ = &tt3; + anyhow::Ok(()) }) .await; @@ -404,14 +396,12 @@ async fn gc_resurrect_immutable_recomputes() { // A snapshot + evict must not have tombstoned the resurrected subtree, and the restored data // must survive the round trip. - tt2.backend().snapshot_and_evict_for_testing(&tt2); - let tt4 = tt.clone(); + tt.backend().snapshot_and_evict_for_testing(&tt); let result = turbo_tasks::run_once(tt.clone(), async move { let selector_op = create_selector(false); let selector_vc = selector_op.resolve().strongly_consistent().await?; let output = select_imm_reader(selector_vc); assert_eq!(*output.read_strongly_consistent().await?, expected); - let _ = &tt4; anyhow::Ok(()) }) .await; diff --git a/turbopack/crates/turbo-tasks-backend/tests/gc_stress.rs b/turbopack/crates/turbo-tasks-backend/tests/gc_stress.rs index e656e664016a..9c3339fad654 100644 --- a/turbopack/crates/turbo-tasks-backend/tests/gc_stress.rs +++ b/turbopack/crates/turbo-tasks-backend/tests/gc_stress.rs @@ -35,7 +35,6 @@ async fn gc_re_rooting_stays_flat() { tt: &Arc>, gen_value: u32, ) -> (usize, usize, bool) { - let tt_inner = tt.clone(); turbo_tasks::run_once(tt.clone(), async move { // `create_generation` is a cached root operation, so this returns the same task each // round. @@ -47,7 +46,6 @@ async fn gc_re_rooting_stays_flat() { } let output = wide_root(generation_vc, WIDTH); output.read_strongly_consistent().await?; - let _ = &tt_inner; anyhow::Ok(()) }) .await @@ -68,7 +66,7 @@ async fn gc_re_rooting_stays_flat() { // independently of GC — including them would mask the real signal. ( collected, - tt.backend().resident_persistent_task_count_for_testing(), + tt.backend().resident_task_count_for_testing(), interrupted, ) } @@ -119,14 +117,13 @@ async fn gc_re_rooting_stays_flat() { ); // The live graph must still compute correctly after all the churn. - let tt3 = tt.clone(); let result = turbo_tasks::run_once(tt.clone(), async move { let generation_op = create_generation(); let generation_vc = generation_op.resolve().strongly_consistent().await?; let output = wide_root(generation_vc, WIDTH); let expected: u32 = expected_value(ROUNDS, WIDTH); assert_eq!(*output.read_strongly_consistent().await?, expected); - let _ = &tt3; + anyhow::Ok(()) }) .await; diff --git a/turbopack/crates/turbo-tasks/src/manager.rs b/turbopack/crates/turbo-tasks/src/manager.rs index eb648cb71157..4e8053b4e89d 100644 --- a/turbopack/crates/turbo-tasks/src/manager.rs +++ b/turbopack/crates/turbo-tasks/src/manager.rs @@ -964,6 +964,28 @@ impl TurboTasks { self.schedule(id, TaskPriority::initial()); } + /// Runs `future` as a top-level task and returns its result. + /// + /// # Returned values must not contain `Vc` or `ResolvedVc` or if they do they should be covered by a pin + /// + /// `T` is deliberately unconstrained for ergonomics, but a `Vc`/`ResolvedVc` that escapes this + /// call is **not** protected against garbage collection. It can be collected, and + /// dereferencing the stale handle then fails. + /// /// + /// To hand a computation out of a top-level task, return the [`OperationVc`] and pin it: + /// + /// ```ignore + /// let (resolved, op) = tt.run_once(async move { + /// let op = my_operation(args); + /// let resolved = op.resolve().strongly_consistent().await?; + /// // Return the operation too, so it can be pinned below. + /// Ok((resolved, op)) + /// }).await?; + /// let _gc_root = GcRoot::pin(tt.clone(), op); // unpins on drop + /// ``` + /// + /// Returning owned data -- [`ReadRef`](crate::ReadRef), `RcStr`, plain values -- is always + /// fine. pub async fn run_once( &self, future: impl Future> + Send + 'static, @@ -980,6 +1002,11 @@ impl TurboTasks { rx.await? } + /// Runs `future` as a top-level task and returns its result. + /// + /// The same constraint as [`run_once`](Self::run_once) applies to `T`: a `Vc`/`ResolvedVc` + /// returned from here is not anchored against garbage collection. See that method's docs for + /// why, and for the pin-the-`OperationVc` idiom. #[tracing::instrument(level = "trace", skip_all, name = "turbo_tasks::run")] pub async fn run( &self, diff --git a/turbopack/crates/turbo-tasks/src/task_dirty_cause.rs b/turbopack/crates/turbo-tasks/src/task_dirty_cause.rs index c9c49e5706f7..5d8cb28a1149 100644 --- a/turbopack/crates/turbo-tasks/src/task_dirty_cause.rs +++ b/turbopack/crates/turbo-tasks/src/task_dirty_cause.rs @@ -24,6 +24,9 @@ pub enum TaskDirtyCause { /// Re-dirtied because a GC-soft-deleted task was resurrected by a new connection before its /// hard-delete: its edges were scrubbed, so it must re-execute to rebuild them. Resurrected, + /// Dirtied because a task this one depended on was torn down: the value this task's result was + /// derived from no longer exists, so the result cannot be trusted. + DependencyTornDown, Unknown, } @@ -87,6 +90,7 @@ impl std::fmt::Display for TaskDirtyCause { } TaskDirtyCause::Invalidator => write!(f, "invalidator"), TaskDirtyCause::Resurrected => write!(f, "resurrected"), + TaskDirtyCause::DependencyTornDown => write!(f, "dependency torn down"), TaskDirtyCause::Unknown => write!(f, "unknown"), } }