e});function Rq(e,t){return e==null?"":Number(e).toLocaleString(void 0,t==null?{}:{minimumFractionDigits:t,maximumFractionDigits:t})}function Tq(e){return String(e??"").toUpperCase()}function Oq(e,t,o=0){return(e||[]).slice(o,o+t)}function Aq(e,t){return Array.isArray(e)?typeof t=="function"?e.filter(t):e:[]}function Iq(e,t){if(!t)return e||[];let o=Array.isArray(t)?t:[t];return[...e||[]].sort((r,n)=>{for(let i of o){let a=i[0]==="-"?-1:1;i=i.replace(/^[+-]/,"");let u=i.split(".").reduce((h,y)=>h?.[y],r),p=i.split(".").reduce((h,y)=>h?.[y],n);if(typeof u=="string"&&(u=u.toLowerCase()),typeof p=="string"&&(p=p.toLowerCase()),u!==p)return(u==null?-1:p==null?1:ur[n])}function Pq(e,t){return(t?.[e]??t?.[e===1?"one":"other"]??"").replace(/\{\}/g,e??0)}var El={ERRORS:{repository_job_cancelled:"The repository changed or was removed while processing.",storage_delete_failed:"Unable to remove cached files. Please try again later.",unknown_error:"Unknown error, contact the admin.",unreachable:"Anonymous GitHub is unreachable, contact the admin.",request_error:"Unable to download the file, check your connection or contact the admin.",not_found:"The requested resource could not be found.",not_connected:"You must be logged in to perform this action.",not_authorized:"You do not have permission to perform this action.",unable_to_connect_user:"Unable to connect your account. Please try again later.",user_not_found:"The requested user could not be found.",user_banned:"Your account has been banned. Contact the admin for more information.",repo_access_limited:"GitHub blocked access because the repository's organization restricts third-party OAuth apps. Ask an org owner to approve Anonymous GitHub under Settings \u2192 Third-party Access \u2192 OAuth app policy, or anonymize a personal fork instead.",repo_saml_enforcement:"The repository's organization enforces SAML single sign-on. Authorize your token for that organization (GitHub \u2192 Settings \u2192 Applications, or re-run the org's SSO sign-in), then retry. Alternatively, anonymize a personal fork.",repo_not_found:"The repository was not found on GitHub. Check the URL and spelling, make sure you are signed in to the account that can see it, and confirm the repo isn't hidden under an org that restricts third-party app access.",repo_empty:"The selected branch has no commits on GitHub. Push at least one commit, or pick a different branch, then retry.",repo_not_accessible:"Anonymous GitHub cannot access this repository. Verify the repository exists and that Anonymous GitHub has been authorized for the owning organization.",repository_archived:"This repository has been archived and its files are no longer available.",repository_expired:"The repository is expired.",invalid_status:"This action cannot be performed while the resource is in its current state.",repository_not_ready:"Anonymous GitHub is still processing the repository, it can take several minutes.",repository_not_accessible:"This repository is currently not accessible.",repo_is_updating:"Anonymous GitHub is still processing the repository, it can take several minutes.",invalid_repo:"The provided repository is not valid.",invalid_source_repository:"The repository source name is invalid; expected the form 'owner/name'.",repoId_not_defined:"A repository ID must be provided.",repoUrl_not_defined:"The repository URL needs to be defined.",source_not_provided:"A repository source must be provided.",github_rate_limit_exceeded:"GitHub temporarily blocked the request because we hit its API rate limit. Wait a few minutes and try again. If the problem persists and you contact GitHub Support, include the request ID and timestamp shown in GitHub's response (the 'X-GitHub-Request-Id' header).",rate_limited:"GitHub API rate limit reached. The repository will be available shortly \u2014 please wait a moment and refresh.",repoId_already_used:"The repository ID is already used.",invalid_repoId:"The format of the repository ID is invalid.",unsupported_source:"The repository source type is not supported.",branch_not_specified:"The branch is not specified.",branch_not_found:"The branch of the repository cannot be found.",commit_not_specified:"A commit must be specified.",commit_not_found:"The configured commit no longer exists in the source repository. It may have been force-pushed, rebased, or removed.",repo_renamed:"The source repository appears to have been renamed or moved on GitHub. Please refresh and try again \u2014 the cached name has been updated.",invalid_commit_format:"The commit hash format is invalid. It must be a hexadecimal string.",pull_request_not_found:"The requested pull request could not be found.",pull_request_expired:"This pull request has expired and is no longer available.",pull_request_not_ready:"This pull request is still being prepared. Please try again shortly.",pull_request_not_available:"This pull request is currently not available.",invalid_pullRequestId:"The pull request ID is invalid.",pullRequestId_already_used:"This pull request ID is already in use. Please choose a different one.",repository_not_specified:"A repository must be specified for this pull request.",pullRequestId_not_specified:"A pull request ID must be specified.",pullRequestId_is_not_a_number:"The source pull request ID must be a number.",options_not_provided:"Anonymization options are mandatory.",terms_not_specified:"Anonymization terms must be specified.",invalid_terms_format:"Terms are in an invalid format.",missing_content:"No content was provided to the anonymization preview.",unable_to_anonymize:"An error happened during the anonymization process. Please try later or report the issue.",non_supported_mode:"The selected anonymization mode is invalid, only download and stream are supported.",invalid_path:"The provided path is invalid or missing.",path_not_specified:"A file path must be specified.",path_not_defined:"The file path has not been resolved yet.",invalid_file_path:"The requested file path is not valid.",invalid_request:"The request is missing required fields or is malformed.",no_file_selected:"Please select a file.",file_not_found:"The requested file is not found.",file_not_accessible:"The requested file is not accessible.",file_not_supported:"The file type is not supported. Anonymous GitHub cannot handle it.",file_too_big:"The file size exceeds the limit of Anonymous GitHub.",is_folder:"The path points to a folder.",folder_not_supported:"The path points to a folder. Please select a file.",unable_to_write_file:"Unable to write file on disk.",download_not_enabled:"Repository downloads are not enabled on this server.",unable_to_download:"The repository could not be downloaded. Please try again later.",s3_config_not_provided:"Object storage has not been configured on this server.",stats_unsupported:"Statistics are only supported in download mode.",branches_not_found:"The requested branch is not found.",readme_not_available:"No README for the repository is found.",page_not_supported_on_different_branch:"GitHub Pages is served from a different branch than the one selected. Pick the branch that GitHub Pages is configured to use.",page_not_activated:"GitHub Pages is not enabled on this repository. Enable it in the repository settings on GitHub before anonymizing.",is_removed:"This resource has been removed and is no longer available.",conf_name_missing:"A conference name is required.",conf_id_missing:"A conference ID is required.",conf_id_used:"This conference ID is already in use. Please choose a different one.",conf_start_date_missing:"A start date is required for the conference.",conf_end_date_missing:"An end date is required for the conference.",conf_start_date_invalid:"The start date must be before the end date.",conf_end_date_invalid:"The end date must be in the future.",invalid_plan:"The selected plan is not valid.",conference_not_found:"The requested conference could not be found.",conf_not_found:"The requested conference could not be found.",conf_not_activated:"The conference is not activated.",billing_missing:"Billing information is required for this plan.",billing_name_missing:"A billing name is required.",billing_email_missing:"A billing email is required.",billing_address_missing:"A billing address is required.",billing_city_missing:"A billing city is required.",billing_zip_missing:"A billing ZIP/postal code is required.",billing_country_missing:"A billing country is required.",queue_not_found:"The specified queue could not be found.",job_not_found:"The specified job could not be found in the queue.",error_retrying_job:"An error occurred while retrying the job.",gist_expired:"The gist is expired.",gist_not_ready:"Anonymous GitHub is still processing the gist, it can take several minutes.",gist_not_found:"The requested gist could not be found.",gist_not_available:"This gist is currently not available.",invalid_gistId:"The format of the gist ID is invalid.",gistId_not_specified:"A gist ID must be provided.",gistId_already_used:"The gist ID is already used.",missing_token:"An authentication token is required.",invalid_token:"The provided authentication token is invalid.",login_failed:"Login failed. Please try again.",server_error:"An unexpected server error occurred. Please try again later.",username_not_defined:"A username must be provided.",github_user_not_found:"The specified GitHub user could not be found.",cannot_coauthor_self:"You cannot add yourself as a co-author.",storage_write_size_mismatch:"The downloaded file was smaller than expected. The upstream source may have returned an incomplete response \u2014 please try again.",storage_read_error:"An error occurred while reading the file from storage \u2014 please try again.",upstream_error:"A temporary error occurred while fetching from GitHub \u2014 please try again.",token_expired:"Your GitHub access token has expired. Please log out and log in again to refresh it.",job_is_active:"This job is currently running \u2014 wait for it to finish or remove it first.",github_app_access_required:"Allow this repository on GitHub using the read-only access button, then retry. Organization access may require administrator approval.",github_app_reconnect_required:"Reconnect your GitHub App account to restore read-only access.",github_app_disabled:"New GitHub App connections are currently unavailable.",github_oauth_required:"Connect legacy GitHub OAuth to use this feature.",github_identity_mismatch:"Use the same GitHub account that you are signed in with here.",github_app_permissions_invalid:"The GitHub App must be configured with read-only permissions.",github_unavailable:"GitHub is temporarily unavailable. Please retry.",connection_changed:"The repository connection changed. Reload and try again.",repository_busy:"Wait for the repository operation to finish before changing its connection.",invalid_connection:"Select a supported GitHub connection.",github_oauth_disabled:"Legacy OAuth sign-in is currently disabled.",invalid_auth_state:"The authorization session expired. Please start again from Anonymous GitHub.",invalid_webhook_signature:"The webhook signature is invalid.",webhook_processing_failed:"Webhook processing failed. Retry the delivery.",github_app_authorization_cancelled:"GitHub authorization was cancelled.",github_account_link_required:"This username belongs to an existing account. Sign in with legacy GitHub OAuth to keep access to your existing anonymizations, then connect the read-only GitHub App from GitHub connections.",github_app_refresh_busy:"GitHub credentials are being renewed. Please retry shortly.",github_grant_revocation_failed:"Unable to revoke the GitHub grant. Please retry.",another_login_required:"Connect and verify another sign-in method before disconnecting OAuth.",oauth_resources_remaining:"Some resources still require OAuth. Migrate or remove them before disconnecting.",invalid_github_path:"The GitHub repository path is invalid."},WARNINGS:{page_not_enabled_on_repo:"GitHub Pages is not enabled on this repository. Enable it in the repository's Settings \u2192 Pages on GitHub, then refresh.",page_branch_mismatch:"GitHub Pages on this repository is served from the '{{pageBranch}}' branch, but you selected '{{selectedBranch}}'. Switch the branch above to '{{pageBranch}}' to anonymize the Pages site.",folder_truncated:"This folder has more than 10,000 entries; only a partial listing is shown.",repo_truncated:"Some folders in this repository have too many files to be fully listed. Affected folders are marked with a warning icon.",submodules_not_included:"This repository uses git submodules. Submodule contents are not included in the anonymized repository and appear as empty folders."}};var Cl={name:"htmlDoc",props:["content","baseUrl","allowScripts"],setup(e){let t=Ze(null);return It(()=>{let r=[t.value][0];r.classList.add("html-doc");function n(){r.innerHTML="";let i=e.content;if(typeof i!="string")return;let a=document.createElement("iframe");a.className="html-doc-frame",a.setAttribute("title","Rendered HTML document");let u=["allow-popups","allow-popups-to-escape-sandbox"];e.allowScripts&&u.push("allow-scripts","allow-forms","allow-modals"),a.setAttribute("sandbox",u.join(" ")),a.setAttribute("referrerpolicy","no-referrer"),r.appendChild(a);let p="";e.baseUrl&&(p=' '),a.srcdoc=p+i}He(()=>e.content,n,{immediate:!0}),He(()=>e.baseUrl,n,{immediate:!0}),He(()=>e.allowScripts,n,{immediate:!0})}),()=>Ne("html-doc",{ref:t})}};var Nl={name:"pdfviewer",props:["src"],setup(e){let t=Ze(null);return It(()=>{let o=[t.value],r=400,n=[.5,.75,1,1.25,1.5,2,3],i=1e3,a=o[0];a.classList.add("pdf-viewer");let u=null,p=[],h=!1,y=null,g=1,m=1,w=0,k=0,b=612/792,T=0,N=document.createElement("div");N.className="pdf-toolbar";let R=document.createElement("div");R.className="pdf-pages",R.tabIndex=0,R.setAttribute("aria-label","PDF pages"),a.appendChild(N),a.appendChild(R);function I(F,W,fe){let Ce=document.createElement("button");return Ce.type="button",Ce.className="pdf-toolbar-btn",Ce.title=W,Ce.setAttribute("aria-label",W),Ce.innerHTML=' ',Ce.addEventListener("click",fe),Ce}let C=I("fa-chevron-up","Previous page",function(){be(m-1)}),A=I("fa-chevron-down","Next page",function(){be(m+1)}),q=document.createElement("input");q.type="text",q.className="pdf-toolbar-page",q.setAttribute("aria-label","Page number"),q.title="Page number \u2014 type a page and press Enter",q.addEventListener("keydown",function(F){F.key==="Enter"&&(be(parseInt(q.value,10)),q.blur())}),q.addEventListener("blur",function(){q.value=String(m)});let x=document.createElement("span");x.className="pdf-toolbar-count";let X=I("fa-search-minus","Zoom out",function(){qe(n[Math.max(0,re()-1)])}),K=I("fa-search-plus","Zoom in",function(){qe(n[Math.min(n.length-1,re()+1)])}),U=document.createElement("button");U.type="button",U.className="pdf-toolbar-btn pdf-toolbar-zoom",U.title="Reset zoom to fit the width",U.setAttribute("aria-label","Reset zoom to fit the width"),U.addEventListener("click",function(){qe(1)}),N.appendChild(C),N.appendChild(A);let ue=document.createElement("span");ue.className="pdf-toolbar-group",ue.appendChild(q),ue.appendChild(x),N.appendChild(ue);let te=document.createElement("span");te.className="pdf-toolbar-group pdf-toolbar-right",te.appendChild(X),te.appendChild(U),te.appendChild(K),N.appendChild(te);function re(){let F=0;for(let W=0;W=F,U.textContent=Math.round(g*100)+"%",X.disabled=re()===0,K.disabled=re()===n.length-1}function me(F){let W=p[F-1];W&&(R.scrollTop+=W.getBoundingClientRect().top-R.getBoundingClientRect().top)}function be(F){if(!u||isNaN(F))return;let W=Math.min(Math.max(1,F),u.numPages);p[W-1]&&(me(W),m=W,w=W,k=Date.now()+800,pe(),V())}function he(){if(!p.length||Date.now()Ce||S.bottome.src,ne,{immediate:!0}),ss(function(){R.removeEventListener("scroll",H),$(window).off("resize",L),le()})}),()=>Ne("pdfviewer",{ref:t})}};var Dl={props:["content","terms","options"],setup(e){let t=Ze(null),o=()=>{t.value.innerHTML=renderMD(e.content||"",window.location.pathname+"/../")};return It(()=>{o(),He(()=>[e.content,e.terms,e.options],o,{deep:!0})}),()=>Ne("markdown",{ref:t})}},Mq={props:["file","terms","options"],setup(e){let t=Ze(null),o=()=>_t(()=>t.value?.querySelectorAll("pre code").forEach(r=>window.Prism?.highlightElement(r)));return It(o),He(()=>[e.file?.content,e.terms,e.options],o,{deep:!0}),()=>{let r=e.file||{},n=(r.filename||"").split(".").pop().toLowerCase();if(["md","markdown"].includes(n)||r.language==="Markdown")return Ne("gist-file",{ref:t},Ne(Dl,{content:r.content,terms:e.terms,options:e.options}));let i={js:"javascript",jsx:"javascript",ts:"javascript",typescript:"javascript",py:"python",html:"markup",xml:"markup",svg:"markup",sh:"bash"},a=(r.language||n||"none").toLowerCase();return Ne("gist-file",{ref:t},Ne("pre",{class:"line-numbers"},Ne("code",{class:"language-"+(i[a]||a),key:r.content},r.content||"")))}}},$q={props:["file","content"],setup(e){let t=Ze(null),o=0,r,n=async()=>{let i=++o;r?.abort(),r=new AbortController;try{let a=e.content?JSON.parse(e.content):await fetch(e.file?.download_url||e.file,{signal:r.signal}).then(u=>{if(!u.ok)throw Error("Notebook request failed");return u.json()});if(await Wt("notebook"),i!==o)return;t.value.innerHTML=DOMPurify.sanitize(nb.parse(a).render()),t.value.querySelectorAll("pre code").forEach(u=>window.Prism?.highlightElement(u))}catch(a){i===o&&a.name!=="AbortError"&&(t.value.textContent="Unable to render the notebook.")}};return It(()=>{n(),He(()=>[e.file,e.content],n)}),ss(()=>{o++,r?.abort()}),()=>Ne("notebook",{ref:t})}},Lq={props:["stats"],setup(e){return()=>{let t=Object.entries(e.stats||{}).filter(([,r])=>r.code),o=t.reduce((r,[,n])=>r+n.code,0);return Ne("loc",t.map(([r,n])=>Ne("div",{class:"lang",title:`${r}: ${n.code.toLocaleString()} lines`,style:{width:n.code*100/o+"%",background:langColors[r]}})))}}},Rl={Markdown:Dl,GistFile:Mq,Notebook:$q,Loc:Lq,HtmlDoc:Cl,Pdfviewer:xr(async()=>(await Wt("pdf"),pdfjsLib.GlobalWorkerOptions.workerSrc="/script/external/pdf.worker.js",Nl))},Tl={async mounted(e,{value:t}){e._editorValue=t;try{if(await tr(),e._editorDisposed)return;let o=e._editorValue,r=ace.edit(e);e._editor=r,r.setValue(String(o.content??""),-1),Sl(e,o.options),o.options?.onLoad?.(r)}catch(o){e._editorDisposed||(e.textContent=o.message)}},updated(e,{value:t}){e._editorValue=t,e._editor&&(e._editor.getValue()!==String(t.content??"")&&e._editor.setValue(String(t.content??""),-1),Sl(e,t.options))},beforeUnmount(e){e._editorDisposed=!0,e._editor?.destroy()}};function Sl(e,t={}){t.mode&&e._editor.session.setMode("ace/mode/"+t.mode),t.theme&&e._editor.setTheme("ace/theme/"+t.theme),e._editor.setReadOnly(t.readOnly!==!1)}var Ol={mounted(e){if(!window.IntersectionObserver)return;let t=[...e.querySelectorAll('a[href^="#"]')],o=new Set,r=new IntersectionObserver(n=>{n.forEach(a=>a.isIntersecting?o.add(a.target.id):o.delete(a.target.id));let i=t.find(a=>o.has(a.hash.slice(1)));t.forEach(a=>a.classList.toggle("active",a===i))},{rootMargin:"-20% 0px -60% 0px",threshold:0});_t(()=>t.forEach(n=>{let i=document.getElementById(n.hash.slice(1));i&&r.observe(i)})),e._observer=r},beforeUnmount(e){e._observer?.disconnect()}};function Fq(e){let t={children:[]},o=new Map([["",t]]);function r(n){if(o.has(n))return o.get(n);let i=n.lastIndexOf("/"),a=r(i<0?"":n.slice(0,i)),u={name:n.slice(i+1),children:[]};return o.set(n,u),a.children.push(u),u}for(let n of e||[]){let i=n.path?`${n.path}/${n.name}`:n.name;n.size==null?r(i):r(n.path||"").children.push({...n})}return t.children}var Al={name:"FileTree",props:["file","parent","searchQuery","searchResults","page"],setup(e){let t=Ea(),o=Ze(null),r=ze(Object.create(null)),n=null,i=tt(()=>Fq(e.file)),a=()=>"/"+(Array.isArray(t.params.path)?t.params.path.join("/"):t.params.path||"");He(()=>[t.params.repoId,t.params.path],()=>{let m="";a().split("/").filter(Boolean).forEach(w=>{m+="/"+w,r[m]=!0})},{immediate:!0});let u=tt(()=>{if(!e.searchQuery||!e.searchResults)return null;let m=new Set,w=new Set;return e.searchResults.forEach(k=>{m.add(k.path?`${k.path}/${k.name}`:k.name);let b="";(k.path||"").split("/").filter(Boolean).forEach(T=>{b=b?b+"/"+T:T,w.add(b)})}),{files:m,folders:w}}),p=()=>[...o.value.querySelectorAll("li > a")],h=m=>{o.value.querySelector(".tree-focused")?.classList.remove("tree-focused"),m&&(n=m.dataset.path,m.classList.add("tree-focused"),m.scrollIntoView?.({block:"nearest"}),o.value.focus())};function y(m){let w=p(),k=o.value.querySelector(".tree-focused")||w[0],b=w.indexOf(k);if(!["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Enter"].includes(m.key)||(m.preventDefault(),m.key==="ArrowDown"&&h(w[(b+1)%w.length]),m.key==="ArrowUp"&&h(w[(b-1+w.length)%w.length]),!k))return;let T=k.closest("li");m.key==="ArrowRight"&&T.classList.contains("folder")&&(T.classList.contains("open")?h(T.querySelector(":scope > ul > li > a")):k.click()),m.key==="ArrowLeft"&&(T.classList.contains("folder")&&T.classList.contains("open")?k.click():h(T.parentElement.closest("li.folder")?.querySelector(":scope > a"))),m.key==="Enter"&&k.click()}function g(m,w=""){let k=[...m].sort((b,T)=>+!!T.children-+!!b.children||b.name.localeCompare(T.name));return Ne("ul",k.flatMap(b=>{let T=b,N=T.name,R=(w+"/"+N).slice(1);if(u.value&&!u.value.files.has(R)&&!u.value.folders.has(R))return[];for(;T.children?.length===1;)T=T.children[0],N+="/"+T.name;let I=w+"/"+N,C=!!T.children,A=u.value?r[I]!==!1:!!r[I],q=C&&e.page?.options?.truncatedFolders?.includes(I.slice(1)),x=e.page?.fileCounts?.[I.slice(1)]||0,X=Ne("span",{class:C?"tree-icon-folder":"far fa-file tree-icon-file","aria-hidden":"true"},C?Ne("svg",{viewBox:"0 0 14 12",focusable:"false"},Ne("path",{d:"M1.5 10.5v-9h4l1.5 2h5.5v7z"})):null),K=Ne("span",{class:"tree-name"},N),U=te=>{h(te.currentTarget),C&&(te.preventDefault(),r[I]=!A,r[I]&&!T.children.length&&e.page.getFiles(I.slice(1)),_t(()=>h(p().find(re=>re.dataset.path===n))))},ue=Ne("a",{"data-path":I,href:C?void 0:`/r/${encodeURIComponent(t.params.repoId)}${encodePathForUrl(I)}`,class:{"tree-focused":n===I},onClick:U},[C?Ne("span",{class:"tree-toggle"}):w?Ne("span",{class:"tree-spacer"}):null,X,K,q?Ne("span",{class:"truncated-warning",title:e.page.fmt.translate("WARNINGS.folder_truncated")},Ne("i",{class:"fas fa-exclamation-triangle"})):null,C&&x?Ne("span",{class:"tree-count"},x):null]);return Ne("li",{key:I,class:{file:!0,folder:C,open:A,active:a()===I,truncated:q},title:C?"":`Size: ${humanFileSize(T.size||0)}`},[ue,C&&A?g(T.children,I):null])}))}return()=>Ne("tree",{ref:o,tabindex:0,onKeydown:y},e.file?.length?u.value?.files.size===0?Ne("div",{class:"tree-search-empty"},"No files found"):g(i.value):"Empty repository")}};function Vl(){return ze({errors:{},dirty:!1,touched:!1,submitted:!1,get invalid(){return Object.values(this.errors).some(Boolean)},setValidity(e,t){this.errors[e]=!t},setDirty(){this.dirty=!0}})}function Pl(e,t){if(!e[t]){let o=Vl();Object.defineProperty(o,"invalid",{get(){return Object.values(o.errors).some(Boolean)||Object.keys(o).some(r=>r!=="invalid"&&o[r]&&typeof o[r]=="object"&&o[r].invalid)}}),e[t]=o}return e[t]}var ql={beforeMount(e,{value:t}){e._formState=Pl(t,e.name),e.addEventListener("submit",o=>o.preventDefault())}};function Uq(e){return e instanceof Date?isNaN(e)?"":`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`:e||""}function Il(e){let t=e._field.binding.value;e.type==="checkbox"?e.checked=!!t:e.type==="radio"?e.checked=t==(e._value??e.value):e._field.timer||(e.value=e.type==="date"?Uq(t):t??""),Xo(e)}function Xo(e){let t=e._field.validation;if(!t)return;let o=e.validity;for(let[r,n]of Object.entries({required:o.valueMissing,pattern:o.patternMismatch,min:o.rangeUnderflow,max:o.rangeOverflow,date:o.badInput,email:o.typeMismatch&&e.type==="email",url:o.typeMismatch&&e.type==="url"}))t.setValidity(r,e.disabled||!n)}var Ml={beforeMount(e,{value:t}){var a;let o=t.form&&Pl(t.state,t.form),r=o&&e.name?o[a=e.name]||(o[a]=Vl()):null;e._field={binding:t,validation:r,timer:null};let n=()=>{clearTimeout(e._field.timer),e._field.timer=null;let u=e._field.binding,p=e.type==="checkbox"?e.checked:e._value??e.value;if(!(e.type==="radio"&&!e.checked)){if((e.type==="number"||e.type==="range")&&(p=e.value===""?null:Number(e.value)),e.type==="date"&&(p=e.value?new Date(e.value+"T00:00:00"):null),r){r.setDirty();for(let h of Object.keys(r.errors))r.setValidity(h,!0)}u.set(p),Xo(e),u.change?.()}};e._field.commit=n;let i=u=>{let p=e._field.binding.options?.debounce||0,h=typeof p=="number"?p:p[u.type]??p.default??0;clearTimeout(e._field.timer),h?e._field.timer=setTimeout(n,h):n()};e.addEventListener(e.tagName==="SELECT"||["checkbox","radio","date"].includes(e.type)?"change":"input",i),e.addEventListener("blur",()=>{r&&(r.touched=!0),e._field.timer&&n()})},mounted:Il,updated(e,{value:t}){e._field.binding=t,Il(e)},beforeUnmount(e){clearTimeout(e._field.timer);let{binding:t,validation:o}=e._field,r=t.form&&t.state[t.form];r&&r[e.name]===o&&delete r[e.name]}};function $l(e,t){let o=e.target.closest("form");if(o?._formState&&(o._formState.submitted=!0),o?.querySelectorAll("input, select, textarea").forEach(r=>{r._field?.timer&&r._field.commit(),r._field&&Xo(r)}),o&&!o.checkValidity()){let r=o.querySelector(":invalid");r?.scrollIntoView?.({block:"center"}),r?.focus(),o.reportValidity();return}return t()}var Ll=Symbol("session"),zq=/^\/(w|api|github)(\/|$)/;function Fl(e,t={}){let o=e?.split(".").reduce((r,n)=>r?.[n],El);return String(o??e??"").replace(/{{\s*([^}]+?)\s*}}/g,(r,n)=>t[n]??"")}var Hq={...Qo,translate:Fl};function Bq(e){if(e==null||e==="")return e;try{let t=new URL(e,window.location.href);return["http:","https:","mailto:","tel:","blob:"].includes(t.protocol)?e:void 0}catch{return}}function Gq(e="#app",t={}){let o=new Map,r=wl(t.fetch),n,i;function a(){let h=mt(),y={url(m){return m===void 0?i.currentRoute.value.fullPath:(i.push(m),y)},path(){return i.currentRoute.value.path},search(){return i.currentRoute.value.query}},g=new Proxy({},{get(m,w){let k=i.currentRoute.value.params[w];return Array.isArray(k)?k.join("/"):k}});return{http:r,...h,location:y,params:g,window,promises:kl,html:{trustAsHtml:m=>m},translate:(m,w)=>Promise.resolve(Fl(m,w)),quotaService:_l(r)}}function u(h){return{name:h.template,setup(){let y=Ko(lt(Ll),o);h.setup(y,a()),bl(h.template,y);let g=[];return()=>Jn[h.template](y,g)}}}i=_a({history:t.history||ba(),routes:Ga.map(h=>h.redirect?h:{path:h.path,component:u(h),meta:{title:h.title,preserveExplorer:h.preserveExplorer}})});let p=Ui({setup(){n=Ko(null,o),n.fmt=Hq,n.window=window,n.Math=Math,n.sanitize=m=>DOMPurify.sanitize(m??""),n.submitForm=$l,n.safeUrl=Bq;let h=a();Na(n,h.http,h.location,h.timeout),ts(Ll,n),He(()=>n.title,m=>{document.title=m||"Anonymous GitHub"});let y=[],g=m=>{let w=m.target.closest?.("a[href]");if(m.defaultPrevented||m.button!==0||m.metaKey||m.ctrlKey||m.shiftKey||m.altKey||!w||w.hasAttribute("download")||w.target&&w.target!=="_self"||w.dataset.toggle||w.getAttribute("href").startsWith("#"))return;let b=new URL(w.href,window.location.href);b.origin!==window.location.origin||zq.test(b.pathname)||(m.preventDefault(),i.push(b.pathname+b.search+b.hash),$("#navbarSupportedContent.show").collapse("hide"))};return document.addEventListener("click",g),ss(()=>document.removeEventListener("click",g)),()=>[Ne("header",{class:"app-header"},Jn["partials/header.htm"](n,y)),Ne("main",{class:"app-view align-items-stretch w-100"},Ne(Wo,null,{default:({Component:m,route:w})=>m?Ne(m,{key:w.meta.preserveExplorer?w.matched[0]?.path:w.path}):null})),Ne("div",{class:"position-fixed p-3",style:{zIndex:999999999,right:0,bottom:0}},n.toasts.map((m,w)=>Ne("div",{class:"toast show",role:"alert","aria-live":"assertive","aria-atomic":"true",key:w},[Ne("div",{class:"toast-header"},[Ne("strong",{class:"mr-auto"},m.title),Ne("button",{type:"button",class:"ml-2 mb-1 close","aria-label":"Close",onClick:()=>n.removeToast(m)},"\xD7")]),Ne("div",{class:"toast-body"},m.body)])))]}});for(let[h,y]of Object.entries(Rl))p.component(h,y);return p.component("Tree",Al),p.component("PartialView",{props:["name","state"],setup(h){let y=[];return()=>Jn[h.name](h.state,y)}}),p.directive("field",Ml),p.directive("form",ql),p.directive("code-editor",Tl),p.directive("paper-scrollspy",Ol),p.use(i),i.beforeEach(async h=>{n?.emit("routeLeave"),/^\/(r|repository|anonymize|pull-request-anonymize|gist-anonymize|pr|gist)(\/|$)/.test(h.path)&&await Wt("markdown"),/^\/(r|repository)\//.test(h.path)&&await Wt("org")}),i.afterEach(h=>{if(!n)return;n.title=h.meta.title,n.emit("routeChange",{title:h.meta.title}),n.emit("routeUpdate",{title:h.meta.title});let y=document.querySelector(".app-view");!h.meta.preserveExplorer&&y&&(y.scrollTop=0)}),p.mount(e),{app:p,router:i,state:n}}document.querySelector("#app")&&(window.anonymousApp=Gq());})();
+jane.smith@example.org`},MP={id:"termsHelp",class:"form-text text-muted"},$P={class:"invalid-feedback"},LP={id:"settings-display",class:"paper-settings-section"},UP={class:"form-group paper-check-list"},FP={class:"form-check"},zP={class:"form-check-input",type:"checkbox",id:"link",name:"link"},HP={class:"form-check"},BP={class:"form-check-input",type:"checkbox",id:"image",name:"image"},GP={class:"form-check"},jP={class:"form-check-input",type:"checkbox",id:"pdf",name:"pdf"},WP={class:"form-check"},KP={class:"form-check-input",type:"checkbox",id:"notebook",name:"notebook"},YP={id:"settings-features",class:"paper-settings-section"},JP={class:"form-group paper-check-list"},QP={class:"form-check"},XP={class:"form-check-input",type:"checkbox",id:"page",name:"page"},ZP={class:"form-check"},xP={class:"form-check-input",type:"checkbox",id:"loc",name:"loc"},e3={class:"form-check"},t3={class:"form-check-input",type:"checkbox",id:"update",name:"update"},s3={class:"form-group"},n3={class:"form-control",id:"mode",name:"mode"},o3={class:"form-text text-muted"},r3={id:"settings-expiration",class:"paper-settings-section"},i3={class:"form-group"},a3={class:"form-control",id:"expiration",name:"expiration"},l3={class:"paper-settings-footer"},d3={key:0,class:"paper-inline-alert paper-inline-alert-error",role:"alert"},u3=["textContent"],c3={class:"paper-settings-actions"},p3=["disabled"],f3={key:0,class:"fas fa-check mr-1","aria-hidden":"true"},m3={id:"settings-account",class:"paper-settings-section"},h3={class:"paper-account-card"},v3=["src"],y3={class:"paper-account-identity"},g3={class:"paper-account-name"},b3={class:"paper-danger-zone"},w3={key:0,class:"paper-inline-alert paper-inline-alert-error",role:"alert"},k3=["textContent"],_3=["disabled"];function yl(e,t){let o=be("paper-scrollspy"),r=be("field"),n=be("form");return l(),d("div",hP,[t[48]||(t[48]=ge('Your settings Defaults applied to every new anonymization, your quotas, and your account.
',3)),s("div",vP,[E((l(),d("aside",yP,[...t[2]||(t[2]=[ge('On this page
Quota Terms to redact Display Features Expiration Account ',2)])])),[[o]]),s("div",gP,[m(" Quota: same markup as the dashboard, driven by quotaService "),s("section",bP,[t[4]||(t[4]=s("div",{class:"paper-section-eyebrow"},"Quota",-1)),t[5]||(t[5]=s("p",{class:"paper-section-copy"},"What you are using across all your anonymizations. Conferences can lift these limits during a review window.",-1)),e.quota?(l(),d("div",wP,[(l(),d(Y,null,ne([{key:"repository",label:"Repositories",kind:"count"},{key:"storage",label:"Storage",kind:"bytes"},{key:"file",label:"Files",kind:"count"}],(i,a)=>s("div",kP,[s("div",_P,[s("span",EP,c(i?.label),1),i?.kind==="count"?(l(),d("span",CP,[y(c(e.fmt?.number(e.quota[i?.key].used)),1),e.quota[i?.key].unlimited?m("v-if",!0):(l(),d("span",NP," / "+c(e.fmt?.number(e.quota[i?.key].total)),1)),e.quota[i?.key].unlimited?(l(),d("span",SP,"Unlimited")):m("v-if",!0)])):m("v-if",!0),i?.kind==="bytes"?(l(),d("span",DP,[y(c(e.fmt?.humanFileSize(e.quota[i?.key].used)),1),e.quota[i?.key].unlimited?m("v-if",!0):(l(),d("span",RP," / "+c(e.fmt?.humanFileSize(e.quota[i?.key].total)),1)),e.quota[i?.key].unlimited?(l(),d("span",TP,"Unlimited")):m("v-if",!0)])):m("v-if",!0)]),s("div",{class:O(["quota-track","quota-"+e.quota[i?.key].level]),role:"progressbar","aria-valuemin":"0","aria-label":i?.label+" quota","aria-valuenow":e.quota[i?.key].used,"aria-valuemax":e.quota[i?.key].unlimited?e.quota[i?.key].used:e.quota[i?.key].total,"aria-valuetext":e.quota[i?.key].unlimited?"unlimited":e.fmt.number(e.quota[i?.key].percent,0)+"% used"},[e.quota[i?.key].unlimited?m("v-if",!0):(l(),d("div",{key:0,class:"quota-fill",style:Pe({width:e.quota[i?.key].percent+"%"})},null,4))],10,OP)])),64))])):m("v-if",!0),e.quota?m("v-if",!0):(l(),d("div",AP,[(l(),d(Y,null,ne([1,2,3],(i,a)=>s("div",IP,[...t[3]||(t[3]=[s("div",{class:"quota-header"},[s("span",{class:"skeleton skeleton-line",style:{width:"40%"}}),s("span",{class:"skeleton skeleton-line",style:{width:"25%"}})],-1),s("div",{class:"quota-track"},null,-1)])])),64))]))]),E((l(),d("form",{class:"form needs-validation",name:"defaultsForm",novalidate:"",onSubmit:t[0]||(t[0]=we(i=>e.submitForm(i,()=>{e.saveDefault(i)}),["prevent"]))},[s("section",VP,[t[11]||(t[11]=s("div",{class:"paper-section-eyebrow"},"Terms to redact",-1)),t[12]||(t[12]=s("p",{class:"paper-section-copy"},"Pre-filled in every new anonymization. You can still edit the list per anonymization.",-1)),s("div",PP,[t[10]||(t[10]=s("label",{class:"paper-field-label",for:"terms"},"Default terms",-1)),E(s("textarea",qP,null,512),[[r,{state:e.viewState,set:i=>{e.terms=i},value:e.terms,form:"defaultsForm",options:{debounce:250}}]]),s("small",MP,[t[6]||(t[6]=y(" One term per line (regex allowed). Each match is replaced by ",-1)),s("code",null,c(e.site_options?.ANONYMIZATION_MASK||"XXX")+"-[N]",1),t[7]||(t[7]=y(", or use ",-1)),t[8]||(t[8]=s("code",null,"term=>replacement",-1)),t[9]||(t[9]=y(" to pick your own. ",-1))]),E(s("div",$P," Terms are in an invalid format ",512),[[B,e.defaultsForm?.terms?.errors?.format]])])]),s("section",LP,[t[21]||(t[21]=s("div",{class:"paper-section-eyebrow"},"Display",-1)),t[22]||(t[22]=s("p",{class:"paper-section-copy"},"What reviewers can see inside an anonymized repository.",-1)),s("div",UP,[s("div",FP,[E(s("input",zP,null,512),[[r,{state:e.viewState,set:i=>{e.options.link=i},value:e.options?.link,form:"defaultsForm",options:{}}]]),t[13]||(t[13]=s("label",{class:"form-check-label",for:"link"},"Keep links",-1)),t[14]||(t[14]=s("small",{class:"form-text text-muted"},"Unchecked, every link in text files is removed.",-1))]),s("div",HP,[E(s("input",BP,null,512),[[r,{state:e.viewState,set:i=>{e.options.image=i},value:e.options?.image,form:"defaultsForm",options:{}}]]),t[15]||(t[15]=s("label",{class:"form-check-label",for:"image"},"Display images",-1)),t[16]||(t[16]=s("small",{class:"form-text text-muted"},"Images are shown as is. They are not anonymized.",-1))]),s("div",GP,[E(s("input",jP,null,512),[[r,{state:e.viewState,set:i=>{e.options.pdf=i},value:e.options?.pdf,form:"defaultsForm",options:{}}]]),t[17]||(t[17]=s("label",{class:"form-check-label",for:"pdf"},"Display PDFs",-1)),t[18]||(t[18]=s("small",{class:"form-text text-muted"},"PDFs are shown as is. They are not anonymized.",-1))]),s("div",WP,[E(s("input",KP,null,512),[[r,{state:e.viewState,set:i=>{e.options.notebook=i},value:e.options?.notebook,form:"defaultsForm",options:{}}]]),t[19]||(t[19]=s("label",{class:"form-check-label",for:"notebook"},"Display notebooks",-1)),t[20]||(t[20]=s("small",{class:"form-text text-muted"},"Render Jupyter notebooks instead of raw JSON.",-1))])])]),s("section",YP,[t[31]||(t[31]=s("div",{class:"paper-section-eyebrow"},"Features",-1)),s("div",JP,[s("div",QP,[E(s("input",XP,null,512),[[r,{state:e.viewState,set:i=>{e.options.page=i},value:e.options?.page,form:"defaultsForm",options:{}}]]),t[23]||(t[23]=s("label",{class:"form-check-label",for:"page"},"GitHub Pages",-1)),t[24]||(t[24]=s("small",{class:"form-text text-muted"},"Serve an anonymized copy of the repository's GitHub Pages site. Only pages built from the anonymized branch are supported.",-1))]),s("div",ZP,[E(s("input",xP,null,512),[[r,{state:e.viewState,set:i=>{e.options.loc=i},value:e.options?.loc,form:"defaultsForm",options:{}}]]),t[25]||(t[25]=s("label",{class:"form-check-label",for:"loc"},"Lines of code",-1)),t[26]||(t[26]=s("small",{class:"form-text text-muted"},"Show the line count of the repository in the explorer.",-1))]),s("div",e3,[E(s("input",t3,null,512),[[r,{state:e.viewState,set:i=>{e.options.update=i},value:e.options?.update,form:"defaultsForm",options:{}}]]),t[27]||(t[27]=s("label",{class:"form-check-label",for:"update"},"Auto-update from GitHub",-1)),t[28]||(t[28]=s("small",{class:"form-text text-muted"},"Follow the branch and pull the latest commit automatically, at most once an hour.",-1))])]),s("div",s3,[t[30]||(t[30]=s("label",{class:"paper-field-label",for:"mode"},"Fetch mode",-1)),E((l(),d("select",n3,[...t[29]||(t[29]=[s("option",{value:"GitHubStream",selected:""},"Stream from GitHub on demand",-1),s("option",{value:"GitHubDownload"},"Download to Anonymous GitHub",-1)])])),[[r,{state:e.viewState,set:i=>{e.options.mode=i},value:e.options?.mode,form:"defaultsForm",options:{}}]]),s("small",o3," Download is faster for reviewers and enables every feature. Streaming is the only option above "+c(e.fmt?.humanFileSize(e.site_options?.MAX_REPO_SIZE*1024))+". ",1)])]),s("section",r3,[t[35]||(t[35]=s("div",{class:"paper-section-eyebrow"},"Expiration",-1)),s("div",i3,[t[33]||(t[33]=s("label",{class:"paper-field-label",for:"expiration"},"When an anonymization expires",-1)),E((l(),d("select",a3,[...t[32]||(t[32]=[s("option",{value:"redirect"},"Redirect visitors to the GitHub repository",-1),s("option",{value:"remove",selected:""},"Remove the anonymized repository",-1)])])),[[r,{state:e.viewState,set:i=>{e.options.expirationMode=i},value:e.options?.expirationMode,form:"defaultsForm",options:{}}]]),t[34]||(t[34]=s("small",{class:"form-text text-muted"}," The expiration date itself is chosen per anonymization. Redirecting reveals the source repository, so pick it only once the review is over. ",-1))])]),s("div",l3,[e.error?(l(),d("div",d3,[t[36]||(t[36]=s("i",{class:"fas fa-exclamation-circle","aria-hidden":"true"},null,-1)),t[37]||(t[37]=y()),s("span",{textContent:c(e.error)},null,8,u3)])):m("v-if",!0),s("div",c3,[s("button",{id:"save",type:"submit",class:"btn btn-ink",disabled:e.saving},[e.message?(l(),d("i",f3)):m("v-if",!0),y(c(e.saving?"Saving\u2026":e.message?"Saved":"Save defaults"),1)],8,p3),t[38]||(t[38]=s("span",{class:"paper-settings-hint"},"Applies to new anonymizations only. Existing ones keep their settings.",-1))])])],32)),[[n,e.viewState]]),s("section",m3,[t[46]||(t[46]=s("div",{class:"paper-section-eyebrow"},"Account",-1)),s("div",h3,[e.user?.photo?(l(),d("img",{key:0,width:"40",height:"40",class:"rounded-circle",alt:"",src:e.safeUrl(e.user?.photo)},null,8,v3)):m("v-if",!0),s("div",y3,[s("div",g3,"@"+c(e.user?.username),1),t[39]||(t[39]=s("div",{class:"paper-account-meta"},"Signed in with GitHub. Anonymous GitHub only reads your repositories.",-1))]),t[40]||(t[40]=s("a",{class:"btn btn-outline-ink",href:"/api/user/logout",target:"_self"},"Sign out",-1))]),t[47]||(t[47]=s("a",{class:"paper-settings-connection-link",href:"/connections"},[s("i",{class:"fab fa-github","aria-hidden":"true"}),s("span",null,[y("GitHub connections"),s("small",null,"Manage repository access and legacy OAuth.")]),s("span",{"aria-hidden":"true"},"\u2192")],-1)),s("div",b3,[t[44]||(t[44]=s("div",{class:"paper-danger-head"},"Delete account",-1)),t[45]||(t[45]=s("p",{class:"paper-danger-copy"}," Removes all your anonymized repositories, gists, and pull requests, revokes the application's access to your GitHub account, and erases your personal data (username, email, access tokens). This cannot be undone. ",-1)),e.deleteError?(l(),d("div",w3,[t[41]||(t[41]=s("i",{class:"fas fa-exclamation-circle","aria-hidden":"true"},null,-1)),t[42]||(t[42]=y()),s("span",{textContent:c(e.deleteError)},null,8,k3)])):m("v-if",!0),s("button",{type:"button",class:"btn btn-outline-danger",onClick:t[1]||(t[1]=i=>e.deleteAccount()),disabled:e.deletingAccount},[t[43]||(t[43]=s("i",{class:"fas fa-trash-alt mr-1","aria-hidden":"true"},null,-1)),y(c(e.deletingAccount?"Deleting\u2026":"Delete my account"),1)],8,_3)])])])])])}var E3={class:"pr-page"},C3={class:"container paper-page pr-page-inner"},N3={class:"pr-header"},S3={class:"paper-page-title pr-title"},D3=["textContent"],R3={key:1,class:"text-muted"},T3=["href"],O3={class:"pr-header-meta"},A3={key:0,class:"pr-meta-item"},I3=["textContent"],V3={key:1,class:"pr-meta-item"},P3=["textContent"],q3={key:2,class:"pr-meta-item"},M3=["textContent"],$3={key:0,class:"pr-body-card"},L3={key:1,class:"paper-tabs",role:"tablist"},U3=["textContent"],F3={class:"paper-tab-content"},z3={key:0},H3=["innerHTML"],B3={key:1},G3={class:"pr-comments"},j3={class:"pr-comment"},W3={class:"pr-comment-head"},K3={key:0,class:"pr-comment-author"},Y3=["textContent"],J3=["textContent"],Q3={key:0,class:"pr-comment-body"},X3={key:0,class:"paper-table-empty"};function gl(e,t){let o=et("markdown");return l(),d("div",E3,[s("div",C3,[t[15]||(t[15]=s("div",{class:"paper-crumbs"},[s("a",{href:"/dashboard"},"Reviewer"),y(" \xA0/\xA0 "),s("span",{class:"here"},"Pull request")],-1)),s("header",N3,[s("h1",S3,[e.details?.title?(l(),d("span",{key:0,textContent:c(e.details?.title)},null,8,D3)):m("v-if",!0),e.details?.title?m("v-if",!0):(l(),d("span",R3,"Untitled pull request")),e.options?.isAdmin||e.options?.isOwner?(l(),d("a",{key:2,class:"btn btn-sm","aria-label":"Edit",href:e.safeUrl("/pull-request-anonymize/"+e.pullRequestId)},[...t[2]||(t[2]=[s("i",{class:"far fa-edit"},null,-1),s("span",{class:"d-none d-md-inline"}," Edit",-1)])],8,T3)):m("v-if",!0)]),s("div",O3,[s("span",{class:O(["paper-pill",{good:e.details?.merged,warn:e.details?.state=="open",bad:e.details?.state=="closed"&&!e.details?.merged}])},[s("span",{class:O(["status-dot",{"status-ready":e.details?.merged,"status-error":e.details?.state=="closed"&&!e.details?.merged}])},null,2),y(" "+c(e.details?.merged?"Merged":e.fmt.title(e.details?.state)),1)],2),e.details?.baseRepositoryFullName?(l(),d("span",A3,[t[3]||(t[3]=s("i",{class:"fab fa-github"},null,-1)),t[4]||(t[4]=y()),s("span",{textContent:c(e.details?.baseRepositoryFullName)},null,8,I3)])):m("v-if",!0),e.details?.updatedDate?(l(),d("span",V3,[t[5]||(t[5]=s("i",{class:"far fa-clock"},null,-1)),t[6]||(t[6]=y()),s("span",{textContent:c(e.fmt?.date(e.details?.updatedDate))},null,8,P3)])):m("v-if",!0),e.details?.anonymizeDate?(l(),d("span",q3,[t[7]||(t[7]=s("i",{class:"fas fa-user-secret"},null,-1)),t[8]||(t[8]=y(" Anonymized ",-1)),s("span",{textContent:c(e.fmt?.date(e.details?.anonymizeDate))},null,8,M3)])):m("v-if",!0)])]),e.details?.body?(l(),d("section",$3,[t[9]||(t[9]=s("div",{class:"paper-section-eyebrow"},"Description",-1)),De(o,{content:e.details?.body},null,8,["content"])])):m("v-if",!0),e.details?.diff||e.details?.comments?(l(),d("nav",L3,[e.details?.diff?(l(),d("button",{key:0,class:O(["paper-tab",{active:e.tabState?.active=="diff"}]),type:"button",role:"tab",onClick:t[0]||(t[0]=r=>e.tabState.active="diff")},[...t[10]||(t[10]=[s("i",{class:"fas fa-code"},null,-1),y(" Diff ",-1)])],2)):m("v-if",!0),e.details?.comments?(l(),d("button",{key:1,class:O(["paper-tab",{active:e.tabState?.active=="comments"}]),type:"button",role:"tab",onClick:t[1]||(t[1]=r=>e.tabState.active="comments")},[t[11]||(t[11]=s("i",{class:"far fa-comment-dots"},null,-1)),s("span",{textContent:c(e.fmt.plural(e.details?.comments?.length,{0:"No comments",one:"1 comment",other:"{} comments"}))},null,8,U3)],2)):m("v-if",!0)])):m("v-if",!0),s("div",F3,[e.details?.diff&&e.tabState?.active=="diff"?(l(),d("div",z3,[s("div",{class:"pr-diff",innerHTML:e.sanitize(e.fmt?.diff(e.details?.diff))},null,8,H3)])):m("v-if",!0),e.details?.comments&&e.tabState?.active=="comments"?(l(),d("div",B3,[s("ul",G3,[(l(!0),d(Y,null,ne(e.details?.comments,(r,n)=>(l(),d("li",j3,[s("div",W3,[r?.author?(l(),d("span",K3,[t[12]||(t[12]=s("i",{class:"far fa-user"},null,-1)),t[13]||(t[13]=y(" @",-1)),s("span",{textContent:c(r?.author)},null,8,Y3)])):m("v-if",!0),r?.updatedDate?(l(),d("span",{key:1,class:"pr-comment-date",textContent:c(e.fmt?.date(r?.updatedDate))},null,8,J3)):m("v-if",!0)]),r?.body?(l(),d("div",Q3,[De(o,{content:r?.body},null,8,["content"])])):m("v-if",!0)]))),256)),e.details?.comments?.length?m("v-if",!0):(l(),d("li",X3,[...t[14]||(t[14]=[s("i",{class:"far fa-comment-dots"},null,-1),s("span",null,"No comments on this pull request.",-1)])]))])])):m("v-if",!0)])])])}var Z3={class:"container paper-page"},x3={class:"d-flex align-items-end justify-content-between flex-wrap",style:{gap:"12px"}},eq={class:"paper-page-title"},tq=["textContent"],sq={key:1},nq={class:"paper-settings-section"},oq={key:0,class:"paper-ratelimit-card",role:"status"},rq={class:"paper-error-msg"},iq=["aria-valuenow"],aq={class:"paper-progress-label"},lq=["textContent"],dq={key:0},uq=["textContent"],cq={class:"paper-progress-pct"},pq={key:2,class:"paper-error-card",role:"alert"},fq={key:0,class:"paper-error-msg"},mq={key:1,class:"paper-error-msg"},hq={class:"paper-error-actions"},vq=["href"],yq={class:"paper-detail-grid"},gq={class:"detail-value"},bq=["href"],wq={key:0,class:"detail-label"},kq={key:1,class:"detail-value"},_q=["href"],Eq={key:3,class:"anonymize-submit-bar"},Cq=["href"],Nq=["href"];function bl(e,t){return l(),d("div",Z3,[t[16]||(t[16]=s("div",{class:"paper-crumbs"},[y("Anonymization \xA0/\xA0 "),s("span",{class:"here"},"Status")],-1)),s("div",x3,[s("div",null,[s("h1",eq,[t[0]||(t[0]=y("Status of ",-1)),s("em",null,c(e.repoId),1)]),t[1]||(t[1]=s("p",{class:"paper-page-lede"},"Track progress as your anonymization is prepared.",-1))]),s("span",{class:O(["status-pill",{"status-pill-ready":e.repo?.status=="ready","status-pill-error":e.repo?.status=="error","status-pill-removed":e.repo?.status=="removed"||e.repo?.status=="expired","status-pill-ratelimit":e.rateLimitResetAt}])},[s("span",{class:O(["status-dot",{"status-ready":e.repo?.status=="ready","status-error":e.repo?.status=="error","status-removed":e.repo?.status=="removed"||e.repo?.status=="expired","status-ratelimit":e.rateLimitResetAt}])},null,2),e.rateLimitResetAt?m("v-if",!0):(l(),d("span",{key:0,textContent:c(e.fmt?.title(e.repo?.status))},null,8,tq)),e.rateLimitResetAt?(l(),d("span",sq,"Rate limited")):m("v-if",!0)],2)]),s("section",nq,[t[14]||(t[14]=s("div",{class:"paper-section-eyebrow"},"Progress",-1)),t[15]||(t[15]=s("p",{class:"paper-section-copy"},[y(" The repository will take a few minutes to get ready, depending on its size. Visit the "),s("a",{href:"/faq"},"FAQ"),y(" for more information. ")],-1)),e.rateLimitResetAt?(l(),d("div",oq,[t[4]||(t[4]=s("div",{class:"paper-ratelimit-head"},[s("i",{class:"fas fa-hourglass-half"}),s("div",null,[s("div",{class:"paper-error-eyebrow"},"Temporarily paused"),s("div",{class:"paper-error-title"},"GitHub API rate limit reached")])],-1)),s("p",rq,[t[2]||(t[2]=y("Anonymization will resume automatically in ",-1)),s("strong",null,c(e.rateLimitCountdown),1),t[3]||(t[3]=y(". No action needed \u2014 the job is queued and will continue where it left off.",-1))])])):m("v-if",!0),e.repo?.status!="error"&&!e.rateLimitResetAt?(l(),d("div",{key:1,class:O(["paper-progress",{"paper-progress-ready":e.repo?.status=="ready"}]),role:"progressbar","aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":e.progress},[s("div",{class:"paper-progress-bar",style:Pe("width: "+e.progress+"%;")},null,4),s("div",aq,[s("span",{textContent:c(e.fmt?.title(e.repo?.status))},null,8,lq),e.repo?.statusMessage?(l(),d("span",dq,[t[5]||(t[5]=y("\xA0\xB7\xA0",-1)),s("span",{textContent:c(e.repo?.statusMessage)},null,8,uq)])):m("v-if",!0),s("span",cq,c(e.progress||0)+"%",1)])],10,iq)):m("v-if",!0),e.repo?.status=="error"&&!e.rateLimitResetAt?(l(),d("div",pq,[t[9]||(t[9]=s("div",{class:"paper-error-head"},[s("i",{class:"fas fa-exclamation-triangle"}),s("div",null,[s("div",{class:"paper-error-eyebrow"},"Anonymization failed"),s("div",{class:"paper-error-title"},"Something went wrong while preparing this repository.")])],-1)),e.repo?.statusMessage?(l(),d("p",fq,c(e.fmt?.translate("ERRORS."+e.repo?.statusMessage)),1)):m("v-if",!0),e.repo?.statusMessage?m("v-if",!0):(l(),d("p",mq,"No additional details were reported. The most common causes are private repositories, missing branches, and rate limits.")),t[10]||(t[10]=s("ul",{class:"paper-error-hints"},[s("li",null,"Make sure the source URL points to a repository or pull request you can access."),s("li",null,"Check that the chosen branch and commit still exist on GitHub."),s("li",null,"If you just signed in, the access token may need a moment to propagate \u2014 try again.")],-1)),s("div",hq,[s("a",{class:"btn btn-ink",href:e.safeUrl("/anonymize/"+e.repoId)},[...t[6]||(t[6]=[s("i",{class:"far fa-edit mr-1"},null,-1),y(" Edit anonymization",-1)])],8,vq),t[7]||(t[7]=s("a",{class:"btn",href:"/faq"},[s("i",{class:"far fa-question-circle mr-1"}),y(" Read the FAQ")],-1)),t[8]||(t[8]=s("a",{class:"btn",href:"https://github.com/tdurieux/anonymous_github/issues/new?template=issue_report.yml",target:"_blank",rel:"noopener"},[s("i",{class:"fas fa-bug mr-1","aria-hidden":"true"}),y(" Report a bug in Anonymous GitHub")],-1))])])):m("v-if",!0),s("div",yq,[t[11]||(t[11]=s("div",{class:"detail-label"},"Repository",-1)),s("div",gq,[s("a",{target:"_self",href:e.safeUrl("/r/"+e.repoId+"/")},"/r/"+c(e.repoId)+"/",9,bq)]),e.repo?.options?.page?(l(),d("div",wq,"GitHub Page")):m("v-if",!0),e.repo?.options?.page?(l(),d("div",kq,[s("a",{target:"_self",href:e.safeUrl("/w/"+e.repoId+"/")},"/w/"+c(e.repoId)+"/",9,_q)])):m("v-if",!0)]),e.repo?.status=="ready"?(l(),d("div",Eq,[s("a",{class:"btn btn-ink",target:"_self",href:e.safeUrl("/r/"+e.repoId+"/")},[...t[12]||(t[12]=[s("i",{class:"far fa-eye mr-1"},null,-1),y(" Go to anonymized repository ",-1)])],8,Cq),e.repo?.options?.page?(l(),d("a",{key:0,class:"btn",target:"_self",href:e.safeUrl("/w/"+e.repoId+"/")},[...t[13]||(t[13]=[s("i",{class:"fas fa-globe mr-1"},null,-1),y(" Go to anonymized GitHub page ",-1)])],8,Nq)):m("v-if",!0)])):m("v-if",!0)]),t[17]||(t[17]=ge('Support Anonymous GitHub
A small team keeps this running. If it helps you, please consider contributing back \u2014 in code, ideas, or coffee.
',1))])}var Jn={"partials/connections.htm":Wa,"partials/signin.htm":Ka,"partials/404.htm":Ya,"partials/admin/conferences.htm":Ja,"partials/admin/errors.htm":Qa,"partials/admin/overview.htm":Xa,"partials/admin/queues.htm":Za,"partials/admin/repositories.htm":xa,"partials/admin/user.htm":el,"partials/admin/users.htm":tl,"partials/anonymize.htm":sl,"partials/anonymizePullRequest.htm":nl,"partials/claim.htm":ol,"partials/conference.htm":rl,"partials/conferences.htm":il,"partials/dashboard.htm":al,"partials/explorer.htm":ll,"partials/faq.htm":dl,"partials/gist.htm":ul,"partials/header.htm":cl,"partials/home.htm":pl,"partials/loading.htm":fl,"partials/newConference.htm":ml,"partials/pageView.htm":hl,"partials/pr-dashboard.htm":vl,"partials/profile.htm":yl,"partials/pullRequest.htm":gl,"partials/status.htm":bl};function wl(e,t){e==="partials/explorer.htm"&&(t.sidebarCollapsed=window.matchMedia?.("(max-width: 767px)").matches||!1),e==="partials/anonymize.htm"&&(t.prTabState={active:t.options.diff?"diff":"comments"}),e==="partials/gist.htm"&&(t.tabState={active:t.details?.files?"files":"comments"}),["partials/conferences.htm","partials/conference.htm"].includes(e)&&(t.statusLabels={ready:"Ready",expired:"Expired",removed:"Removed"});let r={"partials/dashboard.htm":["filteredItems",()=>t.items,"itemFilter"],"partials/conferences.htm":["filteredConferences",()=>t.conferences,"conferenceFilter"],"partials/conference.htm":["filteredRepositories",()=>t.conference?.repositories,"repoFiler"],"partials/admin/repositories.htm":["filteredRepositories",()=>t.repositories,"repoFiler"],"partials/admin/user.htm":["filteredRepositories",()=>t.repositories,"repoFiler"],"partials/admin/users.htm":["filteredUsers",()=>t.users,"userFiler"],"partials/admin/conferences.htm":["filteredConferences",()=>t.conferences]}[e];if(r){let n=tt(()=>t.fmt.orderBy(t.fmt.filter(r[1](),t[r[2]]),t.orderBy));Object.defineProperty(t,r[0],{configurable:!0,get:()=>n.value})}}function kl(e=(...t)=>fetch(...t)){async function t(r,n,i,a={}){let u=new URL(n,window.location.href);for(let[v,k]of Object.entries(a.params||{}))if(k!=null)for(let p of Array.isArray(k)?k:[k])u.searchParams.append(v,typeof p=="object"?JSON.stringify(p):p);let f=new AbortController,h;a.timeout?.then?a.timeout.then(()=>f.abort()):a.timeout&&(h=setTimeout(()=>f.abort(),a.timeout));try{let v={Accept:"application/json, text/plain, */*",...a.headers},k=document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]*)/);k&&u.origin===window.location.origin&&(v["X-XSRF-TOKEN"]=decodeURIComponent(k[1]));let p;i!==void 0&&(i instanceof FormData?p=i:(v["Content-Type"]||(v["Content-Type"]="application/json;charset=utf-8"),p=JSON.stringify(i)));let g=await e(u.href,{method:r,headers:v,body:p,credentials:"same-origin",signal:f.signal}),w=await g.text(),b=w;if(a.transformResponse)b=a.transformResponse(w);else if(w&&(/json/i.test(g.headers.get("content-type")||"")||/^[\[{]/.test(w.trim())))try{b=JSON.parse(w)}catch{}let T={data:b,status:g.status,headers:N=>g.headers.get(N)};if(!g.ok)throw T;return T}catch(v){throw v.name==="AbortError"?{status:-1,data:null}:v}finally{clearTimeout(h)}}let o={};for(let r of["get","delete","head"])o[r]=(n,i)=>t(r.toUpperCase(),n,void 0,i);for(let r of["post","put","patch"])o[r]=(n,i,a)=>t(r.toUpperCase(),n,i,a);return o}var _l={resolve:e=>Promise.resolve(e),reject:e=>Promise.reject(e),all:e=>Promise.all(e),defer(){let e,t;return{promise:new Promise((r,n)=>{e=r,t=n}),resolve:e,reject:t}}};var El=function(e){function t(o){return o=o||{used:0,total:0},o.unlimited=!o.total,o.percent=o.unlimited?0:Math.min(100,o.used*100/o.total),o.level=o.unlimited?"unlimited":o.percent>=95?"danger":o.percent>=80?"warn":"ok",o}return{decorate:t,load:function(){return e.get("/api/user/quota").then(o=>{let r=o.data||{};return r.storage=t(r.storage),r.file=t(r.file),r.repository=t(r.repository),r})}}};var Qo={};Hl(Qo,{bigNum:()=>Dq,date:()=>Lq,diff:()=>Iq,filter:()=>Mq,humanFileSize:()=>Sq,humanTime:()=>Rq,limitTo:()=>qq,number:()=>Vq,orderBy:()=>$q,plural:()=>Uq,statusLabel:()=>Oq,statusMsg:()=>Aq,title:()=>Tq,uppercase:()=>Pq});var Sq=e=>window.humanFileSize(e),Dq=(function(){return function(t){let o=Number(t)||0,r=Math.abs(o);return r<1e3?String(o):r<1e4?(o/1e3).toFixed(1).replace(/\.0$/,"")+"k":r<1e6?Math.round(o/1e3)+"k":r<1e7?(o/1e6).toFixed(1).replace(/\.0$/,"")+"M":Math.round(o/1e6)+"M"}})(),Rq=(function(){return function(t){if(!t)return"never";t instanceof Date&&(t=Math.round((Date.now()-t)/1e3)),(typeof t=="string"||typeof t=="number")&&(t=Math.round((Date.now()-new Date(t))/1e3));var o=t<0?"from now":"ago";if(Math.abs(t)>7200*24){let f=new Date;return f.setSeconds(f.getSeconds()-t),"on "+f.toLocaleDateString(void 0,{day:"numeric",month:"short",year:"numeric"})}t=Math.abs(t);for(var r=[t/60/60/24/365,t/60/60/24/30,t/60/60/24/7,t/60/60/24,t/60/60,t/60,t],n=["year","month","week","day","hour","minute","second"],i=0;i1&&(u+="s"),a>=1)return a+" "+u+" "+o}return"0 seconds "+o}})(),Tq=(function(){return function(e){if(!e)return e;e=e.toLowerCase();var t=e.split(" "),o=t.map(function(r){return r.charAt(0).toUpperCase()+r.substring(1,r.length)});return o.join(" ")}})(),Oq=(function(){var e={ready:"Ready",error:"Error",expired:"Expired",expiring:"Expiring",removed:"Removed",removing:"Removing",queue:"Queued",download:"Downloading",downloaded:"Downloaded",preparing:"Preparing",anonymizing:"Anonymizing"};return function(t){if(!t)return"";if(e[t])return e[t];var o=String(t).replace(/[_-]+/g," ").toLowerCase();return o.charAt(0).toUpperCase()+o.slice(1)}})(),Aq=(function(){var e={branch_not_found:"Branch not found on GitHub",repo_not_found:"Repository not found on GitHub",repository_not_found:"Repository not found on GitHub",repo_not_accessible:"Repository is not accessible with your token",pr_not_found:"Pull request not found on GitHub",gist_not_found:"Gist not found on GitHub",commit_not_found:"Commit not found on GitHub",repo_too_big:"Repository exceeds the size limit",quota_exceeded:"Storage quota exceeded",incomplete_record:"Incomplete record: missing identifier"};return function(t){if(!t)return t;var o=t.match(/^rate_limited:(\d+)$/);if(o){var r=Math.max(0,Math.ceil((parseInt(o[1],10)-Date.now())/1e3));if(r<=0)return"Rate limited \u2014 resuming soon";var n=Math.floor(r/60),i=r%60;return"Rate limited \u2014 retrying in "+(n>0?n+"m "+i+"s":i+"s")}if(e[t])return e[t];if(/^[a-z0-9]+(_[a-z0-9]+)+$/.test(t)){var a=t.replace(/_/g," ");return a.charAt(0).toUpperCase()+a.slice(1)}return t}})(),Iq=(function(e){let t=r=>r.replace(/&/g,"&").replace(//g,">");function o(r,n){if(!n)return;let i=n.newPath&&n.newPath!=="/dev/null"?n.newPath:n.oldPath||"",a=n.oldPath==="/dev/null"?"added":n.newPath==="/dev/null"?"deleted":n.oldPath&&n.newPath&&n.oldPath!==n.newPath?"renamed":"modified";if(r.push(''),r.push('"),n.lines.length){r.push('
');for(let u of n.lines)r.push(''+(u.oldNo||"")+' '+(u.newNo||"")+' '+(u.kind==="add"?"+":u.kind==="remove"?"-":u.kind==="hunk"?"@":"")+' '+t(u.text)+" ");r.push("
")}r.push("
")}return function(r){if(!r)return r;let n=[],i=null,a=0,u=0,f=()=>(i||(i={oldPath:"",newPath:"",lines:[]}),i),h=()=>{i&&(i.lines.length||i.oldPath||i.newPath)&&(o(n,i),i=null)},v=r.split(`
+`);for(let k=0;ke});function Vq(e,t){return e==null?"":Number(e).toLocaleString(void 0,t==null?{}:{minimumFractionDigits:t,maximumFractionDigits:t})}function Pq(e){return String(e??"").toUpperCase()}function qq(e,t,o=0){return(e||[]).slice(o,o+t)}function Mq(e,t){return Array.isArray(e)?typeof t=="function"?e.filter(t):e:[]}function $q(e,t){if(!t)return e||[];let o=Array.isArray(t)?t:[t];return[...e||[]].sort((r,n)=>{for(let i of o){let a=i[0]==="-"?-1:1;i=i.replace(/^[+-]/,"");let u=i.split(".").reduce((h,v)=>h?.[v],r),f=i.split(".").reduce((h,v)=>h?.[v],n);if(typeof u=="string"&&(u=u.toLowerCase()),typeof f=="string"&&(f=f.toLowerCase()),u!==f)return(u==null?-1:f==null?1:ur[n])}function Uq(e,t){return(t?.[e]??t?.[e===1?"one":"other"]??"").replace(/\{\}/g,e??0)}var Cl={ERRORS:{repository_job_cancelled:"The repository changed or was removed while processing.",storage_delete_failed:"Unable to remove cached files. Please try again later.",unknown_error:"Unknown error, contact the admin.",unreachable:"Anonymous GitHub is unreachable, contact the admin.",request_error:"Unable to download the file, check your connection or contact the admin.",not_found:"The requested resource could not be found.",not_connected:"You must be logged in to perform this action.",not_authorized:"You do not have permission to perform this action.",unable_to_connect_user:"Unable to connect your account. Please try again later.",user_not_found:"The requested user could not be found.",user_banned:"Your account has been banned. Contact the admin for more information.",repo_access_limited:"GitHub blocked access because the repository's organization restricts third-party OAuth apps. Ask an org owner to approve Anonymous GitHub under Settings \u2192 Third-party Access \u2192 OAuth app policy, or anonymize a personal fork instead.",repo_saml_enforcement:"The repository's organization enforces SAML single sign-on. Authorize your token for that organization (GitHub \u2192 Settings \u2192 Applications, or re-run the org's SSO sign-in), then retry. Alternatively, anonymize a personal fork.",repo_not_found:"The repository was not found on GitHub. Check the URL and spelling, make sure you are signed in to the account that can see it, and confirm the repo isn't hidden under an org that restricts third-party app access.",repo_empty:"The selected branch has no commits on GitHub. Push at least one commit, or pick a different branch, then retry.",repo_not_accessible:"Anonymous GitHub cannot access this repository. Verify the repository exists and that Anonymous GitHub has been authorized for the owning organization.",repository_archived:"This repository has been archived and its files are no longer available.",repository_expired:"The repository is expired.",invalid_status:"This action cannot be performed while the resource is in its current state.",repository_not_ready:"Anonymous GitHub is still processing the repository, it can take several minutes.",repository_not_accessible:"This repository is currently not accessible.",repo_is_updating:"Anonymous GitHub is still processing the repository, it can take several minutes.",invalid_repo:"The provided repository is not valid.",invalid_source_repository:"The repository source name is invalid; expected the form 'owner/name'.",repoId_not_defined:"A repository ID must be provided.",repoUrl_not_defined:"The repository URL needs to be defined.",source_not_provided:"A repository source must be provided.",github_rate_limit_exceeded:"GitHub temporarily blocked the request because we hit its API rate limit. Wait a few minutes and try again. If the problem persists and you contact GitHub Support, include the request ID and timestamp shown in GitHub's response (the 'X-GitHub-Request-Id' header).",rate_limited:"GitHub API rate limit reached. The repository will be available shortly \u2014 please wait a moment and refresh.",repoId_already_used:"The repository ID is already used.",invalid_repoId:"The format of the repository ID is invalid.",unsupported_source:"The repository source type is not supported.",branch_not_specified:"The branch is not specified.",branch_not_found:"The branch of the repository cannot be found.",commit_not_specified:"A commit must be specified.",commit_not_found:"The configured commit no longer exists in the source repository. It may have been force-pushed, rebased, or removed.",repo_renamed:"The source repository appears to have been renamed or moved on GitHub. Please refresh and try again \u2014 the cached name has been updated.",invalid_commit_format:"The commit hash format is invalid. It must be a hexadecimal string.",pull_request_not_found:"The requested pull request could not be found.",pull_request_expired:"This pull request has expired and is no longer available.",pull_request_not_ready:"This pull request is still being prepared. Please try again shortly.",pull_request_not_available:"This pull request is currently not available.",invalid_pullRequestId:"The pull request ID is invalid.",pullRequestId_already_used:"This pull request ID is already in use. Please choose a different one.",repository_not_specified:"A repository must be specified for this pull request.",pullRequestId_not_specified:"A pull request ID must be specified.",pullRequestId_is_not_a_number:"The source pull request ID must be a number.",options_not_provided:"Anonymization options are mandatory.",terms_not_specified:"Anonymization terms must be specified.",invalid_terms_format:"Terms are in an invalid format.",missing_content:"No content was provided to the anonymization preview.",unable_to_anonymize:"An error happened during the anonymization process. Please try later or report the issue.",non_supported_mode:"The selected anonymization mode is invalid, only download and stream are supported.",invalid_path:"The provided path is invalid or missing.",path_not_specified:"A file path must be specified.",path_not_defined:"The file path has not been resolved yet.",invalid_file_path:"The requested file path is not valid.",invalid_request:"The request is missing required fields or is malformed.",no_file_selected:"Please select a file.",file_not_found:"The requested file is not found.",file_not_accessible:"The requested file is not accessible.",file_not_supported:"The file type is not supported. Anonymous GitHub cannot handle it.",file_too_big:"The file size exceeds the limit of Anonymous GitHub.",is_folder:"The path points to a folder.",folder_not_supported:"The path points to a folder. Please select a file.",unable_to_write_file:"Unable to write file on disk.",download_not_enabled:"Repository downloads are not enabled on this server.",unable_to_download:"The repository could not be downloaded. Please try again later.",s3_config_not_provided:"Object storage has not been configured on this server.",stats_unsupported:"Statistics are only supported in download mode.",branches_not_found:"The requested branch is not found.",readme_not_available:"No README for the repository is found.",page_not_supported_on_different_branch:"GitHub Pages is served from a different branch than the one selected. Pick the branch that GitHub Pages is configured to use.",page_not_activated:"GitHub Pages is not enabled on this repository. Enable it in the repository settings on GitHub before anonymizing.",is_removed:"This resource has been removed and is no longer available.",conf_name_missing:"A conference name is required.",conf_id_missing:"A conference ID is required.",conf_id_used:"This conference ID is already in use. Please choose a different one.",conf_start_date_missing:"A start date is required for the conference.",conf_end_date_missing:"An end date is required for the conference.",conf_start_date_invalid:"The start date must be before the end date.",conf_end_date_invalid:"The end date must be in the future.",invalid_plan:"The selected plan is not valid.",conference_not_found:"The requested conference could not be found.",conf_not_found:"The requested conference could not be found.",conf_not_activated:"The conference is not activated.",billing_missing:"Billing information is required for this plan.",billing_name_missing:"A billing name is required.",billing_email_missing:"A billing email is required.",billing_address_missing:"A billing address is required.",billing_city_missing:"A billing city is required.",billing_zip_missing:"A billing ZIP/postal code is required.",billing_country_missing:"A billing country is required.",queue_not_found:"The specified queue could not be found.",job_not_found:"The specified job could not be found in the queue.",error_retrying_job:"An error occurred while retrying the job.",gist_expired:"The gist is expired.",gist_not_ready:"Anonymous GitHub is still processing the gist, it can take several minutes.",gist_not_found:"The requested gist could not be found.",gist_not_available:"This gist is currently not available.",invalid_gistId:"The format of the gist ID is invalid.",gistId_not_specified:"A gist ID must be provided.",gistId_already_used:"The gist ID is already used.",missing_token:"An authentication token is required.",invalid_token:"The provided authentication token is invalid.",login_failed:"Login failed. Please try again.",server_error:"An unexpected server error occurred. Please try again later.",username_not_defined:"A username must be provided.",github_user_not_found:"The specified GitHub user could not be found.",cannot_coauthor_self:"You cannot add yourself as a co-author.",storage_write_size_mismatch:"The downloaded file was smaller than expected. The upstream source may have returned an incomplete response \u2014 please try again.",storage_read_error:"An error occurred while reading the file from storage \u2014 please try again.",upstream_error:"A temporary error occurred while fetching from GitHub \u2014 please try again.",token_expired:"Your GitHub access token has expired. Please log out and log in again to refresh it.",job_is_active:"This job is currently running \u2014 wait for it to finish or remove it first.",github_app_access_required:"Allow this repository on GitHub using the read-only access button, then retry. Organization access may require administrator approval.",github_app_reconnect_required:"Reconnect your GitHub App account to restore read-only access.",github_app_disabled:"New GitHub App connections are currently unavailable.",github_oauth_required:"Connect legacy GitHub OAuth to use this feature.",github_identity_mismatch:"Use the same GitHub account that you are signed in with here.",github_app_permissions_invalid:"The GitHub App must be configured with read-only permissions.",github_unavailable:"GitHub is temporarily unavailable. Please retry.",connection_changed:"The repository connection changed. Reload and try again.",repository_busy:"Wait for the repository operation to finish before changing its connection.",invalid_connection:"Select a supported GitHub connection.",github_oauth_disabled:"Legacy OAuth sign-in is currently disabled.",invalid_auth_state:"The authorization session expired. Please start again from Anonymous GitHub.",invalid_webhook_signature:"The webhook signature is invalid.",webhook_processing_failed:"Webhook processing failed. Retry the delivery.",github_app_authorization_cancelled:"GitHub authorization was cancelled.",github_account_link_required:"Verify your existing account with your previous GitHub connection. Future sign-ins will use the GitHub App.",github_app_refresh_busy:"GitHub credentials are being renewed. Please retry shortly.",github_grant_revocation_failed:"Unable to revoke the GitHub grant. Please retry.",another_login_required:"Connect and verify another sign-in method before disconnecting OAuth.",oauth_resources_remaining:"Some resources still require OAuth. Migrate or remove them before disconnecting.",invalid_github_path:"The GitHub repository path is invalid."},WARNINGS:{page_not_enabled_on_repo:"GitHub Pages is not enabled on this repository. Enable it in the repository's Settings \u2192 Pages on GitHub, then refresh.",page_branch_mismatch:"GitHub Pages on this repository is served from the '{{pageBranch}}' branch, but you selected '{{selectedBranch}}'. Switch the branch above to '{{pageBranch}}' to anonymize the Pages site.",folder_truncated:"This folder has more than 10,000 entries; only a partial listing is shown.",repo_truncated:"Some folders in this repository have too many files to be fully listed. Affected folders are marked with a warning icon.",submodules_not_included:"This repository uses git submodules. Submodule contents are not included in the anonymized repository and appear as empty folders."}};var Nl={name:"htmlDoc",props:["content","baseUrl","allowScripts"],setup(e){let t=Ze(null);return It(()=>{let r=[t.value][0];r.classList.add("html-doc");function n(){r.innerHTML="";let i=e.content;if(typeof i!="string")return;let a=document.createElement("iframe");a.className="html-doc-frame",a.setAttribute("title","Rendered HTML document");let u=["allow-popups","allow-popups-to-escape-sandbox"];e.allowScripts&&u.push("allow-scripts","allow-forms","allow-modals"),a.setAttribute("sandbox",u.join(" ")),a.setAttribute("referrerpolicy","no-referrer"),r.appendChild(a);let f="";e.baseUrl&&(f=' '),a.srcdoc=f+i}He(()=>e.content,n,{immediate:!0}),He(()=>e.baseUrl,n,{immediate:!0}),He(()=>e.allowScripts,n,{immediate:!0})}),()=>Ne("html-doc",{ref:t})}};var Sl={name:"pdfviewer",props:["src"],setup(e){let t=Ze(null);return It(()=>{let o=[t.value],r=400,n=[.5,.75,1,1.25,1.5,2,3],i=1e3,a=o[0];a.classList.add("pdf-viewer");let u=null,f=[],h=!1,v=null,k=1,p=1,g=0,w=0,b=612/792,T=0,N=document.createElement("div");N.className="pdf-toolbar";let R=document.createElement("div");R.className="pdf-pages",R.tabIndex=0,R.setAttribute("aria-label","PDF pages"),a.appendChild(N),a.appendChild(R);function I(U,W,fe){let Ce=document.createElement("button");return Ce.type="button",Ce.className="pdf-toolbar-btn",Ce.title=W,Ce.setAttribute("aria-label",W),Ce.innerHTML=' ',Ce.addEventListener("click",fe),Ce}let C=I("fa-chevron-up","Previous page",function(){he(p-1)}),A=I("fa-chevron-down","Next page",function(){he(p+1)}),P=document.createElement("input");P.type="text",P.className="pdf-toolbar-page",P.setAttribute("aria-label","Page number"),P.title="Page number \u2014 type a page and press Enter",P.addEventListener("keydown",function(U){U.key==="Enter"&&(he(parseInt(P.value,10)),P.blur())}),P.addEventListener("blur",function(){P.value=String(p)});let x=document.createElement("span");x.className="pdf-toolbar-count";let Q=I("fa-search-minus","Zoom out",function(){le(n[Math.max(0,re()-1)])}),K=I("fa-search-plus","Zoom in",function(){le(n[Math.min(n.length-1,re()+1)])}),z=document.createElement("button");z.type="button",z.className="pdf-toolbar-btn pdf-toolbar-zoom",z.title="Reset zoom to fit the width",z.setAttribute("aria-label","Reset zoom to fit the width"),z.addEventListener("click",function(){le(1)}),N.appendChild(C),N.appendChild(A);let ae=document.createElement("span");ae.className="pdf-toolbar-group",ae.appendChild(P),ae.appendChild(x),N.appendChild(ae);let se=document.createElement("span");se.className="pdf-toolbar-group pdf-toolbar-right",se.appendChild(Q),se.appendChild(z),se.appendChild(K),N.appendChild(se);function re(){let U=0;for(let W=0;W=U,z.textContent=Math.round(k*100)+"%",Q.disabled=re()===0,K.disabled=re()===n.length-1}function ke(U){let W=f[U-1];W&&(R.scrollTop+=W.getBoundingClientRect().top-R.getBoundingClientRect().top)}function he(U){if(!u||isNaN(U))return;let W=Math.min(Math.max(1,U),u.numPages);f[W-1]&&(ke(W),p=W,g=W,w=Date.now()+800,ue(),D())}function me(){if(!f.length||Date.now()Ce||S.bottome.src,te,{immediate:!0}),ss(function(){R.removeEventListener("scroll",V),$(window).off("resize",L),pe()})}),()=>Ne("pdfviewer",{ref:t})}};var Rl={props:["content","terms","options"],setup(e){let t=Ze(null),o=()=>{t.value.innerHTML=renderMD(e.content||"",window.location.pathname+"/../")};return It(()=>{o(),He(()=>[e.content,e.terms,e.options],o,{deep:!0})}),()=>Ne("markdown",{ref:t})}},zq={props:["file","terms","options"],setup(e){let t=Ze(null),o=()=>_t(()=>t.value?.querySelectorAll("pre code").forEach(r=>window.Prism?.highlightElement(r)));return It(o),He(()=>[e.file?.content,e.terms,e.options],o,{deep:!0}),()=>{let r=e.file||{},n=(r.filename||"").split(".").pop().toLowerCase();if(["md","markdown"].includes(n)||r.language==="Markdown")return Ne("gist-file",{ref:t},Ne(Rl,{content:r.content,terms:e.terms,options:e.options}));let i={js:"javascript",jsx:"javascript",ts:"javascript",typescript:"javascript",py:"python",html:"markup",xml:"markup",svg:"markup",sh:"bash"},a=(r.language||n||"none").toLowerCase();return Ne("gist-file",{ref:t},Ne("pre",{class:"line-numbers"},Ne("code",{class:"language-"+(i[a]||a),key:r.content},r.content||"")))}}},Hq={props:["file","content"],setup(e){let t=Ze(null),o=0,r,n=async()=>{let i=++o;r?.abort(),r=new AbortController;try{let a=e.content?JSON.parse(e.content):await fetch(e.file?.download_url||e.file,{signal:r.signal}).then(u=>{if(!u.ok)throw Error("Notebook request failed");return u.json()});if(await Wt("notebook"),i!==o)return;t.value.innerHTML=DOMPurify.sanitize(nb.parse(a).render()),t.value.querySelectorAll("pre code").forEach(u=>window.Prism?.highlightElement(u))}catch(a){i===o&&a.name!=="AbortError"&&(t.value.textContent="Unable to render the notebook.")}};return It(()=>{n(),He(()=>[e.file,e.content],n)}),ss(()=>{o++,r?.abort()}),()=>Ne("notebook",{ref:t})}},Bq={props:["stats"],setup(e){return()=>{let t=Object.entries(e.stats||{}).filter(([,r])=>r.code),o=t.reduce((r,[,n])=>r+n.code,0);return Ne("loc",t.map(([r,n])=>Ne("div",{class:"lang",title:`${r}: ${n.code.toLocaleString()} lines`,style:{width:n.code*100/o+"%",background:langColors[r]}})))}}},Tl={Markdown:Rl,GistFile:zq,Notebook:Hq,Loc:Bq,HtmlDoc:Nl,Pdfviewer:xr(async()=>(await Wt("pdf"),pdfjsLib.GlobalWorkerOptions.workerSrc="/script/external/pdf.worker.js",Sl))},Ol={async mounted(e,{value:t}){e._editorValue=t;try{if(await tr(),e._editorDisposed)return;let o=e._editorValue,r=ace.edit(e);e._editor=r,r.setValue(String(o.content??""),-1),Dl(e,o.options),o.options?.onLoad?.(r)}catch(o){e._editorDisposed||(e.textContent=o.message)}},updated(e,{value:t}){e._editorValue=t,e._editor&&(e._editor.getValue()!==String(t.content??"")&&e._editor.setValue(String(t.content??""),-1),Dl(e,t.options))},beforeUnmount(e){e._editorDisposed=!0,e._editor?.destroy()}};function Dl(e,t={}){t.mode&&e._editor.session.setMode("ace/mode/"+t.mode),t.theme&&e._editor.setTheme("ace/theme/"+t.theme),e._editor.setReadOnly(t.readOnly!==!1)}var Al={mounted(e){if(!window.IntersectionObserver)return;let t=[...e.querySelectorAll('a[href^="#"]')],o=new Set,r=new IntersectionObserver(n=>{n.forEach(a=>a.isIntersecting?o.add(a.target.id):o.delete(a.target.id));let i=t.find(a=>o.has(a.hash.slice(1)));t.forEach(a=>a.classList.toggle("active",a===i))},{rootMargin:"-20% 0px -60% 0px",threshold:0});_t(()=>t.forEach(n=>{let i=document.getElementById(n.hash.slice(1));i&&r.observe(i)})),e._observer=r},beforeUnmount(e){e._observer?.disconnect()}};function Gq(e){let t={children:[]},o=new Map([["",t]]);function r(n){if(o.has(n))return o.get(n);let i=n.lastIndexOf("/"),a=r(i<0?"":n.slice(0,i)),u={name:n.slice(i+1),children:[]};return o.set(n,u),a.children.push(u),u}for(let n of e||[]){let i=n.path?`${n.path}/${n.name}`:n.name;n.size==null?r(i):r(n.path||"").children.push({...n})}return t.children}var Il={name:"FileTree",props:["file","parent","searchQuery","searchResults","page"],setup(e){let t=Ea(),o=Ze(null),r=ze(Object.create(null)),n=null,i=tt(()=>Gq(e.file)),a=()=>"/"+(Array.isArray(t.params.path)?t.params.path.join("/"):t.params.path||"");He(()=>[t.params.repoId,t.params.path],()=>{let p="";a().split("/").filter(Boolean).forEach(g=>{p+="/"+g,r[p]=!0})},{immediate:!0});let u=tt(()=>{if(!e.searchQuery||!e.searchResults)return null;let p=new Set,g=new Set;return e.searchResults.forEach(w=>{p.add(w.path?`${w.path}/${w.name}`:w.name);let b="";(w.path||"").split("/").filter(Boolean).forEach(T=>{b=b?b+"/"+T:T,g.add(b)})}),{files:p,folders:g}}),f=()=>[...o.value.querySelectorAll("li > a")],h=p=>{o.value.querySelector(".tree-focused")?.classList.remove("tree-focused"),p&&(n=p.dataset.path,p.classList.add("tree-focused"),p.scrollIntoView?.({block:"nearest"}),o.value.focus())};function v(p){let g=f(),w=o.value.querySelector(".tree-focused")||g[0],b=g.indexOf(w);if(!["ArrowDown","ArrowUp","ArrowRight","ArrowLeft","Enter"].includes(p.key)||(p.preventDefault(),p.key==="ArrowDown"&&h(g[(b+1)%g.length]),p.key==="ArrowUp"&&h(g[(b-1+g.length)%g.length]),!w))return;let T=w.closest("li");p.key==="ArrowRight"&&T.classList.contains("folder")&&(T.classList.contains("open")?h(T.querySelector(":scope > ul > li > a")):w.click()),p.key==="ArrowLeft"&&(T.classList.contains("folder")&&T.classList.contains("open")?w.click():h(T.parentElement.closest("li.folder")?.querySelector(":scope > a"))),p.key==="Enter"&&w.click()}function k(p,g=""){let w=[...p].sort((b,T)=>+!!T.children-+!!b.children||b.name.localeCompare(T.name));return Ne("ul",w.flatMap(b=>{let T=b,N=T.name,R=(g+"/"+N).slice(1);if(u.value&&!u.value.files.has(R)&&!u.value.folders.has(R))return[];for(;T.children?.length===1;)T=T.children[0],N+="/"+T.name;let I=g+"/"+N,C=!!T.children,A=u.value?r[I]!==!1:!!r[I],P=C&&e.page?.options?.truncatedFolders?.includes(I.slice(1)),x=e.page?.fileCounts?.[I.slice(1)]||0,Q=Ne("span",{class:C?"tree-icon-folder":"far fa-file tree-icon-file","aria-hidden":"true"},C?Ne("svg",{viewBox:"0 0 14 12",focusable:"false"},Ne("path",{d:"M1.5 10.5v-9h4l1.5 2h5.5v7z"})):null),K=Ne("span",{class:"tree-name"},N),z=se=>{h(se.currentTarget),C&&(se.preventDefault(),r[I]=!A,r[I]&&!T.children.length&&e.page.getFiles(I.slice(1)),_t(()=>h(f().find(re=>re.dataset.path===n))))},ae=Ne("a",{"data-path":I,href:C?void 0:`/r/${encodeURIComponent(t.params.repoId)}${encodePathForUrl(I)}`,class:{"tree-focused":n===I},onClick:z},[C?Ne("span",{class:"tree-toggle"}):g?Ne("span",{class:"tree-spacer"}):null,Q,K,P?Ne("span",{class:"truncated-warning",title:e.page.fmt.translate("WARNINGS.folder_truncated")},Ne("i",{class:"fas fa-exclamation-triangle"})):null,C&&x?Ne("span",{class:"tree-count"},x):null]);return Ne("li",{key:I,class:{file:!0,folder:C,open:A,active:a()===I,truncated:P},title:C?"":`Size: ${humanFileSize(T.size||0)}`},[ae,C&&A?k(T.children,I):null])}))}return()=>Ne("tree",{ref:o,tabindex:0,onKeydown:v},e.file?.length?u.value?.files.size===0?Ne("div",{class:"tree-search-empty"},"No files found"):k(i.value):"Empty repository")}};function Pl(){return ze({errors:{},dirty:!1,touched:!1,submitted:!1,get invalid(){return Object.values(this.errors).some(Boolean)},setValidity(e,t){this.errors[e]=!t},setDirty(){this.dirty=!0}})}function ql(e,t){if(!e[t]){let o=Pl();Object.defineProperty(o,"invalid",{get(){return Object.values(o.errors).some(Boolean)||Object.keys(o).some(r=>r!=="invalid"&&o[r]&&typeof o[r]=="object"&&o[r].invalid)}}),e[t]=o}return e[t]}var Ml={beforeMount(e,{value:t}){e._formState=ql(t,e.name),e.addEventListener("submit",o=>o.preventDefault())}};function jq(e){return e instanceof Date?isNaN(e)?"":`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`:e||""}function Vl(e){let t=e._field.binding.value;e.type==="checkbox"?e.checked=!!t:e.type==="radio"?e.checked=t==(e._value??e.value):e._field.timer||(e.value=e.type==="date"?jq(t):t??""),Xo(e)}function Xo(e){let t=e._field.validation;if(!t)return;let o=e.validity;for(let[r,n]of Object.entries({required:o.valueMissing,pattern:o.patternMismatch,min:o.rangeUnderflow,max:o.rangeOverflow,date:o.badInput,email:o.typeMismatch&&e.type==="email",url:o.typeMismatch&&e.type==="url"}))t.setValidity(r,e.disabled||!n)}var $l={beforeMount(e,{value:t}){var a;let o=t.form&&ql(t.state,t.form),r=o&&e.name?o[a=e.name]||(o[a]=Pl()):null;e._field={binding:t,validation:r,timer:null};let n=()=>{clearTimeout(e._field.timer),e._field.timer=null;let u=e._field.binding,f=e.type==="checkbox"?e.checked:e._value??e.value;if(!(e.type==="radio"&&!e.checked)){if((e.type==="number"||e.type==="range")&&(f=e.value===""?null:Number(e.value)),e.type==="date"&&(f=e.value?new Date(e.value+"T00:00:00"):null),r){r.setDirty();for(let h of Object.keys(r.errors))r.setValidity(h,!0)}u.set(f),Xo(e),u.change?.()}};e._field.commit=n;let i=u=>{let f=e._field.binding.options?.debounce||0,h=typeof f=="number"?f:f[u.type]??f.default??0;clearTimeout(e._field.timer),h?e._field.timer=setTimeout(n,h):n()};e.addEventListener(e.tagName==="SELECT"||["checkbox","radio","date"].includes(e.type)?"change":"input",i),e.addEventListener("blur",()=>{r&&(r.touched=!0),e._field.timer&&n()})},mounted:Vl,updated(e,{value:t}){e._field.binding=t,Vl(e)},beforeUnmount(e){clearTimeout(e._field.timer);let{binding:t,validation:o}=e._field,r=t.form&&t.state[t.form];r&&r[e.name]===o&&delete r[e.name]}};function Ll(e,t){let o=e.target.closest("form");if(o?._formState&&(o._formState.submitted=!0),o?.querySelectorAll("input, select, textarea").forEach(r=>{r._field?.timer&&r._field.commit(),r._field&&Xo(r)}),o&&!o.checkValidity()){let r=o.querySelector(":invalid");r?.scrollIntoView?.({block:"center"}),r?.focus(),o.reportValidity();return}return t()}var Ul=Symbol("session"),Wq=/^\/(w|api|github)(\/|$)/;function Fl(e,t={}){let o=e?.split(".").reduce((r,n)=>r?.[n],Cl);return String(o??e??"").replace(/{{\s*([^}]+?)\s*}}/g,(r,n)=>t[n]??"")}var Kq={...Qo,translate:Fl};function Yq(e){if(e==null||e==="")return e;try{let t=new URL(e,window.location.href);return["http:","https:","mailto:","tel:","blob:"].includes(t.protocol)?e:void 0}catch{return}}function Jq(e="#app",t={}){let o=new Map,r=kl(t.fetch),n,i;function a(){let h=mt(),v={url(p){return p===void 0?i.currentRoute.value.fullPath:(i.push(p),v)},path(){return i.currentRoute.value.path},search(){return i.currentRoute.value.query}},k=new Proxy({},{get(p,g){let w=i.currentRoute.value.params[g];return Array.isArray(w)?w.join("/"):w}});return{http:r,...h,location:v,params:k,window,promises:_l,html:{trustAsHtml:p=>p},translate:(p,g)=>Promise.resolve(Fl(p,g)),quotaService:El(r)}}function u(h){return{name:h.template,setup(){let v=Ko(lt(Ul),o);h.setup(v,a()),wl(h.template,v);let k=[];return()=>Jn[h.template](v,k)}}}i=_a({history:t.history||ba(),routes:ja.map(h=>h.redirect?h:{path:h.path,component:u(h),meta:{title:h.title,preserveExplorer:h.preserveExplorer}})});let f=Fi({setup(){n=Ko(null,o),n.fmt=Kq,n.window=window,n.Math=Math,n.sanitize=p=>DOMPurify.sanitize(p??""),n.submitForm=Ll,n.safeUrl=Yq;let h=a();Na(n,h.http,h.location,h.timeout),ts(Ul,n),He(()=>n.title,p=>{document.title=p||"Anonymous GitHub"});let v=[],k=p=>{let g=p.target.closest?.("a[href]");if(p.defaultPrevented||p.button!==0||p.metaKey||p.ctrlKey||p.shiftKey||p.altKey||!g||g.hasAttribute("download")||g.target&&g.target!=="_self"||g.dataset.toggle||g.getAttribute("href").startsWith("#"))return;let b=new URL(g.href,window.location.href);b.origin!==window.location.origin||Wq.test(b.pathname)||(p.preventDefault(),i.push(b.pathname+b.search+b.hash),$("#navbarSupportedContent.show").collapse("hide"))};return document.addEventListener("click",k),ss(()=>document.removeEventListener("click",k)),()=>[Ne("header",{class:"app-header"},Jn["partials/header.htm"](n,v)),Ne("main",{class:"app-view align-items-stretch w-100"},Ne(Wo,null,{default:({Component:p,route:g})=>p?Ne(p,{key:g.meta.preserveExplorer?g.matched[0]?.path:g.path}):null})),Ne("div",{class:"position-fixed p-3",style:{zIndex:999999999,right:0,bottom:0}},n.toasts.map((p,g)=>Ne("div",{class:"toast show",role:"alert","aria-live":"assertive","aria-atomic":"true",key:g},[Ne("div",{class:"toast-header"},[Ne("strong",{class:"mr-auto"},p.title),Ne("button",{type:"button",class:"ml-2 mb-1 close","aria-label":"Close",onClick:()=>n.removeToast(p)},"\xD7")]),Ne("div",{class:"toast-body"},p.body)])))]}});for(let[h,v]of Object.entries(Tl))f.component(h,v);return f.component("Tree",Il),f.component("PartialView",{props:["name","state"],setup(h){let v=[];return()=>Jn[h.name](h.state,v)}}),f.directive("field",$l),f.directive("form",Ml),f.directive("code-editor",Ol),f.directive("paper-scrollspy",Al),f.use(i),i.beforeEach(async h=>{n?.emit("routeLeave"),/^\/(r|repository|anonymize|pull-request-anonymize|gist-anonymize|pr|gist)(\/|$)/.test(h.path)&&await Wt("markdown"),/^\/(r|repository)\//.test(h.path)&&await Wt("org")}),i.afterEach(h=>{if(!n)return;n.title=h.meta.title,n.emit("routeChange",{title:h.meta.title}),n.emit("routeUpdate",{title:h.meta.title});let v=document.querySelector(".app-view");!h.meta.preserveExplorer&&v&&(v.scrollTop=0)}),f.mount(e),{app:f,router:i,state:n}}document.querySelector("#app")&&(window.anonymousApp=Jq());})();
/*! Bundled license information:
@vue/shared/dist/shared.esm-bundler.js:
diff --git a/src/server/routes/auth-utils.ts b/src/server/routes/auth-utils.ts
index 80dd39b..fe04430 100644
--- a/src/server/routes/auth-utils.ts
+++ b/src/server/routes/auth-utils.ts
@@ -20,3 +20,18 @@ export function getLoginToken(
}
return null;
}
+
+export function safeAuthReturnTo(value: unknown, fallback = "/dashboard"): string {
+ return typeof value === "string" && /^\/(?:(?:anonymize|pull-request-anonymize|gist-anonymize)(?:\/[\w-]+)?|connections|dashboard)(?:\?[^\\\r\n]*)?$/.test(value) ? value : fallback;
+}
+
+export type OAuthContext = { ownerId?: string; githubId?: string; returnTo: string; expires: number; recovery?: boolean };
+declare module "express-session" {
+ interface SessionData {
+ githubRecovery?: OAuthContext;
+ githubOAuthFlow?: OAuthContext;
+ }
+}
+declare module "express-serve-static-core" {
+ interface Request { githubOAuthContext?: OAuthContext; }
+}
diff --git a/src/server/routes/connection.ts b/src/server/routes/connection.ts
index 2669a63..8eadfeb 100644
--- a/src/server/routes/connection.ts
+++ b/src/server/routes/connection.ts
@@ -13,7 +13,7 @@ import AnonymousError from "../../core/AnonymousError";
import { setCredential } from "../../core/credentials";
import { hashToken } from "./token-auth";
import { createLogger, serializeError } from "../../core/logger";
-import { getLoginToken, isDisabledAccount } from "./auth-utils";
+import { getLoginToken, isDisabledAccount, safeAuthReturnTo } from "./auth-utils";
const logger = createLogger("auth");
@@ -28,7 +28,8 @@ export function ensureAuthenticated(
res.status(401).json({ error: "not_connected" });
}
-const verify = async (
+export const verify = async (
+ req: express.Request,
accessToken: string,
_refreshToken: string,
profile: Profile,
@@ -36,8 +37,19 @@ const verify = async (
): Promise => {
let user: IUserDocument | null;
try {
+ const flow = req.githubOAuthContext;
+ const currentId = (req.user as { user?: { id?: string } } | undefined)?.user?.id;
+ if (!flow || flow.expires < Date.now() || (flow.githubId && flow.githubId !== profile.id)
+ || (currentId && currentId !== flow.ownerId)) {
+ done(new AnonymousError("github_identity_mismatch", { httpStatus: 409 }));
+ return;
+ }
user = await UserModel.findOne({ "externalIDs.github": profile.id });
if (user) {
+ if (flow.ownerId && user.id !== flow.ownerId) {
+ done(new AnonymousError("github_identity_mismatch", { httpStatus: 409 }));
+ return;
+ }
if (isDisabledAccount(user.status)) {
done(
new AnonymousError(
@@ -53,6 +65,10 @@ const verify = async (
// existing account instead of creating a duplicate that would lose
// the isAdmin flag.
user = await UserModel.findOne({ username: profile.username });
+ if (flow.ownerId && (!user || user.id !== flow.ownerId)) {
+ done(new AnonymousError("github_identity_mismatch", { httpStatus: 409 }));
+ return;
+ }
if (user) {
if (user.externalIDs?.github && user.externalIDs.github !== profile.id) {
done(new AnonymousError("not_connected", { httpStatus: 401 }));
@@ -120,6 +136,7 @@ const verify = async (
if (config.GITHUB_OAUTH_ENABLED) passport.use(
new Strategy(
{
+ passReqToCallback: true,
clientID: config.CLIENT_ID,
clientSecret: config.CLIENT_SECRET,
callbackURL: config.AUTH_CALLBACK,
@@ -174,9 +191,22 @@ export function initSession() {
export const router = express.Router();
+router.get("/account-recovery", (req, res) => {
+ res.set("Cache-Control", "no-store");
+ res.json({ required: !!req.session.githubRecovery && req.session.githubRecovery.expires > Date.now() });
+});
+
router.get(
"/login",
(req, res, next) => config.GITHUB_OAUTH_ENABLED ? next() : res.status(503).json({ error: "github_oauth_disabled" }),
+ (req, res, next) => {
+ const recovery = req.query.recover === "1" ? req.session.githubRecovery : undefined;
+ if (req.query.recover === "1" && (!recovery || recovery.expires < Date.now())) return res.redirect("/signin");
+ const current = (req.user as { user?: IUserDocument } | undefined)?.user;
+ req.session.githubOAuthFlow = recovery || { ownerId: current?.id, githubId: current?.externalIDs?.github,
+ returnTo: safeAuthReturnTo(req.query.returnTo, current ? "/connections" : "/dashboard"), expires: Date.now() + 10 * 60000 };
+ next();
+ },
passport.authenticate("github", { scope: ["repo"] }), // Note the scope here
function (req: express.Request, res: express.Response) {
res.redirect("/");
@@ -187,6 +217,9 @@ router.get(
"/auth",
(req, res, next) => {
if (!config.GITHUB_OAUTH_ENABLED) return res.status(503).json({ error: "github_oauth_disabled" });
+ const flow = req.session.githubOAuthFlow;
+ delete req.session.githubOAuthFlow;
+ req.githubOAuthContext = flow;
const existingId = (req.user as { user?: { id?: string } } | undefined)?.user?.id;
passport.authenticate("github", (error: Error | null, identity: Express.User | false) => {
if (error) return next(error);
@@ -195,7 +228,11 @@ router.get(
if (existingId && id !== existingId) return res.status(409).json({ error: "github_identity_mismatch" });
req.login(identity, loginError => {
if (loginError) return next(loginError);
- res.redirect(existingId ? "/connections" : "/dashboard");
+ if (flow?.recovery) {
+ delete req.session.githubRecovery;
+ return res.redirect("/github/app/login?returnTo=" + encodeURIComponent(flow.returnTo));
+ }
+ res.redirect(flow?.returnTo || (existingId ? "/connections" : "/dashboard"));
});
})(req, res, next);
}
diff --git a/src/server/routes/github-app.ts b/src/server/routes/github-app.ts
index ca4c452..3928b65 100644
--- a/src/server/routes/github-app.ts
+++ b/src/server/routes/github-app.ts
@@ -11,14 +11,14 @@ import { getCredentialToken } from "../../core/credentials";
import { APP_PROVIDER, appError, appUserToken, AppInstallation, clearAppTokenCache, exchangeAppToken,
githubRequest, GitHubRepositoryInfo, reconcileAppGrant, reconcileInstallation, installationURL, saveAppGrant, selectRepositoryAccess, userInstallations } from "../../core/github-app";
import { getUser, handleError } from "./route-utils";
-import { isDisabledAccount } from "./auth-utils";
+import { isDisabledAccount, safeAuthReturnTo } from "./auth-utils";
type Flow = { state: string; expires: number; ownerId?: string; returnTo: string; repository?: string; install?: boolean };
declare module "express-session" {
interface SessionData { githubAppFlow?: Flow; githubInstallFlow?: Flow; githubConnectionCSRF?: string; }
}
export function safeReturnTo(value: unknown): string {
- return typeof value === "string" && /^\/(?:(?:anonymize|pull-request-anonymize|gist-anonymize)(?:\/[\w-]+)?|connections|dashboard)(?:\?[^\\\r\n]*)?$/.test(value) ? value : "/connections";
+ return safeAuthReturnTo(value, "/connections");
}
export function consumeFlow(flow: Flow | undefined, state: unknown): Flow {
if (!flow || typeof state !== "string" || flow.state !== state || flow.expires < Date.now()) throw appError("invalid_auth_state", 400);
@@ -39,7 +39,7 @@ router.use((_req, res, next) => { res.set("Cache-Control", "no-store"); next();
router.get("/app/login", enabled, async (req, res) => {
try {
const ownerId = req.isAuthenticated() ? (await getUser(req)).id : undefined;
- const flow = newFlow(ownerId, req.query.returnTo);
+ const flow = newFlow(ownerId, req.query.returnTo || (ownerId ? "/connections" : "/dashboard"));
flow.install = req.query.install === "1";
if (typeof req.query.repository === "string" && /^[\w.-]+\/[\w.-]+$/.test(req.query.repository)) flow.repository = req.query.repository;
req.session.githubAppFlow = flow;
@@ -73,7 +73,14 @@ router.get("/app/callback", enabled, async (req, res) => {
}
if (!user) {
// A matching login name alone is not proof of account ownership.
- if (await UserModel.exists({ username: profile.login })) throw appError("github_account_link_required", 409);
+ const existing = await UserModel.findOne({ username: profile.login });
+ if (existing) {
+ if (existing.externalIDs?.github || isDisabledAccount(existing.status)) throw appError("github_identity_mismatch", 409);
+ req.session.githubRecovery = { ownerId: existing.id, githubId: String(profile.id), returnTo: flow.returnTo,
+ expires: Date.now() + 10 * 60000, recovery: true };
+ await saveSession(req);
+ return res.redirect("/signin?recover=1");
+ }
user = new UserModel({ username: profile.login, externalIDs: { github: String(profile.id) }, photo: profile.avatar_url, emails: [] });
await user.save();
}
diff --git a/test/credential-migration.test.js b/test/credential-migration.test.js
index 6fafe61..18afd77 100644
--- a/test/credential-migration.test.js
+++ b/test/credential-migration.test.js
@@ -192,7 +192,7 @@ describeMongo("credential access (MongoDB)", function () {
it("OAuth login writes a credential and returns a token-free session user", async () => {
const passport = require("passport");
require("../src/server/routes/connection");
- const result = await new Promise((resolve, reject) => passport._strategy("github")._verify("oauth-secret", "refresh-secret", {
+ const result = await new Promise((resolve, reject) => passport._strategy("github")._verify({ githubOAuthContext: { expires: Date.now() + 60000 } }, "oauth-secret", "refresh-secret", {
id: "external-test", username: owner.username, emails: [], photos: [],
}, (error, user) => error ? reject(error) : resolve(user)));
expect(JSON.stringify(result)).not.to.include("oauth-secret");
diff --git a/test/frontend-regressions.test.js b/test/frontend-regressions.test.js
index 80838ab..aed0e8d 100644
--- a/test/frontend-regressions.test.js
+++ b/test/frontend-regressions.test.js
@@ -197,7 +197,7 @@ describe("frontend production regressions", function () {
it("sends signed-out visitors to sign in instead of the dashboard", function () {
const manage = f => f.key === "manage";
const out = landing(null);
- expect(out.scope.featureHref(out.scope.features.find(manage))).to.equal("/github/login");
+ expect(out.scope.featureHref(out.scope.features.find(manage))).to.equal("/signin");
expect(out.scope.featureTarget(out.scope.features.find(manage))).to.equal("_self");
const signedIn = landing({ username: "jane" });
expect(signedIn.scope.featureHref(signedIn.scope.features.find(manage))).to.equal("/dashboard");
diff --git a/test/github-app.test.js b/test/github-app.test.js
index 876431b..6fb7fdb 100644
--- a/test/github-app.test.js
+++ b/test/github-app.test.js
@@ -1,4 +1,5 @@
const { expect } = require("chai");
+const { URL } = require("node:url");
const { createHmac, generateKeyPairSync, createVerify } = require("crypto");
const process = require("process");
const { setTimeout } = require("timers");
@@ -96,7 +97,7 @@ describe("GitHub App protocol boundaries", () => {
const describeMongo = process.env.TEST_MONGODB_URI ? describe : describe.skip;
describeMongo("GitHub App credential and repository integration", function () {
this.timeout(15000);
- let owner, previousConfig, previousFetch, calls, server, base, session;
+ let owner, previousConfig, previousFetch, calls, server, base, session, authenticated;
const data = (suffix = "1", expires = 3600) => ({ access_token: "ghu_access" + suffix, refresh_token: "ghr_refresh" + suffix,
expires_in: expires, refresh_token_expires_in: 100000 });
before(async () => {
@@ -111,13 +112,15 @@ describeMongo("GitHub App credential and repository integration", function () {
api.use(express.json());
api.use((req, _res, next) => {
req.session = session;
- req.user = { user: owner };
- req.isAuthenticated = () => true;
+ req.session.regenerate = done => done();
+ req.user = authenticated ? { user: owner } : undefined;
+ req.isAuthenticated = () => authenticated;
req.login = (identity, done) => { req.user = identity; done(); };
req.logout = done => done();
next();
});
api.use("/github", githubAppRouter);
+ api.use("/github", require("../src/server/routes/connection").router);
server = await new Promise(resolve => { const listening = api.listen(0, "127.0.0.1", () => resolve(listening)); });
base = `http://127.0.0.1:${server.address().port}`;
});
@@ -134,6 +137,7 @@ describeMongo("GitHub App credential and repository integration", function () {
calls = [];
await Credentials.deleteMany({}); await Users.deleteMany({}); await Installations.deleteMany({});
app.clearAppTokenCache();
+ authenticated = true;
owner = await Users.create({ username: "owner", externalIDs: { github: "10" } });
});
afterEach(() => { globalThis.fetch = previousFetch; });
@@ -249,6 +253,62 @@ describeMongo("GitHub App credential and repository integration", function () {
expect(replay.status).to.equal(400);
expect(calls).to.have.length(2);
});
+ it("signs an existing user in through the App without changing legacy access", async () => {
+ authenticated = false;
+ await setCredential(owner.id, "legacy-secret");
+ session.githubAppFlow = { state: "state", expires: Date.now() + 60000, returnTo: "/dashboard" };
+ mock(url => url.includes("/login/oauth/access_token") ? data() : { id: 10, login: "owner-renamed" });
+ const result = await request("/github/app/callback?state=state&code=code");
+ expect(result.location).to.equal("/dashboard");
+ expect(await Users.countDocuments()).to.equal(1);
+ expect(await getCredentialToken(owner.id)).to.equal("legacy-secret");
+ expect(await app.appUserToken(owner.id)).to.equal("ghu_access1");
+ });
+ it("requires legacy verification for an older account before linking its GitHub ID", async () => {
+ authenticated = false;
+ await Users.updateOne({ _id: owner._id }, { $unset: { externalIDs: 1 } });
+ session.githubAppFlow = { state: "state", expires: Date.now() + 60000, returnTo: "/gist-anonymize" };
+ mock(url => url.includes("/login/oauth/access_token") ? data() : { id: 10, login: "owner" });
+ const result = await request("/github/app/callback?state=state&code=code");
+ expect(result.location).to.equal("/signin?recover=1");
+ expect(session.githubRecovery.ownerId).to.equal(owner.id);
+ expect(session.githubRecovery.githubId).to.equal("10");
+ expect(await Credentials.countDocuments()).to.equal(0);
+ expect((await Users.findById(owner.id)).externalIDs?.github).to.equal(undefined);
+ const { verify } = require("../src/server/routes/connection");
+ const identity = await new Promise((resolve, reject) => verify({ githubOAuthContext: session.githubRecovery },
+ "verified-legacy", "", { id: "10", username: "owner" }, (error, user) => error ? reject(error) : resolve(user)));
+ expect(identity.user.id).to.equal(owner.id);
+ expect((await Users.findById(owner.id)).externalIDs.github).to.equal("10");
+ expect(await getCredentialToken(owner.id)).to.equal("verified-legacy");
+ });
+ it("returns OAuth connections to the gist form and rejects callback replay", async () => {
+ const strategy = require("passport")._strategy("github");
+ const exchange = strategy._oauth2.getOAuthAccessToken;
+ const profile = strategy.userProfile;
+ strategy._oauth2.getOAuthAccessToken = (_code, _params, done) => done(null, "gist-oauth", "", {});
+ strategy.userProfile = (_token, done) => done(null, { id: "10", username: "owner" });
+ try {
+ const start = await request("/github/login?returnTo=%2Fgist-anonymize%2Fsaved");
+ const state = new URL(start.location).searchParams.get("state");
+ const result = await request("/github/auth?code=test&state=" + state);
+ expect(result.location).to.equal("/gist-anonymize/saved");
+ expect(await getCredentialToken(owner.id)).to.equal("gist-oauth");
+ expect((await request("/github/auth?code=test&state=" + state)).location).to.equal("/signin");
+ await request("/github/login?returnTo=https%3A%2F%2Fevil.test");
+ expect(session.githubOAuthFlow.returnTo).to.equal("/connections");
+ } finally {
+ strategy._oauth2.getOAuthAccessToken = exchange;
+ strategy.userProfile = profile;
+ }
+ });
+ it("rejects a different account during OAuth recovery before saving credentials", async () => {
+ const { verify } = require("../src/server/routes/connection");
+ await rejects(new Promise((resolve, reject) => verify({ githubOAuthContext: { ownerId: owner.id, githubId: "10", expires: Date.now() + 60000 } },
+ "wrong-token", "", { id: "99", username: "owner" }, (error, user) => error ? reject(error) : resolve(user))), "github_identity_mismatch");
+ expect(await Credentials.countDocuments()).to.equal(0);
+ expect((await Users.findById(owner.id)).externalIDs.github).to.equal("10");
+ });
it("rejects linking a different GitHub identity", async () => {
session.githubAppFlow = { state: "state", ownerId: owner.id, expires: Date.now() + 60000, returnTo: "/connections" };
mock(url => url.includes("/login/oauth/access_token") ? data() : { id: 99, login: "other" });
diff --git a/test/production-regressions.test.js b/test/production-regressions.test.js
index 07b1265..a2c0c82 100644
--- a/test/production-regressions.test.js
+++ b/test/production-regressions.test.js
@@ -146,7 +146,7 @@ describe("production regressions", function () {
let calls = 0;
stub(UserModel, "findOne", async () => ++calls === 1 ? null : { externalIDs: { github: "old-id" }, isAdmin: true });
stub(UserModel, "updateOne", () => { throw new Error("must not overwrite identity"); });
- const error = await new Promise(resolve => passport._strategy("github")._verify("token", "", { id: "new-id", username: "recycled" }, resolve));
+ const error = await new Promise(resolve => passport._strategy("github")._verify({ githubOAuthContext: { expires: Date.now() + 60000 } }, "token", "", { id: "new-id", username: "recycled" }, resolve));
expect(error.message).to.equal("not_connected");
});
diff --git a/test/vue-ui.test.js b/test/vue-ui.test.js
index 2c3ed14..a8c9d60 100644
--- a/test/vue-ui.test.js
+++ b/test/vue-ui.test.js
@@ -89,10 +89,44 @@ describe("Vue 3 UI", function () {
expect(ui.errors).to.deep.equal([]);
});
- it("offers App and OAuth sign-in and direct repository access links", async function () {
+ it("offers one App sign-in for new and existing accounts", async function () {
ui = await browser("/signin", { "/api/options": { GITHUB_APP_ENABLED: true, GITHUB_OAUTH_ENABLED: true } });
expect(ui.window.document.querySelector('a[href="/github/app/login"]')).not.to.equal(null);
+ expect(ui.window.document.querySelector('a[href="/github/login"]')).to.equal(null);
+ expect(ui.errors).to.deep.equal([]);
+ });
+
+ it("shows previous-connection verification only for a pending recovery", async function () {
+ ui = await browser("/signin?recover=1", {
+ "/api/options": { GITHUB_APP_ENABLED: true, GITHUB_OAUTH_ENABLED: true },
+ "/github/account-recovery": { required: true },
+ });
+ expect(ui.window.document.querySelector('a[href="/github/login?recover=1"]')).not.to.equal(null);
+ expect(ui.window.document.querySelector('a[href="/github/app/login"]')).to.equal(null);
+ expect(ui.errors).to.deep.equal([]);
+ });
+
+ it("falls back to one OAuth sign-in when the App is disabled", async function () {
+ ui = await browser("/signin", { "/api/options": { GITHUB_APP_ENABLED: false, GITHUB_OAUTH_ENABLED: true } });
expect(ui.window.document.querySelector('a[href="/github/login"]')).not.to.equal(null);
+ expect(ui.window.document.querySelector('a[href="/github/app/login"]')).to.equal(null);
+ });
+
+ it("offers OAuth for an App-only gist and saves the draft before connecting", async function () {
+ ui = await browser("/gist-anonymize", {
+ "/api/user": { username: "owner" },
+ "/github/connections": { appEnabled: true, appConnected: true, oauthEnabled: true, oauthConnected: false },
+ });
+ const input = await ui.input("#sourceUrl", "https://gist.github.com/311fc9");
+ input.dispatchEvent(new ui.window.Event("blur"));
+ await delay(60);
+ const button = [...ui.window.document.querySelectorAll("button")].find(node => node.textContent.includes("Connect GitHub OAuth to access gists"));
+ expect(button).not.to.equal(undefined);
+ expect(ui.requests.some(r => r.url.pathname === "/api/gist/source/311fc9")).to.equal(false);
+ button.click();
+ const saved = JSON.parse(ui.window.sessionStorage.getItem("github-access-draft"));
+ expect(saved.path).to.equal("/gist-anonymize");
+ expect(saved.draft.sourceUrl).to.equal("https://gist.github.com/311fc9");
expect(ui.errors).to.deep.equal([]);
});