🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@snack-kit/lib

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@snack-kit/lib - npm Package Compare versions

Comparing version
0.1.0
to
0.2.0
+868
dist/cjs/chunk-YOWLTZM5.cjs
'use strict';
var chunkN7BJS6LI_cjs = require('./chunk-N7BJS6LI.cjs');
// src/debugger/debugger.ts
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];
}
/**
* 初始化并渲染调试面板
*
* 调用后在页面右下角插入浮动按钮,点击展开调试面板。
* 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。
* 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。
*
* @param options 配置项
*
* @example 单网关
* ```ts
* import { Debugger } from '@snack-kit/lib/debugger'
*
* await Debugger.init({
* gateways: 'http://dev-gateway.example.com',
* })
* ```
*
* @example 多网关(开发/测试环境切换)
* ```ts
* await Debugger.init({
* gateways: [
* 'http://dev-gateway.example.com',
* 'http://test-gateway.example.com',
* ],
* timeout: 5000,
* })
* ```
*
* @example 仅在非生产环境加载
* ```ts
* if (import.meta.env.DEV) {
* const { Debugger } = await import('@snack-kit/lib/debugger')
* await Debugger.init({ gateways: 'http://dev-gateway.example.com' })
* }
* ```
*/
static async init(options) {
const instance = new _Debugger({ timeout: 1e4, ...options });
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(), "");
const result = await chunkN7BJS6LI_cjs.Get(`${gwUrl}/web-debug/host/list`, {
cache: true,
timeout: this.options.timeout
});
if (result.error) {
this.setHeader("err");
this.setFooter(T.loadFailed(result.error.message), "err");
return;
}
this.servers = result.data ?? [];
this.renderTypeOptions();
this.setHeader("ok");
this.setFooter(T.loaded(this.servers.length), "ok");
}
/** 渲染标签选项 */
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 chunkN7BJS6LI_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 = chunkN7BJS6LI_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-YOWLTZM5.cjs.map
//# sourceMappingURL=chunk-YOWLTZM5.cjs.map
{"version":3,"sources":["../../src/debugger/debugger.ts"],"names":["Get","Context"],"mappings":";;;;;AAMA,IAAM,cAAA,GAAiB,uBAAA;AACvB,IAAM,WAAA,GAAc,GAAG,cAAc,CAAA,OAAA,CAAA;AACrC,IAAM,QAAA,GAAc,GAAG,cAAc,CAAA,IAAA,CAAA;AACrC,IAAM,UAAA,GAAc,GAAG,cAAc,CAAA,MAAA,CAAA;AAGrC,SAAS,SAAA,GAAqB;AAC5B,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,SAAA,CAAU,QAAA,IAAY,EAAE,CAAA;AAC7C;AAGA,IAAM,CAAA,GAAI;AAAA,EACR,OAAA,EAAe,MAAM,SAAA,EAAU,GAAI,cAAA,GAAgB,SAAA;AAAA,EACnD,GAAA,EAAe,MAAM,SAAA,EAAU,GAAI,cAAA,GAAgB,KAAA;AAAA,EACnD,MAAA,EAAe,MAAM,SAAA,EAAU,GAAI,cAAA,GAAgB,QAAA;AAAA,EACnD,GAAA,EAAe,MAAM,SAAA,EAAU,GAAI,oBAAA,GAAe,KAAA;AAAA,EAClD,QAAA,EAAe,MAAM,SAAA,EAAU,GAAI,gEAAA,GAAsB,oCAAA;AAAA,EACzD,MAAA,EAAe,MAAM,SAAA,EAAU,GAAI,iBAAA,GAAgB,WAAA;AAAA,EACnD,OAAA,EAAe,MAAM,SAAA,EAAU,GAAI,0BAAA,GAAc,YAAA;AAAA,EACjD,OAAA,EAAe,MAAM,SAAA,EAAU,GAAI,0BAAA,GAAc,UAAA;AAAA,EACjD,aAAA,EAAe,MAAM,SAAA,EAAU,GAAI,0BAAA,GAAc,gBAAA;AAAA,EACjD,YAAA,EAAe,MAAM,SAAA,EAAU,GAAI,0BAAA,GAAc,eAAA;AAAA,EACjD,SAAA,EAAe,MAAM,SAAA,EAAU,GAAI,6BAAA,GAAc,kBAAA;AAAA,EACjD,WAAA,EAAe,MAAM,SAAA,EAAU,GAAI,6CAAA,GAAa,8BAAA;AAAA,EAChD,OAAA,EAAe,MAAM,SAAA,EAAU,GAAI,yCAAA,GAAc,YAAA;AAAA,EACjD,UAAA,EAAe,CAAC,CAAA,KAAc,SAAA,KAAc,CAAA,0BAAA,EAAS,CAAC,CAAA,CAAA,GAAK,CAAA,QAAA,EAAW,CAAC,CAAA,CAAA;AAAA,EACvE,MAAA,EAAe,CAAC,CAAA,KAAc,SAAA,KAAc,CAAA,mBAAA,EAAO,CAAC,CAAA,mBAAA,CAAA,GAAS,CAAA,EAAG,CAAC,CAAA,eAAA,CAAA;AAAA,EACjE,SAAA,EAAe,MAAM,SAAA,EAAU,GAAI,mCAAA,GAAc,cAAA;AAAA,EACjD,YAAA,EAAe,CAAC,CAAA,KAAc,SAAA,KAAc,CAAA,0BAAA,EAAS,CAAC,CAAA,CAAA,GAAK,CAAA,QAAA,EAAW,CAAC,CAAA,CAAA;AAAA,EACvE,MAAA,EAAe,CAAC,IAAA,EAAc,CAAA,KAAc,WAAU,GAAI,CAAA,EAAG,IAAI,CAAA,MAAA,EAAM,CAAC,CAAA,mBAAA,CAAA,GAAS,CAAA,EAAG,IAAI,SAAM,CAAC,CAAA,OAAA,CAAA;AAAA,EAC/F,MAAA,EAAe,CAAC,CAAA,KAAc,SAAA,KAAc,CAAA,oBAAA,EAAQ,CAAC,CAAA,CAAA,GAAK,CAAA,QAAA,EAAW,CAAC,CAAA,CAAA;AAAA,EACtE,OAAA,EAAe,MAAM,SAAA,EAAU,GAAI,+BAAA,GAAc,eAAA;AAAA,EACjD,WAAA,EAAe,MAAM,SAAA,EAAU,GAAI,+BAAA,GAAc;AACnD,CAAA;AAGA,IAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","file":"chunk-YOWLTZM5.cjs","sourcesContent":["import { Get } from '../http/client'\nimport { Context } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\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/** 是否为中文环境 */\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 * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置项\n *\n * @example 单网关\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: 'http://dev-gateway.example.com',\n * })\n * ```\n *\n * @example 多网关(开发/测试环境切换)\n * ```ts\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 * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * }\n * ```\n */\n static async init(options: DebuggerOptions): Promise<Debugger> {\n const instance = new Debugger({ timeout: 10000, ...options })\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 const result = await Get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n cache: true,\n timeout: this.options.timeout,\n })\n\n if (result.error) {\n this.setHeader('err')\n this.setFooter(T.loadFailed(result.error.message), 'err')\n return\n }\n\n this.servers = result.data ?? []\n this.renderTypeOptions()\n this.setHeader('ok')\n this.setFooter(T.loaded(this.servers.length), 'ok')\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 { Get, Context } from './chunk-JQYH5FWE.js';
// src/debugger/debugger.ts
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];
}
/**
* 初始化并渲染调试面板
*
* 调用后在页面右下角插入浮动按钮,点击展开调试面板。
* 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。
* 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。
*
* @param options 配置项
*
* @example 单网关
* ```ts
* import { Debugger } from '@snack-kit/lib/debugger'
*
* await Debugger.init({
* gateways: 'http://dev-gateway.example.com',
* })
* ```
*
* @example 多网关(开发/测试环境切换)
* ```ts
* await Debugger.init({
* gateways: [
* 'http://dev-gateway.example.com',
* 'http://test-gateway.example.com',
* ],
* timeout: 5000,
* })
* ```
*
* @example 仅在非生产环境加载
* ```ts
* if (import.meta.env.DEV) {
* const { Debugger } = await import('@snack-kit/lib/debugger')
* await Debugger.init({ gateways: 'http://dev-gateway.example.com' })
* }
* ```
*/
static async init(options) {
const instance = new _Debugger({ timeout: 1e4, ...options });
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(), "");
const result = await Get(`${gwUrl}/web-debug/host/list`, {
cache: true,
timeout: this.options.timeout
});
if (result.error) {
this.setHeader("err");
this.setFooter(T.loadFailed(result.error.message), "err");
return;
}
this.servers = result.data ?? [];
this.renderTypeOptions();
this.setHeader("ok");
this.setFooter(T.loaded(this.servers.length), "ok");
}
/** 渲染标签选项 */
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-4DLFIN3C.js.map
//# sourceMappingURL=chunk-4DLFIN3C.js.map
{"version":3,"sources":["../../src/debugger/debugger.ts"],"names":[],"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;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","file":"chunk-4DLFIN3C.js","sourcesContent":["import { Get } from '../http/client'\nimport { Context } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\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/** 是否为中文环境 */\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 * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置项\n *\n * @example 单网关\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: 'http://dev-gateway.example.com',\n * })\n * ```\n *\n * @example 多网关(开发/测试环境切换)\n * ```ts\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 * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * }\n * ```\n */\n static async init(options: DebuggerOptions): Promise<Debugger> {\n const instance = new Debugger({ timeout: 10000, ...options })\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 const result = await Get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n cache: true,\n timeout: this.options.timeout,\n })\n\n if (result.error) {\n this.setHeader('err')\n this.setFooter(T.loadFailed(result.error.message), 'err')\n return\n }\n\n this.servers = result.data ?? []\n this.renderTypeOptions()\n this.setHeader('ok')\n this.setFooter(T.loaded(this.servers.length), 'ok')\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"]}
+2
-2
'use strict';
var chunkJBJEXFQC_cjs = require('./chunk-JBJEXFQC.cjs');
var chunkYOWLTZM5_cjs = require('./chunk-YOWLTZM5.cjs');
require('./chunk-N7BJS6LI.cjs');

@@ -10,5 +10,5 @@

enumerable: true,
get: function () { return chunkJBJEXFQC_cjs.Debugger; }
get: function () { return chunkYOWLTZM5_cjs.Debugger; }
});
//# sourceMappingURL=debugger.cjs.map
//# sourceMappingURL=debugger.cjs.map
'use strict';
require('./chunk-YY5WQN7B.cjs');
var chunkJBJEXFQC_cjs = require('./chunk-JBJEXFQC.cjs');
var chunkYOWLTZM5_cjs = require('./chunk-YOWLTZM5.cjs');
var chunkN7BJS6LI_cjs = require('./chunk-N7BJS6LI.cjs');
// package.json
var version = "0.1.0";
var version = "0.2.0";
Object.defineProperty(exports, "Debugger", {
enumerable: true,
get: function () { return chunkJBJEXFQC_cjs.Debugger; }
get: function () { return chunkYOWLTZM5_cjs.Debugger; }
});

@@ -14,0 +14,0 @@ Object.defineProperty(exports, "Cancel", {

@@ -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.1.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 },\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.2.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 },\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-QBBEQHXG.js';
export { Debugger } from './chunk-4DLFIN3C.js';
import './chunk-JQYH5FWE.js';
//# sourceMappingURL=debugger.js.map
//# sourceMappingURL=debugger.js.map
import './chunk-BEL6AFK4.js';
export { Debugger } from './chunk-QBBEQHXG.js';
export { Debugger } from './chunk-4DLFIN3C.js';
export { Cancel, CancelAll, Context, Ctx, Del, Get, HttpContext, Origin, Patch, Post, Put, Request } from './chunk-JQYH5FWE.js';
// package.json
var version = "0.1.0";
var version = "0.2.0";

@@ -8,0 +8,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.1.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 },\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.2.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 },\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"]}

@@ -34,3 +34,8 @@ /**

private servers;
private currentCtxMap;
private panelEl;
private headerDot;
private gwVersion;
private footerEl;
private copyAllBtn;
private gwSelect;

@@ -40,3 +45,3 @@ private typeSelect;

private ctxList;
private statusEl;
private ctxSearchInput;
private constructor();

@@ -85,32 +90,31 @@ /**

private buildDOM;
private createRow;
/** 创建普通字段行(label + content) */
private createField;
/** 创建带操作按钮的字段行(label + action button + content) */
private createFieldWithAction;
/** 恢复上次持久化的状态 */
private restoreState;
/** 网关切换处理 */
private onGatewayChange;
/** 服务类型切换处理 */
private onTypeChange;
/** 服务切换处理 */
private onServerChange;
/**
* 从网关加载服务列表
* @param gwUrl 网关 URL
*/
/** 从网关加载服务列表 */
private loadServers;
/** 渲染服务类型选项 */
/** 渲染标签选项 */
private renderTypeOptions;
/** 根据类型渲染服务选项 */
/** 根据标签渲染服务选项 */
private renderServerOptions;
/**
* 切换目标服务,重新加载 Context 路由映射
* @param key 服务 key
*/
/** 切换目标服务,重新加载 Context 路由映射 */
private applyServer;
/** 渲染路由列表 */
private renderCtxList;
/** 复制文本到剪贴板 */
private copyText;
private setStatus;
/** 过滤路由列表 */
private filterCtx;
/** 复制单个 ctx key */
private copyCtxItem;
/** 一键复制全部上下文为 JSON */
private copyAllCtx;
private setHeader;
private setFooter;
}
export { Debugger, type DebuggerOptions };

@@ -5,4 +5,4 @@ export { Cancel, CancelAll, Context, ContextInfo, Ctx, Del, Get, HttpContext, HttpError, HttpRequestConfig, HttpResult, Origin, Patch, Post, Put, Request, ServerItem } from './http.js';

var version = "0.1.0";
var version = "0.2.0";
export { version as VERSION };
{
"name": "@snack-kit/lib",
"version": "0.1.0",
"version": "0.2.0",
"description": "Enterprise-grade utility library",

@@ -5,0 +5,0 @@ "keywords": [],

'use strict';
var chunkN7BJS6LI_cjs = require('./chunk-N7BJS6LI.cjs');
// src/debugger/debugger.ts
var STORAGE_PREFIX = "__snackkit_debugger__";
var KEY_GATEWAY = `${STORAGE_PREFIX}gateway`;
var KEY_TYPE = `${STORAGE_PREFIX}type`;
var KEY_SERVER = `${STORAGE_PREFIX}server`;
var PANEL_STYLE = `
#__snackkit_debugger__ {
position: fixed;
bottom: 16px;
right: 16px;
z-index: 99999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 13px;
color: #e2e8f0;
}
#__snackkit_debugger__ .dbg-toggle {
width: 40px;
height: 40px;
border-radius: 50%;
background: #3b82f6;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
margin-left: auto;
color: #fff;
font-size: 18px;
}
#__snackkit_debugger__ .dbg-panel {
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
padding: 12px;
width: 320px;
margin-bottom: 8px;
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
display: none;
}
#__snackkit_debugger__ .dbg-panel.open { display: block; }
#__snackkit_debugger__ .dbg-row {
margin-bottom: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
#__snackkit_debugger__ label {
font-size: 11px;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 0.05em;
}
#__snackkit_debugger__ select {
background: #0f172a;
border: 1px solid #334155;
border-radius: 4px;
color: #e2e8f0;
padding: 4px 8px;
font-size: 13px;
width: 100%;
cursor: pointer;
}
#__snackkit_debugger__ .dbg-ctx-list {
max-height: 160px;
overflow-y: auto;
border: 1px solid #334155;
border-radius: 4px;
background: #0f172a;
}
#__snackkit_debugger__ .dbg-ctx-item {
padding: 4px 8px;
border-bottom: 1px solid #1e293b;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
#__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }
#__snackkit_debugger__ .dbg-ctx-item:hover { background: #1e293b; }
#__snackkit_debugger__ .dbg-ctx-key { color: #60a5fa; }
#__snackkit_debugger__ .dbg-ctx-val { color: #94a3b8; font-size: 11px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#__snackkit_debugger__ .dbg-status {
font-size: 11px;
color: #64748b;
margin-top: 4px;
min-height: 16px;
}
#__snackkit_debugger__ .dbg-status.ok { color: #22c55e; }
#__snackkit_debugger__ .dbg-status.err { color: #ef4444; }
`;
var Debugger = class _Debugger {
constructor(options) {
this.servers = [];
this.options = options;
this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways];
}
/**
* 初始化并渲染调试面板
*
* 调用后在页面右下角插入浮动按钮,点击展开调试面板。
* 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。
* 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。
*
* @param options 配置项
*
* @example 单网关
* ```ts
* import { Debugger } from '@snack-kit/lib/debugger'
*
* await Debugger.init({
* gateways: 'http://dev-gateway.example.com',
* })
* ```
*
* @example 多网关(开发/测试环境切换)
* ```ts
* await Debugger.init({
* gateways: [
* 'http://dev-gateway.example.com',
* 'http://test-gateway.example.com',
* ],
* timeout: 5000,
* })
* ```
*
* @example 仅在非生产环境加载
* ```ts
* if (import.meta.env.DEV) {
* const { Debugger } = await import('@snack-kit/lib/debugger')
* await Debugger.init({ gateways: 'http://dev-gateway.example.com' })
* }
* ```
*/
static async init(options) {
const instance = new _Debugger({ timeout: 1e4, ...options });
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 gwRow = this.createRow("\u7F51\u5173 (Gateway)");
const gwSelect = document.createElement("select");
this.gateways.forEach((gw) => {
const opt = document.createElement("option");
opt.value = gw;
opt.textContent = gw;
gwSelect.appendChild(opt);
});
gwSelect.addEventListener("change", () => this.onGatewayChange());
gwRow.appendChild(gwSelect);
this.gwSelect = gwSelect;
panel.appendChild(gwRow);
const typeRow = this.createRow("\u670D\u52A1\u7C7B\u578B (Type)");
const typeSelect = document.createElement("select");
typeSelect.addEventListener("change", () => this.onTypeChange());
typeRow.appendChild(typeSelect);
this.typeSelect = typeSelect;
panel.appendChild(typeRow);
const serverRow = this.createRow("\u670D\u52A1 (Server)");
const serverSelect = document.createElement("select");
serverSelect.addEventListener("change", () => this.onServerChange());
serverRow.appendChild(serverSelect);
this.serverSelect = serverSelect;
panel.appendChild(serverRow);
const ctxRow = this.createRow("\u8DEF\u7531 (Ctx) - \u70B9\u51FB\u590D\u5236");
const ctxList = document.createElement("div");
ctxList.className = "dbg-ctx-list";
ctxRow.appendChild(ctxList);
this.ctxList = ctxList;
panel.appendChild(ctxRow);
const status = document.createElement("div");
status.className = "dbg-status";
this.statusEl = status;
panel.appendChild(status);
const toggle = document.createElement("button");
toggle.className = "dbg-toggle";
toggle.textContent = "\u{1F41B}";
toggle.setAttribute("title", "Snack Debugger");
toggle.addEventListener("click", () => panel.classList.toggle("open"));
root.appendChild(panel);
root.appendChild(toggle);
document.body.appendChild(root);
}
createRow(label) {
const row = document.createElement("div");
row.className = "dbg-row";
const lbl = document.createElement("label");
lbl.textContent = label;
row.appendChild(lbl);
return row;
}
/** 恢复上次持久化的状态 */
async restoreState() {
const savedGw = localStorage.getItem(KEY_GATEWAY);
if (savedGw && this.gateways.includes(savedGw)) {
this.gwSelect.value = savedGw;
}
await this.loadServers(this.gwSelect.value);
const savedType = localStorage.getItem(KEY_TYPE);
if (savedType) {
this.typeSelect.value = savedType;
this.renderServerOptions(savedType);
}
const savedServer = localStorage.getItem(KEY_SERVER);
if (savedServer && this.serverSelect.querySelector(`option[value="${savedServer}"]`)) {
this.serverSelect.value = savedServer;
await this.applyServer(savedServer);
}
}
/** 网关切换处理 */
async onGatewayChange() {
const gw = this.gwSelect.value;
localStorage.setItem(KEY_GATEWAY, gw);
await this.loadServers(gw);
}
/** 服务类型切换处理 */
onTypeChange() {
const type = this.typeSelect.value;
localStorage.setItem(KEY_TYPE, type);
this.renderServerOptions(type);
}
/** 服务切换处理 */
async onServerChange() {
const key = this.serverSelect.value;
localStorage.setItem(KEY_SERVER, key);
await this.applyServer(key);
}
/**
* 从网关加载服务列表
* @param gwUrl 网关 URL
*/
async loadServers(gwUrl) {
this.setStatus("\u52A0\u8F7D\u4E2D...", "");
const result = await chunkN7BJS6LI_cjs.Get(`${gwUrl}/web-debug/host/list`, {
cache: true,
timeout: this.options.timeout
});
if (result.error) {
this.setStatus(`\u52A0\u8F7D\u5931\u8D25: ${result.error.message}`, "err");
return;
}
this.servers = result.data ?? [];
this.renderTypeOptions();
this.setStatus(`\u5DF2\u52A0\u8F7D ${this.servers.length} \u4E2A\u670D\u52A1`, "ok");
}
/** 渲染服务类型选项 */
renderTypeOptions() {
const types = [...new Set(this.servers.map((s) => s.type))];
this.typeSelect.innerHTML = "";
const allOpt = document.createElement("option");
allOpt.value = "";
allOpt.textContent = "\u5168\u90E8";
this.typeSelect.appendChild(allOpt);
types.forEach((t) => {
const opt = document.createElement("option");
opt.value = t;
opt.textContent = t;
this.typeSelect.appendChild(opt);
});
this.renderServerOptions(this.typeSelect.value);
}
/** 根据类型渲染服务选项 */
renderServerOptions(type) {
const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers;
this.serverSelect.innerHTML = "";
const emptyOpt = document.createElement("option");
emptyOpt.value = "";
emptyOpt.textContent = "\u8BF7\u9009\u62E9\u670D\u52A1";
this.serverSelect.appendChild(emptyOpt);
filtered.forEach((s) => {
const opt = document.createElement("option");
opt.value = s.key;
opt.textContent = `${s.name} (${s.key})`;
this.serverSelect.appendChild(opt);
});
}
/**
* 切换目标服务,重新加载 Context 路由映射
* @param key 服务 key
*/
async applyServer(key) {
if (!key) {
this.renderCtxList({});
return;
}
const server = this.servers.find((s) => s.key === key);
if (!server) return;
this.setStatus("\u5207\u6362\u670D\u52A1\u4E2D...", "");
try {
await chunkN7BJS6LI_cjs.Context.load(server.origin, this.options.timeout);
const ctxMap = {};
const res = await fetch(`${server.origin}/ngw/context`);
const raw = res.ok ? await res.json() : {};
for (const [k, v] of Object.entries(raw)) {
if (k !== "$info" && typeof v === "string") ctxMap[k] = v;
}
this.renderCtxList(ctxMap);
this.setStatus(`\u5DF2\u5207\u6362\u81F3 ${server.name}`, "ok");
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
this.setStatus(`\u5207\u6362\u5931\u8D25: ${msg}`, "err");
}
}
/** 渲染路由列表 */
renderCtxList(ctxMap) {
this.ctxList.innerHTML = "";
const entries = Object.entries(ctxMap);
if (entries.length === 0) {
this.ctxList.textContent = "\u6682\u65E0\u8DEF\u7531";
return;
}
entries.forEach(([k, v]) => {
const item = document.createElement("div");
item.className = "dbg-ctx-item";
const keySpan = document.createElement("span");
keySpan.className = "dbg-ctx-key";
keySpan.textContent = k;
const valSpan = document.createElement("span");
valSpan.className = "dbg-ctx-val";
valSpan.textContent = v;
valSpan.setAttribute("title", v);
item.appendChild(keySpan);
item.appendChild(valSpan);
item.addEventListener("click", () => this.copyText(k));
this.ctxList.appendChild(item);
});
}
/** 复制文本到剪贴板 */
copyText(text) {
navigator.clipboard.writeText(text).then(() => {
this.setStatus(`\u5DF2\u590D\u5236: ${text}`, "ok");
setTimeout(() => this.setStatus("", ""), 2e3);
});
}
setStatus(msg, cls) {
this.statusEl.textContent = msg;
this.statusEl.className = `dbg-status${cls ? ` ${cls}` : ""}`;
}
};
exports.Debugger = Debugger;
//# sourceMappingURL=chunk-JBJEXFQC.cjs.map
//# sourceMappingURL=chunk-JBJEXFQC.cjs.map
{"version":3,"sources":["../../src/debugger/debugger.ts"],"names":["Get","Context"],"mappings":"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","file":"chunk-JBJEXFQC.cjs","sourcesContent":["import { Get } from '../http/client'\nimport { Context } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\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/** 面板内联样式 */\nconst PANEL_STYLE = `\n #__snackkit_debugger__ {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 99999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 13px;\n color: #e2e8f0;\n }\n #__snackkit_debugger__ .dbg-toggle {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: #3b82f6;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n margin-left: auto;\n color: #fff;\n font-size: 18px;\n }\n #__snackkit_debugger__ .dbg-panel {\n background: #1e293b;\n border: 1px solid #334155;\n border-radius: 8px;\n padding: 12px;\n width: 320px;\n margin-bottom: 8px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n display: none;\n }\n #__snackkit_debugger__ .dbg-panel.open { display: block; }\n #__snackkit_debugger__ .dbg-row {\n margin-bottom: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n #__snackkit_debugger__ label {\n font-size: 11px;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n #__snackkit_debugger__ select {\n background: #0f172a;\n border: 1px solid #334155;\n border-radius: 4px;\n color: #e2e8f0;\n padding: 4px 8px;\n font-size: 13px;\n width: 100%;\n cursor: pointer;\n }\n #__snackkit_debugger__ .dbg-ctx-list {\n max-height: 160px;\n overflow-y: auto;\n border: 1px solid #334155;\n border-radius: 4px;\n background: #0f172a;\n }\n #__snackkit_debugger__ .dbg-ctx-item {\n padding: 4px 8px;\n border-bottom: 1px solid #1e293b;\n cursor: pointer;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }\n #__snackkit_debugger__ .dbg-ctx-item:hover { background: #1e293b; }\n #__snackkit_debugger__ .dbg-ctx-key { color: #60a5fa; }\n #__snackkit_debugger__ .dbg-ctx-val { color: #94a3b8; font-size: 11px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n #__snackkit_debugger__ .dbg-status {\n font-size: 11px;\n color: #64748b;\n margin-top: 4px;\n min-height: 16px;\n }\n #__snackkit_debugger__ .dbg-status.ok { color: #22c55e; }\n #__snackkit_debugger__ .dbg-status.err { color: #ef4444; }\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 panelEl!: HTMLElement\n private gwSelect!: HTMLSelectElement\n private typeSelect!: HTMLSelectElement\n private serverSelect!: HTMLSelectElement\n private ctxList!: HTMLElement\n private statusEl!: HTMLElement\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 * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置项\n *\n * @example 单网关\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: 'http://dev-gateway.example.com',\n * })\n * ```\n *\n * @example 多网关(开发/测试环境切换)\n * ```ts\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 * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * }\n * ```\n */\n static async init(options: DebuggerOptions): Promise<Debugger> {\n const instance = new Debugger({ timeout: 10000, ...options })\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 // 网关选择\n const gwRow = this.createRow('网关 (Gateway)')\n const gwSelect = document.createElement('select')\n this.gateways.forEach((gw) => {\n const opt = document.createElement('option')\n opt.value = gw\n opt.textContent = gw\n gwSelect.appendChild(opt)\n })\n gwSelect.addEventListener('change', () => this.onGatewayChange())\n gwRow.appendChild(gwSelect)\n this.gwSelect = gwSelect\n panel.appendChild(gwRow)\n\n // 服务类型选择\n const typeRow = this.createRow('服务类型 (Type)')\n const typeSelect = document.createElement('select')\n typeSelect.addEventListener('change', () => this.onTypeChange())\n typeRow.appendChild(typeSelect)\n this.typeSelect = typeSelect\n panel.appendChild(typeRow)\n\n // 服务选择\n const serverRow = this.createRow('服务 (Server)')\n const serverSelect = document.createElement('select')\n serverSelect.addEventListener('change', () => this.onServerChange())\n serverRow.appendChild(serverSelect)\n this.serverSelect = serverSelect\n panel.appendChild(serverRow)\n\n // 路由列表\n const ctxRow = this.createRow('路由 (Ctx) - 点击复制')\n const ctxList = document.createElement('div')\n ctxList.className = 'dbg-ctx-list'\n ctxRow.appendChild(ctxList)\n this.ctxList = ctxList\n panel.appendChild(ctxRow)\n\n // 状态提示\n const status = document.createElement('div')\n status.className = 'dbg-status'\n this.statusEl = status\n panel.appendChild(status)\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', () => panel.classList.toggle('open'))\n\n root.appendChild(panel)\n root.appendChild(toggle)\n document.body.appendChild(root)\n }\n\n private createRow(label: string): HTMLElement {\n const row = document.createElement('div')\n row.className = 'dbg-row'\n const lbl = document.createElement('label')\n lbl.textContent = label\n row.appendChild(lbl)\n return row\n }\n\n /** 恢复上次持久化的状态 */\n private async restoreState(): Promise<void> {\n const savedGw = localStorage.getItem(KEY_GATEWAY)\n if (savedGw && this.gateways.includes(savedGw)) {\n this.gwSelect.value = savedGw\n }\n await this.loadServers(this.gwSelect.value)\n\n const savedType = localStorage.getItem(KEY_TYPE)\n if (savedType) {\n this.typeSelect.value = savedType\n this.renderServerOptions(savedType)\n }\n\n const savedServer = localStorage.getItem(KEY_SERVER)\n if (savedServer && this.serverSelect.querySelector(`option[value=\"${savedServer}\"]`)) {\n this.serverSelect.value = savedServer\n await this.applyServer(savedServer)\n }\n }\n\n /** 网关切换处理 */\n private async onGatewayChange(): Promise<void> {\n const gw = this.gwSelect.value\n localStorage.setItem(KEY_GATEWAY, gw)\n await this.loadServers(gw)\n }\n\n /** 服务类型切换处理 */\n private onTypeChange(): void {\n const type = this.typeSelect.value\n localStorage.setItem(KEY_TYPE, type)\n this.renderServerOptions(type)\n }\n\n /** 服务切换处理 */\n private async onServerChange(): Promise<void> {\n const key = this.serverSelect.value\n localStorage.setItem(KEY_SERVER, key)\n await this.applyServer(key)\n }\n\n /**\n * 从网关加载服务列表\n * @param gwUrl 网关 URL\n */\n private async loadServers(gwUrl: string): Promise<void> {\n this.setStatus('加载中...', '')\n const result = await Get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n cache: true,\n timeout: this.options.timeout,\n })\n\n if (result.error) {\n this.setStatus(`加载失败: ${result.error.message}`, 'err')\n return\n }\n\n this.servers = result.data ?? []\n this.renderTypeOptions()\n this.setStatus(`已加载 ${this.servers.length} 个服务`, 'ok')\n }\n\n /** 渲染服务类型选项 */\n private renderTypeOptions(): void {\n const types = [...new Set(this.servers.map((s) => s.type))]\n this.typeSelect.innerHTML = ''\n const allOpt = document.createElement('option')\n allOpt.value = ''\n allOpt.textContent = '全部'\n this.typeSelect.appendChild(allOpt)\n types.forEach((t) => {\n const opt = document.createElement('option')\n opt.value = t\n opt.textContent = t\n this.typeSelect.appendChild(opt)\n })\n this.renderServerOptions(this.typeSelect.value)\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.innerHTML = ''\n const emptyOpt = document.createElement('option')\n emptyOpt.value = ''\n emptyOpt.textContent = '请选择服务'\n this.serverSelect.appendChild(emptyOpt)\n filtered.forEach((s) => {\n const opt = document.createElement('option')\n opt.value = s.key\n opt.textContent = `${s.name} (${s.key})`\n this.serverSelect.appendChild(opt)\n })\n }\n\n /**\n * 切换目标服务,重新加载 Context 路由映射\n * @param key 服务 key\n */\n private async applyServer(key: string): Promise<void> {\n if (!key) {\n this.renderCtxList({})\n return\n }\n const server = this.servers.find((s) => s.key === key)\n if (!server) return\n\n this.setStatus('切换服务中...', '')\n try {\n await Context.load(server.origin, this.options.timeout)\n // 路由映射已由 Context.load 解析,通过内部 store 驱动路由\n // 此处仅展示 Context 加载后可用的 ctx 条目(来自 /ngw/context 响应)\n const ctxMap: Record<string, string> = {}\n // Context 内部 store 不对外暴露迭代,通过重新请求获取展示数据\n const res = await fetch(`${server.origin}/ngw/context`)\n const raw: Record<string, unknown> = res.ok ? await res.json() : {}\n for (const [k, v] of Object.entries(raw)) {\n if (k !== '$info' && typeof v === 'string') ctxMap[k] = v\n }\n this.renderCtxList(ctxMap)\n this.setStatus(`已切换至 ${server.name}`, 'ok')\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this.setStatus(`切换失败: ${msg}`, 'err')\n }\n }\n\n /** 渲染路由列表 */\n private renderCtxList(ctxMap: Record<string, string>): void {\n this.ctxList.innerHTML = ''\n const entries = Object.entries(ctxMap)\n if (entries.length === 0) {\n this.ctxList.textContent = '暂无路由'\n return\n }\n entries.forEach(([k, v]) => {\n const item = document.createElement('div')\n item.className = 'dbg-ctx-item'\n const keySpan = document.createElement('span')\n keySpan.className = 'dbg-ctx-key'\n keySpan.textContent = k\n const valSpan = document.createElement('span')\n valSpan.className = 'dbg-ctx-val'\n valSpan.textContent = v\n valSpan.setAttribute('title', v)\n item.appendChild(keySpan)\n item.appendChild(valSpan)\n item.addEventListener('click', () => this.copyText(k))\n this.ctxList.appendChild(item)\n })\n }\n\n /** 复制文本到剪贴板 */\n private copyText(text: string): void {\n navigator.clipboard.writeText(text).then(() => {\n this.setStatus(`已复制: ${text}`, 'ok')\n setTimeout(() => this.setStatus('', ''), 2000)\n })\n }\n\n private setStatus(msg: string, cls: '' | 'ok' | 'err'): void {\n this.statusEl.textContent = msg\n this.statusEl.className = `dbg-status${cls ? ` ${cls}` : ''}`\n }\n}\n"]}
import { Get, Context } from './chunk-JQYH5FWE.js';
// src/debugger/debugger.ts
var STORAGE_PREFIX = "__snackkit_debugger__";
var KEY_GATEWAY = `${STORAGE_PREFIX}gateway`;
var KEY_TYPE = `${STORAGE_PREFIX}type`;
var KEY_SERVER = `${STORAGE_PREFIX}server`;
var PANEL_STYLE = `
#__snackkit_debugger__ {
position: fixed;
bottom: 16px;
right: 16px;
z-index: 99999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 13px;
color: #e2e8f0;
}
#__snackkit_debugger__ .dbg-toggle {
width: 40px;
height: 40px;
border-radius: 50%;
background: #3b82f6;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
margin-left: auto;
color: #fff;
font-size: 18px;
}
#__snackkit_debugger__ .dbg-panel {
background: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
padding: 12px;
width: 320px;
margin-bottom: 8px;
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
display: none;
}
#__snackkit_debugger__ .dbg-panel.open { display: block; }
#__snackkit_debugger__ .dbg-row {
margin-bottom: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
#__snackkit_debugger__ label {
font-size: 11px;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 0.05em;
}
#__snackkit_debugger__ select {
background: #0f172a;
border: 1px solid #334155;
border-radius: 4px;
color: #e2e8f0;
padding: 4px 8px;
font-size: 13px;
width: 100%;
cursor: pointer;
}
#__snackkit_debugger__ .dbg-ctx-list {
max-height: 160px;
overflow-y: auto;
border: 1px solid #334155;
border-radius: 4px;
background: #0f172a;
}
#__snackkit_debugger__ .dbg-ctx-item {
padding: 4px 8px;
border-bottom: 1px solid #1e293b;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
#__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }
#__snackkit_debugger__ .dbg-ctx-item:hover { background: #1e293b; }
#__snackkit_debugger__ .dbg-ctx-key { color: #60a5fa; }
#__snackkit_debugger__ .dbg-ctx-val { color: #94a3b8; font-size: 11px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#__snackkit_debugger__ .dbg-status {
font-size: 11px;
color: #64748b;
margin-top: 4px;
min-height: 16px;
}
#__snackkit_debugger__ .dbg-status.ok { color: #22c55e; }
#__snackkit_debugger__ .dbg-status.err { color: #ef4444; }
`;
var Debugger = class _Debugger {
constructor(options) {
this.servers = [];
this.options = options;
this.gateways = Array.isArray(options.gateways) ? options.gateways : [options.gateways];
}
/**
* 初始化并渲染调试面板
*
* 调用后在页面右下角插入浮动按钮,点击展开调试面板。
* 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。
* 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。
*
* @param options 配置项
*
* @example 单网关
* ```ts
* import { Debugger } from '@snack-kit/lib/debugger'
*
* await Debugger.init({
* gateways: 'http://dev-gateway.example.com',
* })
* ```
*
* @example 多网关(开发/测试环境切换)
* ```ts
* await Debugger.init({
* gateways: [
* 'http://dev-gateway.example.com',
* 'http://test-gateway.example.com',
* ],
* timeout: 5000,
* })
* ```
*
* @example 仅在非生产环境加载
* ```ts
* if (import.meta.env.DEV) {
* const { Debugger } = await import('@snack-kit/lib/debugger')
* await Debugger.init({ gateways: 'http://dev-gateway.example.com' })
* }
* ```
*/
static async init(options) {
const instance = new _Debugger({ timeout: 1e4, ...options });
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 gwRow = this.createRow("\u7F51\u5173 (Gateway)");
const gwSelect = document.createElement("select");
this.gateways.forEach((gw) => {
const opt = document.createElement("option");
opt.value = gw;
opt.textContent = gw;
gwSelect.appendChild(opt);
});
gwSelect.addEventListener("change", () => this.onGatewayChange());
gwRow.appendChild(gwSelect);
this.gwSelect = gwSelect;
panel.appendChild(gwRow);
const typeRow = this.createRow("\u670D\u52A1\u7C7B\u578B (Type)");
const typeSelect = document.createElement("select");
typeSelect.addEventListener("change", () => this.onTypeChange());
typeRow.appendChild(typeSelect);
this.typeSelect = typeSelect;
panel.appendChild(typeRow);
const serverRow = this.createRow("\u670D\u52A1 (Server)");
const serverSelect = document.createElement("select");
serverSelect.addEventListener("change", () => this.onServerChange());
serverRow.appendChild(serverSelect);
this.serverSelect = serverSelect;
panel.appendChild(serverRow);
const ctxRow = this.createRow("\u8DEF\u7531 (Ctx) - \u70B9\u51FB\u590D\u5236");
const ctxList = document.createElement("div");
ctxList.className = "dbg-ctx-list";
ctxRow.appendChild(ctxList);
this.ctxList = ctxList;
panel.appendChild(ctxRow);
const status = document.createElement("div");
status.className = "dbg-status";
this.statusEl = status;
panel.appendChild(status);
const toggle = document.createElement("button");
toggle.className = "dbg-toggle";
toggle.textContent = "\u{1F41B}";
toggle.setAttribute("title", "Snack Debugger");
toggle.addEventListener("click", () => panel.classList.toggle("open"));
root.appendChild(panel);
root.appendChild(toggle);
document.body.appendChild(root);
}
createRow(label) {
const row = document.createElement("div");
row.className = "dbg-row";
const lbl = document.createElement("label");
lbl.textContent = label;
row.appendChild(lbl);
return row;
}
/** 恢复上次持久化的状态 */
async restoreState() {
const savedGw = localStorage.getItem(KEY_GATEWAY);
if (savedGw && this.gateways.includes(savedGw)) {
this.gwSelect.value = savedGw;
}
await this.loadServers(this.gwSelect.value);
const savedType = localStorage.getItem(KEY_TYPE);
if (savedType) {
this.typeSelect.value = savedType;
this.renderServerOptions(savedType);
}
const savedServer = localStorage.getItem(KEY_SERVER);
if (savedServer && this.serverSelect.querySelector(`option[value="${savedServer}"]`)) {
this.serverSelect.value = savedServer;
await this.applyServer(savedServer);
}
}
/** 网关切换处理 */
async onGatewayChange() {
const gw = this.gwSelect.value;
localStorage.setItem(KEY_GATEWAY, gw);
await this.loadServers(gw);
}
/** 服务类型切换处理 */
onTypeChange() {
const type = this.typeSelect.value;
localStorage.setItem(KEY_TYPE, type);
this.renderServerOptions(type);
}
/** 服务切换处理 */
async onServerChange() {
const key = this.serverSelect.value;
localStorage.setItem(KEY_SERVER, key);
await this.applyServer(key);
}
/**
* 从网关加载服务列表
* @param gwUrl 网关 URL
*/
async loadServers(gwUrl) {
this.setStatus("\u52A0\u8F7D\u4E2D...", "");
const result = await Get(`${gwUrl}/web-debug/host/list`, {
cache: true,
timeout: this.options.timeout
});
if (result.error) {
this.setStatus(`\u52A0\u8F7D\u5931\u8D25: ${result.error.message}`, "err");
return;
}
this.servers = result.data ?? [];
this.renderTypeOptions();
this.setStatus(`\u5DF2\u52A0\u8F7D ${this.servers.length} \u4E2A\u670D\u52A1`, "ok");
}
/** 渲染服务类型选项 */
renderTypeOptions() {
const types = [...new Set(this.servers.map((s) => s.type))];
this.typeSelect.innerHTML = "";
const allOpt = document.createElement("option");
allOpt.value = "";
allOpt.textContent = "\u5168\u90E8";
this.typeSelect.appendChild(allOpt);
types.forEach((t) => {
const opt = document.createElement("option");
opt.value = t;
opt.textContent = t;
this.typeSelect.appendChild(opt);
});
this.renderServerOptions(this.typeSelect.value);
}
/** 根据类型渲染服务选项 */
renderServerOptions(type) {
const filtered = type ? this.servers.filter((s) => s.type === type) : this.servers;
this.serverSelect.innerHTML = "";
const emptyOpt = document.createElement("option");
emptyOpt.value = "";
emptyOpt.textContent = "\u8BF7\u9009\u62E9\u670D\u52A1";
this.serverSelect.appendChild(emptyOpt);
filtered.forEach((s) => {
const opt = document.createElement("option");
opt.value = s.key;
opt.textContent = `${s.name} (${s.key})`;
this.serverSelect.appendChild(opt);
});
}
/**
* 切换目标服务,重新加载 Context 路由映射
* @param key 服务 key
*/
async applyServer(key) {
if (!key) {
this.renderCtxList({});
return;
}
const server = this.servers.find((s) => s.key === key);
if (!server) return;
this.setStatus("\u5207\u6362\u670D\u52A1\u4E2D...", "");
try {
await Context.load(server.origin, this.options.timeout);
const ctxMap = {};
const res = await fetch(`${server.origin}/ngw/context`);
const raw = res.ok ? await res.json() : {};
for (const [k, v] of Object.entries(raw)) {
if (k !== "$info" && typeof v === "string") ctxMap[k] = v;
}
this.renderCtxList(ctxMap);
this.setStatus(`\u5DF2\u5207\u6362\u81F3 ${server.name}`, "ok");
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
this.setStatus(`\u5207\u6362\u5931\u8D25: ${msg}`, "err");
}
}
/** 渲染路由列表 */
renderCtxList(ctxMap) {
this.ctxList.innerHTML = "";
const entries = Object.entries(ctxMap);
if (entries.length === 0) {
this.ctxList.textContent = "\u6682\u65E0\u8DEF\u7531";
return;
}
entries.forEach(([k, v]) => {
const item = document.createElement("div");
item.className = "dbg-ctx-item";
const keySpan = document.createElement("span");
keySpan.className = "dbg-ctx-key";
keySpan.textContent = k;
const valSpan = document.createElement("span");
valSpan.className = "dbg-ctx-val";
valSpan.textContent = v;
valSpan.setAttribute("title", v);
item.appendChild(keySpan);
item.appendChild(valSpan);
item.addEventListener("click", () => this.copyText(k));
this.ctxList.appendChild(item);
});
}
/** 复制文本到剪贴板 */
copyText(text) {
navigator.clipboard.writeText(text).then(() => {
this.setStatus(`\u5DF2\u590D\u5236: ${text}`, "ok");
setTimeout(() => this.setStatus("", ""), 2e3);
});
}
setStatus(msg, cls) {
this.statusEl.textContent = msg;
this.statusEl.className = `dbg-status${cls ? ` ${cls}` : ""}`;
}
};
export { Debugger };
//# sourceMappingURL=chunk-QBBEQHXG.js.map
//# sourceMappingURL=chunk-QBBEQHXG.js.map
{"version":3,"sources":["../../src/debugger/debugger.ts"],"names":[],"mappings":"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","file":"chunk-QBBEQHXG.js","sourcesContent":["import { Get } from '../http/client'\nimport { Context } from '../http/context'\nimport type { ServerItem } from '../http/types'\nimport type { DebuggerOptions } from './types'\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/** 面板内联样式 */\nconst PANEL_STYLE = `\n #__snackkit_debugger__ {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 99999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 13px;\n color: #e2e8f0;\n }\n #__snackkit_debugger__ .dbg-toggle {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: #3b82f6;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n margin-left: auto;\n color: #fff;\n font-size: 18px;\n }\n #__snackkit_debugger__ .dbg-panel {\n background: #1e293b;\n border: 1px solid #334155;\n border-radius: 8px;\n padding: 12px;\n width: 320px;\n margin-bottom: 8px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.4);\n display: none;\n }\n #__snackkit_debugger__ .dbg-panel.open { display: block; }\n #__snackkit_debugger__ .dbg-row {\n margin-bottom: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n #__snackkit_debugger__ label {\n font-size: 11px;\n color: #94a3b8;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n #__snackkit_debugger__ select {\n background: #0f172a;\n border: 1px solid #334155;\n border-radius: 4px;\n color: #e2e8f0;\n padding: 4px 8px;\n font-size: 13px;\n width: 100%;\n cursor: pointer;\n }\n #__snackkit_debugger__ .dbg-ctx-list {\n max-height: 160px;\n overflow-y: auto;\n border: 1px solid #334155;\n border-radius: 4px;\n background: #0f172a;\n }\n #__snackkit_debugger__ .dbg-ctx-item {\n padding: 4px 8px;\n border-bottom: 1px solid #1e293b;\n cursor: pointer;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n #__snackkit_debugger__ .dbg-ctx-item:last-child { border-bottom: none; }\n #__snackkit_debugger__ .dbg-ctx-item:hover { background: #1e293b; }\n #__snackkit_debugger__ .dbg-ctx-key { color: #60a5fa; }\n #__snackkit_debugger__ .dbg-ctx-val { color: #94a3b8; font-size: 11px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n #__snackkit_debugger__ .dbg-status {\n font-size: 11px;\n color: #64748b;\n margin-top: 4px;\n min-height: 16px;\n }\n #__snackkit_debugger__ .dbg-status.ok { color: #22c55e; }\n #__snackkit_debugger__ .dbg-status.err { color: #ef4444; }\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 panelEl!: HTMLElement\n private gwSelect!: HTMLSelectElement\n private typeSelect!: HTMLSelectElement\n private serverSelect!: HTMLSelectElement\n private ctxList!: HTMLElement\n private statusEl!: HTMLElement\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 * 面板支持:选择调试网关、切换目标服务、查看并复制路由 ctx。\n * 网关/服务选择通过 `localStorage` 持久化,刷新后自动恢复。\n *\n * @param options 配置项\n *\n * @example 单网关\n * ```ts\n * import { Debugger } from '@snack-kit/lib/debugger'\n *\n * await Debugger.init({\n * gateways: 'http://dev-gateway.example.com',\n * })\n * ```\n *\n * @example 多网关(开发/测试环境切换)\n * ```ts\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 * if (import.meta.env.DEV) {\n * const { Debugger } = await import('@snack-kit/lib/debugger')\n * await Debugger.init({ gateways: 'http://dev-gateway.example.com' })\n * }\n * ```\n */\n static async init(options: DebuggerOptions): Promise<Debugger> {\n const instance = new Debugger({ timeout: 10000, ...options })\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 // 网关选择\n const gwRow = this.createRow('网关 (Gateway)')\n const gwSelect = document.createElement('select')\n this.gateways.forEach((gw) => {\n const opt = document.createElement('option')\n opt.value = gw\n opt.textContent = gw\n gwSelect.appendChild(opt)\n })\n gwSelect.addEventListener('change', () => this.onGatewayChange())\n gwRow.appendChild(gwSelect)\n this.gwSelect = gwSelect\n panel.appendChild(gwRow)\n\n // 服务类型选择\n const typeRow = this.createRow('服务类型 (Type)')\n const typeSelect = document.createElement('select')\n typeSelect.addEventListener('change', () => this.onTypeChange())\n typeRow.appendChild(typeSelect)\n this.typeSelect = typeSelect\n panel.appendChild(typeRow)\n\n // 服务选择\n const serverRow = this.createRow('服务 (Server)')\n const serverSelect = document.createElement('select')\n serverSelect.addEventListener('change', () => this.onServerChange())\n serverRow.appendChild(serverSelect)\n this.serverSelect = serverSelect\n panel.appendChild(serverRow)\n\n // 路由列表\n const ctxRow = this.createRow('路由 (Ctx) - 点击复制')\n const ctxList = document.createElement('div')\n ctxList.className = 'dbg-ctx-list'\n ctxRow.appendChild(ctxList)\n this.ctxList = ctxList\n panel.appendChild(ctxRow)\n\n // 状态提示\n const status = document.createElement('div')\n status.className = 'dbg-status'\n this.statusEl = status\n panel.appendChild(status)\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', () => panel.classList.toggle('open'))\n\n root.appendChild(panel)\n root.appendChild(toggle)\n document.body.appendChild(root)\n }\n\n private createRow(label: string): HTMLElement {\n const row = document.createElement('div')\n row.className = 'dbg-row'\n const lbl = document.createElement('label')\n lbl.textContent = label\n row.appendChild(lbl)\n return row\n }\n\n /** 恢复上次持久化的状态 */\n private async restoreState(): Promise<void> {\n const savedGw = localStorage.getItem(KEY_GATEWAY)\n if (savedGw && this.gateways.includes(savedGw)) {\n this.gwSelect.value = savedGw\n }\n await this.loadServers(this.gwSelect.value)\n\n const savedType = localStorage.getItem(KEY_TYPE)\n if (savedType) {\n this.typeSelect.value = savedType\n this.renderServerOptions(savedType)\n }\n\n const savedServer = localStorage.getItem(KEY_SERVER)\n if (savedServer && this.serverSelect.querySelector(`option[value=\"${savedServer}\"]`)) {\n this.serverSelect.value = savedServer\n await this.applyServer(savedServer)\n }\n }\n\n /** 网关切换处理 */\n private async onGatewayChange(): Promise<void> {\n const gw = this.gwSelect.value\n localStorage.setItem(KEY_GATEWAY, gw)\n await this.loadServers(gw)\n }\n\n /** 服务类型切换处理 */\n private onTypeChange(): void {\n const type = this.typeSelect.value\n localStorage.setItem(KEY_TYPE, type)\n this.renderServerOptions(type)\n }\n\n /** 服务切换处理 */\n private async onServerChange(): Promise<void> {\n const key = this.serverSelect.value\n localStorage.setItem(KEY_SERVER, key)\n await this.applyServer(key)\n }\n\n /**\n * 从网关加载服务列表\n * @param gwUrl 网关 URL\n */\n private async loadServers(gwUrl: string): Promise<void> {\n this.setStatus('加载中...', '')\n const result = await Get<ServerItem[]>(`${gwUrl}/web-debug/host/list`, {\n cache: true,\n timeout: this.options.timeout,\n })\n\n if (result.error) {\n this.setStatus(`加载失败: ${result.error.message}`, 'err')\n return\n }\n\n this.servers = result.data ?? []\n this.renderTypeOptions()\n this.setStatus(`已加载 ${this.servers.length} 个服务`, 'ok')\n }\n\n /** 渲染服务类型选项 */\n private renderTypeOptions(): void {\n const types = [...new Set(this.servers.map((s) => s.type))]\n this.typeSelect.innerHTML = ''\n const allOpt = document.createElement('option')\n allOpt.value = ''\n allOpt.textContent = '全部'\n this.typeSelect.appendChild(allOpt)\n types.forEach((t) => {\n const opt = document.createElement('option')\n opt.value = t\n opt.textContent = t\n this.typeSelect.appendChild(opt)\n })\n this.renderServerOptions(this.typeSelect.value)\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.innerHTML = ''\n const emptyOpt = document.createElement('option')\n emptyOpt.value = ''\n emptyOpt.textContent = '请选择服务'\n this.serverSelect.appendChild(emptyOpt)\n filtered.forEach((s) => {\n const opt = document.createElement('option')\n opt.value = s.key\n opt.textContent = `${s.name} (${s.key})`\n this.serverSelect.appendChild(opt)\n })\n }\n\n /**\n * 切换目标服务,重新加载 Context 路由映射\n * @param key 服务 key\n */\n private async applyServer(key: string): Promise<void> {\n if (!key) {\n this.renderCtxList({})\n return\n }\n const server = this.servers.find((s) => s.key === key)\n if (!server) return\n\n this.setStatus('切换服务中...', '')\n try {\n await Context.load(server.origin, this.options.timeout)\n // 路由映射已由 Context.load 解析,通过内部 store 驱动路由\n // 此处仅展示 Context 加载后可用的 ctx 条目(来自 /ngw/context 响应)\n const ctxMap: Record<string, string> = {}\n // Context 内部 store 不对外暴露迭代,通过重新请求获取展示数据\n const res = await fetch(`${server.origin}/ngw/context`)\n const raw: Record<string, unknown> = res.ok ? await res.json() : {}\n for (const [k, v] of Object.entries(raw)) {\n if (k !== '$info' && typeof v === 'string') ctxMap[k] = v\n }\n this.renderCtxList(ctxMap)\n this.setStatus(`已切换至 ${server.name}`, 'ok')\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n this.setStatus(`切换失败: ${msg}`, 'err')\n }\n }\n\n /** 渲染路由列表 */\n private renderCtxList(ctxMap: Record<string, string>): void {\n this.ctxList.innerHTML = ''\n const entries = Object.entries(ctxMap)\n if (entries.length === 0) {\n this.ctxList.textContent = '暂无路由'\n return\n }\n entries.forEach(([k, v]) => {\n const item = document.createElement('div')\n item.className = 'dbg-ctx-item'\n const keySpan = document.createElement('span')\n keySpan.className = 'dbg-ctx-key'\n keySpan.textContent = k\n const valSpan = document.createElement('span')\n valSpan.className = 'dbg-ctx-val'\n valSpan.textContent = v\n valSpan.setAttribute('title', v)\n item.appendChild(keySpan)\n item.appendChild(valSpan)\n item.addEventListener('click', () => this.copyText(k))\n this.ctxList.appendChild(item)\n })\n }\n\n /** 复制文本到剪贴板 */\n private copyText(text: string): void {\n navigator.clipboard.writeText(text).then(() => {\n this.setStatus(`已复制: ${text}`, 'ok')\n setTimeout(() => this.setStatus('', ''), 2000)\n })\n }\n\n private setStatus(msg: string, cls: '' | 'ok' | 'err'): void {\n this.statusEl.textContent = msg\n this.statusEl.className = `dbg-status${cls ? ` ${cls}` : ''}`\n }\n}\n"]}

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