@yuebai008/cli
Version:
Command line interface for rapid qg-minigame development
1 lines • 20.4 kB
JavaScript
import*as Common from"../../core/common/common.js";import*as Host from"../../core/host/host.js";import*as Platform from"../../core/platform/platform.js";import*as Root from"../../core/root/root.js";import*as SDK from"../../core/sdk/sdk.js";import*as UI from"../../ui/legacy/legacy.js";import*as Breakpoints from"../breakpoints/breakpoints.js";import*as Workspace from"../workspace/workspace.js";import{FileSystemWorkspaceBinding}from"./FileSystemWorkspaceBinding.js";import{IsolatedFileSystemManager}from"./IsolatedFileSystemManager.js";import{PersistenceBinding,PersistenceImpl}from"./PersistenceImpl.js";let networkPersistenceManagerInstance;export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrapper{bindings;originalResponseContentPromises;savingForOverrides;savingSymbol;enabledSetting;workspace;networkUISourceCodeForEncodedPath;interceptionHandlerBound;updateInterceptionThrottler;projectInternal;activeProject;activeInternal;enabled;eventDescriptors;#e=new Map;#t=new WeakMap;#r;#o;constructor(e){super(),this.bindings=new WeakMap,this.originalResponseContentPromises=new WeakMap,this.savingForOverrides=new WeakSet,this.savingSymbol=Symbol("SavingForOverrides"),this.enabledSetting=Common.Settings.Settings.instance().moduleSetting("persistenceNetworkOverridesEnabled"),this.enabledSetting.addChangeListener(this.enabledChanged,this),this.workspace=e,this.networkUISourceCodeForEncodedPath=new Map,this.interceptionHandlerBound=this.interceptionHandler.bind(this),this.updateInterceptionThrottler=new Common.Throttler.Throttler(50),this.#r=new Common.Throttler.Throttler(50),this.#o=new Set,this.projectInternal=null,this.activeProject=null,this.activeInternal=!1,this.enabled=!1,this.workspace.addEventListener(Workspace.Workspace.Events.ProjectAdded,(e=>{this.onProjectAdded(e.data)})),this.workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved,(e=>{this.onProjectRemoved(e.data)})),PersistenceImpl.instance().addNetworkInterceptor(this.canHandleNetworkUISourceCode.bind(this)),Breakpoints.BreakpointManager.BreakpointManager.instance().addUpdateBindingsCallback(this.networkUISourceCodeAdded.bind(this)),this.eventDescriptors=[],this.enabledChanged(),SDK.TargetManager.TargetManager.instance().observeTargets(this)}targetAdded(){this.updateActiveProject()}targetRemoved(){this.updateActiveProject()}static instance(e={forceNew:null,workspace:null}){const{forceNew:t,workspace:r}=e;if(!networkPersistenceManagerInstance||t){if(!r)throw new Error("Missing workspace for NetworkPersistenceManager");networkPersistenceManagerInstance=new NetworkPersistenceManager(r)}return networkPersistenceManagerInstance}active(){return this.activeInternal}project(){return this.projectInternal}originalContentForUISourceCode(e){const t=this.bindings.get(e);if(!t)return null;const r=t.fileSystem;return this.originalResponseContentPromises.get(r)||null}async enabledChanged(){this.enabled!==this.enabledSetting.get()&&(this.enabled=this.enabledSetting.get(),this.enabled?(Host.userMetrics.actionTaken(Host.UserMetrics.Action.PersistenceNetworkOverridesEnabled),this.eventDescriptors=[Workspace.Workspace.WorkspaceImpl.instance().addEventListener(Workspace.Workspace.Events.UISourceCodeRenamed,(e=>{this.uiSourceCodeRenamedListener(e)})),Workspace.Workspace.WorkspaceImpl.instance().addEventListener(Workspace.Workspace.Events.UISourceCodeAdded,(e=>{this.uiSourceCodeAdded(e)})),Workspace.Workspace.WorkspaceImpl.instance().addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved,(e=>{this.uiSourceCodeRemovedListener(e)})),Workspace.Workspace.WorkspaceImpl.instance().addEventListener(Workspace.Workspace.Events.WorkingCopyCommitted,(e=>this.onUISourceCodeWorkingCopyCommitted(e.data.uiSourceCode)))],await this.updateActiveProject()):(Host.userMetrics.actionTaken(Host.UserMetrics.Action.PersistenceNetworkOverridesDisabled),Common.EventTarget.removeEventListeners(this.eventDescriptors),await this.updateActiveProject()),this.dispatchEventToListeners(Events.LocalOverridesProjectUpdated,this.enabled))}async uiSourceCodeRenamedListener(e){const t=e.data.uiSourceCode;await this.onUISourceCodeRemoved(t),await this.onUISourceCodeAdded(t)}async uiSourceCodeRemovedListener(e){await this.onUISourceCodeRemoved(e.data)}async uiSourceCodeAdded(e){await this.onUISourceCodeAdded(e.data)}async updateActiveProject(){const e=this.activeInternal;if(this.activeInternal=Boolean(this.enabledSetting.get()&&SDK.TargetManager.TargetManager.instance().rootTarget()&&this.projectInternal),this.activeInternal!==e){if(this.activeInternal&&this.projectInternal){await Promise.all([...this.projectInternal.uiSourceCodes()].map((e=>this.filesystemUISourceCodeAdded(e))));const e=this.workspace.projectsForType(Workspace.Workspace.projectTypes.Network);for(const t of e)await Promise.all([...t.uiSourceCodes()].map((e=>this.networkUISourceCodeAdded(e))))}else this.projectInternal&&(await Promise.all([...this.projectInternal.uiSourceCodes()].map((e=>this.filesystemUISourceCodeRemoved(e)))),this.networkUISourceCodeForEncodedPath.clear());PersistenceImpl.instance().refreshAutomapping()}}encodedPathFromUrl(e,t){return Common.ParsedURL.ParsedURL.rawPathToEncodedPathString(this.rawPathFromUrl(e,t))}rawPathFromUrl(e,t){if(!this.activeInternal&&!t||!this.projectInternal)return Platform.DevToolsPath.EmptyRawPathString;let r=Common.ParsedURL.ParsedURL.urlWithoutHash(e.replace(/^https?:\/\//,""));r.endsWith("/")&&-1===r.indexOf("?")&&(r=Common.ParsedURL.ParsedURL.concatenate(r,"index.html"));let o=NetworkPersistenceManager.encodeEncodedPathToLocalPathParts(r);const s=FileSystemWorkspaceBinding.fileSystemPath(this.projectInternal.id()),n=o.join("/");if(s.length+n.length>200){const e=o[0],t=o[o.length-1],s=t?t.substr(0,10)+"-":"",a=Common.ParsedURL.ParsedURL.extractExtension(r),i=a?"."+a.substr(0,10):"";o=[e,"longurls",s+Platform.StringUtilities.hashCode(n).toString(16)+i]}return Common.ParsedURL.ParsedURL.join(o,"/")}static encodeEncodedPathToLocalPathParts(e){const t=[];for(const r of this.#s(e)){if(!r)continue;let e=encodeURI(r).replace(/[\/\*]/g,(e=>"%"+e[0].charCodeAt(0).toString(16).toUpperCase()));if(Host.Platform.isWin()){e=e.replace(/[:\?]/g,(e=>"%"+e[0].charCodeAt(0).toString(16).toUpperCase())),RESERVED_FILENAMES.has(e.toLowerCase())&&(e=e.split("").map((e=>"%"+e.charCodeAt(0).toString(16).toUpperCase())).join(""));"."===e.charAt(e.length-1)&&(e=e.substr(0,e.length-1)+"%2E")}t.push(e)}return t}static#s(e){const t=(e=Common.ParsedURL.ParsedURL.urlWithoutHash(e)).indexOf("?");if(-1===t)return e.split("/");if(0===t)return[e];const r=e.substr(t),o=e.substr(0,e.length-r.length).split("/");return o[o.length-1]+=r,o}fileUrlFromNetworkUrl(e,t){return this.projectInternal?Common.ParsedURL.ParsedURL.concatenate(this.projectInternal.fileSystemPath(),"/",this.encodedPathFromUrl(e,t)):Platform.DevToolsPath.EmptyUrlString}getHeadersUISourceCodeFromUrl(e){const t=this.fileUrlFromNetworkUrl(e,!0),r=Common.ParsedURL.ParsedURL.substring(t,0,t.lastIndexOf("/")),o=Common.ParsedURL.ParsedURL.concatenate(r,"/",HEADERS_FILENAME);return Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(o)}async getOrCreateHeadersUISourceCodeFromUrl(e){let t=this.getHeadersUISourceCodeFromUrl(e);if(!t&&this.projectInternal){const r=this.encodedPathFromUrl(e,!0),o=Common.ParsedURL.ParsedURL.substring(r,0,r.lastIndexOf("/"));t=await this.projectInternal.createFile(o,HEADERS_FILENAME,""),Host.userMetrics.actionTaken(Host.UserMetrics.Action.HeaderOverrideFileCreated)}return t}decodeLocalPathToUrlPath(e){try{return unescape(e)}catch(e){console.error(e)}return e}async#n(e){const t=this.bindings.get(e);if(t){const e=this.#a(t.network);await e.run(this.#i.bind(this,t))}}async#d(e){const t=this.bindings.get(e);t&&await this.#i(t)}#i(e){return this.bindings.delete(e.network),this.bindings.delete(e.fileSystem),PersistenceImpl.instance().removeBinding(e)}async#c(e,t){const r=this.#a(e);await r.run((async()=>{const r=this.bindings.get(e);if(r){const{network:o,fileSystem:s}=r;if(e===o&&t===s)return;await this.#d(e),await this.#d(t)}await this.#l(e,t)}))}#a(e){let t=this.#t.get(e);return t||(t=new Common.Mutex.Mutex,this.#t.set(e,t)),t}async#l(e,t){const r=new PersistenceBinding(e,t);this.bindings.set(e,r),this.bindings.set(t,r),await PersistenceImpl.instance().addBinding(r);const o=this.savingForOverrides.has(e)?e:t,{content:s,isEncoded:n}=await o.requestContent();PersistenceImpl.instance().syncContent(o,s||"",n)}onUISourceCodeWorkingCopyCommitted(e){this.saveUISourceCodeForOverrides(e),this.updateInterceptionPatterns()}isUISourceCodeOverridable(e){return e.project().type()===Workspace.Workspace.projectTypes.Network}#h(e){return this.bindings.has(e)||this.savingForOverrides.has(e)}#p(e){return this.isUISourceCodeOverridable(e)&&!this.#h(e)&&!this.activeInternal&&!this.projectInternal}#u(e){return this.activeInternal&&this.isUISourceCodeOverridable(e)&&!this.#h(e)}async setupAndStartLocalOverrides(e){return this.#p(e)&&(Host.userMetrics.actionTaken(Host.UserMetrics.Action.OverrideContentContextMenuSetup),await new Promise((e=>UI.InspectorView.InspectorView.instance().displaySelectOverrideFolderInfobar(e))),await IsolatedFileSystemManager.instance().addFileSystem("overrides")),this.project()?(this.enabledSetting.get()||(Host.userMetrics.actionTaken(Host.UserMetrics.Action.OverrideContentContextMenuActivateDisabled),this.enabledSetting.set(!0),await this.once(Events.LocalOverridesProjectUpdated)),this.#h(e)?Host.userMetrics.actionTaken(Host.UserMetrics.Action.OverrideContentContextMenuOpenExistingFile):(Host.userMetrics.actionTaken(Host.UserMetrics.Action.OverrideContentContextMenuSaveNewFile),e.commitWorkingCopy(),await this.saveUISourceCodeForOverrides(e)),!0):(Host.userMetrics.actionTaken(Host.UserMetrics.Action.OverrideContentContextMenuAbandonSetup),!1)}async saveUISourceCodeForOverrides(e){if(!this.#u(e))return;this.savingForOverrides.add(e);let t=this.encodedPathFromUrl(e.url());const{content:r,isEncoded:o}=await e.requestContent(),s=t.lastIndexOf("/"),n=Common.ParsedURL.ParsedURL.substring(t,s+1),a=Common.ParsedURL.ParsedURL.encodedPathToRawPathString(n);t=Common.ParsedURL.ParsedURL.substr(t,0,s),this.projectInternal&&await this.projectInternal.createFile(t,a,r??"",o),this.fileCreatedForTest(t,a),this.savingForOverrides.delete(e)}fileCreatedForTest(e,t){}patternForFileSystemUISourceCode(e){const t=FileSystemWorkspaceBinding.relativePath(e);if(t.length<2)return"";if("longurls"===t[1]&&2!==t.length)return"file:"===t[0]?"file:///*":"http?://"+t[0]+"/*";const r=this.decodeLocalPathToUrlPath(this.decodeLocalPathToUrlPath(t.join("/")));return r.startsWith("file:/")?"file:///"+r.substring(6):"http?://"+r}isForbiddenUrl(e){const t=FileSystemWorkspaceBinding.relativePath(e),r=this.decodeLocalPathToUrlPath(this.decodeLocalPathToUrlPath(t[0]||""));return["chrome:","chromewebstore.google.com","chrome.google.com"].includes(r)}async onUISourceCodeAdded(e){await this.networkUISourceCodeAdded(e),await this.filesystemUISourceCodeAdded(e)}canHandleNetworkUISourceCode(e){return this.activeInternal&&!e.url().startsWith("snippet://")}async networkUISourceCodeAdded(e){if(e.project().type()!==Workspace.Workspace.projectTypes.Network||!this.canHandleNetworkUISourceCode(e))return;const t=Common.ParsedURL.ParsedURL.urlWithoutHash(e.url());this.networkUISourceCodeForEncodedPath.set(this.encodedPathFromUrl(t),e);const r=this.projectInternal.uiSourceCodeForURL(this.fileUrlFromNetworkUrl(t));r&&await this.#c(e,r),this.#m(e)}async filesystemUISourceCodeAdded(e){if(!this.activeInternal||e.project()!==this.projectInternal)return;this.updateInterceptionPatterns();const t=FileSystemWorkspaceBinding.relativePath(e),r=this.networkUISourceCodeForEncodedPath.get(Common.ParsedURL.ParsedURL.join(t,"/"));r&&await this.#c(r,e)}async#g(e){const t=(await e.requestContent()).content||"[]";let r=[];try{if(r=JSON.parse(t),!r.every(isHeaderOverride))throw"Type mismatch after parsing"}catch(t){return console.error("Failed to parse",e.url(),"for locally overriding headers."),[]}return r}#P(e){const t=this.decodeLocalPathToUrlPath(e);return{singlyDecodedPath:t,decodedPath:this.decodeLocalPathToUrlPath(t)}}async generateHeaderPatterns(e){const t=await this.#g(e),r=FileSystemWorkspaceBinding.relativePath(e),o=Common.ParsedURL.ParsedURL.slice(Common.ParsedURL.ParsedURL.join(r,"/"),0,-HEADERS_FILENAME.length),{singlyDecodedPath:s,decodedPath:n}=this.#P(o);let a;return a=r.length>2&&"longurls"===r[1]&&t.length?this.#C(n,t,r[0]):n.startsWith("file:/")?this.#U(Common.ParsedURL.ParsedURL.substring(n,6),t):this.#v(n,t),{...a,path:s}}#v(e,t){const r=new Set,o=[];for(const s of t){r.add("http?://"+e+s.applyTo),""===e&&(r.add("file:///"+s.applyTo),o.push({applyToRegex:new RegExp("^file:///"+escapeRegex(e+s.applyTo)+"$"),headers:s.headers}));const{head:t,tail:n}=extractDirectoryIndex(s.applyTo);n?(r.add("http?://"+e+t),o.push({applyToRegex:new RegExp(`^${escapeRegex(e+t)}(${escapeRegex(n)})?$`),headers:s.headers})):o.push({applyToRegex:new RegExp(`^${escapeRegex(e+s.applyTo)}$`),headers:s.headers})}return{headerPatterns:r,overridesWithRegex:o}}#U(e,t){const r=new Set,o=[];for(const s of t)r.add("file:///"+e+s.applyTo),o.push({applyToRegex:new RegExp(`^file:/${escapeRegex(e+s.applyTo)}$`),headers:s.headers});return{headerPatterns:r,overridesWithRegex:o}}#C(e,t,r){const o=new Set;let{decodedPath:s}=this.#P(Common.ParsedURL.ParsedURL.concatenate(r,"/*"));const n=e.startsWith("file:/");n&&(e=Common.ParsedURL.ParsedURL.substring(e,6),s=Common.ParsedURL.ParsedURL.substring(s,6)),o.add((n?"file:///":"http?://")+s);const a=[];for(const r of t)a.push({applyToRegex:new RegExp(`^${n?"file:/":""}${escapeRegex(e+r.applyTo)}$`),headers:r.headers});return{headerPatterns:o,overridesWithRegex:a}}async updateInterceptionPatternsForTests(){await this.#S()}updateInterceptionPatterns(){this.updateInterceptionThrottler.schedule(this.#S.bind(this))}async#S(){if(this.#e.clear(),!this.activeInternal||!this.projectInternal)return SDK.NetworkManager.MultitargetNetworkManager.instance().setInterceptionHandlerForPatterns([],this.interceptionHandlerBound);let e=new Set;for(const t of this.projectInternal.uiSourceCodes()){if(this.isForbiddenUrl(t))continue;const r=this.patternForFileSystemUISourceCode(t);if(Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.HEADER_OVERRIDES)&&t.name()===HEADERS_FILENAME){const{headerPatterns:r,path:o,overridesWithRegex:s}=await this.generateHeaderPatterns(t);r.size>0&&(e=new Set([...e,...r]),this.#e.set(o,s))}else e.add(r);const{head:o,tail:s}=extractDirectoryIndex(r);s&&e.add(o)}return SDK.NetworkManager.MultitargetNetworkManager.instance().setInterceptionHandlerForPatterns(Array.from(e).map((e=>({urlPattern:e,requestStage:"Response"}))),this.interceptionHandlerBound)}async onUISourceCodeRemoved(e){await this.networkUISourceCodeRemoved(e),await this.filesystemUISourceCodeRemoved(e)}async networkUISourceCodeRemoved(e){e.project().type()===Workspace.Workspace.projectTypes.Network&&(await this.#n(e),this.#t.delete(e),this.networkUISourceCodeForEncodedPath.delete(this.encodedPathFromUrl(e.url()))),this.#m(e)}#m(e){if(!this.projectInternal)return;const t=this.projectInternal,r=this.fileUrlFromNetworkUrl(e.url());for(let e=t.fileSystemPath().length;e<r.length;e++){if("/"!==r[e])continue;const o=Common.ParsedURL.ParsedURL.concatenate(Common.ParsedURL.ParsedURL.substring(r,0,e+1),".headers"),s=t.uiSourceCodeForURL(o);s&&(this.#o.add(s),this.#r.schedule(this.#I.bind(this)))}}#I(){for(const e of this.#o)this.dispatchEventToListeners(Events.RequestsForHeaderOverridesFileChanged,e);return this.#o.clear(),Promise.resolve()}hasMatchingNetworkUISourceCodeForHeaderOverridesFile(e){const t=FileSystemWorkspaceBinding.relativePath(e),r=Common.ParsedURL.ParsedURL.slice(Common.ParsedURL.ParsedURL.join(t,"/"),0,-HEADERS_FILENAME.length);for(const e of this.networkUISourceCodeForEncodedPath.keys())if(e.startsWith(r))return!0;return!1}async filesystemUISourceCodeRemoved(e){e.project()===this.projectInternal&&(this.updateInterceptionPatterns(),this.originalResponseContentPromises.delete(e),await this.#n(e))}async setProject(e){e!==this.projectInternal&&(this.projectInternal&&await Promise.all([...this.projectInternal.uiSourceCodes()].map((e=>this.filesystemUISourceCodeRemoved(e)))),this.projectInternal=e,this.projectInternal&&await Promise.all([...this.projectInternal.uiSourceCodes()].map((e=>this.filesystemUISourceCodeAdded(e)))),await this.updateActiveProject(),this.dispatchEventToListeners(Events.ProjectChanged,this.projectInternal))}async onProjectAdded(e){if(e.type()!==Workspace.Workspace.projectTypes.FileSystem||"overrides"!==FileSystemWorkspaceBinding.fileSystemType(e))return;FileSystemWorkspaceBinding.fileSystemPath(e.id())&&(this.projectInternal&&this.projectInternal.remove(),await this.setProject(e))}async onProjectRemoved(e){for(const t of e.uiSourceCodes())await this.networkUISourceCodeRemoved(t);e===this.projectInternal&&await this.setProject(null)}mergeHeaders(e,t){const r=new Platform.MapUtilities.Multimap;for(const{name:e,value:o}of t)"set-cookie"!==e.toLowerCase()&&r.set(e.toLowerCase(),o);const o=new Set(r.keysArray());for(const{name:t,value:s}of e){const e=t.toLowerCase();o.has(e)||"set-cookie"===e||r.set(e,s)}const s=[];for(const e of r.keysArray())for(const t of r.get(e))s.push({name:e,value:t});const n=e.filter((e=>"set-cookie"===e.name.toLowerCase()))||[],a=t.filter((e=>"set-cookie"===e.name.toLowerCase())),i=SDK.NetworkManager.InterceptedRequest.mergeSetCookieHeaders(n,a);return s.push(...i),s}#w(e,t,r){const o=this.#e.get(e)||[];for(const e of o){const o=this.decodeLocalPathToUrlPath(this.rawPathFromUrl(t));e.applyToRegex.test(o)&&(r=this.mergeHeaders(r,e.headers))}return r}handleHeaderInterception(e){let t=e.responseHeaders||[];const r=this.rawPathFromUrl(e.request.url).split("/");let o=Platform.DevToolsPath.EmptyEncodedPathString;t=this.#w(o,e.request.url,t);for(const s of r)o=Common.ParsedURL.ParsedURL.concatenate(o,s,"/"),t=this.#w(o,e.request.url,t);return t}async interceptionHandler(e){const t=e.request.method;if(!this.activeInternal||"OPTIONS"===t)return;const r=this.projectInternal,o=this.fileUrlFromNetworkUrl(e.request.url),s=r.uiSourceCodeForURL(o);let n=[];if(Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.HEADER_OVERRIDES)&&(n=this.handleHeaderInterception(e)),!s&&!n.length)return;n.length||(n=e.responseHeaders||[]);let a="";if(e.responseHeaders)for(const t of e.responseHeaders)if("content-type"===t.name.toLowerCase()){a=t.value;break}if(!a){const t=Common.ResourceType.resourceTypes[e.resourceType]||Common.ResourceType.resourceTypes.Other;a=s?.mimeType()||"",Common.ResourceType.ResourceType.fromMimeType(a)!==t&&(a=t.canonicalMimeType())}if(s){this.originalResponseContentPromises.set(s,e.responseBody().then((e=>{if(e.error||null===e.content)return null;if(e.encoded){const t=atob(e.content),r=new Uint8Array(t.length);for(let e=0;e<t.length;++e)r[e]=t.charCodeAt(e);return new TextDecoder("utf-8").decode(r)}return e.content})));const t=s.project(),r=await t.requestFileBlob(s);r&&e.continueRequestWithContent(new Blob([r],{type:a}),!1,n,!0)}else if(e.isRedirect())e.continueRequestWithContent(new Blob([],{type:a}),!0,n,!1);else{const t=await e.responseBody();!t.error&&t.content&&e.continueRequestWithContent(new Blob([t.content],{type:a}),!0,n,!1)}}}const RESERVED_FILENAMES=new Set(["con","prn","aux","nul","com1","com2","com3","com4","com5","com6","com7","com8","com9","lpt1","lpt2","lpt3","lpt4","lpt5","lpt6","lpt7","lpt8","lpt9"]);export const HEADERS_FILENAME=".headers";export var Events;!function(e){e.ProjectChanged="ProjectChanged",e.RequestsForHeaderOverridesFileChanged="RequestsForHeaderOverridesFileChanged",e.LocalOverridesProjectUpdated="LocalOverridesProjectUpdated"}(Events||(Events={}));export function isHeaderOverride(e){return!!(e&&"string"==typeof e.applyTo&&e.headers&&e.headers.length&&Array.isArray(e.headers))&&e.headers.every((e=>"string"==typeof e.name&&"string"==typeof e.value))}export function escapeRegex(e){return Platform.StringUtilities.escapeCharacters(e,"[]{}()\\.^$+|-,?").replaceAll("*",".*")}export function extractDirectoryIndex(e){const t=e.lastIndexOf("/"),r=t>=0?e.slice(t+1):e,o=t>=0?e.slice(0,t+1):"",s=new RegExp("^"+escapeRegex(r)+"$");return"*"!==r&&(s.test("index.html")||s.test("index.htm")||s.test("index.php"))?{head:o,tail:r}:{head:e}}