const ALLOWED_TARGET_PATHS = new Set(['/works/detail']); const SHARE_TARGET_MESSAGE_TYPE = 'genarrative:share-target'; const WEB_VIEW_SHARE_PATH = '/pages/web-view/index'; 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 buildWebViewSharePath(query = {}, basePath = WEB_VIEW_SHARE_PATH) { const launchTarget = resolveLaunchTargetQuery(query); if (!launchTarget.targetPath) { return basePath; } return appendQuery(basePath, { targetPath: launchTarget.targetPath, work: launchTarget.work, }); } function buildWebViewShareTimelineQuery(query = {}) { const launchTarget = resolveLaunchTargetQuery(query); if (!launchTarget.targetPath) { return ''; } return new URLSearchParams({ targetPath: launchTarget.targetPath, work: launchTarget.work, }).toString(); } function normalizeShareTargetMessageData(value) { const message = value && value.data ? value.data : value; if (!message || message.type !== SHARE_TARGET_MESSAGE_TYPE) { return null; } const payload = message.payload || {}; const launchTarget = resolveLaunchTargetQuery(payload); if (!launchTarget.targetPath) { return null; } return { ...launchTarget, title: String(payload.title || '').trim(), }; } function resolveShareTargetFromWebViewMessage(detail) { const dataList = detail && Array.isArray(detail.data) ? detail.data : []; for (let index = dataList.length - 1; index >= 0; index -= 1) { const target = normalizeShareTargetMessageData(dataList[index]); if (target) { return target; } } return normalizeShareTargetMessageData(detail); } 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, buildWebViewSharePath, buildWebViewShareTimelineQuery, normalizeTargetPath, resolveShareTargetFromWebViewMessage, resolveLaunchTargetQuery, resolveWebViewUrlFromRuntimeConfig, };