Spaces:
Running
Running
Download modules/space_exploration/src/host/base_runtime_url.js from CREATORJD/massfront-playtest: direct link, hf CLI and curl.
- Browser
- Download file 7.49 kB
-
https://huggingface.co/spaces/CREATORJD/massfront-playtest/resolve/main/modules/space_exploration/src/host/base_runtime_url.js
- Command line
-
hf download hf://spaces/CREATORJD/massfront-playtest/modules/space_exploration/src/host/base_runtime_url.js
-
curl -L -o base_runtime_url.js https://huggingface.co/spaces/CREATORJD/massfront-playtest/resolve/main/modules/space_exploration/src/host/base_runtime_url.js
7.49 kB
| /* A native content mount lives below Capacitor's same-origin file mapping, | |
| while the installed updater, audio and commander portraits stay at their | |
| original game root. Never infer that root by climbing a filesystem URL. */ | |
| export const NATIVE_EXPLORATION_MOUNT_KEY = 'massfront.exploration.mount.v1'; | |
| export const NATIVE_EXPLORATION_READY_KEY = 'massfront.exploration.mount.ready.v1'; | |
| const MOUNT_KEYS = ['baseUrl', 'generation', 'issuedAt', 'kind', 'moduleUrl', 'schema', 'token']; | |
| const MOUNT_SUFFIX = '/modules/space_exploration/index.html'; | |
| const nativeLoaderResolvers = new WeakSet(); | |
| function canonicalUrl(value) { | |
| if (typeof value !== 'string') return null; | |
| const url = new URL(value); | |
| if (!['https:', 'http:'].includes(url.protocol) || url.href !== value | |
| || url.username || url.password || url.search || url.hash) return null; | |
| return url; | |
| } | |
| export function readNativeExplorationMount(options = {}) { | |
| try { | |
| const storage = options.sessionStorage || globalThis.sessionStorage; | |
| const location = options.location || globalThis.location; | |
| const now = options.now === undefined ? Date.now() : options.now; | |
| const current = new URL(location.href); | |
| const record = JSON.parse(storage.getItem(NATIVE_EXPLORATION_MOUNT_KEY) || 'null'); | |
| if (!record || Array.isArray(record) | |
| || Object.keys(record).sort().join('|') !== MOUNT_KEYS.join('|') | |
| || record.schema !== 1 || record.kind !== 'MassfrontNativeExplorationMountV1' | |
| || typeof record.generation !== 'string' || typeof record.token !== 'string' | |
| || !/^[a-f0-9]{64}$/.test(record.generation) || !/^[a-f0-9]{32}$/.test(record.token) | |
| || !Number.isSafeInteger(record.issuedAt) || record.issuedAt < 0 | |
| || !Number.isSafeInteger(now) || now < record.issuedAt) return null; | |
| const base = canonicalUrl(record.baseUrl), module = canonicalUrl(record.moduleUrl); | |
| if (!base || !module || base.origin !== current.origin || module.origin !== current.origin | |
| || !base.pathname.endsWith('/index.html') || base.pathname.startsWith('/_capacitor_file_') | |
| || !module.pathname.startsWith('/_capacitor_file_/') || !module.pathname.endsWith(MOUNT_SUFFIX) | |
| || current.pathname !== module.pathname | |
| || /%(?:2e|2f|5c)/i.test(module.pathname + base.pathname)) return null; | |
| return Object.freeze({ ...record }); | |
| } catch (_) { return null; } | |
| } | |
| export function resolveBaseRuntimeUrl(relativeUrl, referenceUrl, options = {}) { | |
| const fallback = new URL(relativeUrl, referenceUrl); | |
| const mount = readNativeExplorationMount(options); | |
| if (!mount) return fallback; | |
| const module = new URL(mount.moduleUrl), reference = new URL(referenceUrl); | |
| const moduleRoot = module.pathname.slice(0, -'index.html'.length); | |
| const contentRoot = module.pathname.slice(0, -'modules/space_exploration/index.html'.length); | |
| if (reference.origin !== module.origin || !reference.pathname.startsWith(moduleRoot) | |
| || fallback.origin !== module.origin || !fallback.pathname.startsWith(contentRoot) | |
| || fallback.pathname.startsWith(moduleRoot)) return fallback; | |
| const assetPath = fallback.pathname.slice(contentRoot.length); | |
| // Only base assets are relocated; module imports, GLBs and their external | |
| // textures retain the immutable generation path and dependency closure. | |
| if (!assetPath.startsWith('assets/') || /%(?:2e|2f|5c)/i.test(assetPath)) return fallback; | |
| return new URL(assetPath + fallback.search + fallback.hash, new URL('./', mount.baseUrl)); | |
| } | |
| export function resolveBaseRuntimeNavigation(url, options = {}) { | |
| const mount = readNativeExplorationMount(options); | |
| if (typeof url !== 'string' || !/^(?:\.\.\/)+index\.html(?:[?#].*)?$/.test(url)) return url; | |
| if (!mount) { | |
| const current = new URL((options.location || globalThis.location).href); | |
| if (current.pathname.startsWith('/_capacitor_file_/')) { | |
| /* Capacitor's packaged document is always https://localhost/index.html. | |
| A damaged session mount used to make the emergency return throw even | |
| though that unprivileged origin-root document was still available. | |
| Keep the fallback deliberately narrower than the mounted resolver: it | |
| may only leave a local Capacitor file route for localhost itself. */ | |
| if (['https:', 'http:'].includes(current.protocol) && current.hostname === 'localhost' | |
| && !current.username && !current.password) { | |
| return new URL(`/${url.slice(url.indexOf('index.html'))}`, current.origin).href; | |
| } | |
| throw new Error('The installed-game return path is unavailable. Restart MASSFRONT to recover safely.'); | |
| } | |
| return url; | |
| } | |
| // This is a routing record, not authorization. Expiring it overnight would | |
| // strand valid missions in DATA; profile tickets and mission nonces retain | |
| // their independent security lifetimes and validation. | |
| // Existing host callers pass ../../../index.html. That happens to clamp to | |
| // the origin root in ordinary installs, but would escape the mounted tree. | |
| return new URL(url.slice(url.indexOf('index.html')), new URL('./', mount.baseUrl)).href; | |
| } | |
| export function nativeExplorationLoaderUrl(value, options = {}) { | |
| const mount = readNativeExplorationMount(options); | |
| if (!mount || typeof value !== 'string' || !/^https?:\/\//i.test(value)) return value; | |
| try { | |
| const url = new URL(value), module = new URL(mount.moduleUrl), base = new URL('./', mount.baseUrl); | |
| const moduleRoot = module.pathname.slice(0, -'index.html'.length); | |
| if (url.origin !== module.origin || url.username || url.password | |
| || (!url.pathname.startsWith(moduleRoot) && !url.pathname.startsWith(base.pathname + 'assets/'))) return value; | |
| return url.pathname + url.search + url.hash; | |
| } catch (_) { return value; } | |
| } | |
| export function installNativeExplorationLoaderUrls(options = {}) { | |
| if (!readNativeExplorationMount(options)) return false; | |
| const manager = options.manager || globalThis.THREE?.DefaultLoadingManager; | |
| if (!manager || typeof manager.resolveURL !== 'function') return false; | |
| if (nativeLoaderResolvers.has(manager.resolveURL)) return true; | |
| const previous = manager.resolveURL; | |
| const resolve = function (url) { | |
| return nativeExplorationLoaderUrl(previous.call(this, url), options); | |
| }; | |
| // Capacitor 8.5's XHR interceptor proxies absolute localhost URLs whereas | |
| // fetch has a local-origin exemption. Three r128 FileLoader uses XHR. Keep | |
| // its local URLs root-relative without changing global XHR or remote traffic. | |
| // Do not use setURLModifier here: previous resolveURL closes over that | |
| // modifier and calling it from the modifier would recurse. | |
| nativeLoaderResolvers.add(resolve); | |
| manager.resolveURL = resolve; | |
| return true; | |
| } | |
| export function acknowledgeMountedRuntimeReady(context, options = {}) { | |
| if (!context) return false; | |
| const current = readNativeExplorationMount(options); | |
| // A resolved promise from the previous document/generation cannot confirm | |
| // a newer pending activation, even when it reused the same module filename. | |
| if (!current || MOUNT_KEYS.some(key => current[key] !== context[key])) return false; | |
| try { | |
| const readyAt = options.now === undefined ? Date.now() : options.now; | |
| const storage = options.localStorage || globalThis.localStorage; | |
| storage.setItem(NATIVE_EXPLORATION_READY_KEY, JSON.stringify({ | |
| schema: 1, generation: current.generation, token: current.token, | |
| moduleUrl: current.moduleUrl, readyAt | |
| })); | |
| return true; | |
| } catch (_) { return false; } | |
| } | |