@snack-kit/lib
Advanced tools
| 'use strict'; | ||
| var chunkUNFUIZVY_cjs = require('./chunk-UNFUIZVY.cjs'); | ||
| var axios = require('axios'); | ||
| function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } | ||
| var axios__default = /*#__PURE__*/_interopDefault(axios); | ||
| var _http = axios__default.default.create(); | ||
| var STORAGE_PREFIX = "__snackkit_debugger__"; | ||
| var KEY_GATEWAY = `${STORAGE_PREFIX}gateway`; | ||
| var KEY_TYPE = `${STORAGE_PREFIX}type`; | ||
| var KEY_SERVER = `${STORAGE_PREFIX}server`; | ||
| function isChinese() { | ||
| return /^zh/i.test(navigator.language ?? ""); | ||
| } | ||
| var T = { | ||
| gateway: () => isChinese() ? "\u7F51\u5173" : "Gateway", | ||
| tag: () => isChinese() ? "\u6807\u7B7E" : "Tag", | ||
| server: () => isChinese() ? "\u670D\u52A1" : "Server", | ||
| ctx: () => isChinese() ? "\u4E0A\u4E0B\u6587" : "Ctx", | ||
| ctxLabel: () => isChinese() ? "\u4E0A\u4E0B\u6587 \u2014 \u70B9\u51FB\u884C\u590D\u5236 key" : "Ctx \u2014 click row to copy key", | ||
| search: () => isChinese() ? "\u641C\u7D22..." : "Search...", | ||
| noMatch: () => isChinese() ? "\u65E0\u5339\u914D\u9879" : "No results", | ||
| allTags: () => isChinese() ? "\u5168\u90E8\u6807\u7B7E" : "All tags", | ||
| selectGateway: () => isChinese() ? "\u9009\u62E9\u7F51\u5173" : "Select gateway", | ||
| selectServer: () => isChinese() ? "\u9009\u62E9\u670D\u52A1" : "Select server", | ||
| filterCtx: () => isChinese() ? "\u8FC7\u6EE4\u8DEF\u7531..." : "Filter routes...", | ||
| selectFirst: () => isChinese() ? "\u2190 \u8BF7\u5148\u9009\u62E9\u670D\u52A1" : "\u2190 Select a server first", | ||
| loading: () => isChinese() ? "\u52A0\u8F7D\u670D\u52A1\u5217\u8868..." : "Loading...", | ||
| loadFailed: (m) => isChinese() ? `\u52A0\u8F7D\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| loaded: (n) => isChinese() ? `\u5DF2\u52A0\u8F7D ${n} \u4E2A\u670D\u52A1` : `${n} servers loaded`, | ||
| switching: () => isChinese() ? "\u5207\u6362\u670D\u52A1\u4E2D..." : "Switching...", | ||
| switchFailed: (m) => isChinese() ? `\u5207\u6362\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| routes: (name, n) => isChinese() ? `${name} \xB7 ${n} \u6761\u8DEF\u7531` : `${name} \xB7 ${n} routes`, | ||
| copied: (k) => isChinese() ? `\u5DF2\u590D\u5236: ${k}` : `Copied: ${k}`, | ||
| copyAll: () => isChinese() ? "\u590D\u5236\u5168\u90E8 JSON" : "Copy all JSON", | ||
| copyAllDone: () => isChinese() ? "\u5DF2\u590D\u5236\u4E3A JSON" : "Copied as JSON" | ||
| }; | ||
| var PANEL_STYLE = ` | ||
| #__snackkit_debugger__ { | ||
| position: fixed; | ||
| bottom: 20px; | ||
| right: 20px; | ||
| z-index: 99999; | ||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif; | ||
| font-size: 13px; | ||
| color: #b8ccec; | ||
| user-select: none; | ||
| } | ||
| /* \u2500\u2500 \u6D6E\u52A8\u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-toggle { | ||
| width: 42px; | ||
| height: 42px; | ||
| border-radius: 50%; | ||
| background: linear-gradient(135deg, #4f8ef7, #6d6ff5); | ||
| border: none; | ||
| cursor: pointer; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| box-shadow: 0 4px 14px rgba(79,142,247,0.45); | ||
| margin-left: auto; | ||
| font-size: 20px; | ||
| transition: transform 0.15s, box-shadow 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:hover { | ||
| transform: scale(1.08); | ||
| box-shadow: 0 6px 20px rgba(79,142,247,0.55); | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); } | ||
| /* \u2500\u2500 \u9762\u677F\u4E3B\u4F53 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-panel { | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 12px; | ||
| width: 440px; | ||
| margin-bottom: 10px; | ||
| box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05); | ||
| overflow: visible; | ||
| opacity: 0; | ||
| visibility: hidden; | ||
| transform: translateY(8px) scale(0.98); | ||
| pointer-events: none; | ||
| transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease; | ||
| } | ||
| #__snackkit_debugger__ .dbg-panel.open { | ||
| opacity: 1; | ||
| visibility: visible; | ||
| transform: translateY(0) scale(1); | ||
| pointer-events: auto; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F Header \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| padding: 10px 14px; | ||
| background: #171e2e; | ||
| border-bottom: 1px solid #2d3a55; | ||
| border-radius: 12px 12px 0 0; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-left { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 7px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot { | ||
| width: 7px; | ||
| height: 7px; | ||
| border-radius: 50%; | ||
| background: #34d399; | ||
| box-shadow: 0 0 6px rgba(52,211,153,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.loading { | ||
| background: #fbbf24; | ||
| box-shadow: 0 0 6px rgba(251,191,36,0.6); | ||
| animation: dbg-pulse 1s ease-in-out infinite; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.err { | ||
| background: #f87171; | ||
| box-shadow: 0 0 6px rgba(248,113,113,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-title { | ||
| font-size: 12px; | ||
| font-weight: 600; | ||
| color: #dce8fa; | ||
| letter-spacing: 0.03em; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-meta { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F\u5185\u5BB9\u533A \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-body { | ||
| padding: 12px 14px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 10px; | ||
| height: 420px; | ||
| overflow: visible; | ||
| } | ||
| /* \u2500\u2500 \u5B57\u6BB5\u884C \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-field { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-field-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| } | ||
| #__snackkit_debugger__ .dbg-label { | ||
| font-size: 10px; | ||
| font-weight: 600; | ||
| color: #5e72a0; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.07em; | ||
| } | ||
| /* \u2500\u2500 \u4E00\u952E\u590D\u5236\u5168\u90E8 JSON \u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-copy-all { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| background: transparent; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 4px; | ||
| padding: 2px 7px; | ||
| color: #5e72a0; | ||
| font-size: 10px; | ||
| font-family: inherit; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, color 0.15s, background 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all:hover { | ||
| border-color: #4f8ef7; | ||
| color: #7aacfa; | ||
| background: rgba(79,142,247,0.08); | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all.done { | ||
| border-color: #34d399; | ||
| color: #34d399; | ||
| } | ||
| /* \u2500\u2500 \u81EA\u5B9A\u4E49\u4E0B\u62C9 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-select { | ||
| position: relative; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| gap: 6px; | ||
| background: #252f45; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| padding: 6px 10px; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, background 0.15s; | ||
| min-height: 32px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger { | ||
| border-color: #4f8ef7; | ||
| background: #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val { | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| font-size: 12px; | ||
| color: #b8ccec; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-arrow { | ||
| color: #3d5070; | ||
| font-size: 10px; | ||
| flex-shrink: 0; | ||
| transition: transform 0.15s, color 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow { | ||
| transform: rotate(180deg); | ||
| color: #4f8ef7; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-dropdown { | ||
| position: absolute; | ||
| bottom: calc(100% + 4px); | ||
| left: 0; | ||
| right: 0; | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 8px; | ||
| box-shadow: 0 -8px 24px rgba(0,0,0,0.35); | ||
| overflow: hidden; | ||
| opacity: 0; | ||
| transform: translateY(4px); | ||
| pointer-events: none; | ||
| transition: opacity 0.14s, transform 0.14s; | ||
| z-index: 100000; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown { | ||
| opacity: 1; | ||
| transform: translateY(0); | ||
| pointer-events: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; } | ||
| #__snackkit_debugger__ .dbg-select-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-list { | ||
| max-height: 160px; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-select-option { | ||
| padding: 7px 10px; | ||
| cursor: pointer; | ||
| font-size: 12px; | ||
| color: #8aa4c8; | ||
| transition: background 0.1s, color 0.1s; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; } | ||
| #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; } | ||
| #__snackkit_debugger__ .dbg-select-option.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-select-empty { | ||
| padding: 12px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u8DEF\u7531\u5217\u8868 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-ctx-field { | ||
| flex: 1; | ||
| min-height: 0; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-wrap { | ||
| flex: 1; | ||
| min-height: 0; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| overflow: hidden; | ||
| background: #252f45; | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 6px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| background: #1e2638; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-ctx-list { | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-ctx-item { | ||
| padding: 5px 10px; | ||
| cursor: pointer; | ||
| display: grid; | ||
| grid-template-columns: minmax(80px, 148px) 1fr auto; | ||
| gap: 8px; | ||
| align-items: center; | ||
| border-bottom: 1px solid #1e2638; | ||
| transition: background 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-key { | ||
| font-size: 12px; | ||
| color: #7aacfa; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-val { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| transition: color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; } | ||
| #__snackkit_debugger__ .dbg-ctx-copy { | ||
| font-size: 11px; | ||
| color: #3d5070; | ||
| opacity: 0; | ||
| flex-shrink: 0; | ||
| transition: opacity 0.1s, color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; } | ||
| #__snackkit_debugger__ .dbg-ctx-empty { | ||
| padding: 16px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u5E95\u90E8\u72B6\u6001\u680F \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-footer { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 14px; | ||
| background: #171e2e; | ||
| border-top: 1px solid #2d3a55; | ||
| border-radius: 0 0 12px 12px; | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| min-height: 30px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; } | ||
| #__snackkit_debugger__ .dbg-footer.err { color: #f87171; } | ||
| #__snackkit_debugger__ .dbg-footer-dot { | ||
| width: 4px; | ||
| height: 4px; | ||
| border-radius: 50%; | ||
| background: currentColor; | ||
| flex-shrink: 0; | ||
| } | ||
| @keyframes dbg-pulse { | ||
| 0%, 100% { opacity: 1; } | ||
| 50% { opacity: 0.35; } | ||
| } | ||
| `; | ||
| var SearchSelect = class { | ||
| constructor(onChange) { | ||
| this.options = []; | ||
| this.selected = ""; | ||
| this.onChange = onChange; | ||
| this.el = document.createElement("div"); | ||
| this.el.className = "dbg-select"; | ||
| this.trigger = document.createElement("div"); | ||
| this.trigger.className = "dbg-select-trigger"; | ||
| this.valEl = document.createElement("span"); | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| const arrow = document.createElement("span"); | ||
| arrow.className = "dbg-select-arrow"; | ||
| arrow.textContent = "\u25BE"; | ||
| this.trigger.appendChild(this.valEl); | ||
| this.trigger.appendChild(arrow); | ||
| this.dropdown = document.createElement("div"); | ||
| this.dropdown.className = "dbg-select-dropdown"; | ||
| const searchWrap = document.createElement("div"); | ||
| searchWrap.className = "dbg-select-search"; | ||
| const searchIcon = document.createElement("span"); | ||
| searchIcon.className = "dbg-select-search-icon"; | ||
| searchIcon.textContent = "\u2315"; | ||
| this.searchInput = document.createElement("input"); | ||
| this.searchInput.type = "text"; | ||
| this.searchInput.placeholder = T.search(); | ||
| searchWrap.appendChild(searchIcon); | ||
| searchWrap.appendChild(this.searchInput); | ||
| this.listEl = document.createElement("div"); | ||
| this.listEl.className = "dbg-select-list"; | ||
| this.dropdown.appendChild(searchWrap); | ||
| this.dropdown.appendChild(this.listEl); | ||
| this.el.appendChild(this.trigger); | ||
| this.el.appendChild(this.dropdown); | ||
| this.bindEvents(); | ||
| } | ||
| bindEvents() { | ||
| this.trigger.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| const isOpen = this.el.classList.contains("open"); | ||
| document.querySelectorAll("#__snackkit_debugger__ .dbg-select.open").forEach((el) => { | ||
| if (el !== this.el) el.classList.remove("open"); | ||
| }); | ||
| this.el.classList.toggle("open", !isOpen); | ||
| if (!isOpen) { | ||
| this.searchInput.value = ""; | ||
| this.filterOptions(""); | ||
| setTimeout(() => this.searchInput.focus(), 50); | ||
| } | ||
| }); | ||
| this.searchInput.addEventListener("input", () => { | ||
| this.filterOptions(this.searchInput.value); | ||
| }); | ||
| this.searchInput.addEventListener("click", (e) => e.stopPropagation()); | ||
| document.addEventListener("click", () => { | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| } | ||
| filterOptions(query) { | ||
| const q = query.toLowerCase(); | ||
| let visibleCount = 0; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((opt) => { | ||
| const match = !q || opt.textContent.toLowerCase().includes(q); | ||
| opt.classList.toggle("hidden", !match); | ||
| if (match) visibleCount++; | ||
| }); | ||
| let emptyEl = this.listEl.querySelector(".dbg-select-empty"); | ||
| if (visibleCount === 0) { | ||
| if (!emptyEl) { | ||
| emptyEl = document.createElement("div"); | ||
| emptyEl.className = "dbg-select-empty"; | ||
| emptyEl.textContent = T.noMatch(); | ||
| this.listEl.appendChild(emptyEl); | ||
| } | ||
| } else { | ||
| emptyEl?.remove(); | ||
| } | ||
| } | ||
| /** 更新选项列表 */ | ||
| setOptions(options, placeholder) { | ||
| this.options = options; | ||
| this.listEl.innerHTML = ""; | ||
| this.valEl.textContent = placeholder; | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| this.selected = ""; | ||
| options.forEach(({ value, label }) => { | ||
| const opt = document.createElement("div"); | ||
| opt.className = "dbg-select-option"; | ||
| opt.dataset["value"] = value; | ||
| opt.textContent = label; | ||
| opt.setAttribute("title", label); | ||
| opt.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.select(value, label); | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| this.listEl.appendChild(opt); | ||
| }); | ||
| } | ||
| /** 选中某个值(不触发 onChange) */ | ||
| setValue(value) { | ||
| const opt = this.options.find((o) => o.value === value); | ||
| if (opt) this.select(opt.value, opt.label, false); | ||
| } | ||
| select(value, label, emit = true) { | ||
| this.selected = value; | ||
| this.valEl.textContent = label; | ||
| this.valEl.className = "dbg-select-val"; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((el) => { | ||
| el.classList.toggle("active", el.dataset["value"] === value); | ||
| }); | ||
| if (emit) this.onChange(value); | ||
| } | ||
| getValue() { | ||
| return this.selected; | ||
| } | ||
| getElement() { | ||
| return this.el; | ||
| } | ||
| }; | ||
| var Debugger = class _Debugger { | ||
| constructor(options) { | ||
| this.servers = []; | ||
| this.currentCtxMap = {}; | ||
| this.options = options; | ||
| this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]; | ||
| } | ||
| /** | ||
| * 初始化并渲染调试面板 | ||
| * | ||
| * 支持两种调用形式: | ||
| * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间 | ||
| * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }` | ||
| * | ||
| * 调用后在页面右下角插入浮动按钮,点击展开调试面板。 | ||
| * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。 | ||
| * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。 | ||
| * | ||
| * @param options 配置对象 或 网关 URL 数组 | ||
| * | ||
| * @example 配置对象形式 | ||
| * ```ts | ||
| * import { Debugger } from '@snack-kit/lib/debugger' | ||
| * | ||
| * await Debugger.init({ | ||
| * gateways: [ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ], | ||
| * timeout: 5000, | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @example 数组简写形式 | ||
| * ```ts | ||
| * await Debugger.init([ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ]) | ||
| * ``` | ||
| * | ||
| * @example 仅在非生产环境加载 | ||
| * ```ts | ||
| * if (import.meta.env.DEV) { | ||
| * const { Debugger } = await import('@snack-kit/lib/debugger') | ||
| * await Debugger.init(['http://dev-gateway.example.com']) | ||
| * } | ||
| * ``` | ||
| */ | ||
| static async init(options) { | ||
| globalThis[chunkUNFUIZVY_cjs.DEBUGGER_ACTIVE_KEY] = true; | ||
| const normalized = Array.isArray(options) ? { gateways: options } : options; | ||
| const instance = new _Debugger({ timeout: 1e4, ...normalized }); | ||
| instance.injectStyle(); | ||
| instance.buildDOM(); | ||
| await instance.restoreState(); | ||
| return instance; | ||
| } | ||
| /** 注入内联样式 */ | ||
| injectStyle() { | ||
| if (document.getElementById("__snackkit_debugger_style__")) return; | ||
| const style = document.createElement("style"); | ||
| style.id = "__snackkit_debugger_style__"; | ||
| style.textContent = PANEL_STYLE; | ||
| document.head.appendChild(style); | ||
| } | ||
| /** 构建 DOM 结构 */ | ||
| buildDOM() { | ||
| const root = document.createElement("div"); | ||
| root.id = "__snackkit_debugger__"; | ||
| const panel = document.createElement("div"); | ||
| panel.className = "dbg-panel"; | ||
| this.panelEl = panel; | ||
| const header = document.createElement("div"); | ||
| header.className = "dbg-header"; | ||
| const headerLeft = document.createElement("div"); | ||
| headerLeft.className = "dbg-header-left"; | ||
| this.headerDot = document.createElement("span"); | ||
| this.headerDot.className = "dbg-header-dot loading"; | ||
| const headerTitle = document.createElement("span"); | ||
| headerTitle.className = "dbg-header-title"; | ||
| headerTitle.textContent = "Snack Kit Debugger"; | ||
| headerLeft.appendChild(this.headerDot); | ||
| headerLeft.appendChild(headerTitle); | ||
| header.appendChild(headerLeft); | ||
| panel.appendChild(header); | ||
| const body = document.createElement("div"); | ||
| body.className = "dbg-body"; | ||
| this.gwVersion = document.createElement("span"); | ||
| this.gwVersion.className = "dbg-header-meta"; | ||
| this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val)); | ||
| this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway()); | ||
| body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement())); | ||
| this.typeSelect = new SearchSelect((val) => this.onTypeChange(val)); | ||
| body.appendChild(this.createField(T.tag(), this.typeSelect.getElement())); | ||
| this.serverSelect = new SearchSelect((val) => this.onServerChange(val)); | ||
| body.appendChild(this.createField(T.server(), this.serverSelect.getElement())); | ||
| const ctxWrap = document.createElement("div"); | ||
| ctxWrap.className = "dbg-ctx-wrap"; | ||
| const ctxSearch = document.createElement("div"); | ||
| ctxSearch.className = "dbg-ctx-search"; | ||
| const ctxSearchIcon = document.createElement("span"); | ||
| ctxSearchIcon.className = "dbg-select-search-icon"; | ||
| ctxSearchIcon.textContent = "\u2315"; | ||
| this.ctxSearchInput = document.createElement("input"); | ||
| this.ctxSearchInput.type = "text"; | ||
| this.ctxSearchInput.placeholder = T.filterCtx(); | ||
| this.ctxSearchInput.addEventListener("input", () => this.filterCtx(this.ctxSearchInput.value)); | ||
| ctxSearch.appendChild(ctxSearchIcon); | ||
| ctxSearch.appendChild(this.ctxSearchInput); | ||
| this.ctxList = document.createElement("div"); | ||
| this.ctxList.className = "dbg-ctx-list"; | ||
| ctxWrap.appendChild(ctxSearch); | ||
| ctxWrap.appendChild(this.ctxList); | ||
| this.copyAllBtn = document.createElement("button"); | ||
| this.copyAllBtn.className = "dbg-copy-all"; | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.setAttribute("title", isChinese() ? "\u5C06\u5168\u90E8\u4E0A\u4E0B\u6587\u590D\u5236\u4E3A JSON\uFF0C\u53EF\u4F5C\u4E3A Context.load({...}) \u7684\u53C2\u6570" : "Copy all ctx entries as JSON for Context.load({...})"); | ||
| this.copyAllBtn.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.copyAllCtx(); | ||
| }); | ||
| const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap); | ||
| ctxField.classList.add("dbg-ctx-field"); | ||
| body.appendChild(ctxField); | ||
| panel.appendChild(body); | ||
| const footer = document.createElement("div"); | ||
| footer.className = "dbg-footer"; | ||
| this.footerEl = footer; | ||
| panel.appendChild(footer); | ||
| const toggle = document.createElement("button"); | ||
| toggle.className = "dbg-toggle"; | ||
| toggle.textContent = "\u{1F41B}"; | ||
| toggle.setAttribute("title", "Snack Debugger"); | ||
| toggle.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| panel.classList.toggle("open"); | ||
| }); | ||
| root.appendChild(panel); | ||
| root.appendChild(toggle); | ||
| document.body.appendChild(root); | ||
| } | ||
| /** 创建普通字段行(label + content) */ | ||
| createField(label, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| field.appendChild(lbl); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 创建带操作按钮的字段行(label + action button + content) */ | ||
| createFieldWithAction(label, action, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const fieldHeader = document.createElement("div"); | ||
| fieldHeader.className = "dbg-field-header"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| fieldHeader.appendChild(lbl); | ||
| fieldHeader.appendChild(action); | ||
| field.appendChild(fieldHeader); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 恢复上次持久化的状态 */ | ||
| async restoreState() { | ||
| const savedGw = localStorage.getItem(KEY_GATEWAY); | ||
| const gw = savedGw && this.gateways.includes(savedGw) ? savedGw : this.gateways[0]; | ||
| this.gwSelect.setValue(gw); | ||
| await this.loadServers(gw); | ||
| const savedType = localStorage.getItem(KEY_TYPE); | ||
| if (savedType) { | ||
| this.typeSelect.setValue(savedType); | ||
| this.renderServerOptions(savedType); | ||
| } | ||
| const savedServer = localStorage.getItem(KEY_SERVER); | ||
| if (savedServer && this.servers.find((s) => s.key === savedServer)) { | ||
| this.serverSelect.setValue(savedServer); | ||
| await this.applyServer(savedServer); | ||
| } | ||
| } | ||
| async onGatewayChange(gw) { | ||
| localStorage.setItem(KEY_GATEWAY, gw); | ||
| this.gwVersion.textContent = ""; | ||
| await this.loadServers(gw); | ||
| } | ||
| onTypeChange(type) { | ||
| localStorage.setItem(KEY_TYPE, type); | ||
| this.renderServerOptions(type); | ||
| } | ||
| async onServerChange(key) { | ||
| localStorage.setItem(KEY_SERVER, key); | ||
| await this.applyServer(key); | ||
| } | ||
| /** 从网关加载服务列表 */ | ||
| async loadServers(gwUrl) { | ||
| this.setHeader("loading"); | ||
| this.setFooter(T.loading(), ""); | ||
| try { | ||
| const res = await _http.get(`${gwUrl}/web-debug/host/list`, { | ||
| timeout: this.options.timeout | ||
| }); | ||
| this.servers = res.data ?? []; | ||
| this.renderTypeOptions(); | ||
| this.setHeader("ok"); | ||
| this.setFooter(T.loaded(this.servers.length), "ok"); | ||
| } catch (err) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| this.setHeader("err"); | ||
| this.setFooter(T.loadFailed(message), "err"); | ||
| } | ||
| } | ||
| /** 渲染标签选项 */ | ||
| renderTypeOptions() { | ||
| const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]; | ||
| const opts = [{ value: "", label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]; | ||
| this.typeSelect.setOptions(opts, T.allTags()); | ||
| this.typeSelect.setValue(""); | ||
| this.renderServerOptions(""); | ||
| } | ||
| /** 根据标签渲染服务选项 */ | ||
| renderServerOptions(type) { | ||
| const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers; | ||
| this.serverSelect.setOptions( | ||
| filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })), | ||
| T.selectServer() | ||
| ); | ||
| this.renderCtxList({}); | ||
| } | ||
| /** 切换目标服务,重新加载 Context 路由映射 */ | ||
| async applyServer(key) { | ||
| if (!key) { | ||
| this.renderCtxList({}); | ||
| return; | ||
| } | ||
| const server = this.servers.find((s) => s.key === key); | ||
| if (!server) return; | ||
| this.setFooter(T.switching(), ""); | ||
| try { | ||
| await chunkUNFUIZVY_cjs.Context.load(server.origin, this.options.timeout); | ||
| const res = await fetch(`${server.origin}/ngw/context`); | ||
| const raw = res.ok ? await res.json() : {}; | ||
| const ctxMap = {}; | ||
| for (const [k, v] of Object.entries(raw)) { | ||
| if (k !== "$info" && typeof v === "string") ctxMap[k] = v; | ||
| } | ||
| const info = chunkUNFUIZVY_cjs.Context.info; | ||
| if (info) this.gwVersion.textContent = `v${info.version}`; | ||
| this.renderCtxList(ctxMap); | ||
| const count = Object.keys(ctxMap).length; | ||
| this.setFooter(T.routes(server.name, count), "ok"); | ||
| } catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| this.setFooter(T.switchFailed(msg), "err"); | ||
| } | ||
| } | ||
| /** 渲染路由列表 */ | ||
| renderCtxList(ctxMap) { | ||
| this.currentCtxMap = ctxMap; | ||
| this.ctxList.innerHTML = ""; | ||
| this.ctxSearchInput.value = ""; | ||
| const entries = Object.entries(ctxMap); | ||
| if (entries.length === 0) { | ||
| const empty = document.createElement("div"); | ||
| empty.className = "dbg-ctx-empty"; | ||
| empty.textContent = T.selectFirst(); | ||
| this.ctxList.appendChild(empty); | ||
| return; | ||
| } | ||
| entries.forEach(([k, v]) => { | ||
| const item = document.createElement("div"); | ||
| item.className = "dbg-ctx-item"; | ||
| item.dataset["key"] = k; | ||
| const keySpan = document.createElement("span"); | ||
| keySpan.className = "dbg-ctx-key"; | ||
| keySpan.textContent = k; | ||
| keySpan.setAttribute("title", k); | ||
| const valSpan = document.createElement("span"); | ||
| valSpan.className = "dbg-ctx-val"; | ||
| valSpan.textContent = v; | ||
| valSpan.setAttribute("title", `${k} \u2192 ${v}`); | ||
| const copyIcon = document.createElement("span"); | ||
| copyIcon.className = "dbg-ctx-copy"; | ||
| copyIcon.textContent = "\u2398"; | ||
| item.appendChild(keySpan); | ||
| item.appendChild(valSpan); | ||
| item.appendChild(copyIcon); | ||
| item.addEventListener("click", () => this.copyCtxItem(item, k)); | ||
| this.ctxList.appendChild(item); | ||
| }); | ||
| } | ||
| /** 过滤路由列表 */ | ||
| filterCtx(query) { | ||
| const q = query.toLowerCase(); | ||
| this.ctxList.querySelectorAll(".dbg-ctx-item").forEach((item) => { | ||
| const key = item.dataset["key"] ?? ""; | ||
| item.classList.toggle("hidden", !!q && !key.toLowerCase().includes(q)); | ||
| }); | ||
| } | ||
| /** 复制单个 ctx key */ | ||
| copyCtxItem(item, text) { | ||
| navigator.clipboard.writeText(text).then(() => { | ||
| item.classList.add("copied"); | ||
| const icon = item.querySelector(".dbg-ctx-copy"); | ||
| if (icon) icon.textContent = "\u2713"; | ||
| setTimeout(() => { | ||
| item.classList.remove("copied"); | ||
| const ic = item.querySelector(".dbg-ctx-copy"); | ||
| if (ic) ic.textContent = "\u2398"; | ||
| }, 1500); | ||
| this.setFooter(T.copied(text), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2e3); | ||
| }); | ||
| } | ||
| /** 一键复制全部上下文为 JSON */ | ||
| copyAllCtx() { | ||
| const entries = Object.keys(this.currentCtxMap); | ||
| if (entries.length === 0) return; | ||
| const json = JSON.stringify(this.currentCtxMap, null, 2); | ||
| navigator.clipboard.writeText(json).then(() => { | ||
| this.copyAllBtn.textContent = "\u2713 " + T.copyAllDone(); | ||
| this.copyAllBtn.classList.add("done"); | ||
| setTimeout(() => { | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.classList.remove("done"); | ||
| }, 2e3); | ||
| this.setFooter(T.copyAllDone(), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2500); | ||
| }); | ||
| } | ||
| setHeader(state) { | ||
| this.headerDot.className = `dbg-header-dot${state !== "ok" ? ` ${state}` : ""}`; | ||
| } | ||
| setFooter(msg, cls) { | ||
| this.footerEl.innerHTML = msg ? `<span class="dbg-footer-dot"></span>${msg}` : ""; | ||
| this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ""}`; | ||
| } | ||
| }; | ||
| exports.Debugger = Debugger; | ||
| //# sourceMappingURL=chunk-PQTH7HUD.cjs.map | ||
| //# sourceMappingURL=chunk-PQTH7HUD.cjs.map |
| {"version":3,"sources":["../../src/debugger/debugger.ts"],"names":["axios","DEBUGGER_ACTIVE_KEY","Context"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;A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","file":"chunk-PQTH7HUD.cjs","sourcesContent":["import axios from 'axios'\nimport { Context, DEBUGGER_ACTIVE_KEY } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\n\n/**\n * Debugger 专用的隔离 axios 实例。\n * 独立于用户通过 `import { axios } from '@snack-kit/lib/http'` 添加的拦截器,\n * 确保调试面板请求不被外部拦截器污染。\n */\nconst _http = axios.create()\n\n/** localStorage key 前缀 */\nconst STORAGE_PREFIX = '__snackkit_debugger__'\nconst KEY_GATEWAY = `${STORAGE_PREFIX}gateway`\nconst KEY_TYPE = `${STORAGE_PREFIX}type`\nconst KEY_SERVER = `${STORAGE_PREFIX}server`\n\n\n/** 是否为中文环境 */\nfunction isChinese(): boolean {\n return /^zh/i.test(navigator.language ?? '')\n}\n\n/** i18n 文案 */\nconst T = {\n gateway: () => isChinese() ? '网关' : 'Gateway',\n tag: () => isChinese() ? '标签' : 'Tag',\n server: () => isChinese() ? '服务' : 'Server',\n ctx: () => isChinese() ? '上下文' : 'Ctx',\n ctxLabel: () => isChinese() ? '上下文 — 点击行复制 key' : 'Ctx — click row to copy key',\n search: () => isChinese() ? '搜索...' : 'Search...',\n noMatch: () => isChinese() ? '无匹配项' : 'No results',\n allTags: () => isChinese() ? '全部标签' : 'All tags',\n selectGateway: () => isChinese() ? '选择网关' : 'Select gateway',\n selectServer: () => isChinese() ? '选择服务' : 'Select server',\n filterCtx: () => isChinese() ? '过滤路由...' : 'Filter routes...',\n selectFirst: () => isChinese() ? '← 请先选择服务' : '← Select a server first',\n loading: () => isChinese() ? '加载服务列表...' : 'Loading...',\n loadFailed: (m: string) => isChinese() ? `加载失败: ${m}` : `Failed: ${m}`,\n loaded: (n: number) => isChinese() ? `已加载 ${n} 个服务` : `${n} servers loaded`,\n switching: () => isChinese() ? '切换服务中...' : 'Switching...',\n switchFailed: (m: string) => isChinese() ? `切换失败: ${m}` : `Failed: ${m}`,\n routes: (name: string, n: number) => isChinese() ? `${name} · ${n} 条路由` : `${name} · ${n} routes`,\n copied: (k: string) => isChinese() ? `已复制: ${k}` : `Copied: ${k}`,\n copyAll: () => isChinese() ? '复制全部 JSON' : 'Copy all JSON',\n copyAllDone: () => isChinese() ? '已复制为 JSON' : 'Copied as JSON',\n}\n\n/** 面板内联样式 */\nconst PANEL_STYLE = `\n #__snackkit_debugger__ {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 99999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;\n font-size: 13px;\n color: #b8ccec;\n user-select: none;\n }\n\n /* ── 浮动按钮 ── */\n #__snackkit_debugger__ .dbg-toggle {\n width: 42px;\n height: 42px;\n border-radius: 50%;\n background: linear-gradient(135deg, #4f8ef7, #6d6ff5);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 14px rgba(79,142,247,0.45);\n margin-left: auto;\n font-size: 20px;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n #__snackkit_debugger__ .dbg-toggle:hover {\n transform: scale(1.08);\n box-shadow: 0 6px 20px rgba(79,142,247,0.55);\n }\n #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); }\n\n /* ── 面板主体 ── */\n #__snackkit_debugger__ .dbg-panel {\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 12px;\n width: 440px;\n margin-bottom: 10px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05);\n overflow: visible;\n opacity: 0;\n visibility: hidden;\n transform: translateY(8px) scale(0.98);\n pointer-events: none;\n transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;\n }\n #__snackkit_debugger__ .dbg-panel.open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n /* ── 面板 Header ── */\n #__snackkit_debugger__ .dbg-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n background: #171e2e;\n border-bottom: 1px solid #2d3a55;\n border-radius: 12px 12px 0 0;\n }\n #__snackkit_debugger__ .dbg-header-left {\n display: flex;\n align-items: center;\n gap: 7px;\n }\n #__snackkit_debugger__ .dbg-header-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: #34d399;\n box-shadow: 0 0 6px rgba(52,211,153,0.6);\n }\n #__snackkit_debugger__ .dbg-header-dot.loading {\n background: #fbbf24;\n box-shadow: 0 0 6px rgba(251,191,36,0.6);\n animation: dbg-pulse 1s ease-in-out infinite;\n }\n #__snackkit_debugger__ .dbg-header-dot.err {\n background: #f87171;\n box-shadow: 0 0 6px rgba(248,113,113,0.6);\n }\n #__snackkit_debugger__ .dbg-header-title {\n font-size: 12px;\n font-weight: 600;\n color: #dce8fa;\n letter-spacing: 0.03em;\n }\n #__snackkit_debugger__ .dbg-header-meta {\n font-size: 11px;\n color: #4d6080;\n }\n\n /* ── 面板内容区 ── */\n #__snackkit_debugger__ .dbg-body {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n height: 420px;\n overflow: visible;\n }\n\n /* ── 字段行 ── */\n #__snackkit_debugger__ .dbg-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-field-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n #__snackkit_debugger__ .dbg-label {\n font-size: 10px;\n font-weight: 600;\n color: #5e72a0;\n text-transform: uppercase;\n letter-spacing: 0.07em;\n }\n\n /* ── 一键复制全部 JSON 按钮 ── */\n #__snackkit_debugger__ .dbg-copy-all {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: 1px solid #2d3a55;\n border-radius: 4px;\n padding: 2px 7px;\n color: #5e72a0;\n font-size: 10px;\n font-family: inherit;\n cursor: pointer;\n transition: border-color 0.15s, color 0.15s, background 0.15s;\n }\n #__snackkit_debugger__ .dbg-copy-all:hover {\n border-color: #4f8ef7;\n color: #7aacfa;\n background: rgba(79,142,247,0.08);\n }\n #__snackkit_debugger__ .dbg-copy-all.done {\n border-color: #34d399;\n color: #34d399;\n }\n\n /* ── 自定义下拉 ── */\n #__snackkit_debugger__ .dbg-select {\n position: relative;\n }\n #__snackkit_debugger__ .dbg-select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n background: #252f45;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n padding: 6px 10px;\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n min-height: 32px;\n }\n #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger {\n border-color: #4f8ef7;\n background: #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-val {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n color: #b8ccec;\n }\n #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-arrow {\n color: #3d5070;\n font-size: 10px;\n flex-shrink: 0;\n transition: transform 0.15s, color 0.15s;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow {\n transform: rotate(180deg);\n color: #4f8ef7;\n }\n #__snackkit_debugger__ .dbg-select-dropdown {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 0;\n right: 0;\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 8px;\n box-shadow: 0 -8px 24px rgba(0,0,0,0.35);\n overflow: hidden;\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n transition: opacity 0.14s, transform 0.14s;\n z-index: 100000;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n }\n #__snackkit_debugger__ .dbg-select-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 10px;\n border-bottom: 1px solid #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; }\n #__snackkit_debugger__ .dbg-select-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-list {\n max-height: 160px;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-select-option {\n padding: 7px 10px;\n cursor: pointer;\n font-size: 12px;\n color: #8aa4c8;\n transition: background 0.1s, color 0.1s;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; }\n #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; }\n #__snackkit_debugger__ .dbg-select-option.hidden { display: none; }\n #__snackkit_debugger__ .dbg-select-empty {\n padding: 12px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 路由列表 ── */\n #__snackkit_debugger__ .dbg-ctx-field {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-ctx-wrap {\n flex: 1;\n min-height: 0;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n overflow: hidden;\n background: #252f45;\n display: flex;\n flex-direction: column;\n }\n #__snackkit_debugger__ .dbg-ctx-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-bottom: 1px solid #2d3a55;\n background: #1e2638;\n }\n #__snackkit_debugger__ .dbg-ctx-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-ctx-list {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-ctx-item {\n padding: 5px 10px;\n cursor: pointer;\n display: grid;\n grid-template-columns: minmax(80px, 148px) 1fr auto;\n gap: 8px;\n align-items: center;\n border-bottom: 1px solid #1e2638;\n transition: background 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }\n #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; }\n #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; }\n #__snackkit_debugger__ .dbg-ctx-key {\n font-size: 12px;\n color: #7aacfa;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-ctx-val {\n font-size: 11px;\n color: #4d6080;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n transition: color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; }\n #__snackkit_debugger__ .dbg-ctx-copy {\n font-size: 11px;\n color: #3d5070;\n opacity: 0;\n flex-shrink: 0;\n transition: opacity 0.1s, color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; }\n #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; }\n #__snackkit_debugger__ .dbg-ctx-empty {\n padding: 16px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 底部状态栏 ── */\n #__snackkit_debugger__ .dbg-footer {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: #171e2e;\n border-top: 1px solid #2d3a55;\n border-radius: 0 0 12px 12px;\n font-size: 11px;\n color: #4d6080;\n min-height: 30px;\n }\n #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; }\n #__snackkit_debugger__ .dbg-footer.err { color: #f87171; }\n #__snackkit_debugger__ .dbg-footer-dot {\n width: 4px;\n height: 4px;\n border-radius: 50%;\n background: currentColor;\n flex-shrink: 0;\n }\n\n @keyframes dbg-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.35; }\n }\n`\n\n/** 可搜索的自定义下拉组件 */\nclass SearchSelect {\n private el: HTMLElement\n private trigger: HTMLElement\n private valEl: HTMLElement\n private dropdown: HTMLElement\n private searchInput: HTMLInputElement\n private listEl: HTMLElement\n private options: Array<{ value: string; label: string }> = []\n private selected = ''\n private onChange: (value: string) => void\n\n constructor(onChange: (value: string) => void) {\n this.onChange = onChange\n this.el = document.createElement('div')\n this.el.className = 'dbg-select'\n\n // 触发器\n this.trigger = document.createElement('div')\n this.trigger.className = 'dbg-select-trigger'\n this.valEl = document.createElement('span')\n this.valEl.className = 'dbg-select-val placeholder'\n const arrow = document.createElement('span')\n arrow.className = 'dbg-select-arrow'\n arrow.textContent = '▾'\n this.trigger.appendChild(this.valEl)\n this.trigger.appendChild(arrow)\n\n // 下拉面板\n this.dropdown = document.createElement('div')\n this.dropdown.className = 'dbg-select-dropdown'\n\n // 搜索框\n const searchWrap = document.createElement('div')\n searchWrap.className = 'dbg-select-search'\n const searchIcon = document.createElement('span')\n searchIcon.className = 'dbg-select-search-icon'\n searchIcon.textContent = '⌕'\n this.searchInput = document.createElement('input')\n this.searchInput.type = 'text'\n this.searchInput.placeholder = T.search()\n searchWrap.appendChild(searchIcon)\n searchWrap.appendChild(this.searchInput)\n\n this.listEl = document.createElement('div')\n this.listEl.className = 'dbg-select-list'\n\n this.dropdown.appendChild(searchWrap)\n this.dropdown.appendChild(this.listEl)\n this.el.appendChild(this.trigger)\n this.el.appendChild(this.dropdown)\n\n this.bindEvents()\n }\n\n private bindEvents(): void {\n this.trigger.addEventListener('click', (e) => {\n e.stopPropagation()\n const isOpen = this.el.classList.contains('open')\n document.querySelectorAll('#__snackkit_debugger__ .dbg-select.open').forEach((el) => {\n if (el !== this.el) el.classList.remove('open')\n })\n this.el.classList.toggle('open', !isOpen)\n if (!isOpen) {\n this.searchInput.value = ''\n this.filterOptions('')\n setTimeout(() => this.searchInput.focus(), 50)\n }\n })\n\n this.searchInput.addEventListener('input', () => {\n this.filterOptions(this.searchInput.value)\n })\n\n this.searchInput.addEventListener('click', (e) => e.stopPropagation())\n\n document.addEventListener('click', () => {\n this.el.classList.remove('open')\n })\n }\n\n private filterOptions(query: string): void {\n const q = query.toLowerCase()\n let visibleCount = 0\n this.listEl.querySelectorAll<HTMLElement>('.dbg-select-option').forEach((opt) => {\n const match = !q || opt.textContent!.toLowerCase().includes(q)\n opt.classList.toggle('hidden', !match)\n if (match) visibleCount++\n })\n let emptyEl = this.listEl.querySelector<HTMLElement>('.dbg-select-empty')\n if (visibleCount === 0) {\n if (!emptyEl) {\n emptyEl = document.createElement('div')\n emptyEl.className = 'dbg-select-empty'\n emptyEl.textContent = T.noMatch()\n this.listEl.appendChild(emptyEl)\n }\n } else {\n emptyEl?.remove()\n }\n }\n\n /** 更新选项列表 */\n setOptions(options: Array<{ value: string; label: string }>, placeholder: string): void {\n this.options = options\n this.listEl.innerHTML = ''\n this.valEl.textContent = placeholder\n this.valEl.className = 'dbg-select-val placeholder'\n this.selected = ''\n\n options.forEach(({ value, label }) => {\n const opt = document.createElement('div')\n opt.className = 'dbg-select-option'\n opt.dataset['value'] = value\n opt.textContent = label\n opt.setAttribute('title', label)\n opt.addEventListener('click', (e) => {\n e.stopPropagation()\n this.select(value, label)\n this.el.classList.remove('open')\n })\n this.listEl.appendChild(opt)\n })\n }\n\n /** 选中某个值(不触发 onChange) */\n setValue(value: string): void {\n const opt = this.options.find((o) => o.value === value)\n if (opt) this.select(opt.value, opt.label, false)\n }\n\n private select(value: string, label: string, emit = true): void {\n this.selected = value\n this.valEl.textContent = label\n this.valEl.className = 'dbg-select-val'\n this.listEl.querySelectorAll('.dbg-select-option').forEach((el) => {\n el.classList.toggle('active', (el as HTMLElement).dataset['value'] === value)\n })\n if (emit) this.onChange(value)\n }\n\n getValue(): string { return this.selected }\n getElement(): HTMLElement { return this.el }\n}\n\n/**\n * 浏览器端浮动调试面板\n *\n * 允许开发者在运行时:\n * - 选择调试网关 → 拉取服务清单\n * - 切换目标服务 → 重新加载 Context 路由映射\n * - 查看并复制可用的路由 ctx 列表\n *\n * 与 http 模块集成:使用 `Get()` 拉取服务清单,使用 `context.load()` 切换请求目标。\n *\n * @see [交互式 Demo](../demo/debugger.html)\n *\n * @example\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * ```\n */\nexport class Debugger {\n private options: Required<DebuggerOptions>\n private gateways: string[]\n private servers: ServerItem[] = []\n private currentCtxMap: Record<string, string> = {}\n private panelEl!: HTMLElement\n private headerDot!: HTMLElement\n private gwVersion!: HTMLElement\n private footerEl!: HTMLElement\n private copyAllBtn!: HTMLButtonElement\n private gwSelect!: SearchSelect\n private typeSelect!: SearchSelect\n private serverSelect!: SearchSelect\n private ctxList!: HTMLElement\n private ctxSearchInput!: HTMLInputElement\n\n private constructor(options: Required<DebuggerOptions>) {\n this.options = options\n this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]\n }\n\n /**\n * 初始化并渲染调试面板\n *\n * 支持两种调用形式:\n * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间\n * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }`\n *\n * 调用后在页面右下角插入浮动按钮,点击展开调试面板。\n * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置对象 或 网关 URL 数组\n *\n * @example 配置对象形式\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: [\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ],\n * timeout: 5000,\n * })\n * ```\n *\n * @example 数组简写形式\n * ```ts\n * await Debugger.init([\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ])\n * ```\n *\n * @example 仅在非生产环境加载\n * ```ts\n * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init(['http://dev-gateway.example.com'])\n * }\n * ```\n */\n static async init(options: DebuggerOptions | string[]): Promise<Debugger> {\n // 在 globalThis 写入标志位,Context.load() 无参数时检测到后将跳过自动加载\n ;(globalThis as Record<string, unknown>)[DEBUGGER_ACTIVE_KEY] = true\n const normalized: DebuggerOptions = Array.isArray(options) ? { gateways: options } : options\n const instance = new Debugger({ timeout: 10000, ...normalized })\n instance.injectStyle()\n instance.buildDOM()\n await instance.restoreState()\n return instance\n }\n\n /** 注入内联样式 */\n private injectStyle(): void {\n if (document.getElementById('__snackkit_debugger_style__')) return\n const style = document.createElement('style')\n style.id = '__snackkit_debugger_style__'\n style.textContent = PANEL_STYLE\n document.head.appendChild(style)\n }\n\n /** 构建 DOM 结构 */\n private buildDOM(): void {\n const root = document.createElement('div')\n root.id = '__snackkit_debugger__'\n\n // ── 面板 ──\n const panel = document.createElement('div')\n panel.className = 'dbg-panel'\n this.panelEl = panel\n\n // Header\n const header = document.createElement('div')\n header.className = 'dbg-header'\n const headerLeft = document.createElement('div')\n headerLeft.className = 'dbg-header-left'\n this.headerDot = document.createElement('span')\n this.headerDot.className = 'dbg-header-dot loading'\n const headerTitle = document.createElement('span')\n headerTitle.className = 'dbg-header-title'\n headerTitle.textContent = 'Snack Kit Debugger'\n headerLeft.appendChild(this.headerDot)\n headerLeft.appendChild(headerTitle)\n header.appendChild(headerLeft)\n panel.appendChild(header)\n\n // Body\n const body = document.createElement('div')\n body.className = 'dbg-body'\n\n // 网关选择(标签行右侧显示版本号)\n this.gwVersion = document.createElement('span')\n this.gwVersion.className = 'dbg-header-meta'\n this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val))\n this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway())\n body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement()))\n\n // 标签(类型)选择\n this.typeSelect = new SearchSelect((val) => this.onTypeChange(val))\n body.appendChild(this.createField(T.tag(), this.typeSelect.getElement()))\n\n // 服务选择\n this.serverSelect = new SearchSelect((val) => this.onServerChange(val))\n body.appendChild(this.createField(T.server(), this.serverSelect.getElement()))\n\n // 路由列表(含一键复制全部 JSON 按钮)\n const ctxWrap = document.createElement('div')\n ctxWrap.className = 'dbg-ctx-wrap'\n\n const ctxSearch = document.createElement('div')\n ctxSearch.className = 'dbg-ctx-search'\n const ctxSearchIcon = document.createElement('span')\n ctxSearchIcon.className = 'dbg-select-search-icon'\n ctxSearchIcon.textContent = '⌕'\n this.ctxSearchInput = document.createElement('input')\n this.ctxSearchInput.type = 'text'\n this.ctxSearchInput.placeholder = T.filterCtx()\n this.ctxSearchInput.addEventListener('input', () => this.filterCtx(this.ctxSearchInput.value))\n ctxSearch.appendChild(ctxSearchIcon)\n ctxSearch.appendChild(this.ctxSearchInput)\n\n this.ctxList = document.createElement('div')\n this.ctxList.className = 'dbg-ctx-list'\n\n ctxWrap.appendChild(ctxSearch)\n ctxWrap.appendChild(this.ctxList)\n\n // 一键复制全部 JSON 按钮\n this.copyAllBtn = document.createElement('button')\n this.copyAllBtn.className = 'dbg-copy-all'\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.setAttribute('title', isChinese()\n ? '将全部上下文复制为 JSON,可作为 Context.load({...}) 的参数'\n : 'Copy all ctx entries as JSON for Context.load({...})')\n this.copyAllBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.copyAllCtx()\n })\n\n const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap)\n ctxField.classList.add('dbg-ctx-field')\n body.appendChild(ctxField)\n\n panel.appendChild(body)\n\n // Footer 状态栏\n const footer = document.createElement('div')\n footer.className = 'dbg-footer'\n this.footerEl = footer\n panel.appendChild(footer)\n\n // ── 浮动按钮 ──\n const toggle = document.createElement('button')\n toggle.className = 'dbg-toggle'\n toggle.textContent = '🐛'\n toggle.setAttribute('title', 'Snack Debugger')\n toggle.addEventListener('click', (e) => {\n e.stopPropagation()\n panel.classList.toggle('open')\n })\n\n root.appendChild(panel)\n root.appendChild(toggle)\n document.body.appendChild(root)\n }\n\n /** 创建普通字段行(label + content) */\n private createField(label: string, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n field.appendChild(lbl)\n field.appendChild(content)\n return field\n }\n\n /** 创建带操作按钮的字段行(label + action button + content) */\n private createFieldWithAction(label: string, action: HTMLElement, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const fieldHeader = document.createElement('div')\n fieldHeader.className = 'dbg-field-header'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n fieldHeader.appendChild(lbl)\n fieldHeader.appendChild(action)\n field.appendChild(fieldHeader)\n field.appendChild(content)\n return field\n }\n\n /** 恢复上次持久化的状态 */\n private async restoreState(): Promise<void> {\n const savedGw = localStorage.getItem(KEY_GATEWAY)\n const gw = (savedGw && this.gateways.includes(savedGw)) ? savedGw : this.gateways[0]\n this.gwSelect.setValue(gw)\n await this.loadServers(gw)\n\n const savedType = localStorage.getItem(KEY_TYPE)\n if (savedType) {\n this.typeSelect.setValue(savedType)\n this.renderServerOptions(savedType)\n }\n\n const savedServer = localStorage.getItem(KEY_SERVER)\n if (savedServer && this.servers.find((s) => s.key === savedServer)) {\n this.serverSelect.setValue(savedServer)\n await this.applyServer(savedServer)\n }\n }\n\n private async onGatewayChange(gw: string): Promise<void> {\n localStorage.setItem(KEY_GATEWAY, gw)\n this.gwVersion.textContent = ''\n await this.loadServers(gw)\n }\n\n private onTypeChange(type: string): void {\n localStorage.setItem(KEY_TYPE, type)\n this.renderServerOptions(type)\n }\n\n private async onServerChange(key: string): Promise<void> {\n localStorage.setItem(KEY_SERVER, key)\n await this.applyServer(key)\n }\n\n /** 从网关加载服务列表 */\n private async loadServers(gwUrl: string): Promise<void> {\n this.setHeader('loading')\n this.setFooter(T.loading(), '')\n try {\n const res = await _http.get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n timeout: this.options.timeout,\n })\n this.servers = res.data ?? []\n this.renderTypeOptions()\n this.setHeader('ok')\n this.setFooter(T.loaded(this.servers.length), 'ok')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.setHeader('err')\n this.setFooter(T.loadFailed(message), 'err')\n }\n }\n\n /** 渲染标签选项 */\n private renderTypeOptions(): void {\n const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]\n const opts = [{ value: '', label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]\n this.typeSelect.setOptions(opts, T.allTags())\n this.typeSelect.setValue('')\n this.renderServerOptions('')\n }\n\n /** 根据标签渲染服务选项 */\n private renderServerOptions(type: string): void {\n const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers\n this.serverSelect.setOptions(\n filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })),\n T.selectServer(),\n )\n this.renderCtxList({})\n }\n\n /** 切换目标服务,重新加载 Context 路由映射 */\n private async applyServer(key: string): Promise<void> {\n if (!key) { this.renderCtxList({}); return }\n const server = this.servers.find((s) => s.key === key)\n if (!server) return\n\n this.setFooter(T.switching(), '')\n try {\n await Context.load(server.origin, this.options.timeout)\n const res = await fetch(`${server.origin}/ngw/context`)\n const raw: Record<string, unknown> = res.ok ? await res.json() : {}\n const ctxMap: Record<string, string> = {}\n for (const [k, v] of Object.entries(raw)) {\n if (k !== '$info' && typeof v === 'string') ctxMap[k] = v\n }\n const info = Context.info\n if (info) this.gwVersion.textContent = `v${info.version}`\n this.renderCtxList(ctxMap)\n const count = Object.keys(ctxMap).length\n this.setFooter(T.routes(server.name, count), 'ok')\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this.setFooter(T.switchFailed(msg), 'err')\n }\n }\n\n /** 渲染路由列表 */\n private renderCtxList(ctxMap: Record<string, string>): void {\n this.currentCtxMap = ctxMap\n this.ctxList.innerHTML = ''\n this.ctxSearchInput.value = ''\n const entries = Object.entries(ctxMap)\n if (entries.length === 0) {\n const empty = document.createElement('div')\n empty.className = 'dbg-ctx-empty'\n empty.textContent = T.selectFirst()\n this.ctxList.appendChild(empty)\n return\n }\n entries.forEach(([k, v]) => {\n const item = document.createElement('div')\n item.className = 'dbg-ctx-item'\n item.dataset['key'] = k\n\n const keySpan = document.createElement('span')\n keySpan.className = 'dbg-ctx-key'\n keySpan.textContent = k\n keySpan.setAttribute('title', k)\n\n const valSpan = document.createElement('span')\n valSpan.className = 'dbg-ctx-val'\n valSpan.textContent = v\n valSpan.setAttribute('title', `${k} → ${v}`)\n\n const copyIcon = document.createElement('span')\n copyIcon.className = 'dbg-ctx-copy'\n copyIcon.textContent = '⎘'\n\n item.appendChild(keySpan)\n item.appendChild(valSpan)\n item.appendChild(copyIcon)\n item.addEventListener('click', () => this.copyCtxItem(item, k))\n this.ctxList.appendChild(item)\n })\n }\n\n /** 过滤路由列表 */\n private filterCtx(query: string): void {\n const q = query.toLowerCase()\n this.ctxList.querySelectorAll<HTMLElement>('.dbg-ctx-item').forEach((item) => {\n const key = item.dataset['key'] ?? ''\n item.classList.toggle('hidden', !!q && !key.toLowerCase().includes(q))\n })\n }\n\n /** 复制单个 ctx key */\n private copyCtxItem(item: HTMLElement, text: string): void {\n navigator.clipboard.writeText(text).then(() => {\n item.classList.add('copied')\n const icon = item.querySelector('.dbg-ctx-copy')\n if (icon) icon.textContent = '✓'\n setTimeout(() => {\n item.classList.remove('copied')\n const ic = item.querySelector('.dbg-ctx-copy')\n if (ic) ic.textContent = '⎘'\n }, 1500)\n this.setFooter(T.copied(text), 'ok')\n setTimeout(() => this.setFooter('', ''), 2000)\n })\n }\n\n /** 一键复制全部上下文为 JSON */\n private copyAllCtx(): void {\n const entries = Object.keys(this.currentCtxMap)\n if (entries.length === 0) return\n const json = JSON.stringify(this.currentCtxMap, null, 2)\n navigator.clipboard.writeText(json).then(() => {\n this.copyAllBtn.textContent = '✓ ' + T.copyAllDone()\n this.copyAllBtn.classList.add('done')\n setTimeout(() => {\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.classList.remove('done')\n }, 2000)\n this.setFooter(T.copyAllDone(), 'ok')\n setTimeout(() => this.setFooter('', ''), 2500)\n })\n }\n\n private setHeader(state: 'ok' | 'err' | 'loading'): void {\n this.headerDot.className = `dbg-header-dot${state !== 'ok' ? ` ${state}` : ''}`\n }\n\n private setFooter(msg: string, cls: '' | 'ok' | 'err'): void {\n this.footerEl.innerHTML = msg\n ? `<span class=\"dbg-footer-dot\"></span>${msg}`\n : ''\n this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ''}`\n }\n}\n"]} |
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var chunkUNFUIZVY_cjs = require('./chunk-UNFUIZVY.cjs'); | ||
| var axios = require('axios'); | ||
| function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } | ||
| var axios__default = /*#__PURE__*/_interopDefault(axios); | ||
| // src/http/cancel.ts | ||
| var registry = /* @__PURE__ */ new Map(); | ||
| function Register(id) { | ||
| if (registry.has(id)) { | ||
| registry.get(id).abort(); | ||
| } | ||
| const controller = new AbortController(); | ||
| registry.set(id, controller); | ||
| return controller; | ||
| } | ||
| function Cancel(id) { | ||
| const controller = registry.get(id); | ||
| if (!controller) return false; | ||
| controller.abort(); | ||
| registry.delete(id); | ||
| return true; | ||
| } | ||
| function CancelAll() { | ||
| for (const controller of registry.values()) { | ||
| controller.abort(); | ||
| } | ||
| registry.clear(); | ||
| } | ||
| function Unregister(id) { | ||
| registry.delete(id); | ||
| } | ||
| function generateId() { | ||
| if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | ||
| return crypto.randomUUID(); | ||
| } | ||
| return `${Date.now()}-${Math.random().toString(36).slice(2)}`; | ||
| } | ||
| async function Request(config) { | ||
| const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config; | ||
| if (ctx && chunkUNFUIZVY_cjs.Context.loaded) { | ||
| const base = chunkUNFUIZVY_cjs.Context.get(ctx); | ||
| if (base) { | ||
| axiosConfig.baseURL = base; | ||
| } | ||
| } | ||
| if (!cache) { | ||
| const sep = (axiosConfig.url ?? "").includes("?") ? "&" : "?"; | ||
| axiosConfig.url = `${axiosConfig.url ?? ""}${sep}_=${Date.now()}`; | ||
| } | ||
| const cancelId = customCancelId ?? generateId(); | ||
| const controller = Register(cancelId); | ||
| axiosConfig.signal = controller.signal; | ||
| onCancelId?.(cancelId); | ||
| try { | ||
| const response = await axios__default.default.request({ ...axiosConfig }); | ||
| return response; | ||
| } catch (err) { | ||
| if (axios.isAxiosError(err)) { | ||
| return { | ||
| error: { | ||
| status: err.response?.status, | ||
| message: err.message, | ||
| data: err.response?.data | ||
| } | ||
| }; | ||
| } | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| return { error: { message } }; | ||
| } finally { | ||
| Unregister(cancelId); | ||
| } | ||
| } | ||
| function Get(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "GET", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "GET" }); | ||
| } | ||
| function Post(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "POST", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "POST" }); | ||
| } | ||
| function Put(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PUT", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PUT" }); | ||
| } | ||
| function Patch(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PATCH", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PATCH" }); | ||
| } | ||
| function Del(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "DELETE", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "DELETE" }); | ||
| } | ||
| // src/http/ws.ts | ||
| var _registry = /* @__PURE__ */ new Map(); | ||
| function generateId2() { | ||
| if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | ||
| return crypto.randomUUID(); | ||
| } | ||
| return `${Date.now()}-${Math.random().toString(36).slice(2)}`; | ||
| } | ||
| function Ws(urlOrConfig, event) { | ||
| const config = typeof urlOrConfig === "string" ? { url: urlOrConfig } : urlOrConfig; | ||
| const { ctx, cancelId: customCancelId, onCancelId, cache = false, binaryType } = config; | ||
| const cancelId = customCancelId ?? generateId2(); | ||
| if (_registry.has(cancelId)) { | ||
| _registry.get(cancelId).close(); | ||
| _registry.delete(cancelId); | ||
| } | ||
| let url = config.url; | ||
| if (ctx && chunkUNFUIZVY_cjs.Context.loaded && !/^wss?:\/\//.test(url)) { | ||
| const host = chunkUNFUIZVY_cjs.Context.get(ctx); | ||
| if (host) { | ||
| const base = host.startsWith("/") ? chunkUNFUIZVY_cjs.Origin + host : host; | ||
| url = base + url; | ||
| } | ||
| } | ||
| url = url.replace(/^https/, "wss").replace(/^http/, "ws"); | ||
| if (!cache) { | ||
| const sep = url.includes("?") ? "&" : "?"; | ||
| url = `${url}${sep}_=${Date.now()}`; | ||
| } | ||
| const ws = new WebSocket(url); | ||
| if (binaryType) ws.binaryType = binaryType; | ||
| _registry.set(cancelId, ws); | ||
| onCancelId?.(cancelId); | ||
| ws.onopen = (evt) => { | ||
| event("connection", evt, ws); | ||
| }; | ||
| ws.onmessage = (evt) => { | ||
| let data; | ||
| const raw = evt.data; | ||
| if (raw instanceof ArrayBuffer) { | ||
| const text = new TextDecoder("utf-8").decode(raw); | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| data = text; | ||
| } | ||
| } else if (typeof Blob !== "undefined" && raw instanceof Blob) { | ||
| data = raw; | ||
| } else { | ||
| try { | ||
| data = JSON.parse(raw); | ||
| } catch { | ||
| data = raw; | ||
| } | ||
| } | ||
| event("message", data, ws, evt); | ||
| }; | ||
| ws.onclose = (evt) => { | ||
| event("close", evt, ws); | ||
| _registry.delete(cancelId); | ||
| }; | ||
| return ws; | ||
| } | ||
| function WsClose(id) { | ||
| const ws = _registry.get(id); | ||
| if (!ws) return false; | ||
| ws.close(); | ||
| _registry.delete(id); | ||
| return true; | ||
| } | ||
| function WsCloseAll() { | ||
| for (const ws of _registry.values()) { | ||
| ws.close(); | ||
| } | ||
| _registry.clear(); | ||
| } | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function () { return axios__default.default; } | ||
| }); | ||
| exports.Cancel = Cancel; | ||
| exports.CancelAll = CancelAll; | ||
| exports.Del = Del; | ||
| exports.Get = Get; | ||
| exports.Patch = Patch; | ||
| exports.Post = Post; | ||
| exports.Put = Put; | ||
| exports.Request = Request; | ||
| exports.Ws = Ws; | ||
| exports.WsClose = WsClose; | ||
| exports.WsCloseAll = WsCloseAll; | ||
| //# sourceMappingURL=chunk-UT3D47LI.cjs.map | ||
| //# sourceMappingURL=chunk-UT3D47LI.cjs.map |
| {"version":3,"sources":["../../src/http/cancel.ts","../../src/http/client.ts","../../src/http/ws.ts"],"names":["Context","axios","isAxiosError","generateId","Origin"],"mappings":";;;;;;;;;;;;AACA,IAAM,QAAA,uBAAe,GAAA,EAA6B;AAmB3C,SAAS,SAAS,EAAA,EAA6B;AACpD,EAAA,IAAI,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA,EAAG;AACpB,IAAA,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA,CAAG,KAAA,EAAM;AAAA,EAC1B;AACA,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,QAAA,CAAS,GAAA,CAAI,IAAI,UAAU,CAAA;AAC3B,EAAA,OAAO,UAAA;AACT;AAmBO,SAAS,OAAO,EAAA,EAAqB;AAC1C,EAAA,MAAM,UAAA,GAAa,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA;AAClC,EAAA,IAAI,CAAC,YAAY,OAAO,KAAA;AACxB,EAAA,UAAA,CAAW,KAAA,EAAM;AACjB,EAAA,QAAA,CAAS,OAAO,EAAE,CAAA;AAClB,EAAA,OAAO,IAAA;AACT;AAaO,SAAS,SAAA,GAAkB;AAChC,EAAA,KAAA,MAAW,UAAA,IAAc,QAAA,CAAS,MAAA,EAAO,EAAG;AAC1C,IAAA,UAAA,CAAW,KAAA,EAAM;AAAA,EACnB;AACA,EAAA,QAAA,CAAS,KAAA,EAAM;AACjB;AAOO,SAAS,WAAW,EAAA,EAAkB;AAC3C,EAAA,QAAA,CAAS,OAAO,EAAE,CAAA;AACpB;ACzEA,SAAS,UAAA,GAAqB;AAC5B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAA,CAAO,eAAe,UAAA,EAAY;AAC5E,IAAA,OAAO,OAAO,UAAA,EAAW;AAAA,EAC3B;AACA,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAC7D;AAmCA,eAAsB,QAAW,MAAA,EAAmD;AAClF,EAAA,MAAM,EAAE,KAAK,QAAA,EAAU,cAAA,EAAgB,YAAY,KAAA,GAAQ,KAAA,EAAO,GAAG,WAAA,EAAY,GAAI,MAAA;AAGrF,EAAA,IAAI,GAAA,IAAOA,0BAAQ,MAAA,EAAQ;AACzB,IAAA,MAAM,IAAA,GAAOA,yBAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAC5B,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AAAA,IACxB;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,OAAO,WAAA,CAAY,GAAA,IAAO,IAAI,QAAA,CAAS,GAAG,IAAI,GAAA,GAAM,GAAA;AAC1D,IAAA,WAAA,CAAY,GAAA,GAAM,CAAA,EAAG,WAAA,CAAY,GAAA,IAAO,EAAE,GAAG,GAAG,CAAA,EAAA,EAAK,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA;AAAA,EACjE;AAGA,EAAA,MAAM,QAAA,GAAW,kBAAkB,UAAA,EAAW;AAC9C,EAAA,MAAM,UAAA,GAAa,SAAS,QAAQ,CAAA;AACpC,EAAA,WAAA,CAAY,SAAS,UAAA,CAAW,MAAA;AAChC,EAAA,UAAA,GAAa,QAAQ,CAAA;AAErB,EAAA,IAAI;AACF,IAAA,MAAM,WAAW,MAAMC,sBAAA,CAAM,QAAW,EAAE,GAAG,aAAa,CAAA;AAC1D,IAAA,OAAO,QAAA;AAAA,EACT,SAAS,GAAA,EAAK;AACZ,IAAA,IAAIC,kBAAA,CAAa,GAAG,CAAA,EAAG;AACrB,MAAA,OAAO;AAAA,QACL,KAAA,EAAO;AAAA,UACL,MAAA,EAAQ,IAAI,QAAA,EAAU,MAAA;AAAA,UACtB,SAAS,GAAA,CAAI,OAAA;AAAA,UACb,IAAA,EAAM,IAAI,QAAA,EAAU;AAAA;AACtB,OACF;AAAA,IACF;AAEA,IAAA,MAAM,UAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC/D,IAAA,OAAO,EAAE,KAAA,EAAO,EAAE,OAAA,EAAQ,EAAE;AAAA,EAC9B,CAAA,SAAE;AACA,IAAA,UAAA,CAAW,QAAQ,CAAA;AAAA,EACrB;AACF;AA4BO,SAAS,GAAA,CACd,aACA,MAAA,EACwB;AACxB,EAAA,IAAI,OAAO,gBAAgB,QAAA,EAAU;AACnC,IAAA,OAAO,OAAA,CAAW,EAAE,GAAG,MAAA,EAAQ,QAAQ,KAAA,EAAO,GAAA,EAAK,aAAa,CAAA;AAAA,EAClE;AACA,EAAA,OAAO,QAAW,EAAE,GAAG,WAAA,EAAa,MAAA,EAAQ,OAAO,CAAA;AACrD;AA4BO,SAAS,IAAA,CACd,WAAA,EACA,IAAA,EACA,MAAA,EACwB;AACxB,EAAA,IAAI,OAAO,gBAAgB,QAAA,EAAU;AACnC,IAAA,OAAO,OAAA,CAAW,EAAE,GAAG,MAAA,EAAQ,QAAQ,MAAA,EAAQ,GAAA,EAAK,WAAA,EAAa,IAAA,EAAM,CAAA;AAAA,EACzE;AACA,EAAA,OAAO,QAAW,EAAE,GAAG,WAAA,EAAa,MAAA,EAAQ,QAAQ,CAAA;AACtD;AAqBO,SAAS,GAAA,CACd,WAAA,EACA,IAAA,EACA,MAAA,EACwB;AACxB,EAAA,IAAI,OAAO,gBAAgB,QAAA,EAAU;AACnC,IAAA,OAAO,OAAA,CAAW,EAAE,GAAG,MAAA,EAAQ,QAAQ,KAAA,EAAO,GAAA,EAAK,WAAA,EAAa,IAAA,EAAM,CAAA;AAAA,EACxE;AACA,EAAA,OAAO,QAAW,EAAE,GAAG,WAAA,EAAa,MAAA,EAAQ,OAAO,CAAA;AACrD;AAqBO,SAAS,KAAA,CACd,WAAA,EACA,IAAA,EACA,MAAA,EACwB;AACxB,EAAA,IAAI,OAAO,gBAAgB,QAAA,EAAU;AACnC,IAAA,OAAO,OAAA,CAAW,EAAE,GAAG,MAAA,EAAQ,QAAQ,OAAA,EAAS,GAAA,EAAK,WAAA,EAAa,IAAA,EAAM,CAAA;AAAA,EAC1E;AACA,EAAA,OAAO,QAAW,EAAE,GAAG,WAAA,EAAa,MAAA,EAAQ,SAAS,CAAA;AACvD;AAqBO,SAAS,GAAA,CACd,aACA,MAAA,EACwB;AACxB,EAAA,IAAI,OAAO,gBAAgB,QAAA,EAAU;AACnC,IAAA,OAAO,OAAA,CAAW,EAAE,GAAG,MAAA,EAAQ,QAAQ,QAAA,EAAU,GAAA,EAAK,aAAa,CAAA;AAAA,EACrE;AACA,EAAA,OAAO,QAAW,EAAE,GAAG,WAAA,EAAa,MAAA,EAAQ,UAAU,CAAA;AACxD;;;ACtPA,IAAM,SAAA,uBAAgB,GAAA,EAAuB;AAG7C,SAASC,WAAAA,GAAqB;AAC5B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAA,CAAO,eAAe,UAAA,EAAY;AAC5E,IAAA,OAAO,OAAO,UAAA,EAAW;AAAA,EAC3B;AACA,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAC7D;AA8CO,SAAS,EAAA,CACd,aACA,KAAA,EACW;AACX,EAAA,MAAM,SACJ,OAAO,WAAA,KAAgB,WAAW,EAAE,GAAA,EAAK,aAAY,GAAI,WAAA;AAC3D,EAAA,MAAM,EAAE,KAAK,QAAA,EAAU,cAAA,EAAgB,YAAY,KAAA,GAAQ,KAAA,EAAO,YAAW,GAAI,MAAA;AAEjF,EAAA,MAAM,QAAA,GAAW,kBAAkBA,WAAAA,EAAW;AAG9C,EAAA,IAAI,SAAA,CAAU,GAAA,CAAI,QAAQ,CAAA,EAAG;AAC3B,IAAA,SAAA,CAAU,GAAA,CAAI,QAAQ,CAAA,CAAG,KAAA,EAAM;AAC/B,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EAC3B;AAGA,EAAA,IAAI,MAAM,MAAA,CAAO,GAAA;AACjB,EAAA,IAAI,OAAOH,yBAAA,CAAQ,MAAA,IAAU,CAAC,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA,EAAG;AACpD,IAAA,MAAM,IAAA,GAAOA,yBAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAC5B,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,OAAO,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAII,2BAAS,IAAA,GAAO,IAAA;AACpD,MAAA,GAAA,GAAM,IAAA,GAAO,GAAA;AAAA,IACf;AAAA,EACF;AAGA,EAAA,GAAA,GAAM,IAAI,OAAA,CAAQ,QAAA,EAAU,KAAK,CAAA,CAAE,OAAA,CAAQ,SAAS,IAAI,CAAA;AAGxD,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,QAAA,CAAS,GAAG,IAAI,GAAA,GAAM,GAAA;AACtC,IAAA,GAAA,GAAM,GAAG,GAAG,CAAA,EAAG,GAAG,CAAA,EAAA,EAAK,IAAA,CAAK,KAAK,CAAA,CAAA;AAAA,EACnC;AAEA,EAAA,MAAM,EAAA,GAAK,IAAI,SAAA,CAAU,GAAG,CAAA;AAC5B,EAAA,IAAI,UAAA,KAAe,UAAA,GAAa,UAAA;AAChC,EAAA,SAAA,CAAU,GAAA,CAAI,UAAU,EAAE,CAAA;AAC1B,EAAA,UAAA,GAAa,QAAQ,CAAA;AAErB,EAAA,EAAA,CAAG,MAAA,GAAS,CAAC,GAAA,KAAe;AAC1B,IAAA,KAAA,CAAM,YAAA,EAAc,KAA+B,EAAE,CAAA;AAAA,EACvD,CAAA;AAEA,EAAA,EAAA,CAAG,SAAA,GAAY,CAAC,GAAA,KAAsB;AACpC,IAAA,IAAI,IAAA;AACJ,IAAA,MAAM,MAAM,GAAA,CAAI,IAAA;AAEhB,IAAA,IAAI,eAAe,WAAA,EAAa;AAC9B,MAAA,MAAM,OAAO,IAAI,WAAA,CAAY,OAAO,CAAA,CAAE,OAAO,GAAG,CAAA;AAChD,MAAA,IAAI;AACF,QAAA,IAAA,GAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,MACxB,CAAA,CAAA,MAAQ;AACN,QAAA,IAAA,GAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA,MAAA,IAAW,OAAO,IAAA,KAAS,WAAA,IAAe,eAAe,IAAA,EAAM;AAE7D,MAAA,IAAA,GAAO,GAAA;AAAA,IACT,CAAA,MAAO;AACL,MAAA,IAAI;AACF,QAAA,IAAA,GAAO,IAAA,CAAK,MAAM,GAAa,CAAA;AAAA,MACjC,CAAA,CAAA,MAAQ;AACN,QAAA,IAAA,GAAO,GAAA;AAAA,MACT;AAAA,IACF;AACA,IAAA,KAAA,CAAM,SAAA,EAAW,IAAA,EAAgC,EAAA,EAAI,GAAG,CAAA;AAAA,EAC1D,CAAA;AAEA,EAAA,EAAA,CAAG,OAAA,GAAU,CAAC,GAAA,KAAoB;AAChC,IAAA,KAAA,CAAM,OAAA,EAAS,KAA+B,EAAE,CAAA;AAChD,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,OAAO,EAAA;AACT;AAiBO,SAAS,QAAQ,EAAA,EAAqB;AAC3C,EAAA,MAAM,EAAA,GAAK,SAAA,CAAU,GAAA,CAAI,EAAE,CAAA;AAC3B,EAAA,IAAI,CAAC,IAAI,OAAO,KAAA;AAChB,EAAA,EAAA,CAAG,KAAA,EAAM;AACT,EAAA,SAAA,CAAU,OAAO,EAAE,CAAA;AACnB,EAAA,OAAO,IAAA;AACT;AAYO,SAAS,UAAA,GAAmB;AACjC,EAAA,KAAA,MAAW,EAAA,IAAM,SAAA,CAAU,MAAA,EAAO,EAAG;AACnC,IAAA,EAAA,CAAG,KAAA,EAAM;AAAA,EACX;AACA,EAAA,SAAA,CAAU,KAAA,EAAM;AAClB","file":"chunk-UT3D47LI.cjs","sourcesContent":["/** 模块私有注册表,避免全局 window 污染 */\nconst registry = new Map<string, AbortController>()\n\n/**\n * 注册一个请求取消控制器\n *\n * 若已存在相同 id 的控制器,先取消旧请求再注册新的。\n *\n * @param id 请求唯一标识\n * @returns 新创建的 AbortController\n *\n * @example\n * ```ts\n * import { Register, Cancel } from '@snack-kit/lib/http'\n *\n * const ctrl = Register('my-req')\n * // 请求进行中时手动取消\n * Cancel('my-req')\n * ```\n */\nexport function Register(id: string): AbortController {\n if (registry.has(id)) {\n registry.get(id)!.abort()\n }\n const controller = new AbortController()\n registry.set(id, controller)\n return controller\n}\n\n/**\n * 取消指定 id 的请求并从注册表移除\n *\n * @param id 请求唯一标识\n * @returns 是否找到并取消了请求\n *\n * @example\n * ```ts\n * import { Get, Cancel } from '@snack-kit/lib/http'\n *\n * let cancelId = ''\n * Get('/api/data', { onCancelId: (id) => { cancelId = id } })\n *\n * // 需要时取消\n * Cancel(cancelId)\n * ```\n */\nexport function Cancel(id: string): boolean {\n const controller = registry.get(id)\n if (!controller) return false\n controller.abort()\n registry.delete(id)\n return true\n}\n\n/**\n * 取消所有已注册的请求并清空注册表\n *\n * @example\n * ```ts\n * import { CancelAll } from '@snack-kit/lib/http'\n *\n * // 页面卸载时取消所有进行中的请求\n * window.addEventListener('beforeunload', () => CancelAll())\n * ```\n */\nexport function CancelAll(): void {\n for (const controller of registry.values()) {\n controller.abort()\n }\n registry.clear()\n}\n\n/**\n * 从注册表中移除指定 id(不触发取消,用于请求完成后内部清理)\n *\n * @param id 请求唯一标识\n */\nexport function Unregister(id: string): void {\n registry.delete(id)\n}\n","import axios, { isAxiosError } from 'axios'\nimport { Context } from './context'\nimport { Register, Unregister } from './cancel'\nimport type { HttpRequestConfig, HttpMethodConfig, HttpResult } from './types'\n\n/** 生成请求唯一 ID */\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/**\n * 核心请求函数,所有快捷方法基于此实现\n *\n * 自动处理:\n * - `ctx` → 从 HttpContext 查询 baseURL 并注入\n * - `cache: false`(默认)→ URL 附加 `?_=timestamp` 防缓存\n * - 请求取消 → 自动注册 AbortController,完成后自动清理\n * - 错误捕获 → 统一返回 `{ error }` 而非 throw\n *\n * @param config 请求配置\n * @returns 请求结果\n *\n * @example 基础用法\n * ```ts\n * import { Request } from '@snack-kit/lib/http'\n *\n * const result = await Request<{ id: number }>({ url: '/api/user', method: 'GET' })\n * if (result.error) {\n * console.error(result.error.message)\n * } else {\n * console.log(result.data)\n * }\n * ```\n *\n * @example 使用 ctx 自动注入 baseURL\n * ```ts\n * import { context, Request } from '@snack-kit/lib/http'\n *\n * await context.load({ 'user-svc': 'http://user.api.com' })\n * const result = await Request({ url: '/profile', method: 'GET', ctx: 'user-svc' })\n * // 实际请求:GET http://user.api.com/profile\n * ```\n */\nexport async function Request<T>(config: HttpRequestConfig): Promise<HttpResult<T>> {\n const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config\n\n // ctx 自动注入 baseURL\n if (ctx && Context.loaded) {\n const base = Context.get(ctx)\n if (base) {\n axiosConfig.baseURL = base\n }\n }\n\n // 防缓存:默认添加时间戳参数\n if (!cache) {\n const sep = (axiosConfig.url ?? '').includes('?') ? '&' : '?'\n axiosConfig.url = `${axiosConfig.url ?? ''}${sep}_=${Date.now()}`\n }\n\n // 注册取消控制器\n const cancelId = customCancelId ?? generateId()\n const controller = Register(cancelId)\n axiosConfig.signal = controller.signal\n onCancelId?.(cancelId)\n\n try {\n const response = await axios.request<T>({ ...axiosConfig })\n return response\n } catch (err) {\n if (isAxiosError(err)) {\n return {\n error: {\n status: err.response?.status,\n message: err.message,\n data: err.response?.data,\n },\n }\n }\n // AbortError 或其他非 axios 错误\n const message = err instanceof Error ? err.message : String(err)\n return { error: { message } }\n } finally {\n Unregister(cancelId)\n }\n}\n\n/**\n * 发起 GET 请求\n *\n * 支持两种调用方式:\n * - `Get(url, config?)` — 位置参数形式\n * - `Get(config)` — 纯配置对象形式(`method` 已预置,无需传入)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Get<{ name: string }>('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Get({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n *\n * @example 获取 cancelId 以便主动取消\n * ```ts\n * let cancelId = ''\n * Get('/api/list', { onCancelId: (id) => { cancelId = id } })\n * Cancel(cancelId)\n * ```\n */\nexport function Get<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'GET', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'GET' })\n}\n\n/**\n * 发起 POST 请求\n *\n * 支持两种调用方式:\n * - `Post(url, data?, config?)` — 位置参数形式\n * - `Post(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Post<{ token: string }>('/api/login', {\n * username: 'admin',\n * password: '123456',\n * })\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Post({\n * url: '/api/login',\n * data: { username: 'admin', password: '123456' },\n * ctx: 'user-svc',\n * })\n * ```\n */\nexport function Post<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'POST', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'POST' })\n}\n\n/**\n * 发起 PUT 请求\n *\n * 支持两种调用方式:\n * - `Put(url, data?, config?)` — 位置参数形式\n * - `Put(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Put('/api/user/1', { name: 'Alice' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Put({ url: '/api/user/1', data: { name: 'Alice' }, ctx: 'user-svc' })\n * ```\n */\nexport function Put<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PUT', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PUT' })\n}\n\n/**\n * 发起 PATCH 请求\n *\n * 支持两种调用方式:\n * - `Patch(url, data?, config?)` — 位置参数形式\n * - `Patch(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Patch('/api/user/1', { avatar: 'https://...' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Patch({ url: '/api/user/1', data: { avatar: 'https://...' }, ctx: 'user-svc' })\n * ```\n */\nexport function Patch<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PATCH', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PATCH' })\n}\n\n/**\n * 发起 DELETE 请求\n *\n * 支持两种调用方式:\n * - `Del(url, config?)` — 位置参数形式\n * - `Del(config)` — 纯配置对象形式(`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Del('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Del({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n */\nexport function Del<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'DELETE', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'DELETE' })\n}\n","import { Context, Origin } from './context'\nimport type { WsRequestConfig, WsEventCallback } from './types'\n\n/** WS 连接注册表 */\nconst _registry = new Map<string, WebSocket>()\n\n/** 生成唯一 ID */\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/**\n * 创建 WebSocket 连接\n *\n * 自动处理:\n * - `ctx` → 从 HttpContext 查询 host 并拼接 URL\n * - 协议转换 → `http(s)://` 自动替换为 `ws(s)://`\n * - `cache: false`(默认)→ URL 附加 `?_=timestamp` 防缓存\n * - 连接管理 → 通过 cancelId 注册,支持 `WsClose` / `WsCloseAll` 主动关闭\n * - 消息解析 → message 事件自动尝试 JSON.parse\n *\n * @param urlOrConfig 请求地址或配置对象\n * @param event 事件回调,eventName: connection(已连接)、message(收到消息)、close(连接关闭)\n * @returns WebSocket 实例\n *\n * @remarks Chrome 16+ · Firefox 11+ · Safari 7+ · Node.js 21+\n *\n * @example 基础用法\n * ```ts\n * import { Ws } from '@snack-kit/lib/http'\n *\n * const ws = Ws('wss://echo.example.com', (eventName, data, ws) => {\n * if (eventName === 'connection') ws.send('hello')\n * if (eventName === 'message') console.log(data)\n * if (eventName === 'close') console.log('disconnected')\n * })\n * ```\n *\n * @example 使用 ctx 自动拼接地址\n * ```ts\n * import { Context, Ws } from '@snack-kit/lib/http'\n *\n * await Context.load('http://172.16.32.155:20000')\n * const ws = Ws({ url: '/channel', ctx: 'ngw' }, (eventName, data) => {\n * // 实际连接:ws://172.16.32.155:20000/ngw/channel\n * })\n * ```\n *\n * @example 获取 cancelId 以便主动关闭\n * ```ts\n * let id = ''\n * Ws({ url: 'wss://...', onCancelId: (cid) => { id = cid } }, () => {})\n * WsClose(id)\n * ```\n */\nexport function Ws<T = unknown>(\n urlOrConfig: string | WsRequestConfig,\n event: WsEventCallback<T>,\n): WebSocket {\n const config: WsRequestConfig =\n typeof urlOrConfig === 'string' ? { url: urlOrConfig } : urlOrConfig\n const { ctx, cancelId: customCancelId, onCancelId, cache = false, binaryType } = config\n\n const cancelId = customCancelId ?? generateId()\n\n // 相同 cancelId 的旧连接先关闭\n if (_registry.has(cancelId)) {\n _registry.get(cancelId)!.close()\n _registry.delete(cancelId)\n }\n\n // 构建 URL\n let url = config.url\n if (ctx && Context.loaded && !/^wss?:\\/\\//.test(url)) {\n const host = Context.get(ctx)\n if (host) {\n const base = host.startsWith('/') ? Origin + host : host\n url = base + url\n }\n }\n\n // http(s) → ws(s)\n url = url.replace(/^https/, 'wss').replace(/^http/, 'ws')\n\n // 防缓存时间戳\n if (!cache) {\n const sep = url.includes('?') ? '&' : '?'\n url = `${url}${sep}_=${Date.now()}`\n }\n\n const ws = new WebSocket(url)\n if (binaryType) ws.binaryType = binaryType\n _registry.set(cancelId, ws)\n onCancelId?.(cancelId)\n\n ws.onopen = (evt: Event) => {\n event('connection', evt as T | Event | CloseEvent, ws)\n }\n\n ws.onmessage = (evt: MessageEvent) => {\n let data: T | string\n const raw = evt.data\n // 二进制数据:先解码为字符串再尝试 JSON 解析\n if (raw instanceof ArrayBuffer) {\n const text = new TextDecoder('utf-8').decode(raw)\n try {\n data = JSON.parse(text) as T\n } catch {\n data = text as T\n }\n } else if (typeof Blob !== 'undefined' && raw instanceof Blob) {\n // Blob 类型异步处理,直接透传原始数据\n data = raw as T\n } else {\n try {\n data = JSON.parse(raw as string) as T\n } catch {\n data = raw as T\n }\n }\n event('message', data as T | Event | CloseEvent, ws, evt)\n }\n\n ws.onclose = (evt: CloseEvent) => {\n event('close', evt as T | Event | CloseEvent, ws)\n _registry.delete(cancelId)\n }\n\n return ws\n}\n\n/**\n * 关闭指定 ID 的 WebSocket 连接\n *\n * @param id 连接唯一标识(由 `onCancelId` 回调获取,或自定义 `cancelId`)\n * @returns 是否找到并关闭了连接\n *\n * @example\n * ```ts\n * import { Ws, WsClose } from '@snack-kit/lib/http'\n *\n * let cancelId = ''\n * Ws({ url: 'wss://...', onCancelId: (id) => { cancelId = id } }, () => {})\n * WsClose(cancelId)\n * ```\n */\nexport function WsClose(id: string): boolean {\n const ws = _registry.get(id)\n if (!ws) return false\n ws.close()\n _registry.delete(id)\n return true\n}\n\n/**\n * 关闭所有已注册的 WebSocket 连接并清空注册表\n *\n * @example\n * ```ts\n * import { WsCloseAll } from '@snack-kit/lib/http'\n *\n * window.addEventListener('beforeunload', () => WsCloseAll())\n * ```\n */\nexport function WsCloseAll(): void {\n for (const ws of _registry.values()) {\n ws.close()\n }\n _registry.clear()\n}\n"]} |
| import { DEBUGGER_ACTIVE_KEY, Context } from './chunk-M5R2NQYU.js'; | ||
| import axios from 'axios'; | ||
| var _http = axios.create(); | ||
| var STORAGE_PREFIX = "__snackkit_debugger__"; | ||
| var KEY_GATEWAY = `${STORAGE_PREFIX}gateway`; | ||
| var KEY_TYPE = `${STORAGE_PREFIX}type`; | ||
| var KEY_SERVER = `${STORAGE_PREFIX}server`; | ||
| function isChinese() { | ||
| return /^zh/i.test(navigator.language ?? ""); | ||
| } | ||
| var T = { | ||
| gateway: () => isChinese() ? "\u7F51\u5173" : "Gateway", | ||
| tag: () => isChinese() ? "\u6807\u7B7E" : "Tag", | ||
| server: () => isChinese() ? "\u670D\u52A1" : "Server", | ||
| ctx: () => isChinese() ? "\u4E0A\u4E0B\u6587" : "Ctx", | ||
| ctxLabel: () => isChinese() ? "\u4E0A\u4E0B\u6587 \u2014 \u70B9\u51FB\u884C\u590D\u5236 key" : "Ctx \u2014 click row to copy key", | ||
| search: () => isChinese() ? "\u641C\u7D22..." : "Search...", | ||
| noMatch: () => isChinese() ? "\u65E0\u5339\u914D\u9879" : "No results", | ||
| allTags: () => isChinese() ? "\u5168\u90E8\u6807\u7B7E" : "All tags", | ||
| selectGateway: () => isChinese() ? "\u9009\u62E9\u7F51\u5173" : "Select gateway", | ||
| selectServer: () => isChinese() ? "\u9009\u62E9\u670D\u52A1" : "Select server", | ||
| filterCtx: () => isChinese() ? "\u8FC7\u6EE4\u8DEF\u7531..." : "Filter routes...", | ||
| selectFirst: () => isChinese() ? "\u2190 \u8BF7\u5148\u9009\u62E9\u670D\u52A1" : "\u2190 Select a server first", | ||
| loading: () => isChinese() ? "\u52A0\u8F7D\u670D\u52A1\u5217\u8868..." : "Loading...", | ||
| loadFailed: (m) => isChinese() ? `\u52A0\u8F7D\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| loaded: (n) => isChinese() ? `\u5DF2\u52A0\u8F7D ${n} \u4E2A\u670D\u52A1` : `${n} servers loaded`, | ||
| switching: () => isChinese() ? "\u5207\u6362\u670D\u52A1\u4E2D..." : "Switching...", | ||
| switchFailed: (m) => isChinese() ? `\u5207\u6362\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| routes: (name, n) => isChinese() ? `${name} \xB7 ${n} \u6761\u8DEF\u7531` : `${name} \xB7 ${n} routes`, | ||
| copied: (k) => isChinese() ? `\u5DF2\u590D\u5236: ${k}` : `Copied: ${k}`, | ||
| copyAll: () => isChinese() ? "\u590D\u5236\u5168\u90E8 JSON" : "Copy all JSON", | ||
| copyAllDone: () => isChinese() ? "\u5DF2\u590D\u5236\u4E3A JSON" : "Copied as JSON" | ||
| }; | ||
| var PANEL_STYLE = ` | ||
| #__snackkit_debugger__ { | ||
| position: fixed; | ||
| bottom: 20px; | ||
| right: 20px; | ||
| z-index: 99999; | ||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif; | ||
| font-size: 13px; | ||
| color: #b8ccec; | ||
| user-select: none; | ||
| } | ||
| /* \u2500\u2500 \u6D6E\u52A8\u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-toggle { | ||
| width: 42px; | ||
| height: 42px; | ||
| border-radius: 50%; | ||
| background: linear-gradient(135deg, #4f8ef7, #6d6ff5); | ||
| border: none; | ||
| cursor: pointer; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| box-shadow: 0 4px 14px rgba(79,142,247,0.45); | ||
| margin-left: auto; | ||
| font-size: 20px; | ||
| transition: transform 0.15s, box-shadow 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:hover { | ||
| transform: scale(1.08); | ||
| box-shadow: 0 6px 20px rgba(79,142,247,0.55); | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); } | ||
| /* \u2500\u2500 \u9762\u677F\u4E3B\u4F53 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-panel { | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 12px; | ||
| width: 440px; | ||
| margin-bottom: 10px; | ||
| box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05); | ||
| overflow: visible; | ||
| opacity: 0; | ||
| visibility: hidden; | ||
| transform: translateY(8px) scale(0.98); | ||
| pointer-events: none; | ||
| transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease; | ||
| } | ||
| #__snackkit_debugger__ .dbg-panel.open { | ||
| opacity: 1; | ||
| visibility: visible; | ||
| transform: translateY(0) scale(1); | ||
| pointer-events: auto; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F Header \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| padding: 10px 14px; | ||
| background: #171e2e; | ||
| border-bottom: 1px solid #2d3a55; | ||
| border-radius: 12px 12px 0 0; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-left { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 7px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot { | ||
| width: 7px; | ||
| height: 7px; | ||
| border-radius: 50%; | ||
| background: #34d399; | ||
| box-shadow: 0 0 6px rgba(52,211,153,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.loading { | ||
| background: #fbbf24; | ||
| box-shadow: 0 0 6px rgba(251,191,36,0.6); | ||
| animation: dbg-pulse 1s ease-in-out infinite; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.err { | ||
| background: #f87171; | ||
| box-shadow: 0 0 6px rgba(248,113,113,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-title { | ||
| font-size: 12px; | ||
| font-weight: 600; | ||
| color: #dce8fa; | ||
| letter-spacing: 0.03em; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-meta { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F\u5185\u5BB9\u533A \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-body { | ||
| padding: 12px 14px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 10px; | ||
| height: 420px; | ||
| overflow: visible; | ||
| } | ||
| /* \u2500\u2500 \u5B57\u6BB5\u884C \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-field { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-field-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| } | ||
| #__snackkit_debugger__ .dbg-label { | ||
| font-size: 10px; | ||
| font-weight: 600; | ||
| color: #5e72a0; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.07em; | ||
| } | ||
| /* \u2500\u2500 \u4E00\u952E\u590D\u5236\u5168\u90E8 JSON \u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-copy-all { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| background: transparent; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 4px; | ||
| padding: 2px 7px; | ||
| color: #5e72a0; | ||
| font-size: 10px; | ||
| font-family: inherit; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, color 0.15s, background 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all:hover { | ||
| border-color: #4f8ef7; | ||
| color: #7aacfa; | ||
| background: rgba(79,142,247,0.08); | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all.done { | ||
| border-color: #34d399; | ||
| color: #34d399; | ||
| } | ||
| /* \u2500\u2500 \u81EA\u5B9A\u4E49\u4E0B\u62C9 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-select { | ||
| position: relative; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| gap: 6px; | ||
| background: #252f45; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| padding: 6px 10px; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, background 0.15s; | ||
| min-height: 32px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger { | ||
| border-color: #4f8ef7; | ||
| background: #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val { | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| font-size: 12px; | ||
| color: #b8ccec; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-arrow { | ||
| color: #3d5070; | ||
| font-size: 10px; | ||
| flex-shrink: 0; | ||
| transition: transform 0.15s, color 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow { | ||
| transform: rotate(180deg); | ||
| color: #4f8ef7; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-dropdown { | ||
| position: absolute; | ||
| bottom: calc(100% + 4px); | ||
| left: 0; | ||
| right: 0; | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 8px; | ||
| box-shadow: 0 -8px 24px rgba(0,0,0,0.35); | ||
| overflow: hidden; | ||
| opacity: 0; | ||
| transform: translateY(4px); | ||
| pointer-events: none; | ||
| transition: opacity 0.14s, transform 0.14s; | ||
| z-index: 100000; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown { | ||
| opacity: 1; | ||
| transform: translateY(0); | ||
| pointer-events: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; } | ||
| #__snackkit_debugger__ .dbg-select-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-list { | ||
| max-height: 160px; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-select-option { | ||
| padding: 7px 10px; | ||
| cursor: pointer; | ||
| font-size: 12px; | ||
| color: #8aa4c8; | ||
| transition: background 0.1s, color 0.1s; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; } | ||
| #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; } | ||
| #__snackkit_debugger__ .dbg-select-option.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-select-empty { | ||
| padding: 12px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u8DEF\u7531\u5217\u8868 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-ctx-field { | ||
| flex: 1; | ||
| min-height: 0; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-wrap { | ||
| flex: 1; | ||
| min-height: 0; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| overflow: hidden; | ||
| background: #252f45; | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 6px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| background: #1e2638; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-ctx-list { | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-ctx-item { | ||
| padding: 5px 10px; | ||
| cursor: pointer; | ||
| display: grid; | ||
| grid-template-columns: minmax(80px, 148px) 1fr auto; | ||
| gap: 8px; | ||
| align-items: center; | ||
| border-bottom: 1px solid #1e2638; | ||
| transition: background 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-key { | ||
| font-size: 12px; | ||
| color: #7aacfa; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-val { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| transition: color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; } | ||
| #__snackkit_debugger__ .dbg-ctx-copy { | ||
| font-size: 11px; | ||
| color: #3d5070; | ||
| opacity: 0; | ||
| flex-shrink: 0; | ||
| transition: opacity 0.1s, color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; } | ||
| #__snackkit_debugger__ .dbg-ctx-empty { | ||
| padding: 16px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u5E95\u90E8\u72B6\u6001\u680F \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-footer { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 14px; | ||
| background: #171e2e; | ||
| border-top: 1px solid #2d3a55; | ||
| border-radius: 0 0 12px 12px; | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| min-height: 30px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; } | ||
| #__snackkit_debugger__ .dbg-footer.err { color: #f87171; } | ||
| #__snackkit_debugger__ .dbg-footer-dot { | ||
| width: 4px; | ||
| height: 4px; | ||
| border-radius: 50%; | ||
| background: currentColor; | ||
| flex-shrink: 0; | ||
| } | ||
| @keyframes dbg-pulse { | ||
| 0%, 100% { opacity: 1; } | ||
| 50% { opacity: 0.35; } | ||
| } | ||
| `; | ||
| var SearchSelect = class { | ||
| constructor(onChange) { | ||
| this.options = []; | ||
| this.selected = ""; | ||
| this.onChange = onChange; | ||
| this.el = document.createElement("div"); | ||
| this.el.className = "dbg-select"; | ||
| this.trigger = document.createElement("div"); | ||
| this.trigger.className = "dbg-select-trigger"; | ||
| this.valEl = document.createElement("span"); | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| const arrow = document.createElement("span"); | ||
| arrow.className = "dbg-select-arrow"; | ||
| arrow.textContent = "\u25BE"; | ||
| this.trigger.appendChild(this.valEl); | ||
| this.trigger.appendChild(arrow); | ||
| this.dropdown = document.createElement("div"); | ||
| this.dropdown.className = "dbg-select-dropdown"; | ||
| const searchWrap = document.createElement("div"); | ||
| searchWrap.className = "dbg-select-search"; | ||
| const searchIcon = document.createElement("span"); | ||
| searchIcon.className = "dbg-select-search-icon"; | ||
| searchIcon.textContent = "\u2315"; | ||
| this.searchInput = document.createElement("input"); | ||
| this.searchInput.type = "text"; | ||
| this.searchInput.placeholder = T.search(); | ||
| searchWrap.appendChild(searchIcon); | ||
| searchWrap.appendChild(this.searchInput); | ||
| this.listEl = document.createElement("div"); | ||
| this.listEl.className = "dbg-select-list"; | ||
| this.dropdown.appendChild(searchWrap); | ||
| this.dropdown.appendChild(this.listEl); | ||
| this.el.appendChild(this.trigger); | ||
| this.el.appendChild(this.dropdown); | ||
| this.bindEvents(); | ||
| } | ||
| bindEvents() { | ||
| this.trigger.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| const isOpen = this.el.classList.contains("open"); | ||
| document.querySelectorAll("#__snackkit_debugger__ .dbg-select.open").forEach((el) => { | ||
| if (el !== this.el) el.classList.remove("open"); | ||
| }); | ||
| this.el.classList.toggle("open", !isOpen); | ||
| if (!isOpen) { | ||
| this.searchInput.value = ""; | ||
| this.filterOptions(""); | ||
| setTimeout(() => this.searchInput.focus(), 50); | ||
| } | ||
| }); | ||
| this.searchInput.addEventListener("input", () => { | ||
| this.filterOptions(this.searchInput.value); | ||
| }); | ||
| this.searchInput.addEventListener("click", (e) => e.stopPropagation()); | ||
| document.addEventListener("click", () => { | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| } | ||
| filterOptions(query) { | ||
| const q = query.toLowerCase(); | ||
| let visibleCount = 0; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((opt) => { | ||
| const match = !q || opt.textContent.toLowerCase().includes(q); | ||
| opt.classList.toggle("hidden", !match); | ||
| if (match) visibleCount++; | ||
| }); | ||
| let emptyEl = this.listEl.querySelector(".dbg-select-empty"); | ||
| if (visibleCount === 0) { | ||
| if (!emptyEl) { | ||
| emptyEl = document.createElement("div"); | ||
| emptyEl.className = "dbg-select-empty"; | ||
| emptyEl.textContent = T.noMatch(); | ||
| this.listEl.appendChild(emptyEl); | ||
| } | ||
| } else { | ||
| emptyEl?.remove(); | ||
| } | ||
| } | ||
| /** 更新选项列表 */ | ||
| setOptions(options, placeholder) { | ||
| this.options = options; | ||
| this.listEl.innerHTML = ""; | ||
| this.valEl.textContent = placeholder; | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| this.selected = ""; | ||
| options.forEach(({ value, label }) => { | ||
| const opt = document.createElement("div"); | ||
| opt.className = "dbg-select-option"; | ||
| opt.dataset["value"] = value; | ||
| opt.textContent = label; | ||
| opt.setAttribute("title", label); | ||
| opt.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.select(value, label); | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| this.listEl.appendChild(opt); | ||
| }); | ||
| } | ||
| /** 选中某个值(不触发 onChange) */ | ||
| setValue(value) { | ||
| const opt = this.options.find((o) => o.value === value); | ||
| if (opt) this.select(opt.value, opt.label, false); | ||
| } | ||
| select(value, label, emit = true) { | ||
| this.selected = value; | ||
| this.valEl.textContent = label; | ||
| this.valEl.className = "dbg-select-val"; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((el) => { | ||
| el.classList.toggle("active", el.dataset["value"] === value); | ||
| }); | ||
| if (emit) this.onChange(value); | ||
| } | ||
| getValue() { | ||
| return this.selected; | ||
| } | ||
| getElement() { | ||
| return this.el; | ||
| } | ||
| }; | ||
| var Debugger = class _Debugger { | ||
| constructor(options) { | ||
| this.servers = []; | ||
| this.currentCtxMap = {}; | ||
| this.options = options; | ||
| this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]; | ||
| } | ||
| /** | ||
| * 初始化并渲染调试面板 | ||
| * | ||
| * 支持两种调用形式: | ||
| * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间 | ||
| * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }` | ||
| * | ||
| * 调用后在页面右下角插入浮动按钮,点击展开调试面板。 | ||
| * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。 | ||
| * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。 | ||
| * | ||
| * @param options 配置对象 或 网关 URL 数组 | ||
| * | ||
| * @example 配置对象形式 | ||
| * ```ts | ||
| * import { Debugger } from '@snack-kit/lib/debugger' | ||
| * | ||
| * await Debugger.init({ | ||
| * gateways: [ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ], | ||
| * timeout: 5000, | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @example 数组简写形式 | ||
| * ```ts | ||
| * await Debugger.init([ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ]) | ||
| * ``` | ||
| * | ||
| * @example 仅在非生产环境加载 | ||
| * ```ts | ||
| * if (import.meta.env.DEV) { | ||
| * const { Debugger } = await import('@snack-kit/lib/debugger') | ||
| * await Debugger.init(['http://dev-gateway.example.com']) | ||
| * } | ||
| * ``` | ||
| */ | ||
| static async init(options) { | ||
| globalThis[DEBUGGER_ACTIVE_KEY] = true; | ||
| const normalized = Array.isArray(options) ? { gateways: options } : options; | ||
| const instance = new _Debugger({ timeout: 1e4, ...normalized }); | ||
| instance.injectStyle(); | ||
| instance.buildDOM(); | ||
| await instance.restoreState(); | ||
| return instance; | ||
| } | ||
| /** 注入内联样式 */ | ||
| injectStyle() { | ||
| if (document.getElementById("__snackkit_debugger_style__")) return; | ||
| const style = document.createElement("style"); | ||
| style.id = "__snackkit_debugger_style__"; | ||
| style.textContent = PANEL_STYLE; | ||
| document.head.appendChild(style); | ||
| } | ||
| /** 构建 DOM 结构 */ | ||
| buildDOM() { | ||
| const root = document.createElement("div"); | ||
| root.id = "__snackkit_debugger__"; | ||
| const panel = document.createElement("div"); | ||
| panel.className = "dbg-panel"; | ||
| this.panelEl = panel; | ||
| const header = document.createElement("div"); | ||
| header.className = "dbg-header"; | ||
| const headerLeft = document.createElement("div"); | ||
| headerLeft.className = "dbg-header-left"; | ||
| this.headerDot = document.createElement("span"); | ||
| this.headerDot.className = "dbg-header-dot loading"; | ||
| const headerTitle = document.createElement("span"); | ||
| headerTitle.className = "dbg-header-title"; | ||
| headerTitle.textContent = "Snack Kit Debugger"; | ||
| headerLeft.appendChild(this.headerDot); | ||
| headerLeft.appendChild(headerTitle); | ||
| header.appendChild(headerLeft); | ||
| panel.appendChild(header); | ||
| const body = document.createElement("div"); | ||
| body.className = "dbg-body"; | ||
| this.gwVersion = document.createElement("span"); | ||
| this.gwVersion.className = "dbg-header-meta"; | ||
| this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val)); | ||
| this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway()); | ||
| body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement())); | ||
| this.typeSelect = new SearchSelect((val) => this.onTypeChange(val)); | ||
| body.appendChild(this.createField(T.tag(), this.typeSelect.getElement())); | ||
| this.serverSelect = new SearchSelect((val) => this.onServerChange(val)); | ||
| body.appendChild(this.createField(T.server(), this.serverSelect.getElement())); | ||
| const ctxWrap = document.createElement("div"); | ||
| ctxWrap.className = "dbg-ctx-wrap"; | ||
| const ctxSearch = document.createElement("div"); | ||
| ctxSearch.className = "dbg-ctx-search"; | ||
| const ctxSearchIcon = document.createElement("span"); | ||
| ctxSearchIcon.className = "dbg-select-search-icon"; | ||
| ctxSearchIcon.textContent = "\u2315"; | ||
| this.ctxSearchInput = document.createElement("input"); | ||
| this.ctxSearchInput.type = "text"; | ||
| this.ctxSearchInput.placeholder = T.filterCtx(); | ||
| this.ctxSearchInput.addEventListener("input", () => this.filterCtx(this.ctxSearchInput.value)); | ||
| ctxSearch.appendChild(ctxSearchIcon); | ||
| ctxSearch.appendChild(this.ctxSearchInput); | ||
| this.ctxList = document.createElement("div"); | ||
| this.ctxList.className = "dbg-ctx-list"; | ||
| ctxWrap.appendChild(ctxSearch); | ||
| ctxWrap.appendChild(this.ctxList); | ||
| this.copyAllBtn = document.createElement("button"); | ||
| this.copyAllBtn.className = "dbg-copy-all"; | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.setAttribute("title", isChinese() ? "\u5C06\u5168\u90E8\u4E0A\u4E0B\u6587\u590D\u5236\u4E3A JSON\uFF0C\u53EF\u4F5C\u4E3A Context.load({...}) \u7684\u53C2\u6570" : "Copy all ctx entries as JSON for Context.load({...})"); | ||
| this.copyAllBtn.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.copyAllCtx(); | ||
| }); | ||
| const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap); | ||
| ctxField.classList.add("dbg-ctx-field"); | ||
| body.appendChild(ctxField); | ||
| panel.appendChild(body); | ||
| const footer = document.createElement("div"); | ||
| footer.className = "dbg-footer"; | ||
| this.footerEl = footer; | ||
| panel.appendChild(footer); | ||
| const toggle = document.createElement("button"); | ||
| toggle.className = "dbg-toggle"; | ||
| toggle.textContent = "\u{1F41B}"; | ||
| toggle.setAttribute("title", "Snack Debugger"); | ||
| toggle.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| panel.classList.toggle("open"); | ||
| }); | ||
| root.appendChild(panel); | ||
| root.appendChild(toggle); | ||
| document.body.appendChild(root); | ||
| } | ||
| /** 创建普通字段行(label + content) */ | ||
| createField(label, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| field.appendChild(lbl); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 创建带操作按钮的字段行(label + action button + content) */ | ||
| createFieldWithAction(label, action, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const fieldHeader = document.createElement("div"); | ||
| fieldHeader.className = "dbg-field-header"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| fieldHeader.appendChild(lbl); | ||
| fieldHeader.appendChild(action); | ||
| field.appendChild(fieldHeader); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 恢复上次持久化的状态 */ | ||
| async restoreState() { | ||
| const savedGw = localStorage.getItem(KEY_GATEWAY); | ||
| const gw = savedGw && this.gateways.includes(savedGw) ? savedGw : this.gateways[0]; | ||
| this.gwSelect.setValue(gw); | ||
| await this.loadServers(gw); | ||
| const savedType = localStorage.getItem(KEY_TYPE); | ||
| if (savedType) { | ||
| this.typeSelect.setValue(savedType); | ||
| this.renderServerOptions(savedType); | ||
| } | ||
| const savedServer = localStorage.getItem(KEY_SERVER); | ||
| if (savedServer && this.servers.find((s) => s.key === savedServer)) { | ||
| this.serverSelect.setValue(savedServer); | ||
| await this.applyServer(savedServer); | ||
| } | ||
| } | ||
| async onGatewayChange(gw) { | ||
| localStorage.setItem(KEY_GATEWAY, gw); | ||
| this.gwVersion.textContent = ""; | ||
| await this.loadServers(gw); | ||
| } | ||
| onTypeChange(type) { | ||
| localStorage.setItem(KEY_TYPE, type); | ||
| this.renderServerOptions(type); | ||
| } | ||
| async onServerChange(key) { | ||
| localStorage.setItem(KEY_SERVER, key); | ||
| await this.applyServer(key); | ||
| } | ||
| /** 从网关加载服务列表 */ | ||
| async loadServers(gwUrl) { | ||
| this.setHeader("loading"); | ||
| this.setFooter(T.loading(), ""); | ||
| try { | ||
| const res = await _http.get(`${gwUrl}/web-debug/host/list`, { | ||
| timeout: this.options.timeout | ||
| }); | ||
| this.servers = res.data ?? []; | ||
| this.renderTypeOptions(); | ||
| this.setHeader("ok"); | ||
| this.setFooter(T.loaded(this.servers.length), "ok"); | ||
| } catch (err) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| this.setHeader("err"); | ||
| this.setFooter(T.loadFailed(message), "err"); | ||
| } | ||
| } | ||
| /** 渲染标签选项 */ | ||
| renderTypeOptions() { | ||
| const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]; | ||
| const opts = [{ value: "", label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]; | ||
| this.typeSelect.setOptions(opts, T.allTags()); | ||
| this.typeSelect.setValue(""); | ||
| this.renderServerOptions(""); | ||
| } | ||
| /** 根据标签渲染服务选项 */ | ||
| renderServerOptions(type) { | ||
| const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers; | ||
| this.serverSelect.setOptions( | ||
| filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })), | ||
| T.selectServer() | ||
| ); | ||
| this.renderCtxList({}); | ||
| } | ||
| /** 切换目标服务,重新加载 Context 路由映射 */ | ||
| async applyServer(key) { | ||
| if (!key) { | ||
| this.renderCtxList({}); | ||
| return; | ||
| } | ||
| const server = this.servers.find((s) => s.key === key); | ||
| if (!server) return; | ||
| this.setFooter(T.switching(), ""); | ||
| try { | ||
| await Context.load(server.origin, this.options.timeout); | ||
| const res = await fetch(`${server.origin}/ngw/context`); | ||
| const raw = res.ok ? await res.json() : {}; | ||
| const ctxMap = {}; | ||
| for (const [k, v] of Object.entries(raw)) { | ||
| if (k !== "$info" && typeof v === "string") ctxMap[k] = v; | ||
| } | ||
| const info = Context.info; | ||
| if (info) this.gwVersion.textContent = `v${info.version}`; | ||
| this.renderCtxList(ctxMap); | ||
| const count = Object.keys(ctxMap).length; | ||
| this.setFooter(T.routes(server.name, count), "ok"); | ||
| } catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| this.setFooter(T.switchFailed(msg), "err"); | ||
| } | ||
| } | ||
| /** 渲染路由列表 */ | ||
| renderCtxList(ctxMap) { | ||
| this.currentCtxMap = ctxMap; | ||
| this.ctxList.innerHTML = ""; | ||
| this.ctxSearchInput.value = ""; | ||
| const entries = Object.entries(ctxMap); | ||
| if (entries.length === 0) { | ||
| const empty = document.createElement("div"); | ||
| empty.className = "dbg-ctx-empty"; | ||
| empty.textContent = T.selectFirst(); | ||
| this.ctxList.appendChild(empty); | ||
| return; | ||
| } | ||
| entries.forEach(([k, v]) => { | ||
| const item = document.createElement("div"); | ||
| item.className = "dbg-ctx-item"; | ||
| item.dataset["key"] = k; | ||
| const keySpan = document.createElement("span"); | ||
| keySpan.className = "dbg-ctx-key"; | ||
| keySpan.textContent = k; | ||
| keySpan.setAttribute("title", k); | ||
| const valSpan = document.createElement("span"); | ||
| valSpan.className = "dbg-ctx-val"; | ||
| valSpan.textContent = v; | ||
| valSpan.setAttribute("title", `${k} \u2192 ${v}`); | ||
| const copyIcon = document.createElement("span"); | ||
| copyIcon.className = "dbg-ctx-copy"; | ||
| copyIcon.textContent = "\u2398"; | ||
| item.appendChild(keySpan); | ||
| item.appendChild(valSpan); | ||
| item.appendChild(copyIcon); | ||
| item.addEventListener("click", () => this.copyCtxItem(item, k)); | ||
| this.ctxList.appendChild(item); | ||
| }); | ||
| } | ||
| /** 过滤路由列表 */ | ||
| filterCtx(query) { | ||
| const q = query.toLowerCase(); | ||
| this.ctxList.querySelectorAll(".dbg-ctx-item").forEach((item) => { | ||
| const key = item.dataset["key"] ?? ""; | ||
| item.classList.toggle("hidden", !!q && !key.toLowerCase().includes(q)); | ||
| }); | ||
| } | ||
| /** 复制单个 ctx key */ | ||
| copyCtxItem(item, text) { | ||
| navigator.clipboard.writeText(text).then(() => { | ||
| item.classList.add("copied"); | ||
| const icon = item.querySelector(".dbg-ctx-copy"); | ||
| if (icon) icon.textContent = "\u2713"; | ||
| setTimeout(() => { | ||
| item.classList.remove("copied"); | ||
| const ic = item.querySelector(".dbg-ctx-copy"); | ||
| if (ic) ic.textContent = "\u2398"; | ||
| }, 1500); | ||
| this.setFooter(T.copied(text), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2e3); | ||
| }); | ||
| } | ||
| /** 一键复制全部上下文为 JSON */ | ||
| copyAllCtx() { | ||
| const entries = Object.keys(this.currentCtxMap); | ||
| if (entries.length === 0) return; | ||
| const json = JSON.stringify(this.currentCtxMap, null, 2); | ||
| navigator.clipboard.writeText(json).then(() => { | ||
| this.copyAllBtn.textContent = "\u2713 " + T.copyAllDone(); | ||
| this.copyAllBtn.classList.add("done"); | ||
| setTimeout(() => { | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.classList.remove("done"); | ||
| }, 2e3); | ||
| this.setFooter(T.copyAllDone(), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2500); | ||
| }); | ||
| } | ||
| setHeader(state) { | ||
| this.headerDot.className = `dbg-header-dot${state !== "ok" ? ` ${state}` : ""}`; | ||
| } | ||
| setFooter(msg, cls) { | ||
| this.footerEl.innerHTML = msg ? `<span class="dbg-footer-dot"></span>${msg}` : ""; | ||
| this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ""}`; | ||
| } | ||
| }; | ||
| export { Debugger }; | ||
| //# sourceMappingURL=chunk-FNBSAJUM.js.map | ||
| //# sourceMappingURL=chunk-FNBSAJUM.js.map |
| {"version":3,"sources":["../../src/debugger/debugger.ts"],"names":[],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","file":"chunk-FNBSAJUM.js","sourcesContent":["import axios from 'axios'\nimport { Context, DEBUGGER_ACTIVE_KEY } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\n\n/**\n * Debugger 专用的隔离 axios 实例。\n * 独立于用户通过 `import { axios } from '@snack-kit/lib/http'` 添加的拦截器,\n * 确保调试面板请求不被外部拦截器污染。\n */\nconst _http = axios.create()\n\n/** localStorage key 前缀 */\nconst STORAGE_PREFIX = '__snackkit_debugger__'\nconst KEY_GATEWAY = `${STORAGE_PREFIX}gateway`\nconst KEY_TYPE = `${STORAGE_PREFIX}type`\nconst KEY_SERVER = `${STORAGE_PREFIX}server`\n\n\n/** 是否为中文环境 */\nfunction isChinese(): boolean {\n return /^zh/i.test(navigator.language ?? '')\n}\n\n/** i18n 文案 */\nconst T = {\n gateway: () => isChinese() ? '网关' : 'Gateway',\n tag: () => isChinese() ? '标签' : 'Tag',\n server: () => isChinese() ? '服务' : 'Server',\n ctx: () => isChinese() ? '上下文' : 'Ctx',\n ctxLabel: () => isChinese() ? '上下文 — 点击行复制 key' : 'Ctx — click row to copy key',\n search: () => isChinese() ? '搜索...' : 'Search...',\n noMatch: () => isChinese() ? '无匹配项' : 'No results',\n allTags: () => isChinese() ? '全部标签' : 'All tags',\n selectGateway: () => isChinese() ? '选择网关' : 'Select gateway',\n selectServer: () => isChinese() ? '选择服务' : 'Select server',\n filterCtx: () => isChinese() ? '过滤路由...' : 'Filter routes...',\n selectFirst: () => isChinese() ? '← 请先选择服务' : '← Select a server first',\n loading: () => isChinese() ? '加载服务列表...' : 'Loading...',\n loadFailed: (m: string) => isChinese() ? `加载失败: ${m}` : `Failed: ${m}`,\n loaded: (n: number) => isChinese() ? `已加载 ${n} 个服务` : `${n} servers loaded`,\n switching: () => isChinese() ? '切换服务中...' : 'Switching...',\n switchFailed: (m: string) => isChinese() ? `切换失败: ${m}` : `Failed: ${m}`,\n routes: (name: string, n: number) => isChinese() ? `${name} · ${n} 条路由` : `${name} · ${n} routes`,\n copied: (k: string) => isChinese() ? `已复制: ${k}` : `Copied: ${k}`,\n copyAll: () => isChinese() ? '复制全部 JSON' : 'Copy all JSON',\n copyAllDone: () => isChinese() ? '已复制为 JSON' : 'Copied as JSON',\n}\n\n/** 面板内联样式 */\nconst PANEL_STYLE = `\n #__snackkit_debugger__ {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 99999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;\n font-size: 13px;\n color: #b8ccec;\n user-select: none;\n }\n\n /* ── 浮动按钮 ── */\n #__snackkit_debugger__ .dbg-toggle {\n width: 42px;\n height: 42px;\n border-radius: 50%;\n background: linear-gradient(135deg, #4f8ef7, #6d6ff5);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 14px rgba(79,142,247,0.45);\n margin-left: auto;\n font-size: 20px;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n #__snackkit_debugger__ .dbg-toggle:hover {\n transform: scale(1.08);\n box-shadow: 0 6px 20px rgba(79,142,247,0.55);\n }\n #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); }\n\n /* ── 面板主体 ── */\n #__snackkit_debugger__ .dbg-panel {\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 12px;\n width: 440px;\n margin-bottom: 10px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05);\n overflow: visible;\n opacity: 0;\n visibility: hidden;\n transform: translateY(8px) scale(0.98);\n pointer-events: none;\n transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;\n }\n #__snackkit_debugger__ .dbg-panel.open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n /* ── 面板 Header ── */\n #__snackkit_debugger__ .dbg-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n background: #171e2e;\n border-bottom: 1px solid #2d3a55;\n border-radius: 12px 12px 0 0;\n }\n #__snackkit_debugger__ .dbg-header-left {\n display: flex;\n align-items: center;\n gap: 7px;\n }\n #__snackkit_debugger__ .dbg-header-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: #34d399;\n box-shadow: 0 0 6px rgba(52,211,153,0.6);\n }\n #__snackkit_debugger__ .dbg-header-dot.loading {\n background: #fbbf24;\n box-shadow: 0 0 6px rgba(251,191,36,0.6);\n animation: dbg-pulse 1s ease-in-out infinite;\n }\n #__snackkit_debugger__ .dbg-header-dot.err {\n background: #f87171;\n box-shadow: 0 0 6px rgba(248,113,113,0.6);\n }\n #__snackkit_debugger__ .dbg-header-title {\n font-size: 12px;\n font-weight: 600;\n color: #dce8fa;\n letter-spacing: 0.03em;\n }\n #__snackkit_debugger__ .dbg-header-meta {\n font-size: 11px;\n color: #4d6080;\n }\n\n /* ── 面板内容区 ── */\n #__snackkit_debugger__ .dbg-body {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n height: 420px;\n overflow: visible;\n }\n\n /* ── 字段行 ── */\n #__snackkit_debugger__ .dbg-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-field-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n #__snackkit_debugger__ .dbg-label {\n font-size: 10px;\n font-weight: 600;\n color: #5e72a0;\n text-transform: uppercase;\n letter-spacing: 0.07em;\n }\n\n /* ── 一键复制全部 JSON 按钮 ── */\n #__snackkit_debugger__ .dbg-copy-all {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: 1px solid #2d3a55;\n border-radius: 4px;\n padding: 2px 7px;\n color: #5e72a0;\n font-size: 10px;\n font-family: inherit;\n cursor: pointer;\n transition: border-color 0.15s, color 0.15s, background 0.15s;\n }\n #__snackkit_debugger__ .dbg-copy-all:hover {\n border-color: #4f8ef7;\n color: #7aacfa;\n background: rgba(79,142,247,0.08);\n }\n #__snackkit_debugger__ .dbg-copy-all.done {\n border-color: #34d399;\n color: #34d399;\n }\n\n /* ── 自定义下拉 ── */\n #__snackkit_debugger__ .dbg-select {\n position: relative;\n }\n #__snackkit_debugger__ .dbg-select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n background: #252f45;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n padding: 6px 10px;\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n min-height: 32px;\n }\n #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger {\n border-color: #4f8ef7;\n background: #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-val {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n color: #b8ccec;\n }\n #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-arrow {\n color: #3d5070;\n font-size: 10px;\n flex-shrink: 0;\n transition: transform 0.15s, color 0.15s;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow {\n transform: rotate(180deg);\n color: #4f8ef7;\n }\n #__snackkit_debugger__ .dbg-select-dropdown {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 0;\n right: 0;\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 8px;\n box-shadow: 0 -8px 24px rgba(0,0,0,0.35);\n overflow: hidden;\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n transition: opacity 0.14s, transform 0.14s;\n z-index: 100000;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n }\n #__snackkit_debugger__ .dbg-select-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 10px;\n border-bottom: 1px solid #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; }\n #__snackkit_debugger__ .dbg-select-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-list {\n max-height: 160px;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-select-option {\n padding: 7px 10px;\n cursor: pointer;\n font-size: 12px;\n color: #8aa4c8;\n transition: background 0.1s, color 0.1s;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; }\n #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; }\n #__snackkit_debugger__ .dbg-select-option.hidden { display: none; }\n #__snackkit_debugger__ .dbg-select-empty {\n padding: 12px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 路由列表 ── */\n #__snackkit_debugger__ .dbg-ctx-field {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-ctx-wrap {\n flex: 1;\n min-height: 0;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n overflow: hidden;\n background: #252f45;\n display: flex;\n flex-direction: column;\n }\n #__snackkit_debugger__ .dbg-ctx-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-bottom: 1px solid #2d3a55;\n background: #1e2638;\n }\n #__snackkit_debugger__ .dbg-ctx-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-ctx-list {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-ctx-item {\n padding: 5px 10px;\n cursor: pointer;\n display: grid;\n grid-template-columns: minmax(80px, 148px) 1fr auto;\n gap: 8px;\n align-items: center;\n border-bottom: 1px solid #1e2638;\n transition: background 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }\n #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; }\n #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; }\n #__snackkit_debugger__ .dbg-ctx-key {\n font-size: 12px;\n color: #7aacfa;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-ctx-val {\n font-size: 11px;\n color: #4d6080;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n transition: color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; }\n #__snackkit_debugger__ .dbg-ctx-copy {\n font-size: 11px;\n color: #3d5070;\n opacity: 0;\n flex-shrink: 0;\n transition: opacity 0.1s, color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; }\n #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; }\n #__snackkit_debugger__ .dbg-ctx-empty {\n padding: 16px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 底部状态栏 ── */\n #__snackkit_debugger__ .dbg-footer {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: #171e2e;\n border-top: 1px solid #2d3a55;\n border-radius: 0 0 12px 12px;\n font-size: 11px;\n color: #4d6080;\n min-height: 30px;\n }\n #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; }\n #__snackkit_debugger__ .dbg-footer.err { color: #f87171; }\n #__snackkit_debugger__ .dbg-footer-dot {\n width: 4px;\n height: 4px;\n border-radius: 50%;\n background: currentColor;\n flex-shrink: 0;\n }\n\n @keyframes dbg-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.35; }\n }\n`\n\n/** 可搜索的自定义下拉组件 */\nclass SearchSelect {\n private el: HTMLElement\n private trigger: HTMLElement\n private valEl: HTMLElement\n private dropdown: HTMLElement\n private searchInput: HTMLInputElement\n private listEl: HTMLElement\n private options: Array<{ value: string; label: string }> = []\n private selected = ''\n private onChange: (value: string) => void\n\n constructor(onChange: (value: string) => void) {\n this.onChange = onChange\n this.el = document.createElement('div')\n this.el.className = 'dbg-select'\n\n // 触发器\n this.trigger = document.createElement('div')\n this.trigger.className = 'dbg-select-trigger'\n this.valEl = document.createElement('span')\n this.valEl.className = 'dbg-select-val placeholder'\n const arrow = document.createElement('span')\n arrow.className = 'dbg-select-arrow'\n arrow.textContent = '▾'\n this.trigger.appendChild(this.valEl)\n this.trigger.appendChild(arrow)\n\n // 下拉面板\n this.dropdown = document.createElement('div')\n this.dropdown.className = 'dbg-select-dropdown'\n\n // 搜索框\n const searchWrap = document.createElement('div')\n searchWrap.className = 'dbg-select-search'\n const searchIcon = document.createElement('span')\n searchIcon.className = 'dbg-select-search-icon'\n searchIcon.textContent = '⌕'\n this.searchInput = document.createElement('input')\n this.searchInput.type = 'text'\n this.searchInput.placeholder = T.search()\n searchWrap.appendChild(searchIcon)\n searchWrap.appendChild(this.searchInput)\n\n this.listEl = document.createElement('div')\n this.listEl.className = 'dbg-select-list'\n\n this.dropdown.appendChild(searchWrap)\n this.dropdown.appendChild(this.listEl)\n this.el.appendChild(this.trigger)\n this.el.appendChild(this.dropdown)\n\n this.bindEvents()\n }\n\n private bindEvents(): void {\n this.trigger.addEventListener('click', (e) => {\n e.stopPropagation()\n const isOpen = this.el.classList.contains('open')\n document.querySelectorAll('#__snackkit_debugger__ .dbg-select.open').forEach((el) => {\n if (el !== this.el) el.classList.remove('open')\n })\n this.el.classList.toggle('open', !isOpen)\n if (!isOpen) {\n this.searchInput.value = ''\n this.filterOptions('')\n setTimeout(() => this.searchInput.focus(), 50)\n }\n })\n\n this.searchInput.addEventListener('input', () => {\n this.filterOptions(this.searchInput.value)\n })\n\n this.searchInput.addEventListener('click', (e) => e.stopPropagation())\n\n document.addEventListener('click', () => {\n this.el.classList.remove('open')\n })\n }\n\n private filterOptions(query: string): void {\n const q = query.toLowerCase()\n let visibleCount = 0\n this.listEl.querySelectorAll<HTMLElement>('.dbg-select-option').forEach((opt) => {\n const match = !q || opt.textContent!.toLowerCase().includes(q)\n opt.classList.toggle('hidden', !match)\n if (match) visibleCount++\n })\n let emptyEl = this.listEl.querySelector<HTMLElement>('.dbg-select-empty')\n if (visibleCount === 0) {\n if (!emptyEl) {\n emptyEl = document.createElement('div')\n emptyEl.className = 'dbg-select-empty'\n emptyEl.textContent = T.noMatch()\n this.listEl.appendChild(emptyEl)\n }\n } else {\n emptyEl?.remove()\n }\n }\n\n /** 更新选项列表 */\n setOptions(options: Array<{ value: string; label: string }>, placeholder: string): void {\n this.options = options\n this.listEl.innerHTML = ''\n this.valEl.textContent = placeholder\n this.valEl.className = 'dbg-select-val placeholder'\n this.selected = ''\n\n options.forEach(({ value, label }) => {\n const opt = document.createElement('div')\n opt.className = 'dbg-select-option'\n opt.dataset['value'] = value\n opt.textContent = label\n opt.setAttribute('title', label)\n opt.addEventListener('click', (e) => {\n e.stopPropagation()\n this.select(value, label)\n this.el.classList.remove('open')\n })\n this.listEl.appendChild(opt)\n })\n }\n\n /** 选中某个值(不触发 onChange) */\n setValue(value: string): void {\n const opt = this.options.find((o) => o.value === value)\n if (opt) this.select(opt.value, opt.label, false)\n }\n\n private select(value: string, label: string, emit = true): void {\n this.selected = value\n this.valEl.textContent = label\n this.valEl.className = 'dbg-select-val'\n this.listEl.querySelectorAll('.dbg-select-option').forEach((el) => {\n el.classList.toggle('active', (el as HTMLElement).dataset['value'] === value)\n })\n if (emit) this.onChange(value)\n }\n\n getValue(): string { return this.selected }\n getElement(): HTMLElement { return this.el }\n}\n\n/**\n * 浏览器端浮动调试面板\n *\n * 允许开发者在运行时:\n * - 选择调试网关 → 拉取服务清单\n * - 切换目标服务 → 重新加载 Context 路由映射\n * - 查看并复制可用的路由 ctx 列表\n *\n * 与 http 模块集成:使用 `Get()` 拉取服务清单,使用 `context.load()` 切换请求目标。\n *\n * @see [交互式 Demo](../demo/debugger.html)\n *\n * @example\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * ```\n */\nexport class Debugger {\n private options: Required<DebuggerOptions>\n private gateways: string[]\n private servers: ServerItem[] = []\n private currentCtxMap: Record<string, string> = {}\n private panelEl!: HTMLElement\n private headerDot!: HTMLElement\n private gwVersion!: HTMLElement\n private footerEl!: HTMLElement\n private copyAllBtn!: HTMLButtonElement\n private gwSelect!: SearchSelect\n private typeSelect!: SearchSelect\n private serverSelect!: SearchSelect\n private ctxList!: HTMLElement\n private ctxSearchInput!: HTMLInputElement\n\n private constructor(options: Required<DebuggerOptions>) {\n this.options = options\n this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]\n }\n\n /**\n * 初始化并渲染调试面板\n *\n * 支持两种调用形式:\n * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间\n * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }`\n *\n * 调用后在页面右下角插入浮动按钮,点击展开调试面板。\n * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置对象 或 网关 URL 数组\n *\n * @example 配置对象形式\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: [\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ],\n * timeout: 5000,\n * })\n * ```\n *\n * @example 数组简写形式\n * ```ts\n * await Debugger.init([\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ])\n * ```\n *\n * @example 仅在非生产环境加载\n * ```ts\n * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init(['http://dev-gateway.example.com'])\n * }\n * ```\n */\n static async init(options: DebuggerOptions | string[]): Promise<Debugger> {\n // 在 globalThis 写入标志位,Context.load() 无参数时检测到后将跳过自动加载\n ;(globalThis as Record<string, unknown>)[DEBUGGER_ACTIVE_KEY] = true\n const normalized: DebuggerOptions = Array.isArray(options) ? { gateways: options } : options\n const instance = new Debugger({ timeout: 10000, ...normalized })\n instance.injectStyle()\n instance.buildDOM()\n await instance.restoreState()\n return instance\n }\n\n /** 注入内联样式 */\n private injectStyle(): void {\n if (document.getElementById('__snackkit_debugger_style__')) return\n const style = document.createElement('style')\n style.id = '__snackkit_debugger_style__'\n style.textContent = PANEL_STYLE\n document.head.appendChild(style)\n }\n\n /** 构建 DOM 结构 */\n private buildDOM(): void {\n const root = document.createElement('div')\n root.id = '__snackkit_debugger__'\n\n // ── 面板 ──\n const panel = document.createElement('div')\n panel.className = 'dbg-panel'\n this.panelEl = panel\n\n // Header\n const header = document.createElement('div')\n header.className = 'dbg-header'\n const headerLeft = document.createElement('div')\n headerLeft.className = 'dbg-header-left'\n this.headerDot = document.createElement('span')\n this.headerDot.className = 'dbg-header-dot loading'\n const headerTitle = document.createElement('span')\n headerTitle.className = 'dbg-header-title'\n headerTitle.textContent = 'Snack Kit Debugger'\n headerLeft.appendChild(this.headerDot)\n headerLeft.appendChild(headerTitle)\n header.appendChild(headerLeft)\n panel.appendChild(header)\n\n // Body\n const body = document.createElement('div')\n body.className = 'dbg-body'\n\n // 网关选择(标签行右侧显示版本号)\n this.gwVersion = document.createElement('span')\n this.gwVersion.className = 'dbg-header-meta'\n this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val))\n this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway())\n body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement()))\n\n // 标签(类型)选择\n this.typeSelect = new SearchSelect((val) => this.onTypeChange(val))\n body.appendChild(this.createField(T.tag(), this.typeSelect.getElement()))\n\n // 服务选择\n this.serverSelect = new SearchSelect((val) => this.onServerChange(val))\n body.appendChild(this.createField(T.server(), this.serverSelect.getElement()))\n\n // 路由列表(含一键复制全部 JSON 按钮)\n const ctxWrap = document.createElement('div')\n ctxWrap.className = 'dbg-ctx-wrap'\n\n const ctxSearch = document.createElement('div')\n ctxSearch.className = 'dbg-ctx-search'\n const ctxSearchIcon = document.createElement('span')\n ctxSearchIcon.className = 'dbg-select-search-icon'\n ctxSearchIcon.textContent = '⌕'\n this.ctxSearchInput = document.createElement('input')\n this.ctxSearchInput.type = 'text'\n this.ctxSearchInput.placeholder = T.filterCtx()\n this.ctxSearchInput.addEventListener('input', () => this.filterCtx(this.ctxSearchInput.value))\n ctxSearch.appendChild(ctxSearchIcon)\n ctxSearch.appendChild(this.ctxSearchInput)\n\n this.ctxList = document.createElement('div')\n this.ctxList.className = 'dbg-ctx-list'\n\n ctxWrap.appendChild(ctxSearch)\n ctxWrap.appendChild(this.ctxList)\n\n // 一键复制全部 JSON 按钮\n this.copyAllBtn = document.createElement('button')\n this.copyAllBtn.className = 'dbg-copy-all'\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.setAttribute('title', isChinese()\n ? '将全部上下文复制为 JSON,可作为 Context.load({...}) 的参数'\n : 'Copy all ctx entries as JSON for Context.load({...})')\n this.copyAllBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.copyAllCtx()\n })\n\n const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap)\n ctxField.classList.add('dbg-ctx-field')\n body.appendChild(ctxField)\n\n panel.appendChild(body)\n\n // Footer 状态栏\n const footer = document.createElement('div')\n footer.className = 'dbg-footer'\n this.footerEl = footer\n panel.appendChild(footer)\n\n // ── 浮动按钮 ──\n const toggle = document.createElement('button')\n toggle.className = 'dbg-toggle'\n toggle.textContent = '🐛'\n toggle.setAttribute('title', 'Snack Debugger')\n toggle.addEventListener('click', (e) => {\n e.stopPropagation()\n panel.classList.toggle('open')\n })\n\n root.appendChild(panel)\n root.appendChild(toggle)\n document.body.appendChild(root)\n }\n\n /** 创建普通字段行(label + content) */\n private createField(label: string, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n field.appendChild(lbl)\n field.appendChild(content)\n return field\n }\n\n /** 创建带操作按钮的字段行(label + action button + content) */\n private createFieldWithAction(label: string, action: HTMLElement, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const fieldHeader = document.createElement('div')\n fieldHeader.className = 'dbg-field-header'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n fieldHeader.appendChild(lbl)\n fieldHeader.appendChild(action)\n field.appendChild(fieldHeader)\n field.appendChild(content)\n return field\n }\n\n /** 恢复上次持久化的状态 */\n private async restoreState(): Promise<void> {\n const savedGw = localStorage.getItem(KEY_GATEWAY)\n const gw = (savedGw && this.gateways.includes(savedGw)) ? savedGw : this.gateways[0]\n this.gwSelect.setValue(gw)\n await this.loadServers(gw)\n\n const savedType = localStorage.getItem(KEY_TYPE)\n if (savedType) {\n this.typeSelect.setValue(savedType)\n this.renderServerOptions(savedType)\n }\n\n const savedServer = localStorage.getItem(KEY_SERVER)\n if (savedServer && this.servers.find((s) => s.key === savedServer)) {\n this.serverSelect.setValue(savedServer)\n await this.applyServer(savedServer)\n }\n }\n\n private async onGatewayChange(gw: string): Promise<void> {\n localStorage.setItem(KEY_GATEWAY, gw)\n this.gwVersion.textContent = ''\n await this.loadServers(gw)\n }\n\n private onTypeChange(type: string): void {\n localStorage.setItem(KEY_TYPE, type)\n this.renderServerOptions(type)\n }\n\n private async onServerChange(key: string): Promise<void> {\n localStorage.setItem(KEY_SERVER, key)\n await this.applyServer(key)\n }\n\n /** 从网关加载服务列表 */\n private async loadServers(gwUrl: string): Promise<void> {\n this.setHeader('loading')\n this.setFooter(T.loading(), '')\n try {\n const res = await _http.get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n timeout: this.options.timeout,\n })\n this.servers = res.data ?? []\n this.renderTypeOptions()\n this.setHeader('ok')\n this.setFooter(T.loaded(this.servers.length), 'ok')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.setHeader('err')\n this.setFooter(T.loadFailed(message), 'err')\n }\n }\n\n /** 渲染标签选项 */\n private renderTypeOptions(): void {\n const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]\n const opts = [{ value: '', label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]\n this.typeSelect.setOptions(opts, T.allTags())\n this.typeSelect.setValue('')\n this.renderServerOptions('')\n }\n\n /** 根据标签渲染服务选项 */\n private renderServerOptions(type: string): void {\n const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers\n this.serverSelect.setOptions(\n filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })),\n T.selectServer(),\n )\n this.renderCtxList({})\n }\n\n /** 切换目标服务,重新加载 Context 路由映射 */\n private async applyServer(key: string): Promise<void> {\n if (!key) { this.renderCtxList({}); return }\n const server = this.servers.find((s) => s.key === key)\n if (!server) return\n\n this.setFooter(T.switching(), '')\n try {\n await Context.load(server.origin, this.options.timeout)\n const res = await fetch(`${server.origin}/ngw/context`)\n const raw: Record<string, unknown> = res.ok ? await res.json() : {}\n const ctxMap: Record<string, string> = {}\n for (const [k, v] of Object.entries(raw)) {\n if (k !== '$info' && typeof v === 'string') ctxMap[k] = v\n }\n const info = Context.info\n if (info) this.gwVersion.textContent = `v${info.version}`\n this.renderCtxList(ctxMap)\n const count = Object.keys(ctxMap).length\n this.setFooter(T.routes(server.name, count), 'ok')\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this.setFooter(T.switchFailed(msg), 'err')\n }\n }\n\n /** 渲染路由列表 */\n private renderCtxList(ctxMap: Record<string, string>): void {\n this.currentCtxMap = ctxMap\n this.ctxList.innerHTML = ''\n this.ctxSearchInput.value = ''\n const entries = Object.entries(ctxMap)\n if (entries.length === 0) {\n const empty = document.createElement('div')\n empty.className = 'dbg-ctx-empty'\n empty.textContent = T.selectFirst()\n this.ctxList.appendChild(empty)\n return\n }\n entries.forEach(([k, v]) => {\n const item = document.createElement('div')\n item.className = 'dbg-ctx-item'\n item.dataset['key'] = k\n\n const keySpan = document.createElement('span')\n keySpan.className = 'dbg-ctx-key'\n keySpan.textContent = k\n keySpan.setAttribute('title', k)\n\n const valSpan = document.createElement('span')\n valSpan.className = 'dbg-ctx-val'\n valSpan.textContent = v\n valSpan.setAttribute('title', `${k} → ${v}`)\n\n const copyIcon = document.createElement('span')\n copyIcon.className = 'dbg-ctx-copy'\n copyIcon.textContent = '⎘'\n\n item.appendChild(keySpan)\n item.appendChild(valSpan)\n item.appendChild(copyIcon)\n item.addEventListener('click', () => this.copyCtxItem(item, k))\n this.ctxList.appendChild(item)\n })\n }\n\n /** 过滤路由列表 */\n private filterCtx(query: string): void {\n const q = query.toLowerCase()\n this.ctxList.querySelectorAll<HTMLElement>('.dbg-ctx-item').forEach((item) => {\n const key = item.dataset['key'] ?? ''\n item.classList.toggle('hidden', !!q && !key.toLowerCase().includes(q))\n })\n }\n\n /** 复制单个 ctx key */\n private copyCtxItem(item: HTMLElement, text: string): void {\n navigator.clipboard.writeText(text).then(() => {\n item.classList.add('copied')\n const icon = item.querySelector('.dbg-ctx-copy')\n if (icon) icon.textContent = '✓'\n setTimeout(() => {\n item.classList.remove('copied')\n const ic = item.querySelector('.dbg-ctx-copy')\n if (ic) ic.textContent = '⎘'\n }, 1500)\n this.setFooter(T.copied(text), 'ok')\n setTimeout(() => this.setFooter('', ''), 2000)\n })\n }\n\n /** 一键复制全部上下文为 JSON */\n private copyAllCtx(): void {\n const entries = Object.keys(this.currentCtxMap)\n if (entries.length === 0) return\n const json = JSON.stringify(this.currentCtxMap, null, 2)\n navigator.clipboard.writeText(json).then(() => {\n this.copyAllBtn.textContent = '✓ ' + T.copyAllDone()\n this.copyAllBtn.classList.add('done')\n setTimeout(() => {\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.classList.remove('done')\n }, 2000)\n this.setFooter(T.copyAllDone(), 'ok')\n setTimeout(() => this.setFooter('', ''), 2500)\n })\n }\n\n private setHeader(state: 'ok' | 'err' | 'loading'): void {\n this.headerDot.className = `dbg-header-dot${state !== 'ok' ? ` ${state}` : ''}`\n }\n\n private setFooter(msg: string, cls: '' | 'ok' | 'err'): void {\n this.footerEl.innerHTML = msg\n ? `<span class=\"dbg-footer-dot\"></span>${msg}`\n : ''\n this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ''}`\n }\n}\n"]} |
| import { Context, Origin } from './chunk-M5R2NQYU.js'; | ||
| import axios, { isAxiosError } from 'axios'; | ||
| export { default } from 'axios'; | ||
| // src/http/cancel.ts | ||
| var registry = /* @__PURE__ */ new Map(); | ||
| function Register(id) { | ||
| if (registry.has(id)) { | ||
| registry.get(id).abort(); | ||
| } | ||
| const controller = new AbortController(); | ||
| registry.set(id, controller); | ||
| return controller; | ||
| } | ||
| function Cancel(id) { | ||
| const controller = registry.get(id); | ||
| if (!controller) return false; | ||
| controller.abort(); | ||
| registry.delete(id); | ||
| return true; | ||
| } | ||
| function CancelAll() { | ||
| for (const controller of registry.values()) { | ||
| controller.abort(); | ||
| } | ||
| registry.clear(); | ||
| } | ||
| function Unregister(id) { | ||
| registry.delete(id); | ||
| } | ||
| function generateId() { | ||
| if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | ||
| return crypto.randomUUID(); | ||
| } | ||
| return `${Date.now()}-${Math.random().toString(36).slice(2)}`; | ||
| } | ||
| async function Request(config) { | ||
| const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config; | ||
| if (ctx && Context.loaded) { | ||
| const base = Context.get(ctx); | ||
| if (base) { | ||
| axiosConfig.baseURL = base; | ||
| } | ||
| } | ||
| if (!cache) { | ||
| const sep = (axiosConfig.url ?? "").includes("?") ? "&" : "?"; | ||
| axiosConfig.url = `${axiosConfig.url ?? ""}${sep}_=${Date.now()}`; | ||
| } | ||
| const cancelId = customCancelId ?? generateId(); | ||
| const controller = Register(cancelId); | ||
| axiosConfig.signal = controller.signal; | ||
| onCancelId?.(cancelId); | ||
| try { | ||
| const response = await axios.request({ ...axiosConfig }); | ||
| return response; | ||
| } catch (err) { | ||
| if (isAxiosError(err)) { | ||
| return { | ||
| error: { | ||
| status: err.response?.status, | ||
| message: err.message, | ||
| data: err.response?.data | ||
| } | ||
| }; | ||
| } | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| return { error: { message } }; | ||
| } finally { | ||
| Unregister(cancelId); | ||
| } | ||
| } | ||
| function Get(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "GET", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "GET" }); | ||
| } | ||
| function Post(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "POST", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "POST" }); | ||
| } | ||
| function Put(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PUT", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PUT" }); | ||
| } | ||
| function Patch(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PATCH", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PATCH" }); | ||
| } | ||
| function Del(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "DELETE", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "DELETE" }); | ||
| } | ||
| // src/http/ws.ts | ||
| var _registry = /* @__PURE__ */ new Map(); | ||
| function generateId2() { | ||
| if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | ||
| return crypto.randomUUID(); | ||
| } | ||
| return `${Date.now()}-${Math.random().toString(36).slice(2)}`; | ||
| } | ||
| function Ws(urlOrConfig, event) { | ||
| const config = typeof urlOrConfig === "string" ? { url: urlOrConfig } : urlOrConfig; | ||
| const { ctx, cancelId: customCancelId, onCancelId, cache = false, binaryType } = config; | ||
| const cancelId = customCancelId ?? generateId2(); | ||
| if (_registry.has(cancelId)) { | ||
| _registry.get(cancelId).close(); | ||
| _registry.delete(cancelId); | ||
| } | ||
| let url = config.url; | ||
| if (ctx && Context.loaded && !/^wss?:\/\//.test(url)) { | ||
| const host = Context.get(ctx); | ||
| if (host) { | ||
| const base = host.startsWith("/") ? Origin + host : host; | ||
| url = base + url; | ||
| } | ||
| } | ||
| url = url.replace(/^https/, "wss").replace(/^http/, "ws"); | ||
| if (!cache) { | ||
| const sep = url.includes("?") ? "&" : "?"; | ||
| url = `${url}${sep}_=${Date.now()}`; | ||
| } | ||
| const ws = new WebSocket(url); | ||
| if (binaryType) ws.binaryType = binaryType; | ||
| _registry.set(cancelId, ws); | ||
| onCancelId?.(cancelId); | ||
| ws.onopen = (evt) => { | ||
| event("connection", evt, ws); | ||
| }; | ||
| ws.onmessage = (evt) => { | ||
| let data; | ||
| const raw = evt.data; | ||
| if (raw instanceof ArrayBuffer) { | ||
| const text = new TextDecoder("utf-8").decode(raw); | ||
| try { | ||
| data = JSON.parse(text); | ||
| } catch { | ||
| data = text; | ||
| } | ||
| } else if (typeof Blob !== "undefined" && raw instanceof Blob) { | ||
| data = raw; | ||
| } else { | ||
| try { | ||
| data = JSON.parse(raw); | ||
| } catch { | ||
| data = raw; | ||
| } | ||
| } | ||
| event("message", data, ws, evt); | ||
| }; | ||
| ws.onclose = (evt) => { | ||
| event("close", evt, ws); | ||
| _registry.delete(cancelId); | ||
| }; | ||
| return ws; | ||
| } | ||
| function WsClose(id) { | ||
| const ws = _registry.get(id); | ||
| if (!ws) return false; | ||
| ws.close(); | ||
| _registry.delete(id); | ||
| return true; | ||
| } | ||
| function WsCloseAll() { | ||
| for (const ws of _registry.values()) { | ||
| ws.close(); | ||
| } | ||
| _registry.clear(); | ||
| } | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request, Ws, WsClose, WsCloseAll }; | ||
| //# sourceMappingURL=chunk-P5PAPAKJ.js.map | ||
| //# sourceMappingURL=chunk-P5PAPAKJ.js.map |
| {"version":3,"sources":["../../src/http/cancel.ts","../../src/http/client.ts","../../src/http/ws.ts"],"names":["generateId"],"mappings":"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","file":"chunk-P5PAPAKJ.js","sourcesContent":["/** 模块私有注册表,避免全局 window 污染 */\nconst registry = new Map<string, AbortController>()\n\n/**\n * 注册一个请求取消控制器\n *\n * 若已存在相同 id 的控制器,先取消旧请求再注册新的。\n *\n * @param id 请求唯一标识\n * @returns 新创建的 AbortController\n *\n * @example\n * ```ts\n * import { Register, Cancel } from '@snack-kit/lib/http'\n *\n * const ctrl = Register('my-req')\n * // 请求进行中时手动取消\n * Cancel('my-req')\n * ```\n */\nexport function Register(id: string): AbortController {\n if (registry.has(id)) {\n registry.get(id)!.abort()\n }\n const controller = new AbortController()\n registry.set(id, controller)\n return controller\n}\n\n/**\n * 取消指定 id 的请求并从注册表移除\n *\n * @param id 请求唯一标识\n * @returns 是否找到并取消了请求\n *\n * @example\n * ```ts\n * import { Get, Cancel } from '@snack-kit/lib/http'\n *\n * let cancelId = ''\n * Get('/api/data', { onCancelId: (id) => { cancelId = id } })\n *\n * // 需要时取消\n * Cancel(cancelId)\n * ```\n */\nexport function Cancel(id: string): boolean {\n const controller = registry.get(id)\n if (!controller) return false\n controller.abort()\n registry.delete(id)\n return true\n}\n\n/**\n * 取消所有已注册的请求并清空注册表\n *\n * @example\n * ```ts\n * import { CancelAll } from '@snack-kit/lib/http'\n *\n * // 页面卸载时取消所有进行中的请求\n * window.addEventListener('beforeunload', () => CancelAll())\n * ```\n */\nexport function CancelAll(): void {\n for (const controller of registry.values()) {\n controller.abort()\n }\n registry.clear()\n}\n\n/**\n * 从注册表中移除指定 id(不触发取消,用于请求完成后内部清理)\n *\n * @param id 请求唯一标识\n */\nexport function Unregister(id: string): void {\n registry.delete(id)\n}\n","import axios, { isAxiosError } from 'axios'\nimport { Context } from './context'\nimport { Register, Unregister } from './cancel'\nimport type { HttpRequestConfig, HttpMethodConfig, HttpResult } from './types'\n\n/** 生成请求唯一 ID */\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/**\n * 核心请求函数,所有快捷方法基于此实现\n *\n * 自动处理:\n * - `ctx` → 从 HttpContext 查询 baseURL 并注入\n * - `cache: false`(默认)→ URL 附加 `?_=timestamp` 防缓存\n * - 请求取消 → 自动注册 AbortController,完成后自动清理\n * - 错误捕获 → 统一返回 `{ error }` 而非 throw\n *\n * @param config 请求配置\n * @returns 请求结果\n *\n * @example 基础用法\n * ```ts\n * import { Request } from '@snack-kit/lib/http'\n *\n * const result = await Request<{ id: number }>({ url: '/api/user', method: 'GET' })\n * if (result.error) {\n * console.error(result.error.message)\n * } else {\n * console.log(result.data)\n * }\n * ```\n *\n * @example 使用 ctx 自动注入 baseURL\n * ```ts\n * import { context, Request } from '@snack-kit/lib/http'\n *\n * await context.load({ 'user-svc': 'http://user.api.com' })\n * const result = await Request({ url: '/profile', method: 'GET', ctx: 'user-svc' })\n * // 实际请求:GET http://user.api.com/profile\n * ```\n */\nexport async function Request<T>(config: HttpRequestConfig): Promise<HttpResult<T>> {\n const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config\n\n // ctx 自动注入 baseURL\n if (ctx && Context.loaded) {\n const base = Context.get(ctx)\n if (base) {\n axiosConfig.baseURL = base\n }\n }\n\n // 防缓存:默认添加时间戳参数\n if (!cache) {\n const sep = (axiosConfig.url ?? '').includes('?') ? '&' : '?'\n axiosConfig.url = `${axiosConfig.url ?? ''}${sep}_=${Date.now()}`\n }\n\n // 注册取消控制器\n const cancelId = customCancelId ?? generateId()\n const controller = Register(cancelId)\n axiosConfig.signal = controller.signal\n onCancelId?.(cancelId)\n\n try {\n const response = await axios.request<T>({ ...axiosConfig })\n return response\n } catch (err) {\n if (isAxiosError(err)) {\n return {\n error: {\n status: err.response?.status,\n message: err.message,\n data: err.response?.data,\n },\n }\n }\n // AbortError 或其他非 axios 错误\n const message = err instanceof Error ? err.message : String(err)\n return { error: { message } }\n } finally {\n Unregister(cancelId)\n }\n}\n\n/**\n * 发起 GET 请求\n *\n * 支持两种调用方式:\n * - `Get(url, config?)` — 位置参数形式\n * - `Get(config)` — 纯配置对象形式(`method` 已预置,无需传入)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Get<{ name: string }>('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Get({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n *\n * @example 获取 cancelId 以便主动取消\n * ```ts\n * let cancelId = ''\n * Get('/api/list', { onCancelId: (id) => { cancelId = id } })\n * Cancel(cancelId)\n * ```\n */\nexport function Get<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'GET', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'GET' })\n}\n\n/**\n * 发起 POST 请求\n *\n * 支持两种调用方式:\n * - `Post(url, data?, config?)` — 位置参数形式\n * - `Post(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Post<{ token: string }>('/api/login', {\n * username: 'admin',\n * password: '123456',\n * })\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Post({\n * url: '/api/login',\n * data: { username: 'admin', password: '123456' },\n * ctx: 'user-svc',\n * })\n * ```\n */\nexport function Post<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'POST', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'POST' })\n}\n\n/**\n * 发起 PUT 请求\n *\n * 支持两种调用方式:\n * - `Put(url, data?, config?)` — 位置参数形式\n * - `Put(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Put('/api/user/1', { name: 'Alice' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Put({ url: '/api/user/1', data: { name: 'Alice' }, ctx: 'user-svc' })\n * ```\n */\nexport function Put<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PUT', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PUT' })\n}\n\n/**\n * 发起 PATCH 请求\n *\n * 支持两种调用方式:\n * - `Patch(url, data?, config?)` — 位置参数形式\n * - `Patch(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Patch('/api/user/1', { avatar: 'https://...' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Patch({ url: '/api/user/1', data: { avatar: 'https://...' }, ctx: 'user-svc' })\n * ```\n */\nexport function Patch<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PATCH', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PATCH' })\n}\n\n/**\n * 发起 DELETE 请求\n *\n * 支持两种调用方式:\n * - `Del(url, config?)` — 位置参数形式\n * - `Del(config)` — 纯配置对象形式(`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Del('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Del({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n */\nexport function Del<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'DELETE', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'DELETE' })\n}\n","import { Context, Origin } from './context'\nimport type { WsRequestConfig, WsEventCallback } from './types'\n\n/** WS 连接注册表 */\nconst _registry = new Map<string, WebSocket>()\n\n/** 生成唯一 ID */\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/**\n * 创建 WebSocket 连接\n *\n * 自动处理:\n * - `ctx` → 从 HttpContext 查询 host 并拼接 URL\n * - 协议转换 → `http(s)://` 自动替换为 `ws(s)://`\n * - `cache: false`(默认)→ URL 附加 `?_=timestamp` 防缓存\n * - 连接管理 → 通过 cancelId 注册,支持 `WsClose` / `WsCloseAll` 主动关闭\n * - 消息解析 → message 事件自动尝试 JSON.parse\n *\n * @param urlOrConfig 请求地址或配置对象\n * @param event 事件回调,eventName: connection(已连接)、message(收到消息)、close(连接关闭)\n * @returns WebSocket 实例\n *\n * @remarks Chrome 16+ · Firefox 11+ · Safari 7+ · Node.js 21+\n *\n * @example 基础用法\n * ```ts\n * import { Ws } from '@snack-kit/lib/http'\n *\n * const ws = Ws('wss://echo.example.com', (eventName, data, ws) => {\n * if (eventName === 'connection') ws.send('hello')\n * if (eventName === 'message') console.log(data)\n * if (eventName === 'close') console.log('disconnected')\n * })\n * ```\n *\n * @example 使用 ctx 自动拼接地址\n * ```ts\n * import { Context, Ws } from '@snack-kit/lib/http'\n *\n * await Context.load('http://172.16.32.155:20000')\n * const ws = Ws({ url: '/channel', ctx: 'ngw' }, (eventName, data) => {\n * // 实际连接:ws://172.16.32.155:20000/ngw/channel\n * })\n * ```\n *\n * @example 获取 cancelId 以便主动关闭\n * ```ts\n * let id = ''\n * Ws({ url: 'wss://...', onCancelId: (cid) => { id = cid } }, () => {})\n * WsClose(id)\n * ```\n */\nexport function Ws<T = unknown>(\n urlOrConfig: string | WsRequestConfig,\n event: WsEventCallback<T>,\n): WebSocket {\n const config: WsRequestConfig =\n typeof urlOrConfig === 'string' ? { url: urlOrConfig } : urlOrConfig\n const { ctx, cancelId: customCancelId, onCancelId, cache = false, binaryType } = config\n\n const cancelId = customCancelId ?? generateId()\n\n // 相同 cancelId 的旧连接先关闭\n if (_registry.has(cancelId)) {\n _registry.get(cancelId)!.close()\n _registry.delete(cancelId)\n }\n\n // 构建 URL\n let url = config.url\n if (ctx && Context.loaded && !/^wss?:\\/\\//.test(url)) {\n const host = Context.get(ctx)\n if (host) {\n const base = host.startsWith('/') ? Origin + host : host\n url = base + url\n }\n }\n\n // http(s) → ws(s)\n url = url.replace(/^https/, 'wss').replace(/^http/, 'ws')\n\n // 防缓存时间戳\n if (!cache) {\n const sep = url.includes('?') ? '&' : '?'\n url = `${url}${sep}_=${Date.now()}`\n }\n\n const ws = new WebSocket(url)\n if (binaryType) ws.binaryType = binaryType\n _registry.set(cancelId, ws)\n onCancelId?.(cancelId)\n\n ws.onopen = (evt: Event) => {\n event('connection', evt as T | Event | CloseEvent, ws)\n }\n\n ws.onmessage = (evt: MessageEvent) => {\n let data: T | string\n const raw = evt.data\n // 二进制数据:先解码为字符串再尝试 JSON 解析\n if (raw instanceof ArrayBuffer) {\n const text = new TextDecoder('utf-8').decode(raw)\n try {\n data = JSON.parse(text) as T\n } catch {\n data = text as T\n }\n } else if (typeof Blob !== 'undefined' && raw instanceof Blob) {\n // Blob 类型异步处理,直接透传原始数据\n data = raw as T\n } else {\n try {\n data = JSON.parse(raw as string) as T\n } catch {\n data = raw as T\n }\n }\n event('message', data as T | Event | CloseEvent, ws, evt)\n }\n\n ws.onclose = (evt: CloseEvent) => {\n event('close', evt as T | Event | CloseEvent, ws)\n _registry.delete(cancelId)\n }\n\n return ws\n}\n\n/**\n * 关闭指定 ID 的 WebSocket 连接\n *\n * @param id 连接唯一标识(由 `onCancelId` 回调获取,或自定义 `cancelId`)\n * @returns 是否找到并关闭了连接\n *\n * @example\n * ```ts\n * import { Ws, WsClose } from '@snack-kit/lib/http'\n *\n * let cancelId = ''\n * Ws({ url: 'wss://...', onCancelId: (id) => { cancelId = id } }, () => {})\n * WsClose(cancelId)\n * ```\n */\nexport function WsClose(id: string): boolean {\n const ws = _registry.get(id)\n if (!ws) return false\n ws.close()\n _registry.delete(id)\n return true\n}\n\n/**\n * 关闭所有已注册的 WebSocket 连接并清空注册表\n *\n * @example\n * ```ts\n * import { WsCloseAll } from '@snack-kit/lib/http'\n *\n * window.addEventListener('beforeunload', () => WsCloseAll())\n * ```\n */\nexport function WsCloseAll(): void {\n for (const ws of _registry.values()) {\n ws.close()\n }\n _registry.clear()\n}\n"]} |
| import { AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
| /** | ||
| * HTTP 请求配置,扩展自 AxiosRequestConfig | ||
| */ | ||
| interface HttpRequestConfig<D = unknown> extends AxiosRequestConfig<D> { | ||
| /** Context key,自动从 HttpContext 获取 baseURL */ | ||
| ctx?: string; | ||
| /** 自定义请求 ID,用于主动取消 */ | ||
| cancelId?: string; | ||
| /** 回调接收生成的 cancelId */ | ||
| onCancelId?: (id: string) => void; | ||
| /** | ||
| * 是否添加时间戳防缓存,默认 false(添加防缓存时间戳) | ||
| * 设为 true 时不添加时间戳 | ||
| */ | ||
| cache?: boolean; | ||
| } | ||
| /** | ||
| * 快捷请求方法(Get / Post 等)的 config 入参类型。 | ||
| * | ||
| * 与 `HttpRequestConfig` 相同,但不含 `method`(由快捷方法预置)。 | ||
| * 适用于以纯配置对象方式调用时,避免重复指定已知的 method。 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const config: HttpMethodConfig = { url: '/api/user', ctx: 'user-svc' } | ||
| * const { data } = await Get(config) | ||
| * ``` | ||
| */ | ||
| type HttpMethodConfig<D = unknown> = Omit<HttpRequestConfig<D>, 'method'>; | ||
| /** | ||
| * HTTP 请求结果,扩展自 AxiosResponse | ||
| */ | ||
| interface HttpResult<T = unknown> extends Partial<AxiosResponse<T>> { | ||
| /** 有 error 则请求失败,无 error 则成功 */ | ||
| error?: HttpError; | ||
| } | ||
| /** | ||
| * HTTP 错误信息 | ||
| */ | ||
| interface HttpError { | ||
| /** HTTP 状态码 */ | ||
| status?: number; | ||
| /** 错误描述 */ | ||
| message: string; | ||
| /** 服务端返回的原始错误体 */ | ||
| data?: unknown; | ||
| } | ||
| /** | ||
| * WebSocket 请求配置 | ||
| */ | ||
| interface WsRequestConfig { | ||
| /** 请求地址或路径 */ | ||
| url: string; | ||
| /** | ||
| * Context key,自动从 HttpContext 获取 host 进行拼接。 | ||
| * 当 url 为绝对路径(ws:// / wss://)时该参数失效。 | ||
| */ | ||
| ctx?: string; | ||
| /** 自定义连接 ID,用于主动关闭;默认自动生成 */ | ||
| cancelId?: string; | ||
| /** 回调接收生成的 cancelId */ | ||
| onCancelId?: (id: string) => void; | ||
| /** | ||
| * 是否添加时间戳防缓存,默认 false(添加防缓存时间戳)。 | ||
| * 设为 true 时不添加时间戳。 | ||
| */ | ||
| cache?: boolean; | ||
| /** | ||
| * WebSocket 二进制数据类型,默认 `'blob'`。 | ||
| * 设为 `'arraybuffer'` 时,message 事件回调的 data 为 ArrayBuffer 或解码后的对象。 | ||
| */ | ||
| binaryType?: BinaryType; | ||
| } | ||
| /** WebSocket 事件名称 */ | ||
| type WsEventName = 'connection' | 'message' | 'close'; | ||
| /** | ||
| * WebSocket 事件回调 | ||
| * | ||
| * @param eventName 事件类型:connection(已连接)、message(收到消息)、close(连接关闭) | ||
| * @param data 事件数据:connection 为 Event,message 为解析后的数据,close 为 CloseEvent | ||
| * @param ws WebSocket 实例,可调用 ws.send() 发送消息 | ||
| * @param evt message 事件的原始 MessageEvent(仅 message 时有值) | ||
| */ | ||
| type WsEventCallback<T = unknown> = (eventName: WsEventName, data: T | Event | CloseEvent, ws: WebSocket, evt?: MessageEvent) => void; | ||
| /** | ||
| * 网关 `/ngw/context` 接口返回的元数据(`$info` 字段) | ||
| */ | ||
| interface ContextInfo { | ||
| /** 网关版本号,例如 `"4.6.2-r10"` */ | ||
| version: string; | ||
| /** 网关域名,例如 `"http://172.16.32.155:20000"` */ | ||
| domain: string; | ||
| /** 客户端标识 */ | ||
| client: string; | ||
| /** 当前语言 */ | ||
| lang: string; | ||
| } | ||
| /** | ||
| * 网关 `/web-debug/host/list` 接口返回的服务列表项 | ||
| */ | ||
| interface ServerItem { | ||
| /** 服务唯一标识 */ | ||
| key: string; | ||
| /** 服务名称 */ | ||
| name: string; | ||
| /** 服务类型分组,可为空字符串 */ | ||
| type: string; | ||
| /** 服务地址,用于 `context.load()` 切换路由映射 */ | ||
| origin: string; | ||
| /** 代理配置(由网关管理,前端只读) */ | ||
| proxyInfo: Record<string, unknown>; | ||
| } | ||
| /** | ||
| * globalThis 上的标志位 key,由 `Debugger.init()` 写入。 | ||
| * `Context.load()` 无参数调用时,若检测到此标志则跳过自动加载, | ||
| * 由 Debugger 负责在用户选择服务后调用 `Context.load(origin)`。 | ||
| */ | ||
| declare const DEBUGGER_ACTIVE_KEY = "__snackkit_debugger__active"; | ||
| /** | ||
| * 管理服务地址映射的上下文单例 | ||
| * | ||
| * 支持三种加载方式: | ||
| * - **不传参数**:生产环境自动以当前页面 Origin 为网关,开发环境由 `Debugger` 接管 | ||
| * - **远程加载**:传入网关 URL,自动请求 `GET /ngw/context` 获取映射表 | ||
| * - **本地注入**:直接传入键值对象,不发起网络请求 | ||
| * | ||
| * @example 生产环境不传参(推荐) | ||
| * ```ts | ||
| * import { Context } from '@snack-kit/lib/http' | ||
| * | ||
| * await Context.load() | ||
| * Context.get('ngw') // 'https://app.example.com/ngw' | ||
| * ``` | ||
| * | ||
| * @example 远程加载(指定网关) | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.get('ngw') // 'http://172.16.32.155:20000/ngw' | ||
| * Context.info // { version: '4.6.2-r10', domain: '...', ... } | ||
| * ``` | ||
| * | ||
| * @example 本地注入 | ||
| * ```ts | ||
| * await Context.load({ | ||
| * 'user-svc': 'http://user.api.com', | ||
| * 'order-svc': 'http://order.api.com', | ||
| * }) | ||
| * ``` | ||
| */ | ||
| declare class HttpContext { | ||
| private _store; | ||
| private _loaded; | ||
| private _info; | ||
| /** | ||
| * 加载服务地址映射 | ||
| * | ||
| * 支持三种调用方式: | ||
| * - **传入字符串**:作为网关地址,自动请求 `GET /ngw/context` 获取映射表 | ||
| * - **传入对象**:直接注入键值映射,不发起网络请求 | ||
| * - **不传参数**: | ||
| * - 生产环境(`NODE_ENV === 'production'`):以当前页面 `Origin` 为网关地址自动加载 | ||
| * - 开发环境:立即返回,不做任何操作(应由 `Debugger.init()` 接管加载流程) | ||
| * | ||
| * 远程加载时,响应中的 `$info` 元数据字段会被单独存储,不计入路由映射表。 | ||
| * | ||
| * @param source 网关 URL / 键值对象 / 省略 | ||
| * @param timeout 远程加载超时(ms),默认 5000 | ||
| * | ||
| * @example 不传参——生产环境自动使用当前页面 Origin | ||
| * ```ts | ||
| * await Context.load() | ||
| * ``` | ||
| * | ||
| * @example 传入网关地址 | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000', 3000) | ||
| * ``` | ||
| * | ||
| * @example 本地注入 | ||
| * ```ts | ||
| * await Context.load({ 'user-svc': 'http://user.api.com' }) | ||
| * ``` | ||
| */ | ||
| load(source?: string | Record<string, string>, timeout?: number): Promise<void>; | ||
| /** | ||
| * 获取指定 ctx key 对应的服务地址 | ||
| * @param key ctx key,例如 `'ngw'`、`'osc'` | ||
| * @returns 服务地址,未找到时返回空字符串 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.get('osc') // 'http://172.16.32.155:20000/osc' | ||
| * Context.get('none') // '' | ||
| * ``` | ||
| */ | ||
| get(key: string): string; | ||
| /** | ||
| * 网关元数据(仅远程加载后可用) | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.info?.version // '4.6.2-r10' | ||
| * Context.info?.domain // 'http://172.16.32.155:20000' | ||
| * ``` | ||
| */ | ||
| get info(): ContextInfo | undefined; | ||
| /** | ||
| * Context 是否已加载 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * Context.loaded // false | ||
| * await Context.load({ 'user-svc': 'http://user.api.com' }) | ||
| * Context.loaded // true | ||
| * ``` | ||
| */ | ||
| get loaded(): boolean; | ||
| /** | ||
| * 清空所有映射并重置加载状态 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * Context.clear() | ||
| * Context.loaded // false | ||
| * ``` | ||
| */ | ||
| clear(): void; | ||
| } | ||
| /** | ||
| * HttpContext 全局单例 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Context } from '@snack-kit/lib/http' | ||
| * | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.get('osc') // 'http://172.16.32.155:20000/osc' | ||
| * ``` | ||
| */ | ||
| declare const Context: HttpContext; | ||
| /** | ||
| * 当前页面的 origin(`protocol + host`) | ||
| * | ||
| * 在非浏览器环境(如 SSR/Node)返回空字符串。 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Origin } from '@snack-kit/lib/http' | ||
| * // 浏览器中:'https://app.example.com' | ||
| * // Node 环境:'' | ||
| * ``` | ||
| */ | ||
| declare const Origin: string; | ||
| /** | ||
| * 当前页面路径的首段,可直接用作 `ctx` 参数 | ||
| * | ||
| * 例如 `/osc/employee` → `'osc'`,在非浏览器环境返回空字符串。 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Ctx, Get } from '@snack-kit/lib/http' | ||
| * | ||
| * // 当前路径为 /osc/... 时,Ctx === 'osc' | ||
| * const result = await Get('/employee', { ctx: Ctx }) | ||
| * ``` | ||
| */ | ||
| declare const Ctx: string; | ||
| export { Context as C, DEBUGGER_ACTIVE_KEY as D, HttpContext as H, Origin as O, type ServerItem as S, type WsEventCallback as W, type ContextInfo as a, Ctx as b, type HttpError as c, type HttpMethodConfig as d, type HttpRequestConfig as e, type HttpResult as f, type WsEventName as g, type WsRequestConfig as h }; |
| 'use strict'; | ||
| var chunkLQ5WJVQX_cjs = require('./chunk-LQ5WJVQX.cjs'); | ||
| var chunkPQTH7HUD_cjs = require('./chunk-PQTH7HUD.cjs'); | ||
| var chunkUNFUIZVY_cjs = require('./chunk-UNFUIZVY.cjs'); | ||
@@ -10,3 +10,3 @@ | ||
| enumerable: true, | ||
| get: function () { return chunkLQ5WJVQX_cjs.Debugger; } | ||
| get: function () { return chunkPQTH7HUD_cjs.Debugger; } | ||
| }); | ||
@@ -13,0 +13,0 @@ Object.defineProperty(exports, "DEBUGGER_ACTIVE_KEY", { |
+23
-11
| 'use strict'; | ||
| var chunkUM4J7HF4_cjs = require('./chunk-UM4J7HF4.cjs'); | ||
| var chunkUT3D47LI_cjs = require('./chunk-UT3D47LI.cjs'); | ||
| var chunkUNFUIZVY_cjs = require('./chunk-UNFUIZVY.cjs'); | ||
@@ -8,3 +8,3 @@ | ||
| var chunkUM4J7HF4_cjs__default = /*#__PURE__*/_interopDefault(chunkUM4J7HF4_cjs); | ||
| var chunkUT3D47LI_cjs__default = /*#__PURE__*/_interopDefault(chunkUT3D47LI_cjs); | ||
@@ -15,35 +15,47 @@ | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Cancel; } | ||
| get: function () { return chunkUT3D47LI_cjs.Cancel; } | ||
| }); | ||
| Object.defineProperty(exports, "CancelAll", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.CancelAll; } | ||
| get: function () { return chunkUT3D47LI_cjs.CancelAll; } | ||
| }); | ||
| Object.defineProperty(exports, "Del", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Del; } | ||
| get: function () { return chunkUT3D47LI_cjs.Del; } | ||
| }); | ||
| Object.defineProperty(exports, "Get", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Get; } | ||
| get: function () { return chunkUT3D47LI_cjs.Get; } | ||
| }); | ||
| Object.defineProperty(exports, "Patch", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Patch; } | ||
| get: function () { return chunkUT3D47LI_cjs.Patch; } | ||
| }); | ||
| Object.defineProperty(exports, "Post", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Post; } | ||
| get: function () { return chunkUT3D47LI_cjs.Post; } | ||
| }); | ||
| Object.defineProperty(exports, "Put", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Put; } | ||
| get: function () { return chunkUT3D47LI_cjs.Put; } | ||
| }); | ||
| Object.defineProperty(exports, "Request", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Request; } | ||
| get: function () { return chunkUT3D47LI_cjs.Request; } | ||
| }); | ||
| Object.defineProperty(exports, "Ws", { | ||
| enumerable: true, | ||
| get: function () { return chunkUT3D47LI_cjs.Ws; } | ||
| }); | ||
| Object.defineProperty(exports, "WsClose", { | ||
| enumerable: true, | ||
| get: function () { return chunkUT3D47LI_cjs.WsClose; } | ||
| }); | ||
| Object.defineProperty(exports, "WsCloseAll", { | ||
| enumerable: true, | ||
| get: function () { return chunkUT3D47LI_cjs.WsCloseAll; } | ||
| }); | ||
| Object.defineProperty(exports, "axios", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs__default.default; } | ||
| get: function () { return chunkUT3D47LI_cjs__default.default; } | ||
| }); | ||
@@ -50,0 +62,0 @@ Object.defineProperty(exports, "Context", { |
+26
-14
| 'use strict'; | ||
| var chunkUM4J7HF4_cjs = require('./chunk-UM4J7HF4.cjs'); | ||
| var chunkLQ5WJVQX_cjs = require('./chunk-LQ5WJVQX.cjs'); | ||
| var chunkUT3D47LI_cjs = require('./chunk-UT3D47LI.cjs'); | ||
| var chunkPQTH7HUD_cjs = require('./chunk-PQTH7HUD.cjs'); | ||
| var chunkUNFUIZVY_cjs = require('./chunk-UNFUIZVY.cjs'); | ||
@@ -10,46 +10,58 @@ var chunkMNKOKLT2_cjs = require('./chunk-MNKOKLT2.cjs'); | ||
| var chunkUM4J7HF4_cjs__default = /*#__PURE__*/_interopDefault(chunkUM4J7HF4_cjs); | ||
| var chunkUT3D47LI_cjs__default = /*#__PURE__*/_interopDefault(chunkUT3D47LI_cjs); | ||
| // package.json | ||
| var version = "0.8.0"; | ||
| var version = "0.9.0"; | ||
| Object.defineProperty(exports, "Cancel", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Cancel; } | ||
| get: function () { return chunkUT3D47LI_cjs.Cancel; } | ||
| }); | ||
| Object.defineProperty(exports, "CancelAll", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.CancelAll; } | ||
| get: function () { return chunkUT3D47LI_cjs.CancelAll; } | ||
| }); | ||
| Object.defineProperty(exports, "Del", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Del; } | ||
| get: function () { return chunkUT3D47LI_cjs.Del; } | ||
| }); | ||
| Object.defineProperty(exports, "Get", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Get; } | ||
| get: function () { return chunkUT3D47LI_cjs.Get; } | ||
| }); | ||
| Object.defineProperty(exports, "Patch", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Patch; } | ||
| get: function () { return chunkUT3D47LI_cjs.Patch; } | ||
| }); | ||
| Object.defineProperty(exports, "Post", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Post; } | ||
| get: function () { return chunkUT3D47LI_cjs.Post; } | ||
| }); | ||
| Object.defineProperty(exports, "Put", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Put; } | ||
| get: function () { return chunkUT3D47LI_cjs.Put; } | ||
| }); | ||
| Object.defineProperty(exports, "Request", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs.Request; } | ||
| get: function () { return chunkUT3D47LI_cjs.Request; } | ||
| }); | ||
| Object.defineProperty(exports, "Ws", { | ||
| enumerable: true, | ||
| get: function () { return chunkUT3D47LI_cjs.Ws; } | ||
| }); | ||
| Object.defineProperty(exports, "WsClose", { | ||
| enumerable: true, | ||
| get: function () { return chunkUT3D47LI_cjs.WsClose; } | ||
| }); | ||
| Object.defineProperty(exports, "WsCloseAll", { | ||
| enumerable: true, | ||
| get: function () { return chunkUT3D47LI_cjs.WsCloseAll; } | ||
| }); | ||
| Object.defineProperty(exports, "axios", { | ||
| enumerable: true, | ||
| get: function () { return chunkUM4J7HF4_cjs__default.default; } | ||
| get: function () { return chunkUT3D47LI_cjs__default.default; } | ||
| }); | ||
| Object.defineProperty(exports, "Debugger", { | ||
| enumerable: true, | ||
| get: function () { return chunkLQ5WJVQX_cjs.Debugger; } | ||
| get: function () { return chunkPQTH7HUD_cjs.Debugger; } | ||
| }); | ||
@@ -56,0 +68,0 @@ Object.defineProperty(exports, "Context", { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../package.json"],"names":[],"mappings":";;;;;;;;;;;;AAEE,IAAA,OAAA,GAAW","file":"index.cjs","sourcesContent":["{\n \"name\": \"@snack-kit/lib\",\n \"version\": \"0.8.0\",\n \"description\": \"Enterprise-grade utility library\",\n \"keywords\": [],\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/types/index.d.ts\",\n \"import\": \"./dist/es/index.js\",\n \"require\": \"./dist/cjs/index.cjs\"\n },\n \"./http\": {\n \"types\": \"./dist/types/http.d.ts\",\n \"import\": \"./dist/es/http.js\",\n \"require\": \"./dist/cjs/http.cjs\"\n },\n \"./debugger\": {\n \"types\": \"./dist/types/debugger.d.ts\",\n \"import\": \"./dist/es/debugger.js\",\n \"require\": \"./dist/cjs/debugger.cjs\"\n },\n \"./utils\": {\n \"types\": \"./dist/types/utils.d.ts\",\n \"import\": \"./dist/es/utils.js\",\n \"require\": \"./dist/cjs/utils.cjs\"\n }\n },\n \"main\": \"./dist/cjs/index.cjs\",\n \"module\": \"./dist/es/index.js\",\n \"types\": \"./dist/types/index.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:watch\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"docs\": \"typedoc && cp -r docs/demo docs/api/demo\",\n \"docs:watch\": \"typedoc --watch\",\n \"lint\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run build\",\n \"release\": \"npm run build && npm publish --access public\"\n },\n \"devDependencies\": {\n \"@vitest/coverage-v8\": \"^2.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typedoc\": \"^0.26.0\",\n \"typescript\": \"^5.5.3\",\n \"vitest\": \"^2.0.0\"\n },\n \"dependencies\": {\n \"axios\": \"^1.13.6\"\n }\n}\n"]} | ||
| {"version":3,"sources":["../../package.json"],"names":[],"mappings":";;;;;;;;;;;;AAEE,IAAA,OAAA,GAAW","file":"index.cjs","sourcesContent":["{\n \"name\": \"@snack-kit/lib\",\n \"version\": \"0.9.0\",\n \"description\": \"Enterprise-grade utility library\",\n \"keywords\": [],\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/types/index.d.ts\",\n \"import\": \"./dist/es/index.js\",\n \"require\": \"./dist/cjs/index.cjs\"\n },\n \"./http\": {\n \"types\": \"./dist/types/http.d.ts\",\n \"import\": \"./dist/es/http.js\",\n \"require\": \"./dist/cjs/http.cjs\"\n },\n \"./debugger\": {\n \"types\": \"./dist/types/debugger.d.ts\",\n \"import\": \"./dist/es/debugger.js\",\n \"require\": \"./dist/cjs/debugger.cjs\"\n },\n \"./utils\": {\n \"types\": \"./dist/types/utils.d.ts\",\n \"import\": \"./dist/es/utils.js\",\n \"require\": \"./dist/cjs/utils.cjs\"\n }\n },\n \"main\": \"./dist/cjs/index.cjs\",\n \"module\": \"./dist/es/index.js\",\n \"types\": \"./dist/types/index.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:watch\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"docs\": \"typedoc && cp -r docs/demo docs/api/demo\",\n \"docs:watch\": \"typedoc --watch\",\n \"lint\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run build\",\n \"release\": \"npm run build && npm publish --access public\"\n },\n \"devDependencies\": {\n \"@vitest/coverage-v8\": \"^2.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typedoc\": \"^0.26.0\",\n \"typescript\": \"^5.5.3\",\n \"vitest\": \"^2.0.0\"\n },\n \"dependencies\": {\n \"axios\": \"^1.13.6\"\n }\n}\n"]} |
@@ -1,4 +0,4 @@ | ||
| export { Debugger } from './chunk-YXJTZURC.js'; | ||
| export { Debugger } from './chunk-FNBSAJUM.js'; | ||
| export { DEBUGGER_ACTIVE_KEY } from './chunk-M5R2NQYU.js'; | ||
| //# sourceMappingURL=debugger.js.map | ||
| //# sourceMappingURL=debugger.js.map |
+1
-1
@@ -1,4 +0,4 @@ | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request, default as axios } from './chunk-2YYI2T2B.js'; | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request, Ws, WsClose, WsCloseAll, default as axios } from './chunk-P5PAPAKJ.js'; | ||
| export { Context, Ctx, HttpContext, Origin } from './chunk-M5R2NQYU.js'; | ||
| //# sourceMappingURL=http.js.map | ||
| //# sourceMappingURL=http.js.map |
+3
-3
@@ -1,3 +0,3 @@ | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request, default as axios } from './chunk-2YYI2T2B.js'; | ||
| export { Debugger } from './chunk-YXJTZURC.js'; | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request, Ws, WsClose, WsCloseAll, default as axios } from './chunk-P5PAPAKJ.js'; | ||
| export { Debugger } from './chunk-FNBSAJUM.js'; | ||
| export { Context, Ctx, DEBUGGER_ACTIVE_KEY, HttpContext, Origin } from './chunk-M5R2NQYU.js'; | ||
@@ -7,3 +7,3 @@ export { CleanObject, CopyToClipboard, Debounce, DeepClone, Delay, FormatDate, GetDateOffset, GetDayRange, GetURLParam, GetURLParams, IsArray, IsBoolean, IsEmail, IsEqual, IsFunction, IsInteger, IsIpv4, IsNull, IsNumber, IsObject, IsPhone, IsPositiveInteger, IsRealNumber, IsString, IsUrl, Minus, ObjectToQuery, Omit, Pick, QueryToObject, REGEX, REGEXPRULES, Throttle, UUID, Unique, UniqueByKey } from './chunk-C4CPPDML.js'; | ||
| // package.json | ||
| var version = "0.8.0"; | ||
| var version = "0.9.0"; | ||
@@ -10,0 +10,0 @@ export { version as VERSION }; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../package.json"],"names":[],"mappings":";;;;;;AAEE,IAAA,OAAA,GAAW","file":"index.js","sourcesContent":["{\n \"name\": \"@snack-kit/lib\",\n \"version\": \"0.8.0\",\n \"description\": \"Enterprise-grade utility library\",\n \"keywords\": [],\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/types/index.d.ts\",\n \"import\": \"./dist/es/index.js\",\n \"require\": \"./dist/cjs/index.cjs\"\n },\n \"./http\": {\n \"types\": \"./dist/types/http.d.ts\",\n \"import\": \"./dist/es/http.js\",\n \"require\": \"./dist/cjs/http.cjs\"\n },\n \"./debugger\": {\n \"types\": \"./dist/types/debugger.d.ts\",\n \"import\": \"./dist/es/debugger.js\",\n \"require\": \"./dist/cjs/debugger.cjs\"\n },\n \"./utils\": {\n \"types\": \"./dist/types/utils.d.ts\",\n \"import\": \"./dist/es/utils.js\",\n \"require\": \"./dist/cjs/utils.cjs\"\n }\n },\n \"main\": \"./dist/cjs/index.cjs\",\n \"module\": \"./dist/es/index.js\",\n \"types\": \"./dist/types/index.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:watch\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"docs\": \"typedoc && cp -r docs/demo docs/api/demo\",\n \"docs:watch\": \"typedoc --watch\",\n \"lint\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run build\",\n \"release\": \"npm run build && npm publish --access public\"\n },\n \"devDependencies\": {\n \"@vitest/coverage-v8\": \"^2.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typedoc\": \"^0.26.0\",\n \"typescript\": \"^5.5.3\",\n \"vitest\": \"^2.0.0\"\n },\n \"dependencies\": {\n \"axios\": \"^1.13.6\"\n }\n}\n"]} | ||
| {"version":3,"sources":["../../package.json"],"names":[],"mappings":";;;;;;AAEE,IAAA,OAAA,GAAW","file":"index.js","sourcesContent":["{\n \"name\": \"@snack-kit/lib\",\n \"version\": \"0.9.0\",\n \"description\": \"Enterprise-grade utility library\",\n \"keywords\": [],\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/types/index.d.ts\",\n \"import\": \"./dist/es/index.js\",\n \"require\": \"./dist/cjs/index.cjs\"\n },\n \"./http\": {\n \"types\": \"./dist/types/http.d.ts\",\n \"import\": \"./dist/es/http.js\",\n \"require\": \"./dist/cjs/http.cjs\"\n },\n \"./debugger\": {\n \"types\": \"./dist/types/debugger.d.ts\",\n \"import\": \"./dist/es/debugger.js\",\n \"require\": \"./dist/cjs/debugger.cjs\"\n },\n \"./utils\": {\n \"types\": \"./dist/types/utils.d.ts\",\n \"import\": \"./dist/es/utils.js\",\n \"require\": \"./dist/cjs/utils.cjs\"\n }\n },\n \"main\": \"./dist/cjs/index.cjs\",\n \"module\": \"./dist/es/index.js\",\n \"types\": \"./dist/types/index.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:watch\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"docs\": \"typedoc && cp -r docs/demo docs/api/demo\",\n \"docs:watch\": \"typedoc --watch\",\n \"lint\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run build\",\n \"release\": \"npm run build && npm publish --access public\"\n },\n \"devDependencies\": {\n \"@vitest/coverage-v8\": \"^2.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typedoc\": \"^0.26.0\",\n \"typescript\": \"^5.5.3\",\n \"vitest\": \"^2.0.0\"\n },\n \"dependencies\": {\n \"axios\": \"^1.13.6\"\n }\n}\n"]} |
@@ -1,2 +0,2 @@ | ||
| export { D as DEBUGGER_ACTIVE_KEY } from './context-C4dFUDbw.js'; | ||
| export { D as DEBUGGER_ACTIVE_KEY } from './context-DcZ8YTMj.js'; | ||
| import 'axios'; | ||
@@ -3,0 +3,0 @@ |
+76
-3
@@ -1,3 +0,3 @@ | ||
| import { d as HttpMethodConfig, f as HttpResult, e as HttpRequestConfig } from './context-C4dFUDbw.js'; | ||
| export { C as Context, a as ContextInfo, b as Ctx, H as HttpContext, c as HttpError, O as Origin, S as ServerItem } from './context-C4dFUDbw.js'; | ||
| import { d as HttpMethodConfig, f as HttpResult, e as HttpRequestConfig, h as WsRequestConfig, W as WsEventCallback } from './context-DcZ8YTMj.js'; | ||
| export { C as Context, a as ContextInfo, b as Ctx, H as HttpContext, c as HttpError, O as Origin, S as ServerItem, g as WsEventName } from './context-DcZ8YTMj.js'; | ||
| export { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, default as axios } from 'axios'; | ||
@@ -180,2 +180,75 @@ | ||
| export { Cancel, CancelAll, Del, Get, HttpMethodConfig, HttpRequestConfig, HttpResult, Patch, Post, Put, Request }; | ||
| /** | ||
| * 创建 WebSocket 连接 | ||
| * | ||
| * 自动处理: | ||
| * - `ctx` → 从 HttpContext 查询 host 并拼接 URL | ||
| * - 协议转换 → `http(s)://` 自动替换为 `ws(s)://` | ||
| * - `cache: false`(默认)→ URL 附加 `?_=timestamp` 防缓存 | ||
| * - 连接管理 → 通过 cancelId 注册,支持 `WsClose` / `WsCloseAll` 主动关闭 | ||
| * - 消息解析 → message 事件自动尝试 JSON.parse | ||
| * | ||
| * @param urlOrConfig 请求地址或配置对象 | ||
| * @param event 事件回调,eventName: connection(已连接)、message(收到消息)、close(连接关闭) | ||
| * @returns WebSocket 实例 | ||
| * | ||
| * @remarks Chrome 16+ · Firefox 11+ · Safari 7+ · Node.js 21+ | ||
| * | ||
| * @example 基础用法 | ||
| * ```ts | ||
| * import { Ws } from '@snack-kit/lib/http' | ||
| * | ||
| * const ws = Ws('wss://echo.example.com', (eventName, data, ws) => { | ||
| * if (eventName === 'connection') ws.send('hello') | ||
| * if (eventName === 'message') console.log(data) | ||
| * if (eventName === 'close') console.log('disconnected') | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @example 使用 ctx 自动拼接地址 | ||
| * ```ts | ||
| * import { Context, Ws } from '@snack-kit/lib/http' | ||
| * | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * const ws = Ws({ url: '/channel', ctx: 'ngw' }, (eventName, data) => { | ||
| * // 实际连接:ws://172.16.32.155:20000/ngw/channel | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @example 获取 cancelId 以便主动关闭 | ||
| * ```ts | ||
| * let id = '' | ||
| * Ws({ url: 'wss://...', onCancelId: (cid) => { id = cid } }, () => {}) | ||
| * WsClose(id) | ||
| * ``` | ||
| */ | ||
| declare function Ws<T = unknown>(urlOrConfig: string | WsRequestConfig, event: WsEventCallback<T>): WebSocket; | ||
| /** | ||
| * 关闭指定 ID 的 WebSocket 连接 | ||
| * | ||
| * @param id 连接唯一标识(由 `onCancelId` 回调获取,或自定义 `cancelId`) | ||
| * @returns 是否找到并关闭了连接 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Ws, WsClose } from '@snack-kit/lib/http' | ||
| * | ||
| * let cancelId = '' | ||
| * Ws({ url: 'wss://...', onCancelId: (id) => { cancelId = id } }, () => {}) | ||
| * WsClose(cancelId) | ||
| * ``` | ||
| */ | ||
| declare function WsClose(id: string): boolean; | ||
| /** | ||
| * 关闭所有已注册的 WebSocket 连接并清空注册表 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { WsCloseAll } from '@snack-kit/lib/http' | ||
| * | ||
| * window.addEventListener('beforeunload', () => WsCloseAll()) | ||
| * ``` | ||
| */ | ||
| declare function WsCloseAll(): void; | ||
| export { Cancel, CancelAll, Del, Get, HttpMethodConfig, HttpRequestConfig, HttpResult, Patch, Post, Put, Request, Ws, WsClose, WsCloseAll, WsEventCallback, WsRequestConfig }; |
@@ -1,3 +0,3 @@ | ||
| export { C as Context, a as ContextInfo, b as Ctx, D as DEBUGGER_ACTIVE_KEY, H as HttpContext, c as HttpError, d as HttpMethodConfig, e as HttpRequestConfig, f as HttpResult, O as Origin, S as ServerItem } from './context-C4dFUDbw.js'; | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request } from './http.js'; | ||
| export { C as Context, a as ContextInfo, b as Ctx, D as DEBUGGER_ACTIVE_KEY, H as HttpContext, c as HttpError, d as HttpMethodConfig, e as HttpRequestConfig, f as HttpResult, O as Origin, S as ServerItem, W as WsEventCallback, g as WsEventName, h as WsRequestConfig } from './context-DcZ8YTMj.js'; | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request, Ws, WsClose, WsCloseAll } from './http.js'; | ||
| export { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, default as axios } from 'axios'; | ||
@@ -7,4 +7,4 @@ export { Debugger, DebuggerOptions } from './debugger.js'; | ||
| var version = "0.8.0"; | ||
| var version = "0.9.0"; | ||
| export { version as VERSION }; |
+1
-1
| { | ||
| "name": "@snack-kit/lib", | ||
| "version": "0.8.0", | ||
| "version": "0.9.0", | ||
| "description": "Enterprise-grade utility library", | ||
@@ -5,0 +5,0 @@ "keywords": [], |
+7
-0
@@ -254,2 +254,9 @@ # @snack-kit/lib | ||
| ### 0.9.0 | ||
| - **[http]** 新增 WebSocket 封装:`Ws(url | config, event)` 创建连接,`WsClose(id)` 关闭指定连接,`WsCloseAll()` 关闭所有连接 | ||
| - **[http]** 新增 WS 相关类型:`WsRequestConfig`、`WsEventName`、`WsEventCallback<T>` | ||
| - **[http]** `Ws` 支持 `ctx` 自动从 Context 获取 host 拼接地址,自动将 `http(s)://` 转换为 `ws(s)://` | ||
| - **[http]** `Ws` message 事件自动尝试 JSON 解析,支持泛型 `<T>` 类型推导 | ||
| ### 0.8.0 | ||
@@ -256,0 +263,0 @@ |
| 'use strict'; | ||
| var chunkUNFUIZVY_cjs = require('./chunk-UNFUIZVY.cjs'); | ||
| var axios = require('axios'); | ||
| function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } | ||
| var axios__default = /*#__PURE__*/_interopDefault(axios); | ||
| var _http = axios__default.default.create(); | ||
| var STORAGE_PREFIX = "__snackkit_debugger__"; | ||
| var KEY_GATEWAY = `${STORAGE_PREFIX}gateway`; | ||
| var KEY_TYPE = `${STORAGE_PREFIX}type`; | ||
| var KEY_SERVER = `${STORAGE_PREFIX}server`; | ||
| function isChinese() { | ||
| return /^zh/i.test(navigator.language ?? ""); | ||
| } | ||
| var T = { | ||
| gateway: () => isChinese() ? "\u7F51\u5173" : "Gateway", | ||
| tag: () => isChinese() ? "\u6807\u7B7E" : "Tag", | ||
| server: () => isChinese() ? "\u670D\u52A1" : "Server", | ||
| ctx: () => isChinese() ? "\u4E0A\u4E0B\u6587" : "Ctx", | ||
| ctxLabel: () => isChinese() ? "\u4E0A\u4E0B\u6587 \u2014 \u70B9\u51FB\u884C\u590D\u5236 key" : "Ctx \u2014 click row to copy key", | ||
| search: () => isChinese() ? "\u641C\u7D22..." : "Search...", | ||
| noMatch: () => isChinese() ? "\u65E0\u5339\u914D\u9879" : "No results", | ||
| allTags: () => isChinese() ? "\u5168\u90E8\u6807\u7B7E" : "All tags", | ||
| selectGateway: () => isChinese() ? "\u9009\u62E9\u7F51\u5173" : "Select gateway", | ||
| selectServer: () => isChinese() ? "\u9009\u62E9\u670D\u52A1" : "Select server", | ||
| filterCtx: () => isChinese() ? "\u8FC7\u6EE4\u8DEF\u7531..." : "Filter routes...", | ||
| selectFirst: () => isChinese() ? "\u2190 \u8BF7\u5148\u9009\u62E9\u670D\u52A1" : "\u2190 Select a server first", | ||
| loading: () => isChinese() ? "\u52A0\u8F7D\u670D\u52A1\u5217\u8868..." : "Loading...", | ||
| loadFailed: (m) => isChinese() ? `\u52A0\u8F7D\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| loaded: (n) => isChinese() ? `\u5DF2\u52A0\u8F7D ${n} \u4E2A\u670D\u52A1` : `${n} servers loaded`, | ||
| switching: () => isChinese() ? "\u5207\u6362\u670D\u52A1\u4E2D..." : "Switching...", | ||
| switchFailed: (m) => isChinese() ? `\u5207\u6362\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| routes: (name, n) => isChinese() ? `${name} \xB7 ${n} \u6761\u8DEF\u7531` : `${name} \xB7 ${n} routes`, | ||
| copied: (k) => isChinese() ? `\u5DF2\u590D\u5236: ${k}` : `Copied: ${k}`, | ||
| copyAll: () => isChinese() ? "\u590D\u5236\u5168\u90E8 JSON" : "Copy all JSON", | ||
| copyAllDone: () => isChinese() ? "\u5DF2\u590D\u5236\u4E3A JSON" : "Copied as JSON" | ||
| }; | ||
| var PANEL_STYLE = ` | ||
| #__snackkit_debugger__ { | ||
| position: fixed; | ||
| bottom: 20px; | ||
| right: 20px; | ||
| z-index: 99999; | ||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif; | ||
| font-size: 13px; | ||
| color: #b8ccec; | ||
| user-select: none; | ||
| } | ||
| /* \u2500\u2500 \u6D6E\u52A8\u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-toggle { | ||
| width: 42px; | ||
| height: 42px; | ||
| border-radius: 50%; | ||
| background: linear-gradient(135deg, #4f8ef7, #6d6ff5); | ||
| border: none; | ||
| cursor: pointer; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| box-shadow: 0 4px 14px rgba(79,142,247,0.45); | ||
| margin-left: auto; | ||
| font-size: 20px; | ||
| transition: transform 0.15s, box-shadow 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:hover { | ||
| transform: scale(1.08); | ||
| box-shadow: 0 6px 20px rgba(79,142,247,0.55); | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); } | ||
| /* \u2500\u2500 \u9762\u677F\u4E3B\u4F53 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-panel { | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 12px; | ||
| width: 440px; | ||
| margin-bottom: 10px; | ||
| box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05); | ||
| overflow: visible; | ||
| opacity: 0; | ||
| transform: translateY(8px) scale(0.98); | ||
| pointer-events: none; | ||
| transition: opacity 0.18s ease, transform 0.18s ease; | ||
| } | ||
| #__snackkit_debugger__ .dbg-panel.open { | ||
| opacity: 1; | ||
| transform: translateY(0) scale(1); | ||
| pointer-events: auto; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F Header \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| padding: 10px 14px; | ||
| background: #171e2e; | ||
| border-bottom: 1px solid #2d3a55; | ||
| border-radius: 12px 12px 0 0; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-left { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 7px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot { | ||
| width: 7px; | ||
| height: 7px; | ||
| border-radius: 50%; | ||
| background: #34d399; | ||
| box-shadow: 0 0 6px rgba(52,211,153,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.loading { | ||
| background: #fbbf24; | ||
| box-shadow: 0 0 6px rgba(251,191,36,0.6); | ||
| animation: dbg-pulse 1s ease-in-out infinite; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.err { | ||
| background: #f87171; | ||
| box-shadow: 0 0 6px rgba(248,113,113,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-title { | ||
| font-size: 12px; | ||
| font-weight: 600; | ||
| color: #dce8fa; | ||
| letter-spacing: 0.03em; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-meta { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F\u5185\u5BB9\u533A \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-body { | ||
| padding: 12px 14px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 10px; | ||
| height: 420px; | ||
| overflow: visible; | ||
| } | ||
| /* \u2500\u2500 \u5B57\u6BB5\u884C \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-field { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-field-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| } | ||
| #__snackkit_debugger__ .dbg-label { | ||
| font-size: 10px; | ||
| font-weight: 600; | ||
| color: #5e72a0; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.07em; | ||
| } | ||
| /* \u2500\u2500 \u4E00\u952E\u590D\u5236\u5168\u90E8 JSON \u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-copy-all { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| background: transparent; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 4px; | ||
| padding: 2px 7px; | ||
| color: #5e72a0; | ||
| font-size: 10px; | ||
| font-family: inherit; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, color 0.15s, background 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all:hover { | ||
| border-color: #4f8ef7; | ||
| color: #7aacfa; | ||
| background: rgba(79,142,247,0.08); | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all.done { | ||
| border-color: #34d399; | ||
| color: #34d399; | ||
| } | ||
| /* \u2500\u2500 \u81EA\u5B9A\u4E49\u4E0B\u62C9 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-select { | ||
| position: relative; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| gap: 6px; | ||
| background: #252f45; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| padding: 6px 10px; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, background 0.15s; | ||
| min-height: 32px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger { | ||
| border-color: #4f8ef7; | ||
| background: #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val { | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| font-size: 12px; | ||
| color: #b8ccec; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-arrow { | ||
| color: #3d5070; | ||
| font-size: 10px; | ||
| flex-shrink: 0; | ||
| transition: transform 0.15s, color 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow { | ||
| transform: rotate(180deg); | ||
| color: #4f8ef7; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-dropdown { | ||
| position: absolute; | ||
| bottom: calc(100% + 4px); | ||
| left: 0; | ||
| right: 0; | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 8px; | ||
| box-shadow: 0 -8px 24px rgba(0,0,0,0.35); | ||
| overflow: hidden; | ||
| opacity: 0; | ||
| transform: translateY(4px); | ||
| pointer-events: none; | ||
| transition: opacity 0.14s, transform 0.14s; | ||
| z-index: 100000; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown { | ||
| opacity: 1; | ||
| transform: translateY(0); | ||
| pointer-events: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; } | ||
| #__snackkit_debugger__ .dbg-select-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-list { | ||
| max-height: 160px; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-select-option { | ||
| padding: 7px 10px; | ||
| cursor: pointer; | ||
| font-size: 12px; | ||
| color: #8aa4c8; | ||
| transition: background 0.1s, color 0.1s; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; } | ||
| #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; } | ||
| #__snackkit_debugger__ .dbg-select-option.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-select-empty { | ||
| padding: 12px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u8DEF\u7531\u5217\u8868 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-ctx-field { | ||
| flex: 1; | ||
| min-height: 0; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-wrap { | ||
| flex: 1; | ||
| min-height: 0; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| overflow: hidden; | ||
| background: #252f45; | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 6px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| background: #1e2638; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-ctx-list { | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-ctx-item { | ||
| padding: 5px 10px; | ||
| cursor: pointer; | ||
| display: grid; | ||
| grid-template-columns: minmax(80px, 148px) 1fr auto; | ||
| gap: 8px; | ||
| align-items: center; | ||
| border-bottom: 1px solid #1e2638; | ||
| transition: background 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-key { | ||
| font-size: 12px; | ||
| color: #7aacfa; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-val { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| transition: color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; } | ||
| #__snackkit_debugger__ .dbg-ctx-copy { | ||
| font-size: 11px; | ||
| color: #3d5070; | ||
| opacity: 0; | ||
| flex-shrink: 0; | ||
| transition: opacity 0.1s, color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; } | ||
| #__snackkit_debugger__ .dbg-ctx-empty { | ||
| padding: 16px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u5E95\u90E8\u72B6\u6001\u680F \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-footer { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 14px; | ||
| background: #171e2e; | ||
| border-top: 1px solid #2d3a55; | ||
| border-radius: 0 0 12px 12px; | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| min-height: 30px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; } | ||
| #__snackkit_debugger__ .dbg-footer.err { color: #f87171; } | ||
| #__snackkit_debugger__ .dbg-footer-dot { | ||
| width: 4px; | ||
| height: 4px; | ||
| border-radius: 50%; | ||
| background: currentColor; | ||
| flex-shrink: 0; | ||
| } | ||
| @keyframes dbg-pulse { | ||
| 0%, 100% { opacity: 1; } | ||
| 50% { opacity: 0.35; } | ||
| } | ||
| `; | ||
| var SearchSelect = class { | ||
| constructor(onChange) { | ||
| this.options = []; | ||
| this.selected = ""; | ||
| this.onChange = onChange; | ||
| this.el = document.createElement("div"); | ||
| this.el.className = "dbg-select"; | ||
| this.trigger = document.createElement("div"); | ||
| this.trigger.className = "dbg-select-trigger"; | ||
| this.valEl = document.createElement("span"); | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| const arrow = document.createElement("span"); | ||
| arrow.className = "dbg-select-arrow"; | ||
| arrow.textContent = "\u25BE"; | ||
| this.trigger.appendChild(this.valEl); | ||
| this.trigger.appendChild(arrow); | ||
| this.dropdown = document.createElement("div"); | ||
| this.dropdown.className = "dbg-select-dropdown"; | ||
| const searchWrap = document.createElement("div"); | ||
| searchWrap.className = "dbg-select-search"; | ||
| const searchIcon = document.createElement("span"); | ||
| searchIcon.className = "dbg-select-search-icon"; | ||
| searchIcon.textContent = "\u2315"; | ||
| this.searchInput = document.createElement("input"); | ||
| this.searchInput.type = "text"; | ||
| this.searchInput.placeholder = T.search(); | ||
| searchWrap.appendChild(searchIcon); | ||
| searchWrap.appendChild(this.searchInput); | ||
| this.listEl = document.createElement("div"); | ||
| this.listEl.className = "dbg-select-list"; | ||
| this.dropdown.appendChild(searchWrap); | ||
| this.dropdown.appendChild(this.listEl); | ||
| this.el.appendChild(this.trigger); | ||
| this.el.appendChild(this.dropdown); | ||
| this.bindEvents(); | ||
| } | ||
| bindEvents() { | ||
| this.trigger.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| const isOpen = this.el.classList.contains("open"); | ||
| document.querySelectorAll("#__snackkit_debugger__ .dbg-select.open").forEach((el) => { | ||
| if (el !== this.el) el.classList.remove("open"); | ||
| }); | ||
| this.el.classList.toggle("open", !isOpen); | ||
| if (!isOpen) { | ||
| this.searchInput.value = ""; | ||
| this.filterOptions(""); | ||
| setTimeout(() => this.searchInput.focus(), 50); | ||
| } | ||
| }); | ||
| this.searchInput.addEventListener("input", () => { | ||
| this.filterOptions(this.searchInput.value); | ||
| }); | ||
| this.searchInput.addEventListener("click", (e) => e.stopPropagation()); | ||
| document.addEventListener("click", () => { | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| } | ||
| filterOptions(query) { | ||
| const q = query.toLowerCase(); | ||
| let visibleCount = 0; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((opt) => { | ||
| const match = !q || opt.textContent.toLowerCase().includes(q); | ||
| opt.classList.toggle("hidden", !match); | ||
| if (match) visibleCount++; | ||
| }); | ||
| let emptyEl = this.listEl.querySelector(".dbg-select-empty"); | ||
| if (visibleCount === 0) { | ||
| if (!emptyEl) { | ||
| emptyEl = document.createElement("div"); | ||
| emptyEl.className = "dbg-select-empty"; | ||
| emptyEl.textContent = T.noMatch(); | ||
| this.listEl.appendChild(emptyEl); | ||
| } | ||
| } else { | ||
| emptyEl?.remove(); | ||
| } | ||
| } | ||
| /** 更新选项列表 */ | ||
| setOptions(options, placeholder) { | ||
| this.options = options; | ||
| this.listEl.innerHTML = ""; | ||
| this.valEl.textContent = placeholder; | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| this.selected = ""; | ||
| options.forEach(({ value, label }) => { | ||
| const opt = document.createElement("div"); | ||
| opt.className = "dbg-select-option"; | ||
| opt.dataset["value"] = value; | ||
| opt.textContent = label; | ||
| opt.setAttribute("title", label); | ||
| opt.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.select(value, label); | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| this.listEl.appendChild(opt); | ||
| }); | ||
| } | ||
| /** 选中某个值(不触发 onChange) */ | ||
| setValue(value) { | ||
| const opt = this.options.find((o) => o.value === value); | ||
| if (opt) this.select(opt.value, opt.label, false); | ||
| } | ||
| select(value, label, emit = true) { | ||
| this.selected = value; | ||
| this.valEl.textContent = label; | ||
| this.valEl.className = "dbg-select-val"; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((el) => { | ||
| el.classList.toggle("active", el.dataset["value"] === value); | ||
| }); | ||
| if (emit) this.onChange(value); | ||
| } | ||
| getValue() { | ||
| return this.selected; | ||
| } | ||
| getElement() { | ||
| return this.el; | ||
| } | ||
| }; | ||
| var Debugger = class _Debugger { | ||
| constructor(options) { | ||
| this.servers = []; | ||
| this.currentCtxMap = {}; | ||
| this.options = options; | ||
| this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]; | ||
| } | ||
| /** | ||
| * 初始化并渲染调试面板 | ||
| * | ||
| * 支持两种调用形式: | ||
| * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间 | ||
| * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }` | ||
| * | ||
| * 调用后在页面右下角插入浮动按钮,点击展开调试面板。 | ||
| * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。 | ||
| * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。 | ||
| * | ||
| * @param options 配置对象 或 网关 URL 数组 | ||
| * | ||
| * @example 配置对象形式 | ||
| * ```ts | ||
| * import { Debugger } from '@snack-kit/lib/debugger' | ||
| * | ||
| * await Debugger.init({ | ||
| * gateways: [ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ], | ||
| * timeout: 5000, | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @example 数组简写形式 | ||
| * ```ts | ||
| * await Debugger.init([ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ]) | ||
| * ``` | ||
| * | ||
| * @example 仅在非生产环境加载 | ||
| * ```ts | ||
| * if (import.meta.env.DEV) { | ||
| * const { Debugger } = await import('@snack-kit/lib/debugger') | ||
| * await Debugger.init(['http://dev-gateway.example.com']) | ||
| * } | ||
| * ``` | ||
| */ | ||
| static async init(options) { | ||
| globalThis[chunkUNFUIZVY_cjs.DEBUGGER_ACTIVE_KEY] = true; | ||
| const normalized = Array.isArray(options) ? { gateways: options } : options; | ||
| const instance = new _Debugger({ timeout: 1e4, ...normalized }); | ||
| instance.injectStyle(); | ||
| instance.buildDOM(); | ||
| await instance.restoreState(); | ||
| return instance; | ||
| } | ||
| /** 注入内联样式 */ | ||
| injectStyle() { | ||
| if (document.getElementById("__snackkit_debugger_style__")) return; | ||
| const style = document.createElement("style"); | ||
| style.id = "__snackkit_debugger_style__"; | ||
| style.textContent = PANEL_STYLE; | ||
| document.head.appendChild(style); | ||
| } | ||
| /** 构建 DOM 结构 */ | ||
| buildDOM() { | ||
| const root = document.createElement("div"); | ||
| root.id = "__snackkit_debugger__"; | ||
| const panel = document.createElement("div"); | ||
| panel.className = "dbg-panel"; | ||
| this.panelEl = panel; | ||
| const header = document.createElement("div"); | ||
| header.className = "dbg-header"; | ||
| const headerLeft = document.createElement("div"); | ||
| headerLeft.className = "dbg-header-left"; | ||
| this.headerDot = document.createElement("span"); | ||
| this.headerDot.className = "dbg-header-dot loading"; | ||
| const headerTitle = document.createElement("span"); | ||
| headerTitle.className = "dbg-header-title"; | ||
| headerTitle.textContent = "Snack Kit Debugger"; | ||
| headerLeft.appendChild(this.headerDot); | ||
| headerLeft.appendChild(headerTitle); | ||
| header.appendChild(headerLeft); | ||
| panel.appendChild(header); | ||
| const body = document.createElement("div"); | ||
| body.className = "dbg-body"; | ||
| this.gwVersion = document.createElement("span"); | ||
| this.gwVersion.className = "dbg-header-meta"; | ||
| this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val)); | ||
| this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway()); | ||
| body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement())); | ||
| this.typeSelect = new SearchSelect((val) => this.onTypeChange(val)); | ||
| body.appendChild(this.createField(T.tag(), this.typeSelect.getElement())); | ||
| this.serverSelect = new SearchSelect((val) => this.onServerChange(val)); | ||
| body.appendChild(this.createField(T.server(), this.serverSelect.getElement())); | ||
| const ctxWrap = document.createElement("div"); | ||
| ctxWrap.className = "dbg-ctx-wrap"; | ||
| const ctxSearch = document.createElement("div"); | ||
| ctxSearch.className = "dbg-ctx-search"; | ||
| const ctxSearchIcon = document.createElement("span"); | ||
| ctxSearchIcon.className = "dbg-select-search-icon"; | ||
| ctxSearchIcon.textContent = "\u2315"; | ||
| this.ctxSearchInput = document.createElement("input"); | ||
| this.ctxSearchInput.type = "text"; | ||
| this.ctxSearchInput.placeholder = T.filterCtx(); | ||
| this.ctxSearchInput.addEventListener("input", () => this.filterCtx(this.ctxSearchInput.value)); | ||
| ctxSearch.appendChild(ctxSearchIcon); | ||
| ctxSearch.appendChild(this.ctxSearchInput); | ||
| this.ctxList = document.createElement("div"); | ||
| this.ctxList.className = "dbg-ctx-list"; | ||
| ctxWrap.appendChild(ctxSearch); | ||
| ctxWrap.appendChild(this.ctxList); | ||
| this.copyAllBtn = document.createElement("button"); | ||
| this.copyAllBtn.className = "dbg-copy-all"; | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.setAttribute("title", isChinese() ? "\u5C06\u5168\u90E8\u4E0A\u4E0B\u6587\u590D\u5236\u4E3A JSON\uFF0C\u53EF\u4F5C\u4E3A Context.load({...}) \u7684\u53C2\u6570" : "Copy all ctx entries as JSON for Context.load({...})"); | ||
| this.copyAllBtn.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.copyAllCtx(); | ||
| }); | ||
| const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap); | ||
| ctxField.classList.add("dbg-ctx-field"); | ||
| body.appendChild(ctxField); | ||
| panel.appendChild(body); | ||
| const footer = document.createElement("div"); | ||
| footer.className = "dbg-footer"; | ||
| this.footerEl = footer; | ||
| panel.appendChild(footer); | ||
| const toggle = document.createElement("button"); | ||
| toggle.className = "dbg-toggle"; | ||
| toggle.textContent = "\u{1F41B}"; | ||
| toggle.setAttribute("title", "Snack Debugger"); | ||
| toggle.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| panel.classList.toggle("open"); | ||
| }); | ||
| root.appendChild(panel); | ||
| root.appendChild(toggle); | ||
| document.body.appendChild(root); | ||
| } | ||
| /** 创建普通字段行(label + content) */ | ||
| createField(label, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| field.appendChild(lbl); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 创建带操作按钮的字段行(label + action button + content) */ | ||
| createFieldWithAction(label, action, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const fieldHeader = document.createElement("div"); | ||
| fieldHeader.className = "dbg-field-header"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| fieldHeader.appendChild(lbl); | ||
| fieldHeader.appendChild(action); | ||
| field.appendChild(fieldHeader); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 恢复上次持久化的状态 */ | ||
| async restoreState() { | ||
| const savedGw = localStorage.getItem(KEY_GATEWAY); | ||
| const gw = savedGw && this.gateways.includes(savedGw) ? savedGw : this.gateways[0]; | ||
| this.gwSelect.setValue(gw); | ||
| await this.loadServers(gw); | ||
| const savedType = localStorage.getItem(KEY_TYPE); | ||
| if (savedType) { | ||
| this.typeSelect.setValue(savedType); | ||
| this.renderServerOptions(savedType); | ||
| } | ||
| const savedServer = localStorage.getItem(KEY_SERVER); | ||
| if (savedServer && this.servers.find((s) => s.key === savedServer)) { | ||
| this.serverSelect.setValue(savedServer); | ||
| await this.applyServer(savedServer); | ||
| } | ||
| } | ||
| async onGatewayChange(gw) { | ||
| localStorage.setItem(KEY_GATEWAY, gw); | ||
| this.gwVersion.textContent = ""; | ||
| await this.loadServers(gw); | ||
| } | ||
| onTypeChange(type) { | ||
| localStorage.setItem(KEY_TYPE, type); | ||
| this.renderServerOptions(type); | ||
| } | ||
| async onServerChange(key) { | ||
| localStorage.setItem(KEY_SERVER, key); | ||
| await this.applyServer(key); | ||
| } | ||
| /** 从网关加载服务列表 */ | ||
| async loadServers(gwUrl) { | ||
| this.setHeader("loading"); | ||
| this.setFooter(T.loading(), ""); | ||
| try { | ||
| const res = await _http.get(`${gwUrl}/web-debug/host/list`, { | ||
| timeout: this.options.timeout | ||
| }); | ||
| this.servers = res.data ?? []; | ||
| this.renderTypeOptions(); | ||
| this.setHeader("ok"); | ||
| this.setFooter(T.loaded(this.servers.length), "ok"); | ||
| } catch (err) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| this.setHeader("err"); | ||
| this.setFooter(T.loadFailed(message), "err"); | ||
| } | ||
| } | ||
| /** 渲染标签选项 */ | ||
| renderTypeOptions() { | ||
| const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]; | ||
| const opts = [{ value: "", label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]; | ||
| this.typeSelect.setOptions(opts, T.allTags()); | ||
| this.typeSelect.setValue(""); | ||
| this.renderServerOptions(""); | ||
| } | ||
| /** 根据标签渲染服务选项 */ | ||
| renderServerOptions(type) { | ||
| const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers; | ||
| this.serverSelect.setOptions( | ||
| filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })), | ||
| T.selectServer() | ||
| ); | ||
| this.renderCtxList({}); | ||
| } | ||
| /** 切换目标服务,重新加载 Context 路由映射 */ | ||
| async applyServer(key) { | ||
| if (!key) { | ||
| this.renderCtxList({}); | ||
| return; | ||
| } | ||
| const server = this.servers.find((s) => s.key === key); | ||
| if (!server) return; | ||
| this.setFooter(T.switching(), ""); | ||
| try { | ||
| await chunkUNFUIZVY_cjs.Context.load(server.origin, this.options.timeout); | ||
| const res = await fetch(`${server.origin}/ngw/context`); | ||
| const raw = res.ok ? await res.json() : {}; | ||
| const ctxMap = {}; | ||
| for (const [k, v] of Object.entries(raw)) { | ||
| if (k !== "$info" && typeof v === "string") ctxMap[k] = v; | ||
| } | ||
| const info = chunkUNFUIZVY_cjs.Context.info; | ||
| if (info) this.gwVersion.textContent = `v${info.version}`; | ||
| this.renderCtxList(ctxMap); | ||
| const count = Object.keys(ctxMap).length; | ||
| this.setFooter(T.routes(server.name, count), "ok"); | ||
| } catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| this.setFooter(T.switchFailed(msg), "err"); | ||
| } | ||
| } | ||
| /** 渲染路由列表 */ | ||
| renderCtxList(ctxMap) { | ||
| this.currentCtxMap = ctxMap; | ||
| this.ctxList.innerHTML = ""; | ||
| this.ctxSearchInput.value = ""; | ||
| const entries = Object.entries(ctxMap); | ||
| if (entries.length === 0) { | ||
| const empty = document.createElement("div"); | ||
| empty.className = "dbg-ctx-empty"; | ||
| empty.textContent = T.selectFirst(); | ||
| this.ctxList.appendChild(empty); | ||
| return; | ||
| } | ||
| entries.forEach(([k, v]) => { | ||
| const item = document.createElement("div"); | ||
| item.className = "dbg-ctx-item"; | ||
| item.dataset["key"] = k; | ||
| const keySpan = document.createElement("span"); | ||
| keySpan.className = "dbg-ctx-key"; | ||
| keySpan.textContent = k; | ||
| keySpan.setAttribute("title", k); | ||
| const valSpan = document.createElement("span"); | ||
| valSpan.className = "dbg-ctx-val"; | ||
| valSpan.textContent = v; | ||
| valSpan.setAttribute("title", `${k} \u2192 ${v}`); | ||
| const copyIcon = document.createElement("span"); | ||
| copyIcon.className = "dbg-ctx-copy"; | ||
| copyIcon.textContent = "\u2398"; | ||
| item.appendChild(keySpan); | ||
| item.appendChild(valSpan); | ||
| item.appendChild(copyIcon); | ||
| item.addEventListener("click", () => this.copyCtxItem(item, k)); | ||
| this.ctxList.appendChild(item); | ||
| }); | ||
| } | ||
| /** 过滤路由列表 */ | ||
| filterCtx(query) { | ||
| const q = query.toLowerCase(); | ||
| this.ctxList.querySelectorAll(".dbg-ctx-item").forEach((item) => { | ||
| const key = item.dataset["key"] ?? ""; | ||
| item.classList.toggle("hidden", !!q && !key.toLowerCase().includes(q)); | ||
| }); | ||
| } | ||
| /** 复制单个 ctx key */ | ||
| copyCtxItem(item, text) { | ||
| navigator.clipboard.writeText(text).then(() => { | ||
| item.classList.add("copied"); | ||
| const icon = item.querySelector(".dbg-ctx-copy"); | ||
| if (icon) icon.textContent = "\u2713"; | ||
| setTimeout(() => { | ||
| item.classList.remove("copied"); | ||
| const ic = item.querySelector(".dbg-ctx-copy"); | ||
| if (ic) ic.textContent = "\u2398"; | ||
| }, 1500); | ||
| this.setFooter(T.copied(text), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2e3); | ||
| }); | ||
| } | ||
| /** 一键复制全部上下文为 JSON */ | ||
| copyAllCtx() { | ||
| const entries = Object.keys(this.currentCtxMap); | ||
| if (entries.length === 0) return; | ||
| const json = JSON.stringify(this.currentCtxMap, null, 2); | ||
| navigator.clipboard.writeText(json).then(() => { | ||
| this.copyAllBtn.textContent = "\u2713 " + T.copyAllDone(); | ||
| this.copyAllBtn.classList.add("done"); | ||
| setTimeout(() => { | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.classList.remove("done"); | ||
| }, 2e3); | ||
| this.setFooter(T.copyAllDone(), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2500); | ||
| }); | ||
| } | ||
| setHeader(state) { | ||
| this.headerDot.className = `dbg-header-dot${state !== "ok" ? ` ${state}` : ""}`; | ||
| } | ||
| setFooter(msg, cls) { | ||
| this.footerEl.innerHTML = msg ? `<span class="dbg-footer-dot"></span>${msg}` : ""; | ||
| this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ""}`; | ||
| } | ||
| }; | ||
| exports.Debugger = Debugger; | ||
| //# sourceMappingURL=chunk-LQ5WJVQX.cjs.map | ||
| //# sourceMappingURL=chunk-LQ5WJVQX.cjs.map |
| {"version":3,"sources":["../../src/debugger/debugger.ts"],"names":["axios","DEBUGGER_ACTIVE_KEY","Context"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","file":"chunk-LQ5WJVQX.cjs","sourcesContent":["import axios from 'axios'\nimport { Context, DEBUGGER_ACTIVE_KEY } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\n\n/**\n * Debugger 专用的隔离 axios 实例。\n * 独立于用户通过 `import { axios } from '@snack-kit/lib/http'` 添加的拦截器,\n * 确保调试面板请求不被外部拦截器污染。\n */\nconst _http = axios.create()\n\n/** localStorage key 前缀 */\nconst STORAGE_PREFIX = '__snackkit_debugger__'\nconst KEY_GATEWAY = `${STORAGE_PREFIX}gateway`\nconst KEY_TYPE = `${STORAGE_PREFIX}type`\nconst KEY_SERVER = `${STORAGE_PREFIX}server`\n\n\n/** 是否为中文环境 */\nfunction isChinese(): boolean {\n return /^zh/i.test(navigator.language ?? '')\n}\n\n/** i18n 文案 */\nconst T = {\n gateway: () => isChinese() ? '网关' : 'Gateway',\n tag: () => isChinese() ? '标签' : 'Tag',\n server: () => isChinese() ? '服务' : 'Server',\n ctx: () => isChinese() ? '上下文' : 'Ctx',\n ctxLabel: () => isChinese() ? '上下文 — 点击行复制 key' : 'Ctx — click row to copy key',\n search: () => isChinese() ? '搜索...' : 'Search...',\n noMatch: () => isChinese() ? '无匹配项' : 'No results',\n allTags: () => isChinese() ? '全部标签' : 'All tags',\n selectGateway: () => isChinese() ? '选择网关' : 'Select gateway',\n selectServer: () => isChinese() ? '选择服务' : 'Select server',\n filterCtx: () => isChinese() ? '过滤路由...' : 'Filter routes...',\n selectFirst: () => isChinese() ? '← 请先选择服务' : '← Select a server first',\n loading: () => isChinese() ? '加载服务列表...' : 'Loading...',\n loadFailed: (m: string) => isChinese() ? `加载失败: ${m}` : `Failed: ${m}`,\n loaded: (n: number) => isChinese() ? `已加载 ${n} 个服务` : `${n} servers loaded`,\n switching: () => isChinese() ? '切换服务中...' : 'Switching...',\n switchFailed: (m: string) => isChinese() ? `切换失败: ${m}` : `Failed: ${m}`,\n routes: (name: string, n: number) => isChinese() ? `${name} · ${n} 条路由` : `${name} · ${n} routes`,\n copied: (k: string) => isChinese() ? `已复制: ${k}` : `Copied: ${k}`,\n copyAll: () => isChinese() ? '复制全部 JSON' : 'Copy all JSON',\n copyAllDone: () => isChinese() ? '已复制为 JSON' : 'Copied as JSON',\n}\n\n/** 面板内联样式 */\nconst PANEL_STYLE = `\n #__snackkit_debugger__ {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 99999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;\n font-size: 13px;\n color: #b8ccec;\n user-select: none;\n }\n\n /* ── 浮动按钮 ── */\n #__snackkit_debugger__ .dbg-toggle {\n width: 42px;\n height: 42px;\n border-radius: 50%;\n background: linear-gradient(135deg, #4f8ef7, #6d6ff5);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 14px rgba(79,142,247,0.45);\n margin-left: auto;\n font-size: 20px;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n #__snackkit_debugger__ .dbg-toggle:hover {\n transform: scale(1.08);\n box-shadow: 0 6px 20px rgba(79,142,247,0.55);\n }\n #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); }\n\n /* ── 面板主体 ── */\n #__snackkit_debugger__ .dbg-panel {\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 12px;\n width: 440px;\n margin-bottom: 10px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05);\n overflow: visible;\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n pointer-events: none;\n transition: opacity 0.18s ease, transform 0.18s ease;\n }\n #__snackkit_debugger__ .dbg-panel.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n /* ── 面板 Header ── */\n #__snackkit_debugger__ .dbg-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n background: #171e2e;\n border-bottom: 1px solid #2d3a55;\n border-radius: 12px 12px 0 0;\n }\n #__snackkit_debugger__ .dbg-header-left {\n display: flex;\n align-items: center;\n gap: 7px;\n }\n #__snackkit_debugger__ .dbg-header-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: #34d399;\n box-shadow: 0 0 6px rgba(52,211,153,0.6);\n }\n #__snackkit_debugger__ .dbg-header-dot.loading {\n background: #fbbf24;\n box-shadow: 0 0 6px rgba(251,191,36,0.6);\n animation: dbg-pulse 1s ease-in-out infinite;\n }\n #__snackkit_debugger__ .dbg-header-dot.err {\n background: #f87171;\n box-shadow: 0 0 6px rgba(248,113,113,0.6);\n }\n #__snackkit_debugger__ .dbg-header-title {\n font-size: 12px;\n font-weight: 600;\n color: #dce8fa;\n letter-spacing: 0.03em;\n }\n #__snackkit_debugger__ .dbg-header-meta {\n font-size: 11px;\n color: #4d6080;\n }\n\n /* ── 面板内容区 ── */\n #__snackkit_debugger__ .dbg-body {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n height: 420px;\n overflow: visible;\n }\n\n /* ── 字段行 ── */\n #__snackkit_debugger__ .dbg-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-field-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n #__snackkit_debugger__ .dbg-label {\n font-size: 10px;\n font-weight: 600;\n color: #5e72a0;\n text-transform: uppercase;\n letter-spacing: 0.07em;\n }\n\n /* ── 一键复制全部 JSON 按钮 ── */\n #__snackkit_debugger__ .dbg-copy-all {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: 1px solid #2d3a55;\n border-radius: 4px;\n padding: 2px 7px;\n color: #5e72a0;\n font-size: 10px;\n font-family: inherit;\n cursor: pointer;\n transition: border-color 0.15s, color 0.15s, background 0.15s;\n }\n #__snackkit_debugger__ .dbg-copy-all:hover {\n border-color: #4f8ef7;\n color: #7aacfa;\n background: rgba(79,142,247,0.08);\n }\n #__snackkit_debugger__ .dbg-copy-all.done {\n border-color: #34d399;\n color: #34d399;\n }\n\n /* ── 自定义下拉 ── */\n #__snackkit_debugger__ .dbg-select {\n position: relative;\n }\n #__snackkit_debugger__ .dbg-select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n background: #252f45;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n padding: 6px 10px;\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n min-height: 32px;\n }\n #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger {\n border-color: #4f8ef7;\n background: #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-val {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n color: #b8ccec;\n }\n #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-arrow {\n color: #3d5070;\n font-size: 10px;\n flex-shrink: 0;\n transition: transform 0.15s, color 0.15s;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow {\n transform: rotate(180deg);\n color: #4f8ef7;\n }\n #__snackkit_debugger__ .dbg-select-dropdown {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 0;\n right: 0;\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 8px;\n box-shadow: 0 -8px 24px rgba(0,0,0,0.35);\n overflow: hidden;\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n transition: opacity 0.14s, transform 0.14s;\n z-index: 100000;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n }\n #__snackkit_debugger__ .dbg-select-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 10px;\n border-bottom: 1px solid #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; }\n #__snackkit_debugger__ .dbg-select-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-list {\n max-height: 160px;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-select-option {\n padding: 7px 10px;\n cursor: pointer;\n font-size: 12px;\n color: #8aa4c8;\n transition: background 0.1s, color 0.1s;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; }\n #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; }\n #__snackkit_debugger__ .dbg-select-option.hidden { display: none; }\n #__snackkit_debugger__ .dbg-select-empty {\n padding: 12px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 路由列表 ── */\n #__snackkit_debugger__ .dbg-ctx-field {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-ctx-wrap {\n flex: 1;\n min-height: 0;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n overflow: hidden;\n background: #252f45;\n display: flex;\n flex-direction: column;\n }\n #__snackkit_debugger__ .dbg-ctx-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-bottom: 1px solid #2d3a55;\n background: #1e2638;\n }\n #__snackkit_debugger__ .dbg-ctx-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-ctx-list {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-ctx-item {\n padding: 5px 10px;\n cursor: pointer;\n display: grid;\n grid-template-columns: minmax(80px, 148px) 1fr auto;\n gap: 8px;\n align-items: center;\n border-bottom: 1px solid #1e2638;\n transition: background 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }\n #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; }\n #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; }\n #__snackkit_debugger__ .dbg-ctx-key {\n font-size: 12px;\n color: #7aacfa;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-ctx-val {\n font-size: 11px;\n color: #4d6080;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n transition: color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; }\n #__snackkit_debugger__ .dbg-ctx-copy {\n font-size: 11px;\n color: #3d5070;\n opacity: 0;\n flex-shrink: 0;\n transition: opacity 0.1s, color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; }\n #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; }\n #__snackkit_debugger__ .dbg-ctx-empty {\n padding: 16px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 底部状态栏 ── */\n #__snackkit_debugger__ .dbg-footer {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: #171e2e;\n border-top: 1px solid #2d3a55;\n border-radius: 0 0 12px 12px;\n font-size: 11px;\n color: #4d6080;\n min-height: 30px;\n }\n #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; }\n #__snackkit_debugger__ .dbg-footer.err { color: #f87171; }\n #__snackkit_debugger__ .dbg-footer-dot {\n width: 4px;\n height: 4px;\n border-radius: 50%;\n background: currentColor;\n flex-shrink: 0;\n }\n\n @keyframes dbg-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.35; }\n }\n`\n\n/** 可搜索的自定义下拉组件 */\nclass SearchSelect {\n private el: HTMLElement\n private trigger: HTMLElement\n private valEl: HTMLElement\n private dropdown: HTMLElement\n private searchInput: HTMLInputElement\n private listEl: HTMLElement\n private options: Array<{ value: string; label: string }> = []\n private selected = ''\n private onChange: (value: string) => void\n\n constructor(onChange: (value: string) => void) {\n this.onChange = onChange\n this.el = document.createElement('div')\n this.el.className = 'dbg-select'\n\n // 触发器\n this.trigger = document.createElement('div')\n this.trigger.className = 'dbg-select-trigger'\n this.valEl = document.createElement('span')\n this.valEl.className = 'dbg-select-val placeholder'\n const arrow = document.createElement('span')\n arrow.className = 'dbg-select-arrow'\n arrow.textContent = '▾'\n this.trigger.appendChild(this.valEl)\n this.trigger.appendChild(arrow)\n\n // 下拉面板\n this.dropdown = document.createElement('div')\n this.dropdown.className = 'dbg-select-dropdown'\n\n // 搜索框\n const searchWrap = document.createElement('div')\n searchWrap.className = 'dbg-select-search'\n const searchIcon = document.createElement('span')\n searchIcon.className = 'dbg-select-search-icon'\n searchIcon.textContent = '⌕'\n this.searchInput = document.createElement('input')\n this.searchInput.type = 'text'\n this.searchInput.placeholder = T.search()\n searchWrap.appendChild(searchIcon)\n searchWrap.appendChild(this.searchInput)\n\n this.listEl = document.createElement('div')\n this.listEl.className = 'dbg-select-list'\n\n this.dropdown.appendChild(searchWrap)\n this.dropdown.appendChild(this.listEl)\n this.el.appendChild(this.trigger)\n this.el.appendChild(this.dropdown)\n\n this.bindEvents()\n }\n\n private bindEvents(): void {\n this.trigger.addEventListener('click', (e) => {\n e.stopPropagation()\n const isOpen = this.el.classList.contains('open')\n document.querySelectorAll('#__snackkit_debugger__ .dbg-select.open').forEach((el) => {\n if (el !== this.el) el.classList.remove('open')\n })\n this.el.classList.toggle('open', !isOpen)\n if (!isOpen) {\n this.searchInput.value = ''\n this.filterOptions('')\n setTimeout(() => this.searchInput.focus(), 50)\n }\n })\n\n this.searchInput.addEventListener('input', () => {\n this.filterOptions(this.searchInput.value)\n })\n\n this.searchInput.addEventListener('click', (e) => e.stopPropagation())\n\n document.addEventListener('click', () => {\n this.el.classList.remove('open')\n })\n }\n\n private filterOptions(query: string): void {\n const q = query.toLowerCase()\n let visibleCount = 0\n this.listEl.querySelectorAll<HTMLElement>('.dbg-select-option').forEach((opt) => {\n const match = !q || opt.textContent!.toLowerCase().includes(q)\n opt.classList.toggle('hidden', !match)\n if (match) visibleCount++\n })\n let emptyEl = this.listEl.querySelector<HTMLElement>('.dbg-select-empty')\n if (visibleCount === 0) {\n if (!emptyEl) {\n emptyEl = document.createElement('div')\n emptyEl.className = 'dbg-select-empty'\n emptyEl.textContent = T.noMatch()\n this.listEl.appendChild(emptyEl)\n }\n } else {\n emptyEl?.remove()\n }\n }\n\n /** 更新选项列表 */\n setOptions(options: Array<{ value: string; label: string }>, placeholder: string): void {\n this.options = options\n this.listEl.innerHTML = ''\n this.valEl.textContent = placeholder\n this.valEl.className = 'dbg-select-val placeholder'\n this.selected = ''\n\n options.forEach(({ value, label }) => {\n const opt = document.createElement('div')\n opt.className = 'dbg-select-option'\n opt.dataset['value'] = value\n opt.textContent = label\n opt.setAttribute('title', label)\n opt.addEventListener('click', (e) => {\n e.stopPropagation()\n this.select(value, label)\n this.el.classList.remove('open')\n })\n this.listEl.appendChild(opt)\n })\n }\n\n /** 选中某个值(不触发 onChange) */\n setValue(value: string): void {\n const opt = this.options.find((o) => o.value === value)\n if (opt) this.select(opt.value, opt.label, false)\n }\n\n private select(value: string, label: string, emit = true): void {\n this.selected = value\n this.valEl.textContent = label\n this.valEl.className = 'dbg-select-val'\n this.listEl.querySelectorAll('.dbg-select-option').forEach((el) => {\n el.classList.toggle('active', (el as HTMLElement).dataset['value'] === value)\n })\n if (emit) this.onChange(value)\n }\n\n getValue(): string { return this.selected }\n getElement(): HTMLElement { return this.el }\n}\n\n/**\n * 浏览器端浮动调试面板\n *\n * 允许开发者在运行时:\n * - 选择调试网关 → 拉取服务清单\n * - 切换目标服务 → 重新加载 Context 路由映射\n * - 查看并复制可用的路由 ctx 列表\n *\n * 与 http 模块集成:使用 `Get()` 拉取服务清单,使用 `context.load()` 切换请求目标。\n *\n * @see [交互式 Demo](../demo/debugger.html)\n *\n * @example\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * ```\n */\nexport class Debugger {\n private options: Required<DebuggerOptions>\n private gateways: string[]\n private servers: ServerItem[] = []\n private currentCtxMap: Record<string, string> = {}\n private panelEl!: HTMLElement\n private headerDot!: HTMLElement\n private gwVersion!: HTMLElement\n private footerEl!: HTMLElement\n private copyAllBtn!: HTMLButtonElement\n private gwSelect!: SearchSelect\n private typeSelect!: SearchSelect\n private serverSelect!: SearchSelect\n private ctxList!: HTMLElement\n private ctxSearchInput!: HTMLInputElement\n\n private constructor(options: Required<DebuggerOptions>) {\n this.options = options\n this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]\n }\n\n /**\n * 初始化并渲染调试面板\n *\n * 支持两种调用形式:\n * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间\n * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }`\n *\n * 调用后在页面右下角插入浮动按钮,点击展开调试面板。\n * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置对象 或 网关 URL 数组\n *\n * @example 配置对象形式\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: [\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ],\n * timeout: 5000,\n * })\n * ```\n *\n * @example 数组简写形式\n * ```ts\n * await Debugger.init([\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ])\n * ```\n *\n * @example 仅在非生产环境加载\n * ```ts\n * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init(['http://dev-gateway.example.com'])\n * }\n * ```\n */\n static async init(options: DebuggerOptions | string[]): Promise<Debugger> {\n // 在 globalThis 写入标志位,Context.load() 无参数时检测到后将跳过自动加载\n ;(globalThis as Record<string, unknown>)[DEBUGGER_ACTIVE_KEY] = true\n const normalized: DebuggerOptions = Array.isArray(options) ? { gateways: options } : options\n const instance = new Debugger({ timeout: 10000, ...normalized })\n instance.injectStyle()\n instance.buildDOM()\n await instance.restoreState()\n return instance\n }\n\n /** 注入内联样式 */\n private injectStyle(): void {\n if (document.getElementById('__snackkit_debugger_style__')) return\n const style = document.createElement('style')\n style.id = '__snackkit_debugger_style__'\n style.textContent = PANEL_STYLE\n document.head.appendChild(style)\n }\n\n /** 构建 DOM 结构 */\n private buildDOM(): void {\n const root = document.createElement('div')\n root.id = '__snackkit_debugger__'\n\n // ── 面板 ──\n const panel = document.createElement('div')\n panel.className = 'dbg-panel'\n this.panelEl = panel\n\n // Header\n const header = document.createElement('div')\n header.className = 'dbg-header'\n const headerLeft = document.createElement('div')\n headerLeft.className = 'dbg-header-left'\n this.headerDot = document.createElement('span')\n this.headerDot.className = 'dbg-header-dot loading'\n const headerTitle = document.createElement('span')\n headerTitle.className = 'dbg-header-title'\n headerTitle.textContent = 'Snack Kit Debugger'\n headerLeft.appendChild(this.headerDot)\n headerLeft.appendChild(headerTitle)\n header.appendChild(headerLeft)\n panel.appendChild(header)\n\n // Body\n const body = document.createElement('div')\n body.className = 'dbg-body'\n\n // 网关选择(标签行右侧显示版本号)\n this.gwVersion = document.createElement('span')\n this.gwVersion.className = 'dbg-header-meta'\n this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val))\n this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway())\n body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement()))\n\n // 标签(类型)选择\n this.typeSelect = new SearchSelect((val) => this.onTypeChange(val))\n body.appendChild(this.createField(T.tag(), this.typeSelect.getElement()))\n\n // 服务选择\n this.serverSelect = new SearchSelect((val) => this.onServerChange(val))\n body.appendChild(this.createField(T.server(), this.serverSelect.getElement()))\n\n // 路由列表(含一键复制全部 JSON 按钮)\n const ctxWrap = document.createElement('div')\n ctxWrap.className = 'dbg-ctx-wrap'\n\n const ctxSearch = document.createElement('div')\n ctxSearch.className = 'dbg-ctx-search'\n const ctxSearchIcon = document.createElement('span')\n ctxSearchIcon.className = 'dbg-select-search-icon'\n ctxSearchIcon.textContent = '⌕'\n this.ctxSearchInput = document.createElement('input')\n this.ctxSearchInput.type = 'text'\n this.ctxSearchInput.placeholder = T.filterCtx()\n this.ctxSearchInput.addEventListener('input', () => this.filterCtx(this.ctxSearchInput.value))\n ctxSearch.appendChild(ctxSearchIcon)\n ctxSearch.appendChild(this.ctxSearchInput)\n\n this.ctxList = document.createElement('div')\n this.ctxList.className = 'dbg-ctx-list'\n\n ctxWrap.appendChild(ctxSearch)\n ctxWrap.appendChild(this.ctxList)\n\n // 一键复制全部 JSON 按钮\n this.copyAllBtn = document.createElement('button')\n this.copyAllBtn.className = 'dbg-copy-all'\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.setAttribute('title', isChinese()\n ? '将全部上下文复制为 JSON,可作为 Context.load({...}) 的参数'\n : 'Copy all ctx entries as JSON for Context.load({...})')\n this.copyAllBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.copyAllCtx()\n })\n\n const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap)\n ctxField.classList.add('dbg-ctx-field')\n body.appendChild(ctxField)\n\n panel.appendChild(body)\n\n // Footer 状态栏\n const footer = document.createElement('div')\n footer.className = 'dbg-footer'\n this.footerEl = footer\n panel.appendChild(footer)\n\n // ── 浮动按钮 ──\n const toggle = document.createElement('button')\n toggle.className = 'dbg-toggle'\n toggle.textContent = '🐛'\n toggle.setAttribute('title', 'Snack Debugger')\n toggle.addEventListener('click', (e) => {\n e.stopPropagation()\n panel.classList.toggle('open')\n })\n\n root.appendChild(panel)\n root.appendChild(toggle)\n document.body.appendChild(root)\n }\n\n /** 创建普通字段行(label + content) */\n private createField(label: string, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n field.appendChild(lbl)\n field.appendChild(content)\n return field\n }\n\n /** 创建带操作按钮的字段行(label + action button + content) */\n private createFieldWithAction(label: string, action: HTMLElement, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const fieldHeader = document.createElement('div')\n fieldHeader.className = 'dbg-field-header'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n fieldHeader.appendChild(lbl)\n fieldHeader.appendChild(action)\n field.appendChild(fieldHeader)\n field.appendChild(content)\n return field\n }\n\n /** 恢复上次持久化的状态 */\n private async restoreState(): Promise<void> {\n const savedGw = localStorage.getItem(KEY_GATEWAY)\n const gw = (savedGw && this.gateways.includes(savedGw)) ? savedGw : this.gateways[0]\n this.gwSelect.setValue(gw)\n await this.loadServers(gw)\n\n const savedType = localStorage.getItem(KEY_TYPE)\n if (savedType) {\n this.typeSelect.setValue(savedType)\n this.renderServerOptions(savedType)\n }\n\n const savedServer = localStorage.getItem(KEY_SERVER)\n if (savedServer && this.servers.find((s) => s.key === savedServer)) {\n this.serverSelect.setValue(savedServer)\n await this.applyServer(savedServer)\n }\n }\n\n private async onGatewayChange(gw: string): Promise<void> {\n localStorage.setItem(KEY_GATEWAY, gw)\n this.gwVersion.textContent = ''\n await this.loadServers(gw)\n }\n\n private onTypeChange(type: string): void {\n localStorage.setItem(KEY_TYPE, type)\n this.renderServerOptions(type)\n }\n\n private async onServerChange(key: string): Promise<void> {\n localStorage.setItem(KEY_SERVER, key)\n await this.applyServer(key)\n }\n\n /** 从网关加载服务列表 */\n private async loadServers(gwUrl: string): Promise<void> {\n this.setHeader('loading')\n this.setFooter(T.loading(), '')\n try {\n const res = await _http.get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n timeout: this.options.timeout,\n })\n this.servers = res.data ?? []\n this.renderTypeOptions()\n this.setHeader('ok')\n this.setFooter(T.loaded(this.servers.length), 'ok')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.setHeader('err')\n this.setFooter(T.loadFailed(message), 'err')\n }\n }\n\n /** 渲染标签选项 */\n private renderTypeOptions(): void {\n const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]\n const opts = [{ value: '', label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]\n this.typeSelect.setOptions(opts, T.allTags())\n this.typeSelect.setValue('')\n this.renderServerOptions('')\n }\n\n /** 根据标签渲染服务选项 */\n private renderServerOptions(type: string): void {\n const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers\n this.serverSelect.setOptions(\n filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })),\n T.selectServer(),\n )\n this.renderCtxList({})\n }\n\n /** 切换目标服务,重新加载 Context 路由映射 */\n private async applyServer(key: string): Promise<void> {\n if (!key) { this.renderCtxList({}); return }\n const server = this.servers.find((s) => s.key === key)\n if (!server) return\n\n this.setFooter(T.switching(), '')\n try {\n await Context.load(server.origin, this.options.timeout)\n const res = await fetch(`${server.origin}/ngw/context`)\n const raw: Record<string, unknown> = res.ok ? await res.json() : {}\n const ctxMap: Record<string, string> = {}\n for (const [k, v] of Object.entries(raw)) {\n if (k !== '$info' && typeof v === 'string') ctxMap[k] = v\n }\n const info = Context.info\n if (info) this.gwVersion.textContent = `v${info.version}`\n this.renderCtxList(ctxMap)\n const count = Object.keys(ctxMap).length\n this.setFooter(T.routes(server.name, count), 'ok')\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this.setFooter(T.switchFailed(msg), 'err')\n }\n }\n\n /** 渲染路由列表 */\n private renderCtxList(ctxMap: Record<string, string>): void {\n this.currentCtxMap = ctxMap\n this.ctxList.innerHTML = ''\n this.ctxSearchInput.value = ''\n const entries = Object.entries(ctxMap)\n if (entries.length === 0) {\n const empty = document.createElement('div')\n empty.className = 'dbg-ctx-empty'\n empty.textContent = T.selectFirst()\n this.ctxList.appendChild(empty)\n return\n }\n entries.forEach(([k, v]) => {\n const item = document.createElement('div')\n item.className = 'dbg-ctx-item'\n item.dataset['key'] = k\n\n const keySpan = document.createElement('span')\n keySpan.className = 'dbg-ctx-key'\n keySpan.textContent = k\n keySpan.setAttribute('title', k)\n\n const valSpan = document.createElement('span')\n valSpan.className = 'dbg-ctx-val'\n valSpan.textContent = v\n valSpan.setAttribute('title', `${k} → ${v}`)\n\n const copyIcon = document.createElement('span')\n copyIcon.className = 'dbg-ctx-copy'\n copyIcon.textContent = '⎘'\n\n item.appendChild(keySpan)\n item.appendChild(valSpan)\n item.appendChild(copyIcon)\n item.addEventListener('click', () => this.copyCtxItem(item, k))\n this.ctxList.appendChild(item)\n })\n }\n\n /** 过滤路由列表 */\n private filterCtx(query: string): void {\n const q = query.toLowerCase()\n this.ctxList.querySelectorAll<HTMLElement>('.dbg-ctx-item').forEach((item) => {\n const key = item.dataset['key'] ?? ''\n item.classList.toggle('hidden', !!q && !key.toLowerCase().includes(q))\n })\n }\n\n /** 复制单个 ctx key */\n private copyCtxItem(item: HTMLElement, text: string): void {\n navigator.clipboard.writeText(text).then(() => {\n item.classList.add('copied')\n const icon = item.querySelector('.dbg-ctx-copy')\n if (icon) icon.textContent = '✓'\n setTimeout(() => {\n item.classList.remove('copied')\n const ic = item.querySelector('.dbg-ctx-copy')\n if (ic) ic.textContent = '⎘'\n }, 1500)\n this.setFooter(T.copied(text), 'ok')\n setTimeout(() => this.setFooter('', ''), 2000)\n })\n }\n\n /** 一键复制全部上下文为 JSON */\n private copyAllCtx(): void {\n const entries = Object.keys(this.currentCtxMap)\n if (entries.length === 0) return\n const json = JSON.stringify(this.currentCtxMap, null, 2)\n navigator.clipboard.writeText(json).then(() => {\n this.copyAllBtn.textContent = '✓ ' + T.copyAllDone()\n this.copyAllBtn.classList.add('done')\n setTimeout(() => {\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.classList.remove('done')\n }, 2000)\n this.setFooter(T.copyAllDone(), 'ok')\n setTimeout(() => this.setFooter('', ''), 2500)\n })\n }\n\n private setHeader(state: 'ok' | 'err' | 'loading'): void {\n this.headerDot.className = `dbg-header-dot${state !== 'ok' ? ` ${state}` : ''}`\n }\n\n private setFooter(msg: string, cls: '' | 'ok' | 'err'): void {\n this.footerEl.innerHTML = msg\n ? `<span class=\"dbg-footer-dot\"></span>${msg}`\n : ''\n this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ''}`\n }\n}\n"]} |
| 'use strict'; | ||
| Object.defineProperty(exports, '__esModule', { value: true }); | ||
| var chunkUNFUIZVY_cjs = require('./chunk-UNFUIZVY.cjs'); | ||
| var axios = require('axios'); | ||
| function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } | ||
| var axios__default = /*#__PURE__*/_interopDefault(axios); | ||
| // src/http/cancel.ts | ||
| var registry = /* @__PURE__ */ new Map(); | ||
| function Register(id) { | ||
| if (registry.has(id)) { | ||
| registry.get(id).abort(); | ||
| } | ||
| const controller = new AbortController(); | ||
| registry.set(id, controller); | ||
| return controller; | ||
| } | ||
| function Cancel(id) { | ||
| const controller = registry.get(id); | ||
| if (!controller) return false; | ||
| controller.abort(); | ||
| registry.delete(id); | ||
| return true; | ||
| } | ||
| function CancelAll() { | ||
| for (const controller of registry.values()) { | ||
| controller.abort(); | ||
| } | ||
| registry.clear(); | ||
| } | ||
| function Unregister(id) { | ||
| registry.delete(id); | ||
| } | ||
| function generateId() { | ||
| if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | ||
| return crypto.randomUUID(); | ||
| } | ||
| return `${Date.now()}-${Math.random().toString(36).slice(2)}`; | ||
| } | ||
| async function Request(config) { | ||
| const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config; | ||
| if (ctx && chunkUNFUIZVY_cjs.Context.loaded) { | ||
| const base = chunkUNFUIZVY_cjs.Context.get(ctx); | ||
| if (base) { | ||
| axiosConfig.baseURL = base; | ||
| } | ||
| } | ||
| if (!cache) { | ||
| const sep = (axiosConfig.url ?? "").includes("?") ? "&" : "?"; | ||
| axiosConfig.url = `${axiosConfig.url ?? ""}${sep}_=${Date.now()}`; | ||
| } | ||
| const cancelId = customCancelId ?? generateId(); | ||
| const controller = Register(cancelId); | ||
| axiosConfig.signal = controller.signal; | ||
| onCancelId?.(cancelId); | ||
| try { | ||
| const response = await axios__default.default.request({ ...axiosConfig }); | ||
| return response; | ||
| } catch (err) { | ||
| if (axios.isAxiosError(err)) { | ||
| return { | ||
| error: { | ||
| status: err.response?.status, | ||
| message: err.message, | ||
| data: err.response?.data | ||
| } | ||
| }; | ||
| } | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| return { error: { message } }; | ||
| } finally { | ||
| Unregister(cancelId); | ||
| } | ||
| } | ||
| function Get(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "GET", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "GET" }); | ||
| } | ||
| function Post(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "POST", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "POST" }); | ||
| } | ||
| function Put(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PUT", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PUT" }); | ||
| } | ||
| function Patch(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PATCH", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PATCH" }); | ||
| } | ||
| function Del(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "DELETE", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "DELETE" }); | ||
| } | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function () { return axios__default.default; } | ||
| }); | ||
| exports.Cancel = Cancel; | ||
| exports.CancelAll = CancelAll; | ||
| exports.Del = Del; | ||
| exports.Get = Get; | ||
| exports.Patch = Patch; | ||
| exports.Post = Post; | ||
| exports.Put = Put; | ||
| exports.Request = Request; | ||
| //# sourceMappingURL=chunk-UM4J7HF4.cjs.map | ||
| //# sourceMappingURL=chunk-UM4J7HF4.cjs.map |
| {"version":3,"sources":["../../src/http/cancel.ts","../../src/http/client.ts"],"names":["Context","axios","isAxiosError"],"mappings":"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","file":"chunk-UM4J7HF4.cjs","sourcesContent":["/** 模块私有注册表,避免全局 window 污染 */\nconst registry = new Map<string, AbortController>()\n\n/**\n * 注册一个请求取消控制器\n *\n * 若已存在相同 id 的控制器,先取消旧请求再注册新的。\n *\n * @param id 请求唯一标识\n * @returns 新创建的 AbortController\n *\n * @example\n * ```ts\n * import { Register, Cancel } from '@snack-kit/lib/http'\n *\n * const ctrl = Register('my-req')\n * // 请求进行中时手动取消\n * Cancel('my-req')\n * ```\n */\nexport function Register(id: string): AbortController {\n if (registry.has(id)) {\n registry.get(id)!.abort()\n }\n const controller = new AbortController()\n registry.set(id, controller)\n return controller\n}\n\n/**\n * 取消指定 id 的请求并从注册表移除\n *\n * @param id 请求唯一标识\n * @returns 是否找到并取消了请求\n *\n * @example\n * ```ts\n * import { Get, Cancel } from '@snack-kit/lib/http'\n *\n * let cancelId = ''\n * Get('/api/data', { onCancelId: (id) => { cancelId = id } })\n *\n * // 需要时取消\n * Cancel(cancelId)\n * ```\n */\nexport function Cancel(id: string): boolean {\n const controller = registry.get(id)\n if (!controller) return false\n controller.abort()\n registry.delete(id)\n return true\n}\n\n/**\n * 取消所有已注册的请求并清空注册表\n *\n * @example\n * ```ts\n * import { CancelAll } from '@snack-kit/lib/http'\n *\n * // 页面卸载时取消所有进行中的请求\n * window.addEventListener('beforeunload', () => CancelAll())\n * ```\n */\nexport function CancelAll(): void {\n for (const controller of registry.values()) {\n controller.abort()\n }\n registry.clear()\n}\n\n/**\n * 从注册表中移除指定 id(不触发取消,用于请求完成后内部清理)\n *\n * @param id 请求唯一标识\n */\nexport function Unregister(id: string): void {\n registry.delete(id)\n}\n","import axios, { isAxiosError } from 'axios'\nimport { Context } from './context'\nimport { Register, Unregister } from './cancel'\nimport type { HttpRequestConfig, HttpMethodConfig, HttpResult } from './types'\n\n/** 生成请求唯一 ID */\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/**\n * 核心请求函数,所有快捷方法基于此实现\n *\n * 自动处理:\n * - `ctx` → 从 HttpContext 查询 baseURL 并注入\n * - `cache: false`(默认)→ URL 附加 `?_=timestamp` 防缓存\n * - 请求取消 → 自动注册 AbortController,完成后自动清理\n * - 错误捕获 → 统一返回 `{ error }` 而非 throw\n *\n * @param config 请求配置\n * @returns 请求结果\n *\n * @example 基础用法\n * ```ts\n * import { Request } from '@snack-kit/lib/http'\n *\n * const result = await Request<{ id: number }>({ url: '/api/user', method: 'GET' })\n * if (result.error) {\n * console.error(result.error.message)\n * } else {\n * console.log(result.data)\n * }\n * ```\n *\n * @example 使用 ctx 自动注入 baseURL\n * ```ts\n * import { context, Request } from '@snack-kit/lib/http'\n *\n * await context.load({ 'user-svc': 'http://user.api.com' })\n * const result = await Request({ url: '/profile', method: 'GET', ctx: 'user-svc' })\n * // 实际请求:GET http://user.api.com/profile\n * ```\n */\nexport async function Request<T>(config: HttpRequestConfig): Promise<HttpResult<T>> {\n const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config\n\n // ctx 自动注入 baseURL\n if (ctx && Context.loaded) {\n const base = Context.get(ctx)\n if (base) {\n axiosConfig.baseURL = base\n }\n }\n\n // 防缓存:默认添加时间戳参数\n if (!cache) {\n const sep = (axiosConfig.url ?? '').includes('?') ? '&' : '?'\n axiosConfig.url = `${axiosConfig.url ?? ''}${sep}_=${Date.now()}`\n }\n\n // 注册取消控制器\n const cancelId = customCancelId ?? generateId()\n const controller = Register(cancelId)\n axiosConfig.signal = controller.signal\n onCancelId?.(cancelId)\n\n try {\n const response = await axios.request<T>({ ...axiosConfig })\n return response\n } catch (err) {\n if (isAxiosError(err)) {\n return {\n error: {\n status: err.response?.status,\n message: err.message,\n data: err.response?.data,\n },\n }\n }\n // AbortError 或其他非 axios 错误\n const message = err instanceof Error ? err.message : String(err)\n return { error: { message } }\n } finally {\n Unregister(cancelId)\n }\n}\n\n/**\n * 发起 GET 请求\n *\n * 支持两种调用方式:\n * - `Get(url, config?)` — 位置参数形式\n * - `Get(config)` — 纯配置对象形式(`method` 已预置,无需传入)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Get<{ name: string }>('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Get({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n *\n * @example 获取 cancelId 以便主动取消\n * ```ts\n * let cancelId = ''\n * Get('/api/list', { onCancelId: (id) => { cancelId = id } })\n * Cancel(cancelId)\n * ```\n */\nexport function Get<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'GET', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'GET' })\n}\n\n/**\n * 发起 POST 请求\n *\n * 支持两种调用方式:\n * - `Post(url, data?, config?)` — 位置参数形式\n * - `Post(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Post<{ token: string }>('/api/login', {\n * username: 'admin',\n * password: '123456',\n * })\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Post({\n * url: '/api/login',\n * data: { username: 'admin', password: '123456' },\n * ctx: 'user-svc',\n * })\n * ```\n */\nexport function Post<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'POST', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'POST' })\n}\n\n/**\n * 发起 PUT 请求\n *\n * 支持两种调用方式:\n * - `Put(url, data?, config?)` — 位置参数形式\n * - `Put(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Put('/api/user/1', { name: 'Alice' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Put({ url: '/api/user/1', data: { name: 'Alice' }, ctx: 'user-svc' })\n * ```\n */\nexport function Put<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PUT', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PUT' })\n}\n\n/**\n * 发起 PATCH 请求\n *\n * 支持两种调用方式:\n * - `Patch(url, data?, config?)` — 位置参数形式\n * - `Patch(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Patch('/api/user/1', { avatar: 'https://...' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Patch({ url: '/api/user/1', data: { avatar: 'https://...' }, ctx: 'user-svc' })\n * ```\n */\nexport function Patch<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PATCH', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PATCH' })\n}\n\n/**\n * 发起 DELETE 请求\n *\n * 支持两种调用方式:\n * - `Del(url, config?)` — 位置参数形式\n * - `Del(config)` — 纯配置对象形式(`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Del('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Del({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n */\nexport function Del<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'DELETE', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'DELETE' })\n}\n"]} |
| import { Context } from './chunk-M5R2NQYU.js'; | ||
| import axios, { isAxiosError } from 'axios'; | ||
| export { default } from 'axios'; | ||
| // src/http/cancel.ts | ||
| var registry = /* @__PURE__ */ new Map(); | ||
| function Register(id) { | ||
| if (registry.has(id)) { | ||
| registry.get(id).abort(); | ||
| } | ||
| const controller = new AbortController(); | ||
| registry.set(id, controller); | ||
| return controller; | ||
| } | ||
| function Cancel(id) { | ||
| const controller = registry.get(id); | ||
| if (!controller) return false; | ||
| controller.abort(); | ||
| registry.delete(id); | ||
| return true; | ||
| } | ||
| function CancelAll() { | ||
| for (const controller of registry.values()) { | ||
| controller.abort(); | ||
| } | ||
| registry.clear(); | ||
| } | ||
| function Unregister(id) { | ||
| registry.delete(id); | ||
| } | ||
| function generateId() { | ||
| if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { | ||
| return crypto.randomUUID(); | ||
| } | ||
| return `${Date.now()}-${Math.random().toString(36).slice(2)}`; | ||
| } | ||
| async function Request(config) { | ||
| const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config; | ||
| if (ctx && Context.loaded) { | ||
| const base = Context.get(ctx); | ||
| if (base) { | ||
| axiosConfig.baseURL = base; | ||
| } | ||
| } | ||
| if (!cache) { | ||
| const sep = (axiosConfig.url ?? "").includes("?") ? "&" : "?"; | ||
| axiosConfig.url = `${axiosConfig.url ?? ""}${sep}_=${Date.now()}`; | ||
| } | ||
| const cancelId = customCancelId ?? generateId(); | ||
| const controller = Register(cancelId); | ||
| axiosConfig.signal = controller.signal; | ||
| onCancelId?.(cancelId); | ||
| try { | ||
| const response = await axios.request({ ...axiosConfig }); | ||
| return response; | ||
| } catch (err) { | ||
| if (isAxiosError(err)) { | ||
| return { | ||
| error: { | ||
| status: err.response?.status, | ||
| message: err.message, | ||
| data: err.response?.data | ||
| } | ||
| }; | ||
| } | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| return { error: { message } }; | ||
| } finally { | ||
| Unregister(cancelId); | ||
| } | ||
| } | ||
| function Get(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "GET", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "GET" }); | ||
| } | ||
| function Post(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "POST", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "POST" }); | ||
| } | ||
| function Put(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PUT", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PUT" }); | ||
| } | ||
| function Patch(urlOrConfig, data, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "PATCH", url: urlOrConfig, data }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "PATCH" }); | ||
| } | ||
| function Del(urlOrConfig, config) { | ||
| if (typeof urlOrConfig === "string") { | ||
| return Request({ ...config, method: "DELETE", url: urlOrConfig }); | ||
| } | ||
| return Request({ ...urlOrConfig, method: "DELETE" }); | ||
| } | ||
| export { Cancel, CancelAll, Del, Get, Patch, Post, Put, Request }; | ||
| //# sourceMappingURL=chunk-2YYI2T2B.js.map | ||
| //# sourceMappingURL=chunk-2YYI2T2B.js.map |
| {"version":3,"sources":["../../src/http/cancel.ts","../../src/http/client.ts"],"names":[],"mappings":"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","file":"chunk-2YYI2T2B.js","sourcesContent":["/** 模块私有注册表,避免全局 window 污染 */\nconst registry = new Map<string, AbortController>()\n\n/**\n * 注册一个请求取消控制器\n *\n * 若已存在相同 id 的控制器,先取消旧请求再注册新的。\n *\n * @param id 请求唯一标识\n * @returns 新创建的 AbortController\n *\n * @example\n * ```ts\n * import { Register, Cancel } from '@snack-kit/lib/http'\n *\n * const ctrl = Register('my-req')\n * // 请求进行中时手动取消\n * Cancel('my-req')\n * ```\n */\nexport function Register(id: string): AbortController {\n if (registry.has(id)) {\n registry.get(id)!.abort()\n }\n const controller = new AbortController()\n registry.set(id, controller)\n return controller\n}\n\n/**\n * 取消指定 id 的请求并从注册表移除\n *\n * @param id 请求唯一标识\n * @returns 是否找到并取消了请求\n *\n * @example\n * ```ts\n * import { Get, Cancel } from '@snack-kit/lib/http'\n *\n * let cancelId = ''\n * Get('/api/data', { onCancelId: (id) => { cancelId = id } })\n *\n * // 需要时取消\n * Cancel(cancelId)\n * ```\n */\nexport function Cancel(id: string): boolean {\n const controller = registry.get(id)\n if (!controller) return false\n controller.abort()\n registry.delete(id)\n return true\n}\n\n/**\n * 取消所有已注册的请求并清空注册表\n *\n * @example\n * ```ts\n * import { CancelAll } from '@snack-kit/lib/http'\n *\n * // 页面卸载时取消所有进行中的请求\n * window.addEventListener('beforeunload', () => CancelAll())\n * ```\n */\nexport function CancelAll(): void {\n for (const controller of registry.values()) {\n controller.abort()\n }\n registry.clear()\n}\n\n/**\n * 从注册表中移除指定 id(不触发取消,用于请求完成后内部清理)\n *\n * @param id 请求唯一标识\n */\nexport function Unregister(id: string): void {\n registry.delete(id)\n}\n","import axios, { isAxiosError } from 'axios'\nimport { Context } from './context'\nimport { Register, Unregister } from './cancel'\nimport type { HttpRequestConfig, HttpMethodConfig, HttpResult } from './types'\n\n/** 生成请求唯一 ID */\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/**\n * 核心请求函数,所有快捷方法基于此实现\n *\n * 自动处理:\n * - `ctx` → 从 HttpContext 查询 baseURL 并注入\n * - `cache: false`(默认)→ URL 附加 `?_=timestamp` 防缓存\n * - 请求取消 → 自动注册 AbortController,完成后自动清理\n * - 错误捕获 → 统一返回 `{ error }` 而非 throw\n *\n * @param config 请求配置\n * @returns 请求结果\n *\n * @example 基础用法\n * ```ts\n * import { Request } from '@snack-kit/lib/http'\n *\n * const result = await Request<{ id: number }>({ url: '/api/user', method: 'GET' })\n * if (result.error) {\n * console.error(result.error.message)\n * } else {\n * console.log(result.data)\n * }\n * ```\n *\n * @example 使用 ctx 自动注入 baseURL\n * ```ts\n * import { context, Request } from '@snack-kit/lib/http'\n *\n * await context.load({ 'user-svc': 'http://user.api.com' })\n * const result = await Request({ url: '/profile', method: 'GET', ctx: 'user-svc' })\n * // 实际请求:GET http://user.api.com/profile\n * ```\n */\nexport async function Request<T>(config: HttpRequestConfig): Promise<HttpResult<T>> {\n const { ctx, cancelId: customCancelId, onCancelId, cache = false, ...axiosConfig } = config\n\n // ctx 自动注入 baseURL\n if (ctx && Context.loaded) {\n const base = Context.get(ctx)\n if (base) {\n axiosConfig.baseURL = base\n }\n }\n\n // 防缓存:默认添加时间戳参数\n if (!cache) {\n const sep = (axiosConfig.url ?? '').includes('?') ? '&' : '?'\n axiosConfig.url = `${axiosConfig.url ?? ''}${sep}_=${Date.now()}`\n }\n\n // 注册取消控制器\n const cancelId = customCancelId ?? generateId()\n const controller = Register(cancelId)\n axiosConfig.signal = controller.signal\n onCancelId?.(cancelId)\n\n try {\n const response = await axios.request<T>({ ...axiosConfig })\n return response\n } catch (err) {\n if (isAxiosError(err)) {\n return {\n error: {\n status: err.response?.status,\n message: err.message,\n data: err.response?.data,\n },\n }\n }\n // AbortError 或其他非 axios 错误\n const message = err instanceof Error ? err.message : String(err)\n return { error: { message } }\n } finally {\n Unregister(cancelId)\n }\n}\n\n/**\n * 发起 GET 请求\n *\n * 支持两种调用方式:\n * - `Get(url, config?)` — 位置参数形式\n * - `Get(config)` — 纯配置对象形式(`method` 已预置,无需传入)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Get<{ name: string }>('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Get({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n *\n * @example 获取 cancelId 以便主动取消\n * ```ts\n * let cancelId = ''\n * Get('/api/list', { onCancelId: (id) => { cancelId = id } })\n * Cancel(cancelId)\n * ```\n */\nexport function Get<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Get<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'GET', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'GET' })\n}\n\n/**\n * 发起 POST 请求\n *\n * 支持两种调用方式:\n * - `Post(url, data?, config?)` — 位置参数形式\n * - `Post(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * const { data, error } = await Post<{ token: string }>('/api/login', {\n * username: 'admin',\n * password: '123456',\n * })\n * ```\n *\n * @example 配置对象\n * ```ts\n * const { data } = await Post({\n * url: '/api/login',\n * data: { username: 'admin', password: '123456' },\n * ctx: 'user-svc',\n * })\n * ```\n */\nexport function Post<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Post<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'POST', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'POST' })\n}\n\n/**\n * 发起 PUT 请求\n *\n * 支持两种调用方式:\n * - `Put(url, data?, config?)` — 位置参数形式\n * - `Put(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Put('/api/user/1', { name: 'Alice' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Put({ url: '/api/user/1', data: { name: 'Alice' }, ctx: 'user-svc' })\n * ```\n */\nexport function Put<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Put<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PUT', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PUT' })\n}\n\n/**\n * 发起 PATCH 请求\n *\n * 支持两种调用方式:\n * - `Patch(url, data?, config?)` — 位置参数形式\n * - `Patch(config)` — 纯配置对象形式(`data` 通过 `config.data` 传入,`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Patch('/api/user/1', { avatar: 'https://...' })\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Patch({ url: '/api/user/1', data: { avatar: 'https://...' }, ctx: 'user-svc' })\n * ```\n */\nexport function Patch<T>(url: string, data?: unknown, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Patch<T>(\n urlOrConfig: string | HttpMethodConfig,\n data?: unknown,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'PATCH', url: urlOrConfig, data })\n }\n return Request<T>({ ...urlOrConfig, method: 'PATCH' })\n}\n\n/**\n * 发起 DELETE 请求\n *\n * 支持两种调用方式:\n * - `Del(url, config?)` — 位置参数形式\n * - `Del(config)` — 纯配置对象形式(`method` 已预置)\n *\n * @example 位置参数\n * ```ts\n * await Del('/api/user/1')\n * ```\n *\n * @example 配置对象\n * ```ts\n * await Del({ url: '/api/user/1', ctx: 'user-svc' })\n * ```\n */\nexport function Del<T>(url: string, config?: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(config: HttpMethodConfig): Promise<HttpResult<T>>\nexport function Del<T>(\n urlOrConfig: string | HttpMethodConfig,\n config?: HttpMethodConfig,\n): Promise<HttpResult<T>> {\n if (typeof urlOrConfig === 'string') {\n return Request<T>({ ...config, method: 'DELETE', url: urlOrConfig })\n }\n return Request<T>({ ...urlOrConfig, method: 'DELETE' })\n}\n"]} |
| import { DEBUGGER_ACTIVE_KEY, Context } from './chunk-M5R2NQYU.js'; | ||
| import axios from 'axios'; | ||
| var _http = axios.create(); | ||
| var STORAGE_PREFIX = "__snackkit_debugger__"; | ||
| var KEY_GATEWAY = `${STORAGE_PREFIX}gateway`; | ||
| var KEY_TYPE = `${STORAGE_PREFIX}type`; | ||
| var KEY_SERVER = `${STORAGE_PREFIX}server`; | ||
| function isChinese() { | ||
| return /^zh/i.test(navigator.language ?? ""); | ||
| } | ||
| var T = { | ||
| gateway: () => isChinese() ? "\u7F51\u5173" : "Gateway", | ||
| tag: () => isChinese() ? "\u6807\u7B7E" : "Tag", | ||
| server: () => isChinese() ? "\u670D\u52A1" : "Server", | ||
| ctx: () => isChinese() ? "\u4E0A\u4E0B\u6587" : "Ctx", | ||
| ctxLabel: () => isChinese() ? "\u4E0A\u4E0B\u6587 \u2014 \u70B9\u51FB\u884C\u590D\u5236 key" : "Ctx \u2014 click row to copy key", | ||
| search: () => isChinese() ? "\u641C\u7D22..." : "Search...", | ||
| noMatch: () => isChinese() ? "\u65E0\u5339\u914D\u9879" : "No results", | ||
| allTags: () => isChinese() ? "\u5168\u90E8\u6807\u7B7E" : "All tags", | ||
| selectGateway: () => isChinese() ? "\u9009\u62E9\u7F51\u5173" : "Select gateway", | ||
| selectServer: () => isChinese() ? "\u9009\u62E9\u670D\u52A1" : "Select server", | ||
| filterCtx: () => isChinese() ? "\u8FC7\u6EE4\u8DEF\u7531..." : "Filter routes...", | ||
| selectFirst: () => isChinese() ? "\u2190 \u8BF7\u5148\u9009\u62E9\u670D\u52A1" : "\u2190 Select a server first", | ||
| loading: () => isChinese() ? "\u52A0\u8F7D\u670D\u52A1\u5217\u8868..." : "Loading...", | ||
| loadFailed: (m) => isChinese() ? `\u52A0\u8F7D\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| loaded: (n) => isChinese() ? `\u5DF2\u52A0\u8F7D ${n} \u4E2A\u670D\u52A1` : `${n} servers loaded`, | ||
| switching: () => isChinese() ? "\u5207\u6362\u670D\u52A1\u4E2D..." : "Switching...", | ||
| switchFailed: (m) => isChinese() ? `\u5207\u6362\u5931\u8D25: ${m}` : `Failed: ${m}`, | ||
| routes: (name, n) => isChinese() ? `${name} \xB7 ${n} \u6761\u8DEF\u7531` : `${name} \xB7 ${n} routes`, | ||
| copied: (k) => isChinese() ? `\u5DF2\u590D\u5236: ${k}` : `Copied: ${k}`, | ||
| copyAll: () => isChinese() ? "\u590D\u5236\u5168\u90E8 JSON" : "Copy all JSON", | ||
| copyAllDone: () => isChinese() ? "\u5DF2\u590D\u5236\u4E3A JSON" : "Copied as JSON" | ||
| }; | ||
| var PANEL_STYLE = ` | ||
| #__snackkit_debugger__ { | ||
| position: fixed; | ||
| bottom: 20px; | ||
| right: 20px; | ||
| z-index: 99999; | ||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif; | ||
| font-size: 13px; | ||
| color: #b8ccec; | ||
| user-select: none; | ||
| } | ||
| /* \u2500\u2500 \u6D6E\u52A8\u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-toggle { | ||
| width: 42px; | ||
| height: 42px; | ||
| border-radius: 50%; | ||
| background: linear-gradient(135deg, #4f8ef7, #6d6ff5); | ||
| border: none; | ||
| cursor: pointer; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| box-shadow: 0 4px 14px rgba(79,142,247,0.45); | ||
| margin-left: auto; | ||
| font-size: 20px; | ||
| transition: transform 0.15s, box-shadow 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:hover { | ||
| transform: scale(1.08); | ||
| box-shadow: 0 6px 20px rgba(79,142,247,0.55); | ||
| } | ||
| #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); } | ||
| /* \u2500\u2500 \u9762\u677F\u4E3B\u4F53 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-panel { | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 12px; | ||
| width: 440px; | ||
| margin-bottom: 10px; | ||
| box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05); | ||
| overflow: visible; | ||
| opacity: 0; | ||
| transform: translateY(8px) scale(0.98); | ||
| pointer-events: none; | ||
| transition: opacity 0.18s ease, transform 0.18s ease; | ||
| } | ||
| #__snackkit_debugger__ .dbg-panel.open { | ||
| opacity: 1; | ||
| transform: translateY(0) scale(1); | ||
| pointer-events: auto; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F Header \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| padding: 10px 14px; | ||
| background: #171e2e; | ||
| border-bottom: 1px solid #2d3a55; | ||
| border-radius: 12px 12px 0 0; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-left { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 7px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot { | ||
| width: 7px; | ||
| height: 7px; | ||
| border-radius: 50%; | ||
| background: #34d399; | ||
| box-shadow: 0 0 6px rgba(52,211,153,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.loading { | ||
| background: #fbbf24; | ||
| box-shadow: 0 0 6px rgba(251,191,36,0.6); | ||
| animation: dbg-pulse 1s ease-in-out infinite; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-dot.err { | ||
| background: #f87171; | ||
| box-shadow: 0 0 6px rgba(248,113,113,0.6); | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-title { | ||
| font-size: 12px; | ||
| font-weight: 600; | ||
| color: #dce8fa; | ||
| letter-spacing: 0.03em; | ||
| } | ||
| #__snackkit_debugger__ .dbg-header-meta { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| } | ||
| /* \u2500\u2500 \u9762\u677F\u5185\u5BB9\u533A \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-body { | ||
| padding: 12px 14px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 10px; | ||
| height: 420px; | ||
| overflow: visible; | ||
| } | ||
| /* \u2500\u2500 \u5B57\u6BB5\u884C \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-field { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-field-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| } | ||
| #__snackkit_debugger__ .dbg-label { | ||
| font-size: 10px; | ||
| font-weight: 600; | ||
| color: #5e72a0; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.07em; | ||
| } | ||
| /* \u2500\u2500 \u4E00\u952E\u590D\u5236\u5168\u90E8 JSON \u6309\u94AE \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-copy-all { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| background: transparent; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 4px; | ||
| padding: 2px 7px; | ||
| color: #5e72a0; | ||
| font-size: 10px; | ||
| font-family: inherit; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, color 0.15s, background 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all:hover { | ||
| border-color: #4f8ef7; | ||
| color: #7aacfa; | ||
| background: rgba(79,142,247,0.08); | ||
| } | ||
| #__snackkit_debugger__ .dbg-copy-all.done { | ||
| border-color: #34d399; | ||
| color: #34d399; | ||
| } | ||
| /* \u2500\u2500 \u81EA\u5B9A\u4E49\u4E0B\u62C9 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-select { | ||
| position: relative; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| gap: 6px; | ||
| background: #252f45; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| padding: 6px 10px; | ||
| cursor: pointer; | ||
| transition: border-color 0.15s, background 0.15s; | ||
| min-height: 32px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger { | ||
| border-color: #4f8ef7; | ||
| background: #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val { | ||
| flex: 1; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| font-size: 12px; | ||
| color: #b8ccec; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-arrow { | ||
| color: #3d5070; | ||
| font-size: 10px; | ||
| flex-shrink: 0; | ||
| transition: transform 0.15s, color 0.15s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow { | ||
| transform: rotate(180deg); | ||
| color: #4f8ef7; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-dropdown { | ||
| position: absolute; | ||
| bottom: calc(100% + 4px); | ||
| left: 0; | ||
| right: 0; | ||
| background: #1e2638; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 8px; | ||
| box-shadow: 0 -8px 24px rgba(0,0,0,0.35); | ||
| overflow: hidden; | ||
| opacity: 0; | ||
| transform: translateY(4px); | ||
| pointer-events: none; | ||
| transition: opacity 0.14s, transform 0.14s; | ||
| z-index: 100000; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown { | ||
| opacity: 1; | ||
| transform: translateY(0); | ||
| pointer-events: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; } | ||
| #__snackkit_debugger__ .dbg-select-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-select-list { | ||
| max-height: 160px; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-select-option { | ||
| padding: 7px 10px; | ||
| cursor: pointer; | ||
| font-size: 12px; | ||
| color: #8aa4c8; | ||
| transition: background 0.1s, color 0.1s; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; } | ||
| #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; } | ||
| #__snackkit_debugger__ .dbg-select-option.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-select-empty { | ||
| padding: 12px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u8DEF\u7531\u5217\u8868 \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-ctx-field { | ||
| flex: 1; | ||
| min-height: 0; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 5px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-wrap { | ||
| flex: 1; | ||
| min-height: 0; | ||
| border: 1px solid #2d3a55; | ||
| border-radius: 6px; | ||
| overflow: hidden; | ||
| background: #252f45; | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 6px 10px; | ||
| border-bottom: 1px solid #2d3a55; | ||
| background: #1e2638; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input { | ||
| flex: 1; | ||
| background: transparent; | ||
| border: none; | ||
| outline: none; | ||
| color: #b8ccec; | ||
| font-size: 12px; | ||
| font-family: inherit; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; } | ||
| #__snackkit_debugger__ .dbg-ctx-list { | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow-y: auto; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; } | ||
| #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; } | ||
| #__snackkit_debugger__ .dbg-ctx-item { | ||
| padding: 5px 10px; | ||
| cursor: pointer; | ||
| display: grid; | ||
| grid-template-columns: minmax(80px, 148px) 1fr auto; | ||
| gap: 8px; | ||
| align-items: center; | ||
| border-bottom: 1px solid #1e2638; | ||
| transition: background 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; } | ||
| #__snackkit_debugger__ .dbg-ctx-key { | ||
| font-size: 12px; | ||
| color: #7aacfa; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-val { | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| white-space: nowrap; | ||
| transition: color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; } | ||
| #__snackkit_debugger__ .dbg-ctx-copy { | ||
| font-size: 11px; | ||
| color: #3d5070; | ||
| opacity: 0; | ||
| flex-shrink: 0; | ||
| transition: opacity 0.1s, color 0.1s; | ||
| } | ||
| #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; } | ||
| #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; } | ||
| #__snackkit_debugger__ .dbg-ctx-empty { | ||
| padding: 16px 10px; | ||
| font-size: 12px; | ||
| color: #3d5070; | ||
| text-align: center; | ||
| } | ||
| /* \u2500\u2500 \u5E95\u90E8\u72B6\u6001\u680F \u2500\u2500 */ | ||
| #__snackkit_debugger__ .dbg-footer { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 6px; | ||
| padding: 7px 14px; | ||
| background: #171e2e; | ||
| border-top: 1px solid #2d3a55; | ||
| border-radius: 0 0 12px 12px; | ||
| font-size: 11px; | ||
| color: #4d6080; | ||
| min-height: 30px; | ||
| } | ||
| #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; } | ||
| #__snackkit_debugger__ .dbg-footer.err { color: #f87171; } | ||
| #__snackkit_debugger__ .dbg-footer-dot { | ||
| width: 4px; | ||
| height: 4px; | ||
| border-radius: 50%; | ||
| background: currentColor; | ||
| flex-shrink: 0; | ||
| } | ||
| @keyframes dbg-pulse { | ||
| 0%, 100% { opacity: 1; } | ||
| 50% { opacity: 0.35; } | ||
| } | ||
| `; | ||
| var SearchSelect = class { | ||
| constructor(onChange) { | ||
| this.options = []; | ||
| this.selected = ""; | ||
| this.onChange = onChange; | ||
| this.el = document.createElement("div"); | ||
| this.el.className = "dbg-select"; | ||
| this.trigger = document.createElement("div"); | ||
| this.trigger.className = "dbg-select-trigger"; | ||
| this.valEl = document.createElement("span"); | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| const arrow = document.createElement("span"); | ||
| arrow.className = "dbg-select-arrow"; | ||
| arrow.textContent = "\u25BE"; | ||
| this.trigger.appendChild(this.valEl); | ||
| this.trigger.appendChild(arrow); | ||
| this.dropdown = document.createElement("div"); | ||
| this.dropdown.className = "dbg-select-dropdown"; | ||
| const searchWrap = document.createElement("div"); | ||
| searchWrap.className = "dbg-select-search"; | ||
| const searchIcon = document.createElement("span"); | ||
| searchIcon.className = "dbg-select-search-icon"; | ||
| searchIcon.textContent = "\u2315"; | ||
| this.searchInput = document.createElement("input"); | ||
| this.searchInput.type = "text"; | ||
| this.searchInput.placeholder = T.search(); | ||
| searchWrap.appendChild(searchIcon); | ||
| searchWrap.appendChild(this.searchInput); | ||
| this.listEl = document.createElement("div"); | ||
| this.listEl.className = "dbg-select-list"; | ||
| this.dropdown.appendChild(searchWrap); | ||
| this.dropdown.appendChild(this.listEl); | ||
| this.el.appendChild(this.trigger); | ||
| this.el.appendChild(this.dropdown); | ||
| this.bindEvents(); | ||
| } | ||
| bindEvents() { | ||
| this.trigger.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| const isOpen = this.el.classList.contains("open"); | ||
| document.querySelectorAll("#__snackkit_debugger__ .dbg-select.open").forEach((el) => { | ||
| if (el !== this.el) el.classList.remove("open"); | ||
| }); | ||
| this.el.classList.toggle("open", !isOpen); | ||
| if (!isOpen) { | ||
| this.searchInput.value = ""; | ||
| this.filterOptions(""); | ||
| setTimeout(() => this.searchInput.focus(), 50); | ||
| } | ||
| }); | ||
| this.searchInput.addEventListener("input", () => { | ||
| this.filterOptions(this.searchInput.value); | ||
| }); | ||
| this.searchInput.addEventListener("click", (e) => e.stopPropagation()); | ||
| document.addEventListener("click", () => { | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| } | ||
| filterOptions(query) { | ||
| const q = query.toLowerCase(); | ||
| let visibleCount = 0; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((opt) => { | ||
| const match = !q || opt.textContent.toLowerCase().includes(q); | ||
| opt.classList.toggle("hidden", !match); | ||
| if (match) visibleCount++; | ||
| }); | ||
| let emptyEl = this.listEl.querySelector(".dbg-select-empty"); | ||
| if (visibleCount === 0) { | ||
| if (!emptyEl) { | ||
| emptyEl = document.createElement("div"); | ||
| emptyEl.className = "dbg-select-empty"; | ||
| emptyEl.textContent = T.noMatch(); | ||
| this.listEl.appendChild(emptyEl); | ||
| } | ||
| } else { | ||
| emptyEl?.remove(); | ||
| } | ||
| } | ||
| /** 更新选项列表 */ | ||
| setOptions(options, placeholder) { | ||
| this.options = options; | ||
| this.listEl.innerHTML = ""; | ||
| this.valEl.textContent = placeholder; | ||
| this.valEl.className = "dbg-select-val placeholder"; | ||
| this.selected = ""; | ||
| options.forEach(({ value, label }) => { | ||
| const opt = document.createElement("div"); | ||
| opt.className = "dbg-select-option"; | ||
| opt.dataset["value"] = value; | ||
| opt.textContent = label; | ||
| opt.setAttribute("title", label); | ||
| opt.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.select(value, label); | ||
| this.el.classList.remove("open"); | ||
| }); | ||
| this.listEl.appendChild(opt); | ||
| }); | ||
| } | ||
| /** 选中某个值(不触发 onChange) */ | ||
| setValue(value) { | ||
| const opt = this.options.find((o) => o.value === value); | ||
| if (opt) this.select(opt.value, opt.label, false); | ||
| } | ||
| select(value, label, emit = true) { | ||
| this.selected = value; | ||
| this.valEl.textContent = label; | ||
| this.valEl.className = "dbg-select-val"; | ||
| this.listEl.querySelectorAll(".dbg-select-option").forEach((el) => { | ||
| el.classList.toggle("active", el.dataset["value"] === value); | ||
| }); | ||
| if (emit) this.onChange(value); | ||
| } | ||
| getValue() { | ||
| return this.selected; | ||
| } | ||
| getElement() { | ||
| return this.el; | ||
| } | ||
| }; | ||
| var Debugger = class _Debugger { | ||
| constructor(options) { | ||
| this.servers = []; | ||
| this.currentCtxMap = {}; | ||
| this.options = options; | ||
| this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]; | ||
| } | ||
| /** | ||
| * 初始化并渲染调试面板 | ||
| * | ||
| * 支持两种调用形式: | ||
| * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间 | ||
| * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }` | ||
| * | ||
| * 调用后在页面右下角插入浮动按钮,点击展开调试面板。 | ||
| * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。 | ||
| * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。 | ||
| * | ||
| * @param options 配置对象 或 网关 URL 数组 | ||
| * | ||
| * @example 配置对象形式 | ||
| * ```ts | ||
| * import { Debugger } from '@snack-kit/lib/debugger' | ||
| * | ||
| * await Debugger.init({ | ||
| * gateways: [ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ], | ||
| * timeout: 5000, | ||
| * }) | ||
| * ``` | ||
| * | ||
| * @example 数组简写形式 | ||
| * ```ts | ||
| * await Debugger.init([ | ||
| * 'http://dev-gateway.example.com', | ||
| * 'http://test-gateway.example.com', | ||
| * ]) | ||
| * ``` | ||
| * | ||
| * @example 仅在非生产环境加载 | ||
| * ```ts | ||
| * if (import.meta.env.DEV) { | ||
| * const { Debugger } = await import('@snack-kit/lib/debugger') | ||
| * await Debugger.init(['http://dev-gateway.example.com']) | ||
| * } | ||
| * ``` | ||
| */ | ||
| static async init(options) { | ||
| globalThis[DEBUGGER_ACTIVE_KEY] = true; | ||
| const normalized = Array.isArray(options) ? { gateways: options } : options; | ||
| const instance = new _Debugger({ timeout: 1e4, ...normalized }); | ||
| instance.injectStyle(); | ||
| instance.buildDOM(); | ||
| await instance.restoreState(); | ||
| return instance; | ||
| } | ||
| /** 注入内联样式 */ | ||
| injectStyle() { | ||
| if (document.getElementById("__snackkit_debugger_style__")) return; | ||
| const style = document.createElement("style"); | ||
| style.id = "__snackkit_debugger_style__"; | ||
| style.textContent = PANEL_STYLE; | ||
| document.head.appendChild(style); | ||
| } | ||
| /** 构建 DOM 结构 */ | ||
| buildDOM() { | ||
| const root = document.createElement("div"); | ||
| root.id = "__snackkit_debugger__"; | ||
| const panel = document.createElement("div"); | ||
| panel.className = "dbg-panel"; | ||
| this.panelEl = panel; | ||
| const header = document.createElement("div"); | ||
| header.className = "dbg-header"; | ||
| const headerLeft = document.createElement("div"); | ||
| headerLeft.className = "dbg-header-left"; | ||
| this.headerDot = document.createElement("span"); | ||
| this.headerDot.className = "dbg-header-dot loading"; | ||
| const headerTitle = document.createElement("span"); | ||
| headerTitle.className = "dbg-header-title"; | ||
| headerTitle.textContent = "Snack Kit Debugger"; | ||
| headerLeft.appendChild(this.headerDot); | ||
| headerLeft.appendChild(headerTitle); | ||
| header.appendChild(headerLeft); | ||
| panel.appendChild(header); | ||
| const body = document.createElement("div"); | ||
| body.className = "dbg-body"; | ||
| this.gwVersion = document.createElement("span"); | ||
| this.gwVersion.className = "dbg-header-meta"; | ||
| this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val)); | ||
| this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway()); | ||
| body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement())); | ||
| this.typeSelect = new SearchSelect((val) => this.onTypeChange(val)); | ||
| body.appendChild(this.createField(T.tag(), this.typeSelect.getElement())); | ||
| this.serverSelect = new SearchSelect((val) => this.onServerChange(val)); | ||
| body.appendChild(this.createField(T.server(), this.serverSelect.getElement())); | ||
| const ctxWrap = document.createElement("div"); | ||
| ctxWrap.className = "dbg-ctx-wrap"; | ||
| const ctxSearch = document.createElement("div"); | ||
| ctxSearch.className = "dbg-ctx-search"; | ||
| const ctxSearchIcon = document.createElement("span"); | ||
| ctxSearchIcon.className = "dbg-select-search-icon"; | ||
| ctxSearchIcon.textContent = "\u2315"; | ||
| this.ctxSearchInput = document.createElement("input"); | ||
| this.ctxSearchInput.type = "text"; | ||
| this.ctxSearchInput.placeholder = T.filterCtx(); | ||
| this.ctxSearchInput.addEventListener("input", () => this.filterCtx(this.ctxSearchInput.value)); | ||
| ctxSearch.appendChild(ctxSearchIcon); | ||
| ctxSearch.appendChild(this.ctxSearchInput); | ||
| this.ctxList = document.createElement("div"); | ||
| this.ctxList.className = "dbg-ctx-list"; | ||
| ctxWrap.appendChild(ctxSearch); | ||
| ctxWrap.appendChild(this.ctxList); | ||
| this.copyAllBtn = document.createElement("button"); | ||
| this.copyAllBtn.className = "dbg-copy-all"; | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.setAttribute("title", isChinese() ? "\u5C06\u5168\u90E8\u4E0A\u4E0B\u6587\u590D\u5236\u4E3A JSON\uFF0C\u53EF\u4F5C\u4E3A Context.load({...}) \u7684\u53C2\u6570" : "Copy all ctx entries as JSON for Context.load({...})"); | ||
| this.copyAllBtn.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| this.copyAllCtx(); | ||
| }); | ||
| const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap); | ||
| ctxField.classList.add("dbg-ctx-field"); | ||
| body.appendChild(ctxField); | ||
| panel.appendChild(body); | ||
| const footer = document.createElement("div"); | ||
| footer.className = "dbg-footer"; | ||
| this.footerEl = footer; | ||
| panel.appendChild(footer); | ||
| const toggle = document.createElement("button"); | ||
| toggle.className = "dbg-toggle"; | ||
| toggle.textContent = "\u{1F41B}"; | ||
| toggle.setAttribute("title", "Snack Debugger"); | ||
| toggle.addEventListener("click", (e) => { | ||
| e.stopPropagation(); | ||
| panel.classList.toggle("open"); | ||
| }); | ||
| root.appendChild(panel); | ||
| root.appendChild(toggle); | ||
| document.body.appendChild(root); | ||
| } | ||
| /** 创建普通字段行(label + content) */ | ||
| createField(label, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| field.appendChild(lbl); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 创建带操作按钮的字段行(label + action button + content) */ | ||
| createFieldWithAction(label, action, content) { | ||
| const field = document.createElement("div"); | ||
| field.className = "dbg-field"; | ||
| const fieldHeader = document.createElement("div"); | ||
| fieldHeader.className = "dbg-field-header"; | ||
| const lbl = document.createElement("div"); | ||
| lbl.className = "dbg-label"; | ||
| lbl.textContent = label; | ||
| fieldHeader.appendChild(lbl); | ||
| fieldHeader.appendChild(action); | ||
| field.appendChild(fieldHeader); | ||
| field.appendChild(content); | ||
| return field; | ||
| } | ||
| /** 恢复上次持久化的状态 */ | ||
| async restoreState() { | ||
| const savedGw = localStorage.getItem(KEY_GATEWAY); | ||
| const gw = savedGw && this.gateways.includes(savedGw) ? savedGw : this.gateways[0]; | ||
| this.gwSelect.setValue(gw); | ||
| await this.loadServers(gw); | ||
| const savedType = localStorage.getItem(KEY_TYPE); | ||
| if (savedType) { | ||
| this.typeSelect.setValue(savedType); | ||
| this.renderServerOptions(savedType); | ||
| } | ||
| const savedServer = localStorage.getItem(KEY_SERVER); | ||
| if (savedServer && this.servers.find((s) => s.key === savedServer)) { | ||
| this.serverSelect.setValue(savedServer); | ||
| await this.applyServer(savedServer); | ||
| } | ||
| } | ||
| async onGatewayChange(gw) { | ||
| localStorage.setItem(KEY_GATEWAY, gw); | ||
| this.gwVersion.textContent = ""; | ||
| await this.loadServers(gw); | ||
| } | ||
| onTypeChange(type) { | ||
| localStorage.setItem(KEY_TYPE, type); | ||
| this.renderServerOptions(type); | ||
| } | ||
| async onServerChange(key) { | ||
| localStorage.setItem(KEY_SERVER, key); | ||
| await this.applyServer(key); | ||
| } | ||
| /** 从网关加载服务列表 */ | ||
| async loadServers(gwUrl) { | ||
| this.setHeader("loading"); | ||
| this.setFooter(T.loading(), ""); | ||
| try { | ||
| const res = await _http.get(`${gwUrl}/web-debug/host/list`, { | ||
| timeout: this.options.timeout | ||
| }); | ||
| this.servers = res.data ?? []; | ||
| this.renderTypeOptions(); | ||
| this.setHeader("ok"); | ||
| this.setFooter(T.loaded(this.servers.length), "ok"); | ||
| } catch (err) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| this.setHeader("err"); | ||
| this.setFooter(T.loadFailed(message), "err"); | ||
| } | ||
| } | ||
| /** 渲染标签选项 */ | ||
| renderTypeOptions() { | ||
| const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]; | ||
| const opts = [{ value: "", label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]; | ||
| this.typeSelect.setOptions(opts, T.allTags()); | ||
| this.typeSelect.setValue(""); | ||
| this.renderServerOptions(""); | ||
| } | ||
| /** 根据标签渲染服务选项 */ | ||
| renderServerOptions(type) { | ||
| const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers; | ||
| this.serverSelect.setOptions( | ||
| filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })), | ||
| T.selectServer() | ||
| ); | ||
| this.renderCtxList({}); | ||
| } | ||
| /** 切换目标服务,重新加载 Context 路由映射 */ | ||
| async applyServer(key) { | ||
| if (!key) { | ||
| this.renderCtxList({}); | ||
| return; | ||
| } | ||
| const server = this.servers.find((s) => s.key === key); | ||
| if (!server) return; | ||
| this.setFooter(T.switching(), ""); | ||
| try { | ||
| await Context.load(server.origin, this.options.timeout); | ||
| const res = await fetch(`${server.origin}/ngw/context`); | ||
| const raw = res.ok ? await res.json() : {}; | ||
| const ctxMap = {}; | ||
| for (const [k, v] of Object.entries(raw)) { | ||
| if (k !== "$info" && typeof v === "string") ctxMap[k] = v; | ||
| } | ||
| const info = Context.info; | ||
| if (info) this.gwVersion.textContent = `v${info.version}`; | ||
| this.renderCtxList(ctxMap); | ||
| const count = Object.keys(ctxMap).length; | ||
| this.setFooter(T.routes(server.name, count), "ok"); | ||
| } catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| this.setFooter(T.switchFailed(msg), "err"); | ||
| } | ||
| } | ||
| /** 渲染路由列表 */ | ||
| renderCtxList(ctxMap) { | ||
| this.currentCtxMap = ctxMap; | ||
| this.ctxList.innerHTML = ""; | ||
| this.ctxSearchInput.value = ""; | ||
| const entries = Object.entries(ctxMap); | ||
| if (entries.length === 0) { | ||
| const empty = document.createElement("div"); | ||
| empty.className = "dbg-ctx-empty"; | ||
| empty.textContent = T.selectFirst(); | ||
| this.ctxList.appendChild(empty); | ||
| return; | ||
| } | ||
| entries.forEach(([k, v]) => { | ||
| const item = document.createElement("div"); | ||
| item.className = "dbg-ctx-item"; | ||
| item.dataset["key"] = k; | ||
| const keySpan = document.createElement("span"); | ||
| keySpan.className = "dbg-ctx-key"; | ||
| keySpan.textContent = k; | ||
| keySpan.setAttribute("title", k); | ||
| const valSpan = document.createElement("span"); | ||
| valSpan.className = "dbg-ctx-val"; | ||
| valSpan.textContent = v; | ||
| valSpan.setAttribute("title", `${k} \u2192 ${v}`); | ||
| const copyIcon = document.createElement("span"); | ||
| copyIcon.className = "dbg-ctx-copy"; | ||
| copyIcon.textContent = "\u2398"; | ||
| item.appendChild(keySpan); | ||
| item.appendChild(valSpan); | ||
| item.appendChild(copyIcon); | ||
| item.addEventListener("click", () => this.copyCtxItem(item, k)); | ||
| this.ctxList.appendChild(item); | ||
| }); | ||
| } | ||
| /** 过滤路由列表 */ | ||
| filterCtx(query) { | ||
| const q = query.toLowerCase(); | ||
| this.ctxList.querySelectorAll(".dbg-ctx-item").forEach((item) => { | ||
| const key = item.dataset["key"] ?? ""; | ||
| item.classList.toggle("hidden", !!q && !key.toLowerCase().includes(q)); | ||
| }); | ||
| } | ||
| /** 复制单个 ctx key */ | ||
| copyCtxItem(item, text) { | ||
| navigator.clipboard.writeText(text).then(() => { | ||
| item.classList.add("copied"); | ||
| const icon = item.querySelector(".dbg-ctx-copy"); | ||
| if (icon) icon.textContent = "\u2713"; | ||
| setTimeout(() => { | ||
| item.classList.remove("copied"); | ||
| const ic = item.querySelector(".dbg-ctx-copy"); | ||
| if (ic) ic.textContent = "\u2398"; | ||
| }, 1500); | ||
| this.setFooter(T.copied(text), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2e3); | ||
| }); | ||
| } | ||
| /** 一键复制全部上下文为 JSON */ | ||
| copyAllCtx() { | ||
| const entries = Object.keys(this.currentCtxMap); | ||
| if (entries.length === 0) return; | ||
| const json = JSON.stringify(this.currentCtxMap, null, 2); | ||
| navigator.clipboard.writeText(json).then(() => { | ||
| this.copyAllBtn.textContent = "\u2713 " + T.copyAllDone(); | ||
| this.copyAllBtn.classList.add("done"); | ||
| setTimeout(() => { | ||
| this.copyAllBtn.textContent = T.copyAll(); | ||
| this.copyAllBtn.classList.remove("done"); | ||
| }, 2e3); | ||
| this.setFooter(T.copyAllDone(), "ok"); | ||
| setTimeout(() => this.setFooter("", ""), 2500); | ||
| }); | ||
| } | ||
| setHeader(state) { | ||
| this.headerDot.className = `dbg-header-dot${state !== "ok" ? ` ${state}` : ""}`; | ||
| } | ||
| setFooter(msg, cls) { | ||
| this.footerEl.innerHTML = msg ? `<span class="dbg-footer-dot"></span>${msg}` : ""; | ||
| this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ""}`; | ||
| } | ||
| }; | ||
| export { Debugger }; | ||
| //# sourceMappingURL=chunk-YXJTZURC.js.map | ||
| //# sourceMappingURL=chunk-YXJTZURC.js.map |
| {"version":3,"sources":["../../src/debugger/debugger.ts"],"names":[],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","file":"chunk-YXJTZURC.js","sourcesContent":["import axios from 'axios'\nimport { Context, DEBUGGER_ACTIVE_KEY } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\n\n/**\n * Debugger 专用的隔离 axios 实例。\n * 独立于用户通过 `import { axios } from '@snack-kit/lib/http'` 添加的拦截器,\n * 确保调试面板请求不被外部拦截器污染。\n */\nconst _http = axios.create()\n\n/** localStorage key 前缀 */\nconst STORAGE_PREFIX = '__snackkit_debugger__'\nconst KEY_GATEWAY = `${STORAGE_PREFIX}gateway`\nconst KEY_TYPE = `${STORAGE_PREFIX}type`\nconst KEY_SERVER = `${STORAGE_PREFIX}server`\n\n\n/** 是否为中文环境 */\nfunction isChinese(): boolean {\n return /^zh/i.test(navigator.language ?? '')\n}\n\n/** i18n 文案 */\nconst T = {\n gateway: () => isChinese() ? '网关' : 'Gateway',\n tag: () => isChinese() ? '标签' : 'Tag',\n server: () => isChinese() ? '服务' : 'Server',\n ctx: () => isChinese() ? '上下文' : 'Ctx',\n ctxLabel: () => isChinese() ? '上下文 — 点击行复制 key' : 'Ctx — click row to copy key',\n search: () => isChinese() ? '搜索...' : 'Search...',\n noMatch: () => isChinese() ? '无匹配项' : 'No results',\n allTags: () => isChinese() ? '全部标签' : 'All tags',\n selectGateway: () => isChinese() ? '选择网关' : 'Select gateway',\n selectServer: () => isChinese() ? '选择服务' : 'Select server',\n filterCtx: () => isChinese() ? '过滤路由...' : 'Filter routes...',\n selectFirst: () => isChinese() ? '← 请先选择服务' : '← Select a server first',\n loading: () => isChinese() ? '加载服务列表...' : 'Loading...',\n loadFailed: (m: string) => isChinese() ? `加载失败: ${m}` : `Failed: ${m}`,\n loaded: (n: number) => isChinese() ? `已加载 ${n} 个服务` : `${n} servers loaded`,\n switching: () => isChinese() ? '切换服务中...' : 'Switching...',\n switchFailed: (m: string) => isChinese() ? `切换失败: ${m}` : `Failed: ${m}`,\n routes: (name: string, n: number) => isChinese() ? `${name} · ${n} 条路由` : `${name} · ${n} routes`,\n copied: (k: string) => isChinese() ? `已复制: ${k}` : `Copied: ${k}`,\n copyAll: () => isChinese() ? '复制全部 JSON' : 'Copy all JSON',\n copyAllDone: () => isChinese() ? '已复制为 JSON' : 'Copied as JSON',\n}\n\n/** 面板内联样式 */\nconst PANEL_STYLE = `\n #__snackkit_debugger__ {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 99999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', sans-serif;\n font-size: 13px;\n color: #b8ccec;\n user-select: none;\n }\n\n /* ── 浮动按钮 ── */\n #__snackkit_debugger__ .dbg-toggle {\n width: 42px;\n height: 42px;\n border-radius: 50%;\n background: linear-gradient(135deg, #4f8ef7, #6d6ff5);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 14px rgba(79,142,247,0.45);\n margin-left: auto;\n font-size: 20px;\n transition: transform 0.15s, box-shadow 0.15s;\n }\n #__snackkit_debugger__ .dbg-toggle:hover {\n transform: scale(1.08);\n box-shadow: 0 6px 20px rgba(79,142,247,0.55);\n }\n #__snackkit_debugger__ .dbg-toggle:active { transform: scale(0.94); }\n\n /* ── 面板主体 ── */\n #__snackkit_debugger__ .dbg-panel {\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 12px;\n width: 440px;\n margin-bottom: 10px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.05);\n overflow: visible;\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n pointer-events: none;\n transition: opacity 0.18s ease, transform 0.18s ease;\n }\n #__snackkit_debugger__ .dbg-panel.open {\n opacity: 1;\n transform: translateY(0) scale(1);\n pointer-events: auto;\n }\n\n /* ── 面板 Header ── */\n #__snackkit_debugger__ .dbg-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 14px;\n background: #171e2e;\n border-bottom: 1px solid #2d3a55;\n border-radius: 12px 12px 0 0;\n }\n #__snackkit_debugger__ .dbg-header-left {\n display: flex;\n align-items: center;\n gap: 7px;\n }\n #__snackkit_debugger__ .dbg-header-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: #34d399;\n box-shadow: 0 0 6px rgba(52,211,153,0.6);\n }\n #__snackkit_debugger__ .dbg-header-dot.loading {\n background: #fbbf24;\n box-shadow: 0 0 6px rgba(251,191,36,0.6);\n animation: dbg-pulse 1s ease-in-out infinite;\n }\n #__snackkit_debugger__ .dbg-header-dot.err {\n background: #f87171;\n box-shadow: 0 0 6px rgba(248,113,113,0.6);\n }\n #__snackkit_debugger__ .dbg-header-title {\n font-size: 12px;\n font-weight: 600;\n color: #dce8fa;\n letter-spacing: 0.03em;\n }\n #__snackkit_debugger__ .dbg-header-meta {\n font-size: 11px;\n color: #4d6080;\n }\n\n /* ── 面板内容区 ── */\n #__snackkit_debugger__ .dbg-body {\n padding: 12px 14px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n height: 420px;\n overflow: visible;\n }\n\n /* ── 字段行 ── */\n #__snackkit_debugger__ .dbg-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-field-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n #__snackkit_debugger__ .dbg-label {\n font-size: 10px;\n font-weight: 600;\n color: #5e72a0;\n text-transform: uppercase;\n letter-spacing: 0.07em;\n }\n\n /* ── 一键复制全部 JSON 按钮 ── */\n #__snackkit_debugger__ .dbg-copy-all {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: 1px solid #2d3a55;\n border-radius: 4px;\n padding: 2px 7px;\n color: #5e72a0;\n font-size: 10px;\n font-family: inherit;\n cursor: pointer;\n transition: border-color 0.15s, color 0.15s, background 0.15s;\n }\n #__snackkit_debugger__ .dbg-copy-all:hover {\n border-color: #4f8ef7;\n color: #7aacfa;\n background: rgba(79,142,247,0.08);\n }\n #__snackkit_debugger__ .dbg-copy-all.done {\n border-color: #34d399;\n color: #34d399;\n }\n\n /* ── 自定义下拉 ── */\n #__snackkit_debugger__ .dbg-select {\n position: relative;\n }\n #__snackkit_debugger__ .dbg-select-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n background: #252f45;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n padding: 6px 10px;\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n min-height: 32px;\n }\n #__snackkit_debugger__ .dbg-select-trigger:hover { border-color: #3d5070; background: #2d3a55; }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-trigger {\n border-color: #4f8ef7;\n background: #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-val {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n color: #b8ccec;\n }\n #__snackkit_debugger__ .dbg-select-val.placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-arrow {\n color: #3d5070;\n font-size: 10px;\n flex-shrink: 0;\n transition: transform 0.15s, color 0.15s;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-arrow {\n transform: rotate(180deg);\n color: #4f8ef7;\n }\n #__snackkit_debugger__ .dbg-select-dropdown {\n position: absolute;\n bottom: calc(100% + 4px);\n left: 0;\n right: 0;\n background: #1e2638;\n border: 1px solid #2d3a55;\n border-radius: 8px;\n box-shadow: 0 -8px 24px rgba(0,0,0,0.35);\n overflow: hidden;\n opacity: 0;\n transform: translateY(4px);\n pointer-events: none;\n transition: opacity 0.14s, transform 0.14s;\n z-index: 100000;\n }\n #__snackkit_debugger__ .dbg-select.open .dbg-select-dropdown {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n }\n #__snackkit_debugger__ .dbg-select-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 10px;\n border-bottom: 1px solid #2d3a55;\n }\n #__snackkit_debugger__ .dbg-select-search-icon { color: #3d5070; font-size: 11px; flex-shrink: 0; }\n #__snackkit_debugger__ .dbg-select-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-select-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-select-list {\n max-height: 160px;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-select-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-select-option {\n padding: 7px 10px;\n cursor: pointer;\n font-size: 12px;\n color: #8aa4c8;\n transition: background 0.1s, color 0.1s;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-select-option:hover { background: #2d3a55; color: #dce8fa; }\n #__snackkit_debugger__ .dbg-select-option.active { color: #7aacfa; background: #1e3060; }\n #__snackkit_debugger__ .dbg-select-option.hidden { display: none; }\n #__snackkit_debugger__ .dbg-select-empty {\n padding: 12px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 路由列表 ── */\n #__snackkit_debugger__ .dbg-ctx-field {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: 5px;\n }\n #__snackkit_debugger__ .dbg-ctx-wrap {\n flex: 1;\n min-height: 0;\n border: 1px solid #2d3a55;\n border-radius: 6px;\n overflow: hidden;\n background: #252f45;\n display: flex;\n flex-direction: column;\n }\n #__snackkit_debugger__ .dbg-ctx-search {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-bottom: 1px solid #2d3a55;\n background: #1e2638;\n }\n #__snackkit_debugger__ .dbg-ctx-search input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n color: #b8ccec;\n font-size: 12px;\n font-family: inherit;\n }\n #__snackkit_debugger__ .dbg-ctx-search input::placeholder { color: #3d5070; }\n #__snackkit_debugger__ .dbg-ctx-list {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar { width: 3px; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-track { background: transparent; }\n #__snackkit_debugger__ .dbg-ctx-list::-webkit-scrollbar-thumb { background: #2d3a55; border-radius: 2px; }\n #__snackkit_debugger__ .dbg-ctx-item {\n padding: 5px 10px;\n cursor: pointer;\n display: grid;\n grid-template-columns: minmax(80px, 148px) 1fr auto;\n gap: 8px;\n align-items: center;\n border-bottom: 1px solid #1e2638;\n transition: background 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }\n #__snackkit_debugger__ .dbg-ctx-item:hover { background: #2d3a55; }\n #__snackkit_debugger__ .dbg-ctx-item.hidden { display: none; }\n #__snackkit_debugger__ .dbg-ctx-key {\n font-size: 12px;\n color: #7aacfa;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #__snackkit_debugger__ .dbg-ctx-val {\n font-size: 11px;\n color: #4d6080;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n transition: color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-val { color: #7a90b0; }\n #__snackkit_debugger__ .dbg-ctx-copy {\n font-size: 11px;\n color: #3d5070;\n opacity: 0;\n flex-shrink: 0;\n transition: opacity 0.1s, color 0.1s;\n }\n #__snackkit_debugger__ .dbg-ctx-item:hover .dbg-ctx-copy { opacity: 1; color: #5e72a0; }\n #__snackkit_debugger__ .dbg-ctx-item.copied .dbg-ctx-copy { color: #34d399; opacity: 1; }\n #__snackkit_debugger__ .dbg-ctx-empty {\n padding: 16px 10px;\n font-size: 12px;\n color: #3d5070;\n text-align: center;\n }\n\n /* ── 底部状态栏 ── */\n #__snackkit_debugger__ .dbg-footer {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: #171e2e;\n border-top: 1px solid #2d3a55;\n border-radius: 0 0 12px 12px;\n font-size: 11px;\n color: #4d6080;\n min-height: 30px;\n }\n #__snackkit_debugger__ .dbg-footer.ok { color: #34d399; }\n #__snackkit_debugger__ .dbg-footer.err { color: #f87171; }\n #__snackkit_debugger__ .dbg-footer-dot {\n width: 4px;\n height: 4px;\n border-radius: 50%;\n background: currentColor;\n flex-shrink: 0;\n }\n\n @keyframes dbg-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.35; }\n }\n`\n\n/** 可搜索的自定义下拉组件 */\nclass SearchSelect {\n private el: HTMLElement\n private trigger: HTMLElement\n private valEl: HTMLElement\n private dropdown: HTMLElement\n private searchInput: HTMLInputElement\n private listEl: HTMLElement\n private options: Array<{ value: string; label: string }> = []\n private selected = ''\n private onChange: (value: string) => void\n\n constructor(onChange: (value: string) => void) {\n this.onChange = onChange\n this.el = document.createElement('div')\n this.el.className = 'dbg-select'\n\n // 触发器\n this.trigger = document.createElement('div')\n this.trigger.className = 'dbg-select-trigger'\n this.valEl = document.createElement('span')\n this.valEl.className = 'dbg-select-val placeholder'\n const arrow = document.createElement('span')\n arrow.className = 'dbg-select-arrow'\n arrow.textContent = '▾'\n this.trigger.appendChild(this.valEl)\n this.trigger.appendChild(arrow)\n\n // 下拉面板\n this.dropdown = document.createElement('div')\n this.dropdown.className = 'dbg-select-dropdown'\n\n // 搜索框\n const searchWrap = document.createElement('div')\n searchWrap.className = 'dbg-select-search'\n const searchIcon = document.createElement('span')\n searchIcon.className = 'dbg-select-search-icon'\n searchIcon.textContent = '⌕'\n this.searchInput = document.createElement('input')\n this.searchInput.type = 'text'\n this.searchInput.placeholder = T.search()\n searchWrap.appendChild(searchIcon)\n searchWrap.appendChild(this.searchInput)\n\n this.listEl = document.createElement('div')\n this.listEl.className = 'dbg-select-list'\n\n this.dropdown.appendChild(searchWrap)\n this.dropdown.appendChild(this.listEl)\n this.el.appendChild(this.trigger)\n this.el.appendChild(this.dropdown)\n\n this.bindEvents()\n }\n\n private bindEvents(): void {\n this.trigger.addEventListener('click', (e) => {\n e.stopPropagation()\n const isOpen = this.el.classList.contains('open')\n document.querySelectorAll('#__snackkit_debugger__ .dbg-select.open').forEach((el) => {\n if (el !== this.el) el.classList.remove('open')\n })\n this.el.classList.toggle('open', !isOpen)\n if (!isOpen) {\n this.searchInput.value = ''\n this.filterOptions('')\n setTimeout(() => this.searchInput.focus(), 50)\n }\n })\n\n this.searchInput.addEventListener('input', () => {\n this.filterOptions(this.searchInput.value)\n })\n\n this.searchInput.addEventListener('click', (e) => e.stopPropagation())\n\n document.addEventListener('click', () => {\n this.el.classList.remove('open')\n })\n }\n\n private filterOptions(query: string): void {\n const q = query.toLowerCase()\n let visibleCount = 0\n this.listEl.querySelectorAll<HTMLElement>('.dbg-select-option').forEach((opt) => {\n const match = !q || opt.textContent!.toLowerCase().includes(q)\n opt.classList.toggle('hidden', !match)\n if (match) visibleCount++\n })\n let emptyEl = this.listEl.querySelector<HTMLElement>('.dbg-select-empty')\n if (visibleCount === 0) {\n if (!emptyEl) {\n emptyEl = document.createElement('div')\n emptyEl.className = 'dbg-select-empty'\n emptyEl.textContent = T.noMatch()\n this.listEl.appendChild(emptyEl)\n }\n } else {\n emptyEl?.remove()\n }\n }\n\n /** 更新选项列表 */\n setOptions(options: Array<{ value: string; label: string }>, placeholder: string): void {\n this.options = options\n this.listEl.innerHTML = ''\n this.valEl.textContent = placeholder\n this.valEl.className = 'dbg-select-val placeholder'\n this.selected = ''\n\n options.forEach(({ value, label }) => {\n const opt = document.createElement('div')\n opt.className = 'dbg-select-option'\n opt.dataset['value'] = value\n opt.textContent = label\n opt.setAttribute('title', label)\n opt.addEventListener('click', (e) => {\n e.stopPropagation()\n this.select(value, label)\n this.el.classList.remove('open')\n })\n this.listEl.appendChild(opt)\n })\n }\n\n /** 选中某个值(不触发 onChange) */\n setValue(value: string): void {\n const opt = this.options.find((o) => o.value === value)\n if (opt) this.select(opt.value, opt.label, false)\n }\n\n private select(value: string, label: string, emit = true): void {\n this.selected = value\n this.valEl.textContent = label\n this.valEl.className = 'dbg-select-val'\n this.listEl.querySelectorAll('.dbg-select-option').forEach((el) => {\n el.classList.toggle('active', (el as HTMLElement).dataset['value'] === value)\n })\n if (emit) this.onChange(value)\n }\n\n getValue(): string { return this.selected }\n getElement(): HTMLElement { return this.el }\n}\n\n/**\n * 浏览器端浮动调试面板\n *\n * 允许开发者在运行时:\n * - 选择调试网关 → 拉取服务清单\n * - 切换目标服务 → 重新加载 Context 路由映射\n * - 查看并复制可用的路由 ctx 列表\n *\n * 与 http 模块集成:使用 `Get()` 拉取服务清单,使用 `context.load()` 切换请求目标。\n *\n * @see [交互式 Demo](../demo/debugger.html)\n *\n * @example\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * ```\n */\nexport class Debugger {\n private options: Required<DebuggerOptions>\n private gateways: string[]\n private servers: ServerItem[] = []\n private currentCtxMap: Record<string, string> = {}\n private panelEl!: HTMLElement\n private headerDot!: HTMLElement\n private gwVersion!: HTMLElement\n private footerEl!: HTMLElement\n private copyAllBtn!: HTMLButtonElement\n private gwSelect!: SearchSelect\n private typeSelect!: SearchSelect\n private serverSelect!: SearchSelect\n private ctxList!: HTMLElement\n private ctxSearchInput!: HTMLInputElement\n\n private constructor(options: Required<DebuggerOptions>) {\n this.options = options\n this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways]\n }\n\n /**\n * 初始化并渲染调试面板\n *\n * 支持两种调用形式:\n * - **配置对象**:传入 `DebuggerOptions`,可配置网关列表和超时时间\n * - **数组简写**:直接传入网关 URL 数组,等价于 `{ gateways: [...] }`\n *\n * 调用后在页面右下角插入浮动按钮,点击展开调试面板。\n * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置对象 或 网关 URL 数组\n *\n * @example 配置对象形式\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: [\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ],\n * timeout: 5000,\n * })\n * ```\n *\n * @example 数组简写形式\n * ```ts\n * await Debugger.init([\n * 'http://dev-gateway.example.com',\n * 'http://test-gateway.example.com',\n * ])\n * ```\n *\n * @example 仅在非生产环境加载\n * ```ts\n * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init(['http://dev-gateway.example.com'])\n * }\n * ```\n */\n static async init(options: DebuggerOptions | string[]): Promise<Debugger> {\n // 在 globalThis 写入标志位,Context.load() 无参数时检测到后将跳过自动加载\n ;(globalThis as Record<string, unknown>)[DEBUGGER_ACTIVE_KEY] = true\n const normalized: DebuggerOptions = Array.isArray(options) ? { gateways: options } : options\n const instance = new Debugger({ timeout: 10000, ...normalized })\n instance.injectStyle()\n instance.buildDOM()\n await instance.restoreState()\n return instance\n }\n\n /** 注入内联样式 */\n private injectStyle(): void {\n if (document.getElementById('__snackkit_debugger_style__')) return\n const style = document.createElement('style')\n style.id = '__snackkit_debugger_style__'\n style.textContent = PANEL_STYLE\n document.head.appendChild(style)\n }\n\n /** 构建 DOM 结构 */\n private buildDOM(): void {\n const root = document.createElement('div')\n root.id = '__snackkit_debugger__'\n\n // ── 面板 ──\n const panel = document.createElement('div')\n panel.className = 'dbg-panel'\n this.panelEl = panel\n\n // Header\n const header = document.createElement('div')\n header.className = 'dbg-header'\n const headerLeft = document.createElement('div')\n headerLeft.className = 'dbg-header-left'\n this.headerDot = document.createElement('span')\n this.headerDot.className = 'dbg-header-dot loading'\n const headerTitle = document.createElement('span')\n headerTitle.className = 'dbg-header-title'\n headerTitle.textContent = 'Snack Kit Debugger'\n headerLeft.appendChild(this.headerDot)\n headerLeft.appendChild(headerTitle)\n header.appendChild(headerLeft)\n panel.appendChild(header)\n\n // Body\n const body = document.createElement('div')\n body.className = 'dbg-body'\n\n // 网关选择(标签行右侧显示版本号)\n this.gwVersion = document.createElement('span')\n this.gwVersion.className = 'dbg-header-meta'\n this.gwSelect = new SearchSelect((val) => this.onGatewayChange(val))\n this.gwSelect.setOptions(this.gateways.map((g) => ({ value: g, label: g })), T.selectGateway())\n body.appendChild(this.createFieldWithAction(T.gateway(), this.gwVersion, this.gwSelect.getElement()))\n\n // 标签(类型)选择\n this.typeSelect = new SearchSelect((val) => this.onTypeChange(val))\n body.appendChild(this.createField(T.tag(), this.typeSelect.getElement()))\n\n // 服务选择\n this.serverSelect = new SearchSelect((val) => this.onServerChange(val))\n body.appendChild(this.createField(T.server(), this.serverSelect.getElement()))\n\n // 路由列表(含一键复制全部 JSON 按钮)\n const ctxWrap = document.createElement('div')\n ctxWrap.className = 'dbg-ctx-wrap'\n\n const ctxSearch = document.createElement('div')\n ctxSearch.className = 'dbg-ctx-search'\n const ctxSearchIcon = document.createElement('span')\n ctxSearchIcon.className = 'dbg-select-search-icon'\n ctxSearchIcon.textContent = '⌕'\n this.ctxSearchInput = document.createElement('input')\n this.ctxSearchInput.type = 'text'\n this.ctxSearchInput.placeholder = T.filterCtx()\n this.ctxSearchInput.addEventListener('input', () => this.filterCtx(this.ctxSearchInput.value))\n ctxSearch.appendChild(ctxSearchIcon)\n ctxSearch.appendChild(this.ctxSearchInput)\n\n this.ctxList = document.createElement('div')\n this.ctxList.className = 'dbg-ctx-list'\n\n ctxWrap.appendChild(ctxSearch)\n ctxWrap.appendChild(this.ctxList)\n\n // 一键复制全部 JSON 按钮\n this.copyAllBtn = document.createElement('button')\n this.copyAllBtn.className = 'dbg-copy-all'\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.setAttribute('title', isChinese()\n ? '将全部上下文复制为 JSON,可作为 Context.load({...}) 的参数'\n : 'Copy all ctx entries as JSON for Context.load({...})')\n this.copyAllBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.copyAllCtx()\n })\n\n const ctxField = this.createFieldWithAction(T.ctxLabel(), this.copyAllBtn, ctxWrap)\n ctxField.classList.add('dbg-ctx-field')\n body.appendChild(ctxField)\n\n panel.appendChild(body)\n\n // Footer 状态栏\n const footer = document.createElement('div')\n footer.className = 'dbg-footer'\n this.footerEl = footer\n panel.appendChild(footer)\n\n // ── 浮动按钮 ──\n const toggle = document.createElement('button')\n toggle.className = 'dbg-toggle'\n toggle.textContent = '🐛'\n toggle.setAttribute('title', 'Snack Debugger')\n toggle.addEventListener('click', (e) => {\n e.stopPropagation()\n panel.classList.toggle('open')\n })\n\n root.appendChild(panel)\n root.appendChild(toggle)\n document.body.appendChild(root)\n }\n\n /** 创建普通字段行(label + content) */\n private createField(label: string, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n field.appendChild(lbl)\n field.appendChild(content)\n return field\n }\n\n /** 创建带操作按钮的字段行(label + action button + content) */\n private createFieldWithAction(label: string, action: HTMLElement, content: HTMLElement): HTMLElement {\n const field = document.createElement('div')\n field.className = 'dbg-field'\n const fieldHeader = document.createElement('div')\n fieldHeader.className = 'dbg-field-header'\n const lbl = document.createElement('div')\n lbl.className = 'dbg-label'\n lbl.textContent = label\n fieldHeader.appendChild(lbl)\n fieldHeader.appendChild(action)\n field.appendChild(fieldHeader)\n field.appendChild(content)\n return field\n }\n\n /** 恢复上次持久化的状态 */\n private async restoreState(): Promise<void> {\n const savedGw = localStorage.getItem(KEY_GATEWAY)\n const gw = (savedGw && this.gateways.includes(savedGw)) ? savedGw : this.gateways[0]\n this.gwSelect.setValue(gw)\n await this.loadServers(gw)\n\n const savedType = localStorage.getItem(KEY_TYPE)\n if (savedType) {\n this.typeSelect.setValue(savedType)\n this.renderServerOptions(savedType)\n }\n\n const savedServer = localStorage.getItem(KEY_SERVER)\n if (savedServer && this.servers.find((s) => s.key === savedServer)) {\n this.serverSelect.setValue(savedServer)\n await this.applyServer(savedServer)\n }\n }\n\n private async onGatewayChange(gw: string): Promise<void> {\n localStorage.setItem(KEY_GATEWAY, gw)\n this.gwVersion.textContent = ''\n await this.loadServers(gw)\n }\n\n private onTypeChange(type: string): void {\n localStorage.setItem(KEY_TYPE, type)\n this.renderServerOptions(type)\n }\n\n private async onServerChange(key: string): Promise<void> {\n localStorage.setItem(KEY_SERVER, key)\n await this.applyServer(key)\n }\n\n /** 从网关加载服务列表 */\n private async loadServers(gwUrl: string): Promise<void> {\n this.setHeader('loading')\n this.setFooter(T.loading(), '')\n try {\n const res = await _http.get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n timeout: this.options.timeout,\n })\n this.servers = res.data ?? []\n this.renderTypeOptions()\n this.setHeader('ok')\n this.setFooter(T.loaded(this.servers.length), 'ok')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.setHeader('err')\n this.setFooter(T.loadFailed(message), 'err')\n }\n }\n\n /** 渲染标签选项 */\n private renderTypeOptions(): void {\n const types = [...new Set(this.servers.map((s) => s.type).filter(Boolean))]\n const opts = [{ value: '', label: T.allTags() }, ...types.map((t) => ({ value: t, label: t }))]\n this.typeSelect.setOptions(opts, T.allTags())\n this.typeSelect.setValue('')\n this.renderServerOptions('')\n }\n\n /** 根据标签渲染服务选项 */\n private renderServerOptions(type: string): void {\n const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers\n this.serverSelect.setOptions(\n filtered.map((s) => ({ value: s.key, label: `${s.name} (${s.key})` })),\n T.selectServer(),\n )\n this.renderCtxList({})\n }\n\n /** 切换目标服务,重新加载 Context 路由映射 */\n private async applyServer(key: string): Promise<void> {\n if (!key) { this.renderCtxList({}); return }\n const server = this.servers.find((s) => s.key === key)\n if (!server) return\n\n this.setFooter(T.switching(), '')\n try {\n await Context.load(server.origin, this.options.timeout)\n const res = await fetch(`${server.origin}/ngw/context`)\n const raw: Record<string, unknown> = res.ok ? await res.json() : {}\n const ctxMap: Record<string, string> = {}\n for (const [k, v] of Object.entries(raw)) {\n if (k !== '$info' && typeof v === 'string') ctxMap[k] = v\n }\n const info = Context.info\n if (info) this.gwVersion.textContent = `v${info.version}`\n this.renderCtxList(ctxMap)\n const count = Object.keys(ctxMap).length\n this.setFooter(T.routes(server.name, count), 'ok')\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this.setFooter(T.switchFailed(msg), 'err')\n }\n }\n\n /** 渲染路由列表 */\n private renderCtxList(ctxMap: Record<string, string>): void {\n this.currentCtxMap = ctxMap\n this.ctxList.innerHTML = ''\n this.ctxSearchInput.value = ''\n const entries = Object.entries(ctxMap)\n if (entries.length === 0) {\n const empty = document.createElement('div')\n empty.className = 'dbg-ctx-empty'\n empty.textContent = T.selectFirst()\n this.ctxList.appendChild(empty)\n return\n }\n entries.forEach(([k, v]) => {\n const item = document.createElement('div')\n item.className = 'dbg-ctx-item'\n item.dataset['key'] = k\n\n const keySpan = document.createElement('span')\n keySpan.className = 'dbg-ctx-key'\n keySpan.textContent = k\n keySpan.setAttribute('title', k)\n\n const valSpan = document.createElement('span')\n valSpan.className = 'dbg-ctx-val'\n valSpan.textContent = v\n valSpan.setAttribute('title', `${k} → ${v}`)\n\n const copyIcon = document.createElement('span')\n copyIcon.className = 'dbg-ctx-copy'\n copyIcon.textContent = '⎘'\n\n item.appendChild(keySpan)\n item.appendChild(valSpan)\n item.appendChild(copyIcon)\n item.addEventListener('click', () => this.copyCtxItem(item, k))\n this.ctxList.appendChild(item)\n })\n }\n\n /** 过滤路由列表 */\n private filterCtx(query: string): void {\n const q = query.toLowerCase()\n this.ctxList.querySelectorAll<HTMLElement>('.dbg-ctx-item').forEach((item) => {\n const key = item.dataset['key'] ?? ''\n item.classList.toggle('hidden', !!q && !key.toLowerCase().includes(q))\n })\n }\n\n /** 复制单个 ctx key */\n private copyCtxItem(item: HTMLElement, text: string): void {\n navigator.clipboard.writeText(text).then(() => {\n item.classList.add('copied')\n const icon = item.querySelector('.dbg-ctx-copy')\n if (icon) icon.textContent = '✓'\n setTimeout(() => {\n item.classList.remove('copied')\n const ic = item.querySelector('.dbg-ctx-copy')\n if (ic) ic.textContent = '⎘'\n }, 1500)\n this.setFooter(T.copied(text), 'ok')\n setTimeout(() => this.setFooter('', ''), 2000)\n })\n }\n\n /** 一键复制全部上下文为 JSON */\n private copyAllCtx(): void {\n const entries = Object.keys(this.currentCtxMap)\n if (entries.length === 0) return\n const json = JSON.stringify(this.currentCtxMap, null, 2)\n navigator.clipboard.writeText(json).then(() => {\n this.copyAllBtn.textContent = '✓ ' + T.copyAllDone()\n this.copyAllBtn.classList.add('done')\n setTimeout(() => {\n this.copyAllBtn.textContent = T.copyAll()\n this.copyAllBtn.classList.remove('done')\n }, 2000)\n this.setFooter(T.copyAllDone(), 'ok')\n setTimeout(() => this.setFooter('', ''), 2500)\n })\n }\n\n private setHeader(state: 'ok' | 'err' | 'loading'): void {\n this.headerDot.className = `dbg-header-dot${state !== 'ok' ? ` ${state}` : ''}`\n }\n\n private setFooter(msg: string, cls: '' | 'ok' | 'err'): void {\n this.footerEl.innerHTML = msg\n ? `<span class=\"dbg-footer-dot\"></span>${msg}`\n : ''\n this.footerEl.className = `dbg-footer${cls ? ` ${cls}` : ''}`\n }\n}\n"]} |
| import { AxiosRequestConfig, AxiosResponse } from 'axios'; | ||
| /** | ||
| * HTTP 请求配置,扩展自 AxiosRequestConfig | ||
| */ | ||
| interface HttpRequestConfig<D = unknown> extends AxiosRequestConfig<D> { | ||
| /** Context key,自动从 HttpContext 获取 baseURL */ | ||
| ctx?: string; | ||
| /** 自定义请求 ID,用于主动取消 */ | ||
| cancelId?: string; | ||
| /** 回调接收生成的 cancelId */ | ||
| onCancelId?: (id: string) => void; | ||
| /** | ||
| * 是否添加时间戳防缓存,默认 false(添加防缓存时间戳) | ||
| * 设为 true 时不添加时间戳 | ||
| */ | ||
| cache?: boolean; | ||
| } | ||
| /** | ||
| * 快捷请求方法(Get / Post 等)的 config 入参类型。 | ||
| * | ||
| * 与 `HttpRequestConfig` 相同,但不含 `method`(由快捷方法预置)。 | ||
| * 适用于以纯配置对象方式调用时,避免重复指定已知的 method。 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * const config: HttpMethodConfig = { url: '/api/user', ctx: 'user-svc' } | ||
| * const { data } = await Get(config) | ||
| * ``` | ||
| */ | ||
| type HttpMethodConfig<D = unknown> = Omit<HttpRequestConfig<D>, 'method'>; | ||
| /** | ||
| * HTTP 请求结果,扩展自 AxiosResponse | ||
| */ | ||
| interface HttpResult<T = unknown> extends Partial<AxiosResponse<T>> { | ||
| /** 有 error 则请求失败,无 error 则成功 */ | ||
| error?: HttpError; | ||
| } | ||
| /** | ||
| * HTTP 错误信息 | ||
| */ | ||
| interface HttpError { | ||
| /** HTTP 状态码 */ | ||
| status?: number; | ||
| /** 错误描述 */ | ||
| message: string; | ||
| /** 服务端返回的原始错误体 */ | ||
| data?: unknown; | ||
| } | ||
| /** | ||
| * 网关 `/ngw/context` 接口返回的元数据(`$info` 字段) | ||
| */ | ||
| interface ContextInfo { | ||
| /** 网关版本号,例如 `"4.6.2-r10"` */ | ||
| version: string; | ||
| /** 网关域名,例如 `"http://172.16.32.155:20000"` */ | ||
| domain: string; | ||
| /** 客户端标识 */ | ||
| client: string; | ||
| /** 当前语言 */ | ||
| lang: string; | ||
| } | ||
| /** | ||
| * 网关 `/web-debug/host/list` 接口返回的服务列表项 | ||
| */ | ||
| interface ServerItem { | ||
| /** 服务唯一标识 */ | ||
| key: string; | ||
| /** 服务名称 */ | ||
| name: string; | ||
| /** 服务类型分组,可为空字符串 */ | ||
| type: string; | ||
| /** 服务地址,用于 `context.load()` 切换路由映射 */ | ||
| origin: string; | ||
| /** 代理配置(由网关管理,前端只读) */ | ||
| proxyInfo: Record<string, unknown>; | ||
| } | ||
| /** | ||
| * globalThis 上的标志位 key,由 `Debugger.init()` 写入。 | ||
| * `Context.load()` 无参数调用时,若检测到此标志则跳过自动加载, | ||
| * 由 Debugger 负责在用户选择服务后调用 `Context.load(origin)`。 | ||
| */ | ||
| declare const DEBUGGER_ACTIVE_KEY = "__snackkit_debugger__active"; | ||
| /** | ||
| * 管理服务地址映射的上下文单例 | ||
| * | ||
| * 支持三种加载方式: | ||
| * - **不传参数**:生产环境自动以当前页面 Origin 为网关,开发环境由 `Debugger` 接管 | ||
| * - **远程加载**:传入网关 URL,自动请求 `GET /ngw/context` 获取映射表 | ||
| * - **本地注入**:直接传入键值对象,不发起网络请求 | ||
| * | ||
| * @example 生产环境不传参(推荐) | ||
| * ```ts | ||
| * import { Context } from '@snack-kit/lib/http' | ||
| * | ||
| * await Context.load() | ||
| * Context.get('ngw') // 'https://app.example.com/ngw' | ||
| * ``` | ||
| * | ||
| * @example 远程加载(指定网关) | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.get('ngw') // 'http://172.16.32.155:20000/ngw' | ||
| * Context.info // { version: '4.6.2-r10', domain: '...', ... } | ||
| * ``` | ||
| * | ||
| * @example 本地注入 | ||
| * ```ts | ||
| * await Context.load({ | ||
| * 'user-svc': 'http://user.api.com', | ||
| * 'order-svc': 'http://order.api.com', | ||
| * }) | ||
| * ``` | ||
| */ | ||
| declare class HttpContext { | ||
| private _store; | ||
| private _loaded; | ||
| private _info; | ||
| /** | ||
| * 加载服务地址映射 | ||
| * | ||
| * 支持三种调用方式: | ||
| * - **传入字符串**:作为网关地址,自动请求 `GET /ngw/context` 获取映射表 | ||
| * - **传入对象**:直接注入键值映射,不发起网络请求 | ||
| * - **不传参数**: | ||
| * - 生产环境(`NODE_ENV === 'production'`):以当前页面 `Origin` 为网关地址自动加载 | ||
| * - 开发环境:立即返回,不做任何操作(应由 `Debugger.init()` 接管加载流程) | ||
| * | ||
| * 远程加载时,响应中的 `$info` 元数据字段会被单独存储,不计入路由映射表。 | ||
| * | ||
| * @param source 网关 URL / 键值对象 / 省略 | ||
| * @param timeout 远程加载超时(ms),默认 5000 | ||
| * | ||
| * @example 不传参——生产环境自动使用当前页面 Origin | ||
| * ```ts | ||
| * await Context.load() | ||
| * ``` | ||
| * | ||
| * @example 传入网关地址 | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000', 3000) | ||
| * ``` | ||
| * | ||
| * @example 本地注入 | ||
| * ```ts | ||
| * await Context.load({ 'user-svc': 'http://user.api.com' }) | ||
| * ``` | ||
| */ | ||
| load(source?: string | Record<string, string>, timeout?: number): Promise<void>; | ||
| /** | ||
| * 获取指定 ctx key 对应的服务地址 | ||
| * @param key ctx key,例如 `'ngw'`、`'osc'` | ||
| * @returns 服务地址,未找到时返回空字符串 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.get('osc') // 'http://172.16.32.155:20000/osc' | ||
| * Context.get('none') // '' | ||
| * ``` | ||
| */ | ||
| get(key: string): string; | ||
| /** | ||
| * 网关元数据(仅远程加载后可用) | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.info?.version // '4.6.2-r10' | ||
| * Context.info?.domain // 'http://172.16.32.155:20000' | ||
| * ``` | ||
| */ | ||
| get info(): ContextInfo | undefined; | ||
| /** | ||
| * Context 是否已加载 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * Context.loaded // false | ||
| * await Context.load({ 'user-svc': 'http://user.api.com' }) | ||
| * Context.loaded // true | ||
| * ``` | ||
| */ | ||
| get loaded(): boolean; | ||
| /** | ||
| * 清空所有映射并重置加载状态 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * Context.clear() | ||
| * Context.loaded // false | ||
| * ``` | ||
| */ | ||
| clear(): void; | ||
| } | ||
| /** | ||
| * HttpContext 全局单例 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Context } from '@snack-kit/lib/http' | ||
| * | ||
| * await Context.load('http://172.16.32.155:20000') | ||
| * Context.get('osc') // 'http://172.16.32.155:20000/osc' | ||
| * ``` | ||
| */ | ||
| declare const Context: HttpContext; | ||
| /** | ||
| * 当前页面的 origin(`protocol + host`) | ||
| * | ||
| * 在非浏览器环境(如 SSR/Node)返回空字符串。 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Origin } from '@snack-kit/lib/http' | ||
| * // 浏览器中:'https://app.example.com' | ||
| * // Node 环境:'' | ||
| * ``` | ||
| */ | ||
| declare const Origin: string; | ||
| /** | ||
| * 当前页面路径的首段,可直接用作 `ctx` 参数 | ||
| * | ||
| * 例如 `/osc/employee` → `'osc'`,在非浏览器环境返回空字符串。 | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * import { Ctx, Get } from '@snack-kit/lib/http' | ||
| * | ||
| * // 当前路径为 /osc/... 时,Ctx === 'osc' | ||
| * const result = await Get('/employee', { ctx: Ctx }) | ||
| * ``` | ||
| */ | ||
| declare const Ctx: string; | ||
| export { Context as C, DEBUGGER_ACTIVE_KEY as D, HttpContext as H, Origin as O, type ServerItem as S, type ContextInfo as a, Ctx as b, type HttpError as c, type HttpMethodConfig as d, type HttpRequestConfig as e, type HttpResult as f }; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 3 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
4934226
0.95%53798
0.85%315
2.27%33
10%