"use strict";
(() => {
// src/popup/view.ts
function renderLoggedOut(root, error) {
root.innerHTML = `
\u767B\u5F55\u540E\u624D\u80FD\u4F7F\u7528\u661F\u56FE\u589E\u5F3A\u529F\u80FD ${error} \u5DF2\u767B\u5F55 ${userInfo?.name ?? userInfo?.username ?? "\u672A\u77E5\u7528\u6237"} ${userInfo?.email ?? ""} \u6B63\u5728\u68C0\u67E5\u66F4\u65B0...Star Chart Search Enhancer
Star Chart Search Enhancer
\u7248\u672C\u66F4\u65B0
\u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...
`; return; } if (options.status === "error") { container.innerHTML = `\u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
\u6682\u65F6\u65E0\u6CD5\u68C0\u67E5\u66F4\u65B0
\u5982\u679C\u9700\u8981\u65B0\u7248\uFF0C\u8BF7\u8054\u7CFB\u7EF4\u62A4\u540C\u4E8B\u83B7\u53D6\u66F4\u65B0\u5305\u3002
`; return; } if (options.status === "latest" || !options.manifest) { container.innerHTML = `\u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C
`; return; } container.innerHTML = `\u5F53\u524D\u7248\u672C\uFF1A${options.currentVersion}
\u53D1\u73B0\u65B0\u7248\u672C\uFF1A${options.manifest.latestVersion}
${renderReleaseNotes(options.manifest.releaseNotes)}\u4E0B\u8F7D\u540E\u8BF7\u89E3\u538B\u65B0\u7248 zip\uFF0C\u5E76\u5728 chrome://extensions \u91CC\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u3002
`; } function setUpdateDownloadStatus(root, value) { const output = root.querySelector('[data-popup-update-download-status="text"]'); if (!output) { return; } output.textContent = value; } function renderReleaseNotes(releaseNotes) { if (releaseNotes.length === 0) { return ""; } return `resource: ${authState.resource ?? ""}
scopes: ${(authState.scopes ?? []).join(", ")}
token: ${authState.tokenAvailable ? "available" : "missing"}
expires: ${authState.accessTokenExpiresAt ?? "unknown"}
error: ${authState.lastError ?? ""}
`; root.appendChild(panel); } function setProtectedApiResult(root, value) { const output = root.querySelector( '[data-popup-protected-api-result="output"]' ); if (!output) { return; } output.textContent = value; } // src/shared/auth-config.ts var defaultAuthConfig = { apiResource: "https://talent-search.intelligrow.cn", appId: "i4jkllbvih0554r4n0fd3", enableDevAuthPanel: false, logtoEndpoint: "https://login-api.intelligrow.cn", scopes: ["openid", "profile", "offline_access", "talent-search:read"] }; function readAuthConfig(overrides = {}) { const nextConfig = { ...defaultAuthConfig, ...overrides }; if (!nextConfig.logtoEndpoint.trim()) { throw new Error("auth config logtoEndpoint is required"); } if (!nextConfig.appId.trim()) { throw new Error("auth config appId is required"); } if (!nextConfig.apiResource.trim()) { throw new Error("auth config apiResource is required"); } return nextConfig; } // src/shared/auth-messages.ts function isAuthResponseMessage(value) { if (!value || typeof value !== "object") { return false; } const candidate = value; if (candidate.ok === false) { return candidate.type === "auth:error" && typeof candidate.error === "string"; } if (candidate.ok !== true || typeof candidate.type !== "string") { return false; } if (candidate.type === "auth:ack") { return true; } if (candidate.type === "auth:token") { return Boolean( candidate.value && typeof candidate.value === "object" && typeof candidate.value.accessToken === "string" ); } if (candidate.type === "auth:state") { return Boolean( candidate.value && typeof candidate.value === "object" && typeof candidate.value.isAuthenticated === "boolean" ); } return false; } // src/shared/protected-api-client.ts function createProtectedApiClient(options) { const fetchImpl = options.fetchImpl ?? fetch; return { async loadProtectedMockData() { const token = await readAccessToken(options.sendMessage); const response = await fetchImpl( new URL("/api/mock/protected", options.baseUrl).toString(), { headers: { Authorization: `Bearer ${token}` }, method: "GET" } ); if (response.status === 401 || response.status === 403) { throw new Error("protected api unauthorized"); } if (!response.ok) { throw new Error(`protected api request failed: ${response.status}`); } return response.json(); } }; } async function readAccessToken(sendMessage) { const response = await sendMessage({ type: "auth:get-access-token" }); if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:token" || !response.value.accessToken.trim()) { throw new Error("protected api token unavailable"); } return response.value.accessToken; } // src/shared/update-check.ts function compareExtensionVersions(left, right) { const leftParts = parseVersionParts(left); const rightParts = parseVersionParts(right); const maxLength = Math.max(leftParts.length, rightParts.length); for (let index = 0; index < maxLength; index += 1) { const leftValue = leftParts[index] ?? 0; const rightValue = rightParts[index] ?? 0; if (leftValue !== rightValue) { return leftValue - rightValue; } } return 0; } function parseUpdateManifest(value) { if (!value || typeof value !== "object") { return null; } const candidate = value; if (!isVersionString(candidate.latestVersion) || !isVersionString(candidate.minSupportedVersion) || !isHttpsUrl(candidate.zipUrl) || !isHttpsUrl(candidate.guideUrl) || typeof candidate.publishedAt !== "string" || !Array.isArray(candidate.releaseNotes) || !candidate.releaseNotes.every((note) => typeof note === "string")) { return null; } return { guideUrl: candidate.guideUrl, latestVersion: candidate.latestVersion, minSupportedVersion: candidate.minSupportedVersion, publishedAt: candidate.publishedAt, releaseNotes: candidate.releaseNotes, zipUrl: candidate.zipUrl }; } async function fetchUpdateManifest(manifestUrl, fetchImpl = fetch) { const response = await fetchImpl(manifestUrl, { cache: "no-store" }); if (!response.ok) { throw new Error(`update manifest request failed: ${response.status}`); } const manifest = parseUpdateManifest(await response.json()); if (!manifest) { throw new Error("update manifest is invalid"); } return manifest; } function parseVersionParts(value) { return value.split(".").map((part) => { const parsed = Number.parseInt(part, 10); return Number.isFinite(parsed) ? parsed : 0; }); } function isVersionString(value) { return typeof value === "string" && /^\d+(?:\.\d+)*$/.test(value); } function isHttpsUrl(value) { if (typeof value !== "string") { return false; } try { return new URL(value).protocol === "https:"; } catch { return false; } } // src/shared/update-config.ts var UPDATE_MANIFEST_URL = "https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/latest.json"; // src/popup/index.ts async function bootPopup(options = {}) { const currentDocument = options.document ?? document; const popupConfig = readAuthConfig(options.config); const currentVersion = options.currentVersion ?? readCurrentVersion(); const root = currentDocument.querySelector("#app"); const HTMLElementCtor = currentDocument.defaultView?.HTMLElement; if (!root || HTMLElementCtor && !(root instanceof HTMLElementCtor)) { throw new Error("popup root #app is required"); } const sendMessage = options.sendMessage ?? ((message) => Promise.resolve( globalThis.chrome?.runtime?.sendMessage?.(message) )); const fetchProtectedApi = options.fetchProtectedApi ?? createProtectedApiClient({ baseUrl: "http://127.0.0.1:4319", sendMessage }).loadProtectedMockData; const fetchUpdateManifest2 = options.fetchUpdateManifest ?? (() => fetchUpdateManifest( options.updateManifestUrl ?? UPDATE_MANIFEST_URL )); await renderCurrentAuthState(root, popupConfig, sendMessage, fetchProtectedApi, { currentVersion, fetchUpdateManifest: fetchUpdateManifest2 }); } async function renderCurrentAuthState(root, popupConfig, sendMessage, fetchProtectedApi, updateOptions) { const response = await sendMessage({ type: "auth:get-state" }); if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:state") { renderLoggedOut(root, "\u8BA4\u8BC1\u72B6\u6001\u8BFB\u53D6\u5931\u8D25"); return; } if (!response.value.isAuthenticated) { renderLoggedOut(root, response.value.lastError); root.querySelector('[data-popup-sign-in="button"]')?.addEventListener("click", () => { void runAuthAction(root, popupConfig, sendMessage, { actionMessage: { type: "auth:sign-in" }, fetchProtectedApi, updateOptions }); }); return; } renderLoggedIn(root, response.value); void runUpdateCheck(root, sendMessage, updateOptions); root.querySelector('[data-popup-sign-out="button"]')?.addEventListener("click", () => { void runAuthAction(root, popupConfig, sendMessage, { actionMessage: { type: "auth:sign-out" }, fetchProtectedApi, updateOptions }); }); if (popupConfig.enableDevAuthPanel) { renderDevPanel(root, response.value); root.querySelector('[data-popup-test-protected-api="button"]')?.addEventListener("click", () => { void runProtectedApiProbe(root, fetchProtectedApi); }); } } async function runAuthAction(root, popupConfig, sendMessage, options) { const response = await sendMessage(options.actionMessage); if (isActionError(response)) { renderLoggedOut(root, response.error); root.querySelector('[data-popup-sign-in="button"]')?.addEventListener("click", () => { void runAuthAction(root, popupConfig, sendMessage, options); }); return; } await renderCurrentAuthState( root, popupConfig, sendMessage, options.fetchProtectedApi, options.updateOptions ); } function isActionError(response) { return isAuthResponseMessage(response) && !response.ok && response.type === "auth:error"; } async function runUpdateCheck(root, sendMessage, options) { renderUpdateStatus(root, { currentVersion: options.currentVersion, status: "checking" }); try { const manifest = await options.fetchUpdateManifest(); if (compareExtensionVersions(manifest.latestVersion, options.currentVersion) <= 0) { renderUpdateStatus(root, { currentVersion: options.currentVersion, status: "latest" }); return; } renderUpdateStatus(root, { currentVersion: options.currentVersion, manifest, status: "available" }); bindUpdateDownloadButtons(root, sendMessage, manifest); } catch { renderUpdateStatus(root, { currentVersion: options.currentVersion, status: "error" }); } } function bindUpdateDownloadButtons(root, sendMessage, manifest) { root.querySelector('[data-popup-download-update="button"]')?.addEventListener("click", () => { void downloadUpdateAsset(root, sendMessage, { filename: "star-chart-search-enhancer-internal.zip", url: manifest.zipUrl }); }); root.querySelector('[data-popup-download-guide="button"]')?.addEventListener("click", () => { void downloadUpdateAsset(root, sendMessage, { filename: "\u661F\u56FE\u589E\u5F3A\u63D2\u4EF6-\u8D85\u7B80\u5355\u5B89\u88C5\u4F7F\u7528\u6307\u5357.pdf", url: manifest.guideUrl }); }); } async function downloadUpdateAsset(root, sendMessage, options) { setUpdateDownloadStatus(root, "\u6B63\u5728\u4E0B\u8F7D..."); try { await sendMessage({ filename: options.filename, type: "update:download", url: options.url }); setUpdateDownloadStatus(root, "\u5DF2\u89E6\u53D1\u4E0B\u8F7D\u3002\u4E0B\u8F7D\u540E\u8BF7\u89E3\u538B\u65B0\u7248 zip\uFF0C\u5E76\u5728 chrome://extensions \u91CC\u91CD\u65B0\u52A0\u8F7D\u63D2\u4EF6\u3002"); } catch (error) { setUpdateDownloadStatus( root, error instanceof Error ? error.message : "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" ); } } function readCurrentVersion() { const runtime = globalThis.chrome?.runtime; return runtime?.getManifest?.().version ?? "0.0.0"; } async function runProtectedApiProbe(root, fetchProtectedApi) { setProtectedApiResult(root, "\u8BF7\u6C42\u4E2D..."); try { const result = await fetchProtectedApi(); setProtectedApiResult(root, JSON.stringify(result, null, 2)); } catch (error) { setProtectedApiResult( root, error instanceof Error ? error.message : String(error) ); } } if (typeof document !== "undefined") { void bootPopup(); } })();