const ALLOWED_TARGET_PATHS = new Set(['/works/detail']); function trimTrailingSlash(value) { return String(value || '').trim().replace(/\/+$/u, ''); } function appendQuery(url, query) { const rawUrl = String(url || ''); const pairs = Object.keys(query) .filter((key) => query[key]) .map( (key) => `${encodeURIComponent(key)}=${encodeURIComponent(String(query[key]))}`, ); if (pairs.length === 0) { return rawUrl; } const hashIndex = rawUrl.indexOf('#'); const baseUrl = hashIndex >= 0 ? rawUrl.slice(0, hashIndex) : rawUrl; const hash = hashIndex >= 0 ? rawUrl.slice(hashIndex) : ''; return `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${pairs.join('&')}${hash}`; } function appendHashParams(url, params) { const nextKeys = new Set(Object.keys(params).filter((key) => params[key])); const pairs = Object.keys(params) .filter((key) => params[key]) .map( (key) => `${encodeURIComponent(key)}=${encodeURIComponent(String(params[key]))}`, ); if (pairs.length === 0) { return url; } const hashIndex = url.indexOf('#'); const baseUrl = hashIndex >= 0 ? url.slice(0, hashIndex) : url; const rawHash = hashIndex >= 0 ? url.slice(hashIndex + 1) : ''; const keptHashParts = rawHash.split('&').filter((part) => { if (!part) { return false; } const [rawKey = ''] = part.split('='); try { return !nextKeys.has(decodeURIComponent(rawKey)); } catch (_error) { return !nextKeys.has(rawKey); } }); return `${baseUrl}#${keptHashParts.concat(pairs).join('&')}`; } function normalizeTargetPath(value) { const trimmed = String(value || '').trim(); if (!trimmed.startsWith('/')) { return ''; } const normalized = trimmed.replace(/\/+$/u, '') || '/'; return ALLOWED_TARGET_PATHS.has(normalized) ? normalized : ''; } function resolveLaunchTargetQuery(query) { const targetPath = normalizeTargetPath(query && query.targetPath); const work = String((query && query.work) || '').trim(); if (!targetPath || !work) { return {}; } return { targetPath, work, }; } function appendLaunchTargetToEntryUrl(entryUrl, query) { const launchTarget = resolveLaunchTargetQuery(query); if (!launchTarget.targetPath) { return entryUrl; } const rawEntryUrl = String(entryUrl || '').trim(); const hashIndex = rawEntryUrl.indexOf('#'); const entryWithoutHash = hashIndex >= 0 ? rawEntryUrl.slice(0, hashIndex) : rawEntryUrl; const hash = hashIndex >= 0 ? rawEntryUrl.slice(hashIndex) : ''; const queryIndex = entryWithoutHash.indexOf('?'); const entryBase = queryIndex >= 0 ? entryWithoutHash.slice(0, queryIndex) : entryWithoutHash; const entrySearch = queryIndex >= 0 ? entryWithoutHash.slice(queryIndex) : ''; const targetUrl = `${trimTrailingSlash(entryBase)}${launchTarget.targetPath}${entrySearch}${hash}`; return appendQuery(targetUrl, { work: launchTarget.work, }); } function resolveWebViewUrlFromRuntimeConfig( authResult, launchQuery = {}, runtimeConfig = {}, ) { const entryUrl = appendLaunchTargetToEntryUrl( String(runtimeConfig.webViewEntryUrl || '').trim(), launchQuery, ); const sourcedUrl = appendQuery(entryUrl, runtimeConfig.sourceQuery || {}); if (!authResult || !authResult.token) { return sourcedUrl; } return appendHashParams(sourcedUrl, { auth_provider: 'wechat', auth_token: authResult.token, auth_binding_status: authResult.bindingStatus, }); } module.exports = { appendHashParams, appendLaunchTargetToEntryUrl, appendQuery, normalizeTargetPath, resolveLaunchTargetQuery, resolveWebViewUrlFromRuntimeConfig, };