binja 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,667 +1,275 @@
1
1
  // @bun
2
- // src/debug/collector.ts
3
- class DebugCollector {
4
- data;
5
- constructor() {
6
- this.data = {
7
- startTime: performance.now(),
8
- templateChain: [],
9
- mode: "runtime",
10
- isAsync: false,
11
- contextKeys: [],
12
- contextSnapshot: {},
13
- filtersUsed: new Map,
14
- testsUsed: new Map,
15
- cacheHits: 0,
16
- cacheMisses: 0,
17
- queries: [],
18
- queryStats: {
19
- count: 0,
20
- totalDuration: 0,
21
- slowCount: 0,
22
- n1Count: 0,
23
- queryCounts: new Map
24
- },
25
- warnings: []
26
- };
27
- }
28
- startLexer() {
29
- this.data._lexerStart = performance.now();
30
- }
31
- endLexer() {
32
- if (this.data._lexerStart) {
33
- this.data.lexerTime = performance.now() - this.data._lexerStart;
34
- }
35
- }
36
- startParser() {
37
- this.data._parserStart = performance.now();
38
- }
39
- endParser() {
40
- if (this.data._parserStart) {
41
- this.data.parserTime = performance.now() - this.data._parserStart;
42
- }
43
- }
44
- startRender() {
45
- this.data._renderStart = performance.now();
46
- }
47
- endRender() {
48
- if (this.data._renderStart) {
49
- this.data.renderTime = performance.now() - this.data._renderStart;
50
- }
51
- this.data.endTime = performance.now();
52
- this.data.totalTime = this.data.endTime - this.data.startTime;
53
- }
54
- addTemplate(name, type, parent) {
55
- this.data.templateChain.push({ name, type, parent });
56
- if (type === "root") {
57
- this.data.rootTemplate = name;
58
- }
59
- }
60
- setMode(mode) {
61
- this.data.mode = mode;
62
- }
63
- setAsync(isAsync) {
64
- this.data.isAsync = isAsync;
65
- }
66
- captureContext(context) {
67
- this.data.contextKeys = Object.keys(context);
68
- for (const [key, value] of Object.entries(context)) {
69
- this.data.contextSnapshot[key] = this.captureValue(value);
70
- }
71
- }
72
- captureValue(value, depth = 0) {
73
- const type = this.getType(value);
74
- const preview = this.getPreview(value);
75
- const expandable = this.isExpandable(value);
76
- const result = { type, preview, value, expandable };
77
- if (expandable && depth < 3) {
78
- result.children = {};
79
- if (Array.isArray(value)) {
80
- value.forEach((item, i) => {
81
- result.children[String(i)] = this.captureValue(item, depth + 1);
82
- });
83
- } else if (typeof value === "object" && value !== null) {
84
- for (const [k, v] of Object.entries(value)) {
85
- result.children[k] = this.captureValue(v, depth + 1);
86
- }
87
- }
88
- }
89
- return result;
90
- }
91
- isExpandable(value) {
92
- if (value === null || value === undefined)
93
- return false;
94
- if (Array.isArray(value))
95
- return value.length > 0;
96
- if (typeof value === "object")
97
- return Object.keys(value).length > 0;
98
- return false;
99
- }
100
- getType(value) {
101
- if (value === null)
102
- return "null";
103
- if (value === undefined)
104
- return "undefined";
105
- if (Array.isArray(value))
106
- return `Array(${value.length})`;
107
- if (value instanceof Date)
108
- return "Date";
109
- if (typeof value === "object")
110
- return "Object";
111
- return typeof value;
112
- }
113
- getPreview(value, maxLen = 50) {
114
- if (value === null)
115
- return "null";
116
- if (value === undefined)
117
- return "undefined";
118
- if (typeof value === "string") {
119
- return value.length > maxLen ? `"${value.slice(0, maxLen)}..."` : `"${value}"`;
120
- }
121
- if (typeof value === "number" || typeof value === "boolean") {
122
- return String(value);
123
- }
124
- if (Array.isArray(value)) {
125
- if (value.length === 0)
126
- return "[]";
127
- if (value.length <= 3) {
128
- const items = value.map((v) => this.getPreview(v, 15)).join(", ");
129
- return `[${items}]`;
130
- }
131
- return `[${this.getPreview(value[0], 15)}, ... +${value.length - 1}]`;
132
- }
133
- if (value instanceof Date) {
134
- return value.toISOString();
135
- }
136
- if (typeof value === "object") {
137
- const keys = Object.keys(value);
138
- if (keys.length === 0)
139
- return "{}";
140
- if (keys.length <= 2) {
141
- return `{ ${keys.join(", ")} }`;
142
- }
143
- return `{ ${keys.slice(0, 2).join(", ")}, ... +${keys.length - 2} }`;
144
- }
145
- if (typeof value === "function") {
146
- return "function()";
147
- }
148
- return String(value);
149
- }
150
- recordFilter(name) {
151
- this.data.filtersUsed.set(name, (this.data.filtersUsed.get(name) || 0) + 1);
152
- }
153
- recordTest(name) {
154
- this.data.testsUsed.set(name, (this.data.testsUsed.get(name) || 0) + 1);
155
- }
156
- recordCacheHit() {
157
- this.data.cacheHits++;
158
- }
159
- recordCacheMiss() {
160
- this.data.cacheMisses++;
161
- }
162
- addWarning(message) {
163
- this.data.warnings.push(message);
164
- }
165
- recordQuery(query) {
166
- const normalizedSql = this.normalizeQuery(query.sql);
167
- const currentCount = this.data.queryStats.queryCounts.get(normalizedSql) || 0;
168
- this.data.queryStats.queryCounts.set(normalizedSql, currentCount + 1);
169
- const isN1 = currentCount >= 2;
170
- const queryInfo = {
171
- ...query,
172
- timestamp: performance.now(),
173
- isN1
174
- };
175
- this.data.queries.push(queryInfo);
176
- this.data.queryStats.count++;
177
- this.data.queryStats.totalDuration += query.duration;
178
- if (query.duration > 100) {
179
- this.data.queryStats.slowCount++;
180
- }
181
- if (isN1 && currentCount === 2) {
182
- this.data.queryStats.n1Count++;
183
- this.addWarning(`N+1 query detected: ${normalizedSql.slice(0, 50)}...`);
184
- }
185
- }
186
- normalizeQuery(sql) {
187
- return sql.replace(/\s+/g, " ").replace(/= \?/g, "= ?").replace(/= \$\d+/g, "= ?").replace(/= '\w+'/g, "= '?'").replace(/= \d+/g, "= ?").replace(/IN \([^)]+\)/gi, "IN (?)").trim();
188
- }
189
- getQueryStats() {
190
- return this.data.queryStats;
191
- }
192
- getData() {
193
- return this.data;
194
- }
195
- getSummary() {
196
- return {
197
- totalTime: this.data.totalTime || 0,
198
- templateCount: this.data.templateChain.length,
199
- filterCount: this.data.filtersUsed.size,
200
- mode: this.data.mode
201
- };
202
- }
203
- }
204
- var currentCollector = null;
205
- function startDebugCollection() {
206
- currentCollector = new DebugCollector;
207
- return currentCollector;
208
- }
209
- function getDebugCollector() {
210
- return currentCollector;
211
- }
212
- function endDebugCollection() {
213
- if (currentCollector) {
214
- const data = currentCollector.getData();
215
- currentCollector = null;
216
- return data;
217
- }
218
- return null;
219
- }
220
-
221
- // src/debug/panel.ts
222
- var DEFAULT_OPTIONS = {
223
- position: "bottom",
224
- height: 300,
225
- width: 400,
226
- open: false,
227
- dark: true
228
- };
229
- function generateDebugPanel(data, options = {}) {
230
- const opts = { ...DEFAULT_OPTIONS, ...options };
231
- const id = `binja-dbg-${Date.now()}`;
232
- const c = opts.dark ? darkTheme : lightTheme;
233
- return `
2
+ class E{data;constructor(){this.data={startTime:performance.now(),templateChain:[],mode:"runtime",isAsync:!1,contextKeys:[],contextSnapshot:{},filtersUsed:new Map,testsUsed:new Map,cacheHits:0,cacheMisses:0,queries:[],queryStats:{count:0,totalDuration:0,slowCount:0,n1Count:0,queryCounts:new Map},warnings:[]}}startLexer(){this.data._lexerStart=performance.now()}endLexer(){if(this.data._lexerStart)this.data.lexerTime=performance.now()-this.data._lexerStart}startParser(){this.data._parserStart=performance.now()}endParser(){if(this.data._parserStart)this.data.parserTime=performance.now()-this.data._parserStart}startRender(){this.data._renderStart=performance.now()}endRender(){if(this.data._renderStart)this.data.renderTime=performance.now()-this.data._renderStart;this.data.endTime=performance.now(),this.data.totalTime=this.data.endTime-this.data.startTime}addTemplate(J,K,X){if(this.data.templateChain.push({name:J,type:K,parent:X}),K==="root")this.data.rootTemplate=J}setMode(J){this.data.mode=J}setAsync(J){this.data.isAsync=J}captureContext(J){this.data.contextKeys=Object.keys(J);for(let[K,X]of Object.entries(J))this.data.contextSnapshot[K]=this.captureValue(X)}captureValue(J,K=0){let X=this.getType(J),Z=this.getPreview(J),Y=this.isExpandable(J),$={type:X,preview:Z,value:J,expandable:Y};if(Y&&K<3){if($.children={},Array.isArray(J))J.forEach((j,U)=>{$.children[String(U)]=this.captureValue(j,K+1)});else if(typeof J==="object"&&J!==null)for(let[j,U]of Object.entries(J))$.children[j]=this.captureValue(U,K+1)}return $}isExpandable(J){if(J===null||J===void 0)return!1;if(Array.isArray(J))return J.length>0;if(typeof J==="object")return Object.keys(J).length>0;return!1}getType(J){if(J===null)return"null";if(J===void 0)return"undefined";if(Array.isArray(J))return`Array(${J.length})`;if(J instanceof Date)return"Date";if(typeof J==="object")return"Object";return typeof J}getPreview(J,K=50){if(J===null)return"null";if(J===void 0)return"undefined";if(typeof J==="string")return J.length>K?`"${J.slice(0,K)}..."`:`"${J}"`;if(typeof J==="number"||typeof J==="boolean")return String(J);if(Array.isArray(J)){if(J.length===0)return"[]";if(J.length<=3)return`[${J.map((Z)=>this.getPreview(Z,15)).join(", ")}]`;return`[${this.getPreview(J[0],15)}, ... +${J.length-1}]`}if(J instanceof Date)return J.toISOString();if(typeof J==="object"){let X=Object.keys(J);if(X.length===0)return"{}";if(X.length<=2)return`{ ${X.join(", ")} }`;return`{ ${X.slice(0,2).join(", ")}, ... +${X.length-2} }`}if(typeof J==="function")return"function()";return String(J)}recordFilter(J){this.data.filtersUsed.set(J,(this.data.filtersUsed.get(J)||0)+1)}recordTest(J){this.data.testsUsed.set(J,(this.data.testsUsed.get(J)||0)+1)}recordCacheHit(){this.data.cacheHits++}recordCacheMiss(){this.data.cacheMisses++}addWarning(J){this.data.warnings.push(J)}recordQuery(J){let K=this.normalizeQuery(J.sql),X=this.data.queryStats.queryCounts.get(K)||0;this.data.queryStats.queryCounts.set(K,X+1);let Z=X>=2,Y={...J,timestamp:performance.now(),isN1:Z};if(this.data.queries.push(Y),this.data.queryStats.count++,this.data.queryStats.totalDuration+=J.duration,J.duration>100)this.data.queryStats.slowCount++;if(Z&&X===2)this.data.queryStats.n1Count++,this.addWarning(`N+1 query detected: ${K.slice(0,50)}...`)}normalizeQuery(J){return J.replace(/\s+/g," ").replace(/= \?/g,"= ?").replace(/= \$\d+/g,"= ?").replace(/= '\w+'/g,"= '?'").replace(/= \d+/g,"= ?").replace(/IN \([^)]+\)/gi,"IN (?)").trim()}getQueryStats(){return this.data.queryStats}getData(){return this.data}getSummary(){return{totalTime:this.data.totalTime||0,templateCount:this.data.templateChain.length,filterCount:this.data.filtersUsed.size,mode:this.data.mode}}}var M=null;function R(){return M=new E,M}function z(){return M}function I(){if(M){let J=M.getData();return M=null,J}return null}var F={position:"bottom",height:300,width:400,open:!1,dark:!0};function O(J,K={}){let X={...F,...K},Z=`binja-dbg-${Date.now()}`,Y=X.dark?L:N;return`
234
3
  <!-- Binja Debug Panel -->
235
- <div id="${id}" class="binja-devtools" data-position="${opts.position}" data-open="${opts.open}">
236
- <style>${generateStyles(id, c, opts)}</style>
237
- ${generateHTML(id, data, c, opts)}
238
- <script>${generateScript(id, data, opts)}</script>
4
+ <div id="${Z}" class="binja-devtools" data-position="${X.position}" data-open="${X.open}">
5
+ <style>${T(Z,Y,X)}</style>
6
+ ${P(Z,J,Y,X)}
7
+ <script>${x(Z,J,X)}</script>
239
8
  </div>
240
9
  <!-- /Binja Debug Panel -->
241
- `;
242
- }
243
- var darkTheme = {
244
- bg: "#1e1e1e",
245
- bgPanel: "#252526",
246
- bgHover: "#2a2d2e",
247
- bgActive: "#37373d",
248
- border: "#3c3c3c",
249
- text: "#cccccc",
250
- textSecondary: "#969696",
251
- textMuted: "#6e6e6e",
252
- accent: "#0078d4",
253
- accentHover: "#1c86d8",
254
- success: "#4ec9b0",
255
- warning: "#dcdcaa",
256
- error: "#f14c4c",
257
- info: "#75beff",
258
- string: "#ce9178",
259
- number: "#b5cea8",
260
- keyword: "#569cd6"
261
- };
262
- var lightTheme = {
263
- bg: "#f3f3f3",
264
- bgPanel: "#ffffff",
265
- bgHover: "#e8e8e8",
266
- bgActive: "#d4d4d4",
267
- border: "#d4d4d4",
268
- text: "#1e1e1e",
269
- textSecondary: "#616161",
270
- textMuted: "#a0a0a0",
271
- accent: "#0078d4",
272
- accentHover: "#106ebe",
273
- success: "#16825d",
274
- warning: "#bf8803",
275
- error: "#cd3131",
276
- info: "#0078d4",
277
- string: "#a31515",
278
- number: "#098658",
279
- keyword: "#0000ff"
280
- };
281
- function generateStyles(id, c, opts) {
282
- return `
283
- #${id} { --bg: ${c.bg}; --bg-panel: ${c.bgPanel}; --bg-hover: ${c.bgHover}; --bg-active: ${c.bgActive}; --border: ${c.border}; --text: ${c.text}; --text-secondary: ${c.textSecondary}; --text-muted: ${c.textMuted}; --accent: ${c.accent}; --success: ${c.success}; --warning: ${c.warning}; --error: ${c.error}; --string: ${c.string}; --number: ${c.number}; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; font-size: 12px; line-height: 1.4; color: var(--text); }
284
- #${id} * { box-sizing: border-box; margin: 0; padding: 0; }
10
+ `}var L={bg:"#1e1e1e",bgPanel:"#252526",bgHover:"#2a2d2e",bgActive:"#37373d",border:"#3c3c3c",text:"#cccccc",textSecondary:"#969696",textMuted:"#6e6e6e",accent:"#0078d4",accentHover:"#1c86d8",success:"#4ec9b0",warning:"#dcdcaa",error:"#f14c4c",info:"#75beff",string:"#ce9178",number:"#b5cea8",keyword:"#569cd6"},N={bg:"#f3f3f3",bgPanel:"#ffffff",bgHover:"#e8e8e8",bgActive:"#d4d4d4",border:"#d4d4d4",text:"#1e1e1e",textSecondary:"#616161",textMuted:"#a0a0a0",accent:"#0078d4",accentHover:"#106ebe",success:"#16825d",warning:"#bf8803",error:"#cd3131",info:"#0078d4",string:"#a31515",number:"#098658",keyword:"#0000ff"};function T(J,K,X){return`
11
+ #${J} { --bg: ${K.bg}; --bg-panel: ${K.bgPanel}; --bg-hover: ${K.bgHover}; --bg-active: ${K.bgActive}; --border: ${K.border}; --text: ${K.text}; --text-secondary: ${K.textSecondary}; --text-muted: ${K.textMuted}; --accent: ${K.accent}; --success: ${K.success}; --warning: ${K.warning}; --error: ${K.error}; --string: ${K.string}; --number: ${K.number}; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; font-size: 12px; line-height: 1.4; color: var(--text); }
12
+ #${J} * { box-sizing: border-box; margin: 0; padding: 0; }
285
13
 
286
14
  /* Toggle Button */
287
- #${id} .devtools-toggle { position: fixed; bottom: 10px; right: 10px; z-index: 2147483646; display: flex; align-items: center; gap: 6px; padding: 8px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--text); cursor: pointer; font-size: 11px; font-weight: 500; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: all 0.15s; }
288
- #${id} .devtools-toggle:hover { background: var(--bg-hover); border-color: var(--accent); }
289
- #${id} .devtools-toggle svg { width: 14px; height: 14px; color: var(--accent); }
290
- #${id} .devtools-toggle .badge { padding: 2px 6px; background: var(--accent); color: #fff; border-radius: 3px; font-size: 10px; }
291
- #${id}[data-open="true"] .devtools-toggle { display: none; }
15
+ #${J} .devtools-toggle { position: fixed; bottom: 10px; right: 10px; z-index: 2147483646; display: flex; align-items: center; gap: 6px; padding: 8px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--text); cursor: pointer; font-size: 11px; font-weight: 500; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: all 0.15s; }
16
+ #${J} .devtools-toggle:hover { background: var(--bg-hover); border-color: var(--accent); }
17
+ #${J} .devtools-toggle svg { width: 14px; height: 14px; color: var(--accent); }
18
+ #${J} .devtools-toggle .badge { padding: 2px 6px; background: var(--accent); color: #fff; border-radius: 3px; font-size: 10px; }
19
+ #${J}[data-open="true"] .devtools-toggle { display: none; }
292
20
 
293
21
  /* Panel Container */
294
- #${id} .devtools-panel { display: none; position: fixed; z-index: 2147483647; background: var(--bg); border: 1px solid var(--border); box-shadow: 0 -2px 12px rgba(0,0,0,0.3); }
295
- #${id}[data-open="true"] .devtools-panel { display: flex; flex-direction: column; }
296
- #${id}[data-position="bottom"] .devtools-panel { left: 0; right: 0; bottom: 0; height: ${opts.height}px; border-left: none; border-right: none; border-bottom: none; }
297
- #${id}[data-position="right"] .devtools-panel { top: 0; right: 0; bottom: 0; width: ${opts.width}px; border-top: none; border-right: none; border-bottom: none; }
298
- #${id}[data-position="popup"] .devtools-panel { bottom: 50px; right: 20px; width: 700px; height: 500px; border-radius: 8px; }
22
+ #${J} .devtools-panel { display: none; position: fixed; z-index: 2147483647; background: var(--bg); border: 1px solid var(--border); box-shadow: 0 -2px 12px rgba(0,0,0,0.3); }
23
+ #${J}[data-open="true"] .devtools-panel { display: flex; flex-direction: column; }
24
+ #${J}[data-position="bottom"] .devtools-panel { left: 0; right: 0; bottom: 0; height: ${X.height}px; border-left: none; border-right: none; border-bottom: none; }
25
+ #${J}[data-position="right"] .devtools-panel { top: 0; right: 0; bottom: 0; width: ${X.width}px; border-top: none; border-right: none; border-bottom: none; }
26
+ #${J}[data-position="popup"] .devtools-panel { bottom: 50px; right: 20px; width: 700px; height: 500px; border-radius: 8px; }
299
27
 
300
28
  /* Resize Handle */
301
- #${id} .devtools-resize { position: absolute; background: transparent; }
302
- #${id}[data-position="bottom"] .devtools-resize { top: 0; left: 0; right: 0; height: 4px; cursor: ns-resize; }
303
- #${id}[data-position="right"] .devtools-resize { top: 0; left: 0; bottom: 0; width: 4px; cursor: ew-resize; }
304
- #${id} .devtools-resize:hover { background: var(--accent); }
29
+ #${J} .devtools-resize { position: absolute; background: transparent; }
30
+ #${J}[data-position="bottom"] .devtools-resize { top: 0; left: 0; right: 0; height: 4px; cursor: ns-resize; }
31
+ #${J}[data-position="right"] .devtools-resize { top: 0; left: 0; bottom: 0; width: 4px; cursor: ew-resize; }
32
+ #${J} .devtools-resize:hover { background: var(--accent); }
305
33
 
306
34
  /* Toolbar */
307
- #${id} .devtools-toolbar { display: flex; align-items: center; justify-content: space-between; height: 30px; padding: 0 8px; background: var(--bg-panel); border-bottom: 1px solid var(--border); flex-shrink: 0; }
308
- #${id} .devtools-tabs { display: flex; height: 100%; }
309
- #${id} .devtools-tab { display: flex; align-items: center; gap: 4px; padding: 0 12px; border: none; background: none; color: var(--text-secondary); font-size: 11px; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.1s; }
310
- #${id} .devtools-tab:hover { color: var(--text); background: var(--bg-hover); }
311
- #${id} .devtools-tab.active { color: var(--text); border-bottom-color: var(--accent); }
312
- #${id} .devtools-tab svg { width: 12px; height: 12px; opacity: 0.7; }
313
- #${id} .devtools-tab .count { margin-left: 4px; padding: 1px 5px; background: var(--bg-active); border-radius: 8px; font-size: 10px; }
314
- #${id} .devtools-tab .count.warn { background: rgba(241,76,76,0.2); color: var(--error); }
315
- #${id} .devtools-actions { display: flex; align-items: center; gap: 4px; }
316
- #${id} .devtools-btn { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: none; background: none; color: var(--text-secondary); cursor: pointer; border-radius: 3px; }
317
- #${id} .devtools-btn:hover { background: var(--bg-hover); color: var(--text); }
318
- #${id} .devtools-btn svg { width: 14px; height: 14px; }
35
+ #${J} .devtools-toolbar { display: flex; align-items: center; justify-content: space-between; height: 30px; padding: 0 8px; background: var(--bg-panel); border-bottom: 1px solid var(--border); flex-shrink: 0; }
36
+ #${J} .devtools-tabs { display: flex; height: 100%; }
37
+ #${J} .devtools-tab { display: flex; align-items: center; gap: 4px; padding: 0 12px; border: none; background: none; color: var(--text-secondary); font-size: 11px; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.1s; }
38
+ #${J} .devtools-tab:hover { color: var(--text); background: var(--bg-hover); }
39
+ #${J} .devtools-tab.active { color: var(--text); border-bottom-color: var(--accent); }
40
+ #${J} .devtools-tab svg { width: 12px; height: 12px; opacity: 0.7; }
41
+ #${J} .devtools-tab .count { margin-left: 4px; padding: 1px 5px; background: var(--bg-active); border-radius: 8px; font-size: 10px; }
42
+ #${J} .devtools-tab .count.warn { background: rgba(241,76,76,0.2); color: var(--error); }
43
+ #${J} .devtools-actions { display: flex; align-items: center; gap: 4px; }
44
+ #${J} .devtools-btn { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: none; background: none; color: var(--text-secondary); cursor: pointer; border-radius: 3px; }
45
+ #${J} .devtools-btn:hover { background: var(--bg-hover); color: var(--text); }
46
+ #${J} .devtools-btn svg { width: 14px; height: 14px; }
319
47
 
320
48
  /* Content Area */
321
- #${id} .devtools-content { flex: 1; overflow: hidden; }
322
- #${id} .devtools-pane { display: none; height: 100%; overflow: auto; padding: 8px; }
323
- #${id} .devtools-pane.active { display: block; }
49
+ #${J} .devtools-content { flex: 1; overflow: hidden; }
50
+ #${J} .devtools-pane { display: none; height: 100%; overflow: auto; padding: 8px; }
51
+ #${J} .devtools-pane.active { display: block; }
324
52
 
325
53
  /* Performance Tab */
326
- #${id} .perf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 12px; }
327
- #${id} .perf-card { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; padding: 10px; text-align: center; }
328
- #${id} .perf-card-value { font-size: 20px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; color: var(--success); }
329
- #${id} .perf-card-label { font-size: 10px; color: var(--text-muted); margin-top: 4px; text-transform: uppercase; }
330
- #${id} .perf-breakdown { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; overflow: hidden; }
331
- #${id} .perf-row { display: flex; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--border); }
332
- #${id} .perf-row:last-child { border-bottom: none; }
333
- #${id} .perf-row-label { flex: 1; font-size: 11px; color: var(--text-secondary); }
334
- #${id} .perf-row-value { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; min-width: 60px; text-align: right; }
335
- #${id} .perf-row-bar { flex: 2; height: 4px; background: var(--bg-active); border-radius: 2px; margin: 0 12px; overflow: hidden; }
336
- #${id} .perf-row-fill { height: 100%; border-radius: 2px; }
337
- #${id} .perf-row-fill.lexer { background: ${c.info}; }
338
- #${id} .perf-row-fill.parser { background: ${c.warning}; }
339
- #${id} .perf-row-fill.render { background: ${c.success}; }
54
+ #${J} .perf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 12px; }
55
+ #${J} .perf-card { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; padding: 10px; text-align: center; }
56
+ #${J} .perf-card-value { font-size: 20px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; color: var(--success); }
57
+ #${J} .perf-card-label { font-size: 10px; color: var(--text-muted); margin-top: 4px; text-transform: uppercase; }
58
+ #${J} .perf-breakdown { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; overflow: hidden; }
59
+ #${J} .perf-row { display: flex; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--border); }
60
+ #${J} .perf-row:last-child { border-bottom: none; }
61
+ #${J} .perf-row-label { flex: 1; font-size: 11px; color: var(--text-secondary); }
62
+ #${J} .perf-row-value { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; min-width: 60px; text-align: right; }
63
+ #${J} .perf-row-bar { flex: 2; height: 4px; background: var(--bg-active); border-radius: 2px; margin: 0 12px; overflow: hidden; }
64
+ #${J} .perf-row-fill { height: 100%; border-radius: 2px; }
65
+ #${J} .perf-row-fill.lexer { background: ${K.info}; }
66
+ #${J} .perf-row-fill.parser { background: ${K.warning}; }
67
+ #${J} .perf-row-fill.render { background: ${K.success}; }
340
68
 
341
69
  /* Context Tab - Tree View */
342
- #${id} .tree { font-family: 'SF Mono', Monaco, Consolas, monospace; font-size: 11px; }
343
- #${id} .tree-item { }
344
- #${id} .tree-row { display: flex; align-items: center; padding: 2px 4px; cursor: default; border-radius: 2px; }
345
- #${id} .tree-row:hover { background: var(--bg-hover); }
346
- #${id} .tree-row.expandable { cursor: pointer; }
347
- #${id} .tree-arrow { width: 12px; height: 12px; color: var(--text-muted); transition: transform 0.1s; flex-shrink: 0; }
348
- #${id} .tree-item.open > .tree-row .tree-arrow { transform: rotate(90deg); }
349
- #${id} .tree-key { color: var(--keyword); margin-right: 4px; }
350
- #${id} .tree-colon { color: var(--text-muted); margin-right: 6px; }
351
- #${id} .tree-value { color: var(--text); }
352
- #${id} .tree-value.string { color: var(--string); }
353
- #${id} .tree-value.number { color: var(--number); }
354
- #${id} .tree-value.null { color: var(--text-muted); font-style: italic; }
355
- #${id} .tree-type { color: var(--text-muted); margin-left: 6px; font-size: 10px; }
356
- #${id} .tree-children { display: none; padding-left: 16px; }
357
- #${id} .tree-item.open > .tree-children { display: block; }
70
+ #${J} .tree { font-family: 'SF Mono', Monaco, Consolas, monospace; font-size: 11px; }
71
+ #${J} .tree-item { }
72
+ #${J} .tree-row { display: flex; align-items: center; padding: 2px 4px; cursor: default; border-radius: 2px; }
73
+ #${J} .tree-row:hover { background: var(--bg-hover); }
74
+ #${J} .tree-row.expandable { cursor: pointer; }
75
+ #${J} .tree-arrow { width: 12px; height: 12px; color: var(--text-muted); transition: transform 0.1s; flex-shrink: 0; }
76
+ #${J} .tree-item.open > .tree-row .tree-arrow { transform: rotate(90deg); }
77
+ #${J} .tree-key { color: var(--keyword); margin-right: 4px; }
78
+ #${J} .tree-colon { color: var(--text-muted); margin-right: 6px; }
79
+ #${J} .tree-value { color: var(--text); }
80
+ #${J} .tree-value.string { color: var(--string); }
81
+ #${J} .tree-value.number { color: var(--number); }
82
+ #${J} .tree-value.null { color: var(--text-muted); font-style: italic; }
83
+ #${J} .tree-type { color: var(--text-muted); margin-left: 6px; font-size: 10px; }
84
+ #${J} .tree-children { display: none; padding-left: 16px; }
85
+ #${J} .tree-item.open > .tree-children { display: block; }
358
86
 
359
87
  /* Templates Tab */
360
- #${id} .template-list { display: flex; flex-direction: column; gap: 4px; }
361
- #${id} .template-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; }
362
- #${id} .template-icon { width: 14px; height: 14px; color: var(--text-muted); }
363
- #${id} .template-name { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; }
364
- #${id} .template-badge { font-size: 9px; padding: 2px 6px; border-radius: 3px; text-transform: uppercase; font-weight: 500; }
365
- #${id} .template-badge.root { background: rgba(0,120,212,0.15); color: var(--accent); }
366
- #${id} .template-badge.extends { background: rgba(156,86,246,0.15); color: #9c56f6; }
367
- #${id} .template-badge.include { background: rgba(78,201,176,0.15); color: var(--success); }
88
+ #${J} .template-list { display: flex; flex-direction: column; gap: 4px; }
89
+ #${J} .template-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; }
90
+ #${J} .template-icon { width: 14px; height: 14px; color: var(--text-muted); }
91
+ #${J} .template-name { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; }
92
+ #${J} .template-badge { font-size: 9px; padding: 2px 6px; border-radius: 3px; text-transform: uppercase; font-weight: 500; }
93
+ #${J} .template-badge.root { background: rgba(0,120,212,0.15); color: var(--accent); }
94
+ #${J} .template-badge.extends { background: rgba(156,86,246,0.15); color: #9c56f6; }
95
+ #${J} .template-badge.include { background: rgba(78,201,176,0.15); color: var(--success); }
368
96
 
369
97
  /* Queries Tab */
370
- #${id} .queries-stats { display: flex; gap: 16px; padding: 8px 12px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; margin-bottom: 8px; }
371
- #${id} .queries-stat { text-align: center; }
372
- #${id} .queries-stat-value { font-size: 16px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
373
- #${id} .queries-stat-value.warn { color: var(--warning); }
374
- #${id} .queries-stat-value.error { color: var(--error); }
375
- #${id} .queries-stat-label { font-size: 9px; color: var(--text-muted); text-transform: uppercase; }
376
- #${id} .query-list { display: flex; flex-direction: column; gap: 4px; }
377
- #${id} .query-item { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; overflow: hidden; }
378
- #${id} .query-item.n1 { border-left: 3px solid var(--error); }
379
- #${id} .query-item.slow { border-left: 3px solid var(--warning); }
380
- #${id} .query-header { display: flex; align-items: center; gap: 8px; padding: 6px 10px; }
381
- #${id} .query-sql { flex: 1; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
382
- #${id} .query-meta { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
383
- #${id} .query-badge { font-size: 9px; padding: 2px 5px; border-radius: 3px; font-weight: 600; }
384
- #${id} .query-badge.n1 { background: rgba(241,76,76,0.15); color: var(--error); }
385
- #${id} .query-source { font-size: 9px; padding: 2px 5px; border-radius: 3px; background: var(--bg-active); color: var(--text-muted); text-transform: uppercase; }
386
- #${id} .query-time { font-family: 'SF Mono', Monaco, monospace; font-size: 10px; color: var(--text-secondary); }
387
- #${id} .query-time.slow { color: var(--warning); }
388
- #${id} .query-rows { font-size: 10px; color: var(--text-muted); }
98
+ #${J} .queries-stats { display: flex; gap: 16px; padding: 8px 12px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; margin-bottom: 8px; }
99
+ #${J} .queries-stat { text-align: center; }
100
+ #${J} .queries-stat-value { font-size: 16px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
101
+ #${J} .queries-stat-value.warn { color: var(--warning); }
102
+ #${J} .queries-stat-value.error { color: var(--error); }
103
+ #${J} .queries-stat-label { font-size: 9px; color: var(--text-muted); text-transform: uppercase; }
104
+ #${J} .query-list { display: flex; flex-direction: column; gap: 4px; }
105
+ #${J} .query-item { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; overflow: hidden; }
106
+ #${J} .query-item.n1 { border-left: 3px solid var(--error); }
107
+ #${J} .query-item.slow { border-left: 3px solid var(--warning); }
108
+ #${J} .query-header { display: flex; align-items: center; gap: 8px; padding: 6px 10px; }
109
+ #${J} .query-sql { flex: 1; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
110
+ #${J} .query-meta { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
111
+ #${J} .query-badge { font-size: 9px; padding: 2px 5px; border-radius: 3px; font-weight: 600; }
112
+ #${J} .query-badge.n1 { background: rgba(241,76,76,0.15); color: var(--error); }
113
+ #${J} .query-source { font-size: 9px; padding: 2px 5px; border-radius: 3px; background: var(--bg-active); color: var(--text-muted); text-transform: uppercase; }
114
+ #${J} .query-time { font-family: 'SF Mono', Monaco, monospace; font-size: 10px; color: var(--text-secondary); }
115
+ #${J} .query-time.slow { color: var(--warning); }
116
+ #${J} .query-rows { font-size: 10px; color: var(--text-muted); }
389
117
 
390
118
  /* Filters Tab */
391
- #${id} .filter-grid { display: flex; flex-wrap: wrap; gap: 6px; }
392
- #${id} .filter-chip { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; }
393
- #${id} .filter-count { color: var(--accent); font-weight: 600; font-size: 10px; }
119
+ #${J} .filter-grid { display: flex; flex-wrap: wrap; gap: 6px; }
120
+ #${J} .filter-chip { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; }
121
+ #${J} .filter-count { color: var(--accent); font-weight: 600; font-size: 10px; }
394
122
 
395
123
  /* Cache Tab */
396
- #${id} .cache-stats { display: flex; gap: 20px; }
397
- #${id} .cache-stat { flex: 1; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; padding: 16px; text-align: center; }
398
- #${id} .cache-value { font-size: 32px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
399
- #${id} .cache-value.hit { color: var(--success); }
400
- #${id} .cache-value.miss { color: var(--error); }
401
- #${id} .cache-label { font-size: 11px; color: var(--text-muted); margin-top: 4px; }
124
+ #${J} .cache-stats { display: flex; gap: 20px; }
125
+ #${J} .cache-stat { flex: 1; background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; padding: 16px; text-align: center; }
126
+ #${J} .cache-value { font-size: 32px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
127
+ #${J} .cache-value.hit { color: var(--success); }
128
+ #${J} .cache-value.miss { color: var(--error); }
129
+ #${J} .cache-label { font-size: 11px; color: var(--text-muted); margin-top: 4px; }
402
130
 
403
131
  /* Warnings Tab */
404
- #${id} .warning-list { display: flex; flex-direction: column; gap: 6px; }
405
- #${id} .warning-item { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; background: rgba(241,76,76,0.08); border: 1px solid rgba(241,76,76,0.2); border-radius: 4px; }
406
- #${id} .warning-icon { color: var(--error); flex-shrink: 0; width: 14px; height: 14px; }
407
- #${id} .warning-text { font-size: 11px; color: var(--text); }
132
+ #${J} .warning-list { display: flex; flex-direction: column; gap: 6px; }
133
+ #${J} .warning-item { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; background: rgba(241,76,76,0.08); border: 1px solid rgba(241,76,76,0.2); border-radius: 4px; }
134
+ #${J} .warning-icon { color: var(--error); flex-shrink: 0; width: 14px; height: 14px; }
135
+ #${J} .warning-text { font-size: 11px; color: var(--text); }
408
136
 
409
137
  /* Empty State */
410
- #${id} .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--text-muted); font-size: 12px; }
411
- #${id} .empty-state svg { width: 32px; height: 32px; margin-bottom: 8px; opacity: 0.5; }
412
- `;
413
- }
414
- var icons = {
415
- logo: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>`,
416
- close: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>`,
417
- minimize: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"/></svg>`,
418
- dock: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 15h18"/></svg>`,
419
- dockRight: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M15 3v18"/></svg>`,
420
- popup: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v6h6"/></svg>`,
421
- arrow: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>`,
422
- perf: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>`,
423
- context: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"/></svg>`,
424
- template: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>`,
425
- filter: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>`,
426
- database: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>`,
427
- cache: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a10 10 0 1010 10H12V2z"/><path d="M12 2a10 10 0 00-8.66 15"/></svg>`,
428
- warning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`
429
- };
430
- function generateHTML(id, data, c, opts) {
431
- const time = (data.totalTime || 0).toFixed(1);
432
- const queryCount = data.queries?.length || 0;
433
- const hasWarnings = data.warnings.length > 0 || data.queryStats?.n1Count > 0;
434
- const tabs = [
435
- { id: "perf", icon: icons.perf, label: "Performance", count: `${time}ms` },
436
- { id: "context", icon: icons.context, label: "Context", count: Object.keys(data.contextSnapshot).length || null },
437
- { id: "templates", icon: icons.template, label: "Templates", count: data.templateChain.length || null },
438
- { id: "filters", icon: icons.filter, label: "Filters", count: data.filtersUsed.size || null },
439
- { id: "queries", icon: icons.database, label: "Queries", count: queryCount || null, warn: data.queryStats?.n1Count > 0 },
440
- { id: "cache", icon: icons.cache, label: "Cache", count: data.cacheHits + data.cacheMisses || null },
441
- { id: "warnings", icon: icons.warning, label: "Warnings", count: data.warnings.length || null, warn: hasWarnings }
442
- ];
443
- const tabsHtml = tabs.map((t, i) => {
444
- const countHtml = t.count !== null ? `<span class="count${t.warn ? " warn" : ""}">${t.count}</span>` : "";
445
- return `<button class="devtools-tab${i === 0 ? " active" : ""}" data-tab="${t.id}">${t.icon}${t.label}${countHtml}</button>`;
446
- }).join("");
447
- return `
448
- <button class="devtools-toggle" onclick="document.getElementById('${id}').dataset.open='true'">
449
- ${icons.logo}
138
+ #${J} .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--text-muted); font-size: 12px; }
139
+ #${J} .empty-state svg { width: 32px; height: 32px; margin-bottom: 8px; opacity: 0.5; }
140
+ `}var V={logo:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>',minimize:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"/></svg>',dock:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 15h18"/></svg>',dockRight:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M15 3v18"/></svg>',popup:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v6h6"/></svg>',arrow:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>',perf:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>',context:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"/></svg>',template:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>',filter:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>',database:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>',cache:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a10 10 0 1010 10H12V2z"/><path d="M12 2a10 10 0 00-8.66 15"/></svg>',warning:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>'};function P(J,K,X,Z){let Y=(K.totalTime||0).toFixed(1),$=K.queries?.length||0,j=K.warnings.length>0||K.queryStats?.n1Count>0,_=[{id:"perf",icon:V.perf,label:"Performance",count:`${Y}ms`},{id:"context",icon:V.context,label:"Context",count:Object.keys(K.contextSnapshot).length||null},{id:"templates",icon:V.template,label:"Templates",count:K.templateChain.length||null},{id:"filters",icon:V.filter,label:"Filters",count:K.filtersUsed.size||null},{id:"queries",icon:V.database,label:"Queries",count:$||null,warn:K.queryStats?.n1Count>0},{id:"cache",icon:V.cache,label:"Cache",count:K.cacheHits+K.cacheMisses||null},{id:"warnings",icon:V.warning,label:"Warnings",count:K.warnings.length||null,warn:j}].map((G,A)=>{let Q=G.count!==null?`<span class="count${G.warn?" warn":""}">${G.count}</span>`:"";return`<button class="devtools-tab${A===0?" active":""}" data-tab="${G.id}">${G.icon}${G.label}${Q}</button>`}).join("");return`
141
+ <button class="devtools-toggle" onclick="document.getElementById('${J}').dataset.open='true'">
142
+ ${V.logo}
450
143
  <span>Binja</span>
451
- <span class="badge">${time}ms</span>
144
+ <span class="badge">${Y}ms</span>
452
145
  </button>
453
146
 
454
147
  <div class="devtools-panel">
455
148
  <div class="devtools-resize"></div>
456
149
  <div class="devtools-toolbar">
457
- <div class="devtools-tabs">${tabsHtml}</div>
150
+ <div class="devtools-tabs">${_}</div>
458
151
  <div class="devtools-actions">
459
- <button class="devtools-btn" title="Dock to bottom" data-dock="bottom">${icons.dock}</button>
460
- <button class="devtools-btn" title="Dock to right" data-dock="right">${icons.dockRight}</button>
461
- <button class="devtools-btn" title="Popup" data-dock="popup">${icons.popup}</button>
462
- <button class="devtools-btn" title="Close" onclick="document.getElementById('${id}').dataset.open='false'">${icons.close}</button>
152
+ <button class="devtools-btn" title="Dock to bottom" data-dock="bottom">${V.dock}</button>
153
+ <button class="devtools-btn" title="Dock to right" data-dock="right">${V.dockRight}</button>
154
+ <button class="devtools-btn" title="Popup" data-dock="popup">${V.popup}</button>
155
+ <button class="devtools-btn" title="Close" onclick="document.getElementById('${J}').dataset.open='false'">${V.close}</button>
463
156
  </div>
464
157
  </div>
465
158
  <div class="devtools-content">
466
- ${generatePerfPane(data)}
467
- ${generateContextPane(data)}
468
- ${generateTemplatesPane(data)}
469
- ${generateFiltersPane(data)}
470
- ${generateQueriesPane(data)}
471
- ${generateCachePane(data)}
472
- ${generateWarningsPane(data)}
159
+ ${S(K)}
160
+ ${w(K)}
161
+ ${k(K)}
162
+ ${D(K)}
163
+ ${h(K)}
164
+ ${C(K)}
165
+ ${b(K)}
473
166
  </div>
474
- </div>`;
475
- }
476
- function generatePerfPane(data) {
477
- const total = data.totalTime || 0.01;
478
- const lexer = data.lexerTime || 0;
479
- const parser = data.parserTime || 0;
480
- const render = data.renderTime || 0;
481
- const mode = data.mode === "aot" ? "AOT" : "Runtime";
482
- return `
167
+ </div>`}function S(J){let K=J.totalTime||0.01,X=J.lexerTime||0,Z=J.parserTime||0,Y=J.renderTime||0,$=J.mode==="aot"?"AOT":"Runtime";return`
483
168
  <div class="devtools-pane active" data-pane="perf">
484
169
  <div class="perf-grid">
485
170
  <div class="perf-card">
486
- <div class="perf-card-value">${total.toFixed(2)}ms</div>
171
+ <div class="perf-card-value">${K.toFixed(2)}ms</div>
487
172
  <div class="perf-card-label">Total Time</div>
488
173
  </div>
489
174
  <div class="perf-card">
490
- <div class="perf-card-value" style="color:var(--accent)">${mode}</div>
175
+ <div class="perf-card-value" style="color:var(--accent)">${$}</div>
491
176
  <div class="perf-card-label">Mode</div>
492
177
  </div>
493
178
  <div class="perf-card">
494
- <div class="perf-card-value">${data.templateChain.length}</div>
179
+ <div class="perf-card-value">${J.templateChain.length}</div>
495
180
  <div class="perf-card-label">Templates</div>
496
181
  </div>
497
182
  <div class="perf-card">
498
- <div class="perf-card-value">${data.queries?.length || 0}</div>
183
+ <div class="perf-card-value">${J.queries?.length||0}</div>
499
184
  <div class="perf-card-label">Queries</div>
500
185
  </div>
501
186
  </div>
502
187
  <div class="perf-breakdown">
503
188
  <div class="perf-row">
504
189
  <span class="perf-row-label">Lexer</span>
505
- <div class="perf-row-bar"><div class="perf-row-fill lexer" style="width:${lexer / total * 100}%"></div></div>
506
- <span class="perf-row-value">${lexer.toFixed(2)}ms</span>
190
+ <div class="perf-row-bar"><div class="perf-row-fill lexer" style="width:${X/K*100}%"></div></div>
191
+ <span class="perf-row-value">${X.toFixed(2)}ms</span>
507
192
  </div>
508
193
  <div class="perf-row">
509
194
  <span class="perf-row-label">Parser</span>
510
- <div class="perf-row-bar"><div class="perf-row-fill parser" style="width:${parser / total * 100}%"></div></div>
511
- <span class="perf-row-value">${parser.toFixed(2)}ms</span>
195
+ <div class="perf-row-bar"><div class="perf-row-fill parser" style="width:${Z/K*100}%"></div></div>
196
+ <span class="perf-row-value">${Z.toFixed(2)}ms</span>
512
197
  </div>
513
198
  <div class="perf-row">
514
199
  <span class="perf-row-label">Render</span>
515
- <div class="perf-row-bar"><div class="perf-row-fill render" style="width:${render / total * 100}%"></div></div>
516
- <span class="perf-row-value">${render.toFixed(2)}ms</span>
200
+ <div class="perf-row-bar"><div class="perf-row-fill render" style="width:${Y/K*100}%"></div></div>
201
+ <span class="perf-row-value">${Y.toFixed(2)}ms</span>
517
202
  </div>
518
203
  </div>
519
- </div>`;
520
- }
521
- function generateContextPane(data) {
522
- const keys = Object.keys(data.contextSnapshot);
523
- if (keys.length === 0) {
524
- return `<div class="devtools-pane" data-pane="context"><div class="empty-state">${icons.context}<span>No context variables</span></div></div>`;
525
- }
526
- const items = keys.map((key) => renderTreeItem(key, data.contextSnapshot[key])).join("");
527
- return `<div class="devtools-pane" data-pane="context"><div class="tree">${items}</div></div>`;
528
- }
529
- function renderTreeItem(key, ctx) {
530
- const hasChildren = ctx.expandable && ctx.children && Object.keys(ctx.children).length > 0;
531
- const arrowHtml = hasChildren ? `<span class="tree-arrow">${icons.arrow}</span>` : '<span class="tree-arrow" style="visibility:hidden">${icons.arrow}</span>';
532
- const expandableClass = hasChildren ? "expandable" : "";
533
- const valueClass = getValueClass(ctx.type);
534
- let childrenHtml = "";
535
- if (hasChildren && ctx.children) {
536
- childrenHtml = `<div class="tree-children">${Object.entries(ctx.children).map(([k, v]) => renderTreeItem(k, v)).join("")}</div>`;
537
- }
538
- return `
204
+ </div>`}function w(J){let K=Object.keys(J.contextSnapshot);if(K.length===0)return`<div class="devtools-pane" data-pane="context"><div class="empty-state">${V.context}<span>No context variables</span></div></div>`;return`<div class="devtools-pane" data-pane="context"><div class="tree">${K.map((Z)=>W(Z,J.contextSnapshot[Z])).join("")}</div></div>`}function W(J,K){let X=K.expandable&&K.children&&Object.keys(K.children).length>0,Z=X?`<span class="tree-arrow">${V.arrow}</span>`:'<span class="tree-arrow" style="visibility:hidden">${icons.arrow}</span>',Y=X?"expandable":"",$=H(K.type),j="";if(X&&K.children)j=`<div class="tree-children">${Object.entries(K.children).map(([U,_])=>W(U,_)).join("")}</div>`;return`
539
205
  <div class="tree-item">
540
- <div class="tree-row ${expandableClass}">
541
- ${arrowHtml}
542
- <span class="tree-key">${escapeHtml(key)}</span>
206
+ <div class="tree-row ${Y}">
207
+ ${Z}
208
+ <span class="tree-key">${B(J)}</span>
543
209
  <span class="tree-colon">:</span>
544
- <span class="tree-value ${valueClass}">${escapeHtml(ctx.preview)}</span>
545
- <span class="tree-type">${ctx.type}</span>
210
+ <span class="tree-value ${$}">${B(K.preview)}</span>
211
+ <span class="tree-type">${K.type}</span>
546
212
  </div>
547
- ${childrenHtml}
548
- </div>`;
549
- }
550
- function getValueClass(type) {
551
- if (type === "string")
552
- return "string";
553
- if (type === "number" || type === "integer" || type === "float")
554
- return "number";
555
- if (type === "null" || type === "undefined")
556
- return "null";
557
- return "";
558
- }
559
- function generateTemplatesPane(data) {
560
- if (data.templateChain.length === 0) {
561
- return `<div class="devtools-pane" data-pane="templates"><div class="empty-state">${icons.template}<span>No templates loaded</span></div></div>`;
562
- }
563
- const items = data.templateChain.map((t) => `
213
+ ${j}
214
+ </div>`}function H(J){if(J==="string")return"string";if(J==="number"||J==="integer"||J==="float")return"number";if(J==="null"||J==="undefined")return"null";return""}function k(J){if(J.templateChain.length===0)return`<div class="devtools-pane" data-pane="templates"><div class="empty-state">${V.template}<span>No templates loaded</span></div></div>`;return`<div class="devtools-pane" data-pane="templates"><div class="template-list">${J.templateChain.map((X)=>`
564
215
  <div class="template-item">
565
- <span class="template-icon">${icons.template}</span>
566
- <span class="template-name">${escapeHtml(t.name)}</span>
567
- <span class="template-badge ${t.type}">${t.type}</span>
216
+ <span class="template-icon">${V.template}</span>
217
+ <span class="template-name">${B(X.name)}</span>
218
+ <span class="template-badge ${X.type}">${X.type}</span>
568
219
  </div>
569
- `).join("");
570
- return `<div class="devtools-pane" data-pane="templates"><div class="template-list">${items}</div></div>`;
571
- }
572
- function generateFiltersPane(data) {
573
- const filters = Array.from(data.filtersUsed.entries());
574
- if (filters.length === 0) {
575
- return `<div class="devtools-pane" data-pane="filters"><div class="empty-state">${icons.filter}<span>No filters used</span></div></div>`;
576
- }
577
- const items = filters.map(([name, count]) => `
578
- <span class="filter-chip">${escapeHtml(name)}<span class="filter-count">\xD7${count}</span></span>
579
- `).join("");
580
- return `<div class="devtools-pane" data-pane="filters"><div class="filter-grid">${items}</div></div>`;
581
- }
582
- function generateQueriesPane(data) {
583
- if (!data.queries || data.queries.length === 0) {
584
- return `<div class="devtools-pane" data-pane="queries"><div class="empty-state">${icons.database}<span>No queries recorded</span></div></div>`;
585
- }
586
- const stats = data.queryStats;
587
- const statsHtml = `
220
+ `).join("")}</div></div>`}function D(J){let K=Array.from(J.filtersUsed.entries());if(K.length===0)return`<div class="devtools-pane" data-pane="filters"><div class="empty-state">${V.filter}<span>No filters used</span></div></div>`;return`<div class="devtools-pane" data-pane="filters"><div class="filter-grid">${K.map(([Z,Y])=>`
221
+ <span class="filter-chip">${B(Z)}<span class="filter-count">\xD7${Y}</span></span>
222
+ `).join("")}</div></div>`}function h(J){if(!J.queries||J.queries.length===0)return`<div class="devtools-pane" data-pane="queries"><div class="empty-state">${V.database}<span>No queries recorded</span></div></div>`;let K=J.queryStats,X=`
588
223
  <div class="queries-stats">
589
224
  <div class="queries-stat">
590
- <div class="queries-stat-value">${stats.count}</div>
225
+ <div class="queries-stat-value">${K.count}</div>
591
226
  <div class="queries-stat-label">Queries</div>
592
227
  </div>
593
228
  <div class="queries-stat">
594
- <div class="queries-stat-value">${stats.totalDuration.toFixed(1)}ms</div>
229
+ <div class="queries-stat-value">${K.totalDuration.toFixed(1)}ms</div>
595
230
  <div class="queries-stat-label">Total</div>
596
231
  </div>
597
232
  <div class="queries-stat">
598
- <div class="queries-stat-value ${stats.slowCount > 0 ? "warn" : ""}">${stats.slowCount}</div>
233
+ <div class="queries-stat-value ${K.slowCount>0?"warn":""}">${K.slowCount}</div>
599
234
  <div class="queries-stat-label">Slow</div>
600
235
  </div>
601
236
  <div class="queries-stat">
602
- <div class="queries-stat-value ${stats.n1Count > 0 ? "error" : ""}">${stats.n1Count}</div>
237
+ <div class="queries-stat-value ${K.n1Count>0?"error":""}">${K.n1Count}</div>
603
238
  <div class="queries-stat-label">N+1</div>
604
239
  </div>
605
- </div>`;
606
- const queries = data.queries.map((q) => {
607
- const isSlow = q.duration > 100;
608
- const classes = ["query-item", q.isN1 ? "n1" : "", isSlow ? "slow" : ""].filter(Boolean).join(" ");
609
- const badge = q.isN1 ? '<span class="query-badge n1">N+1</span>' : "";
610
- const source = q.source ? `<span class="query-source">${escapeHtml(q.source)}</span>` : "";
611
- const rows = q.rows !== undefined ? `<span class="query-rows">${q.rows} rows</span>` : "";
612
- return `
613
- <div class="${classes}">
240
+ </div>`,Z=J.queries.map((Y)=>{let $=Y.duration>100,j=["query-item",Y.isN1?"n1":"",$?"slow":""].filter(Boolean).join(" "),U=Y.isN1?'<span class="query-badge n1">N+1</span>':"",_=Y.source?`<span class="query-source">${B(Y.source)}</span>`:"",G=Y.rows!==void 0?`<span class="query-rows">${Y.rows} rows</span>`:"";return`
241
+ <div class="${j}">
614
242
  <div class="query-header">
615
- <span class="query-sql" title="${escapeHtml(q.sql)}">${escapeHtml(q.sql)}</span>
243
+ <span class="query-sql" title="${B(Y.sql)}">${B(Y.sql)}</span>
616
244
  <div class="query-meta">
617
- ${badge}${source}${rows}
618
- <span class="query-time ${isSlow ? "slow" : ""}">${q.duration.toFixed(1)}ms</span>
245
+ ${U}${_}${G}
246
+ <span class="query-time ${$?"slow":""}">${Y.duration.toFixed(1)}ms</span>
619
247
  </div>
620
248
  </div>
621
- </div>`;
622
- }).join("");
623
- return `<div class="devtools-pane" data-pane="queries">${statsHtml}<div class="query-list">${queries}</div></div>`;
624
- }
625
- function generateCachePane(data) {
626
- const total = data.cacheHits + data.cacheMisses;
627
- if (total === 0) {
628
- return `<div class="devtools-pane" data-pane="cache"><div class="empty-state">${icons.cache}<span>No cache activity</span></div></div>`;
629
- }
630
- const hitRate = (data.cacheHits / total * 100).toFixed(0);
631
- return `
249
+ </div>`}).join("");return`<div class="devtools-pane" data-pane="queries">${X}<div class="query-list">${Z}</div></div>`}function C(J){let K=J.cacheHits+J.cacheMisses;if(K===0)return`<div class="devtools-pane" data-pane="cache"><div class="empty-state">${V.cache}<span>No cache activity</span></div></div>`;let X=(J.cacheHits/K*100).toFixed(0);return`
632
250
  <div class="devtools-pane" data-pane="cache">
633
251
  <div class="cache-stats">
634
252
  <div class="cache-stat">
635
- <div class="cache-value hit">${data.cacheHits}</div>
253
+ <div class="cache-value hit">${J.cacheHits}</div>
636
254
  <div class="cache-label">Cache Hits</div>
637
255
  </div>
638
256
  <div class="cache-stat">
639
- <div class="cache-value miss">${data.cacheMisses}</div>
257
+ <div class="cache-value miss">${J.cacheMisses}</div>
640
258
  <div class="cache-label">Cache Misses</div>
641
259
  </div>
642
260
  <div class="cache-stat">
643
- <div class="cache-value">${hitRate}%</div>
261
+ <div class="cache-value">${X}%</div>
644
262
  <div class="cache-label">Hit Rate</div>
645
263
  </div>
646
264
  </div>
647
- </div>`;
648
- }
649
- function generateWarningsPane(data) {
650
- if (data.warnings.length === 0) {
651
- return `<div class="devtools-pane" data-pane="warnings"><div class="empty-state">${icons.warning}<span>No warnings</span></div></div>`;
652
- }
653
- const items = data.warnings.map((w) => `
265
+ </div>`}function b(J){if(J.warnings.length===0)return`<div class="devtools-pane" data-pane="warnings"><div class="empty-state">${V.warning}<span>No warnings</span></div></div>`;return`<div class="devtools-pane" data-pane="warnings"><div class="warning-list">${J.warnings.map((X)=>`
654
266
  <div class="warning-item">
655
- <span class="warning-icon">${icons.warning}</span>
656
- <span class="warning-text">${escapeHtml(w)}</span>
267
+ <span class="warning-icon">${V.warning}</span>
268
+ <span class="warning-text">${B(X)}</span>
657
269
  </div>
658
- `).join("");
659
- return `<div class="devtools-pane" data-pane="warnings"><div class="warning-list">${items}</div></div>`;
660
- }
661
- function generateScript(id, data, opts) {
662
- return `
270
+ `).join("")}</div></div>`}function x(J,K,X){return`
663
271
  (function() {
664
- var root = document.getElementById('${id}');
272
+ var root = document.getElementById('${J}');
665
273
  if (!root) return;
666
274
 
667
275
  // Tab switching
@@ -726,273 +334,4 @@ function generateScript(id, data, opts) {
726
334
  document.body.style.cursor = '';
727
335
  });
728
336
  }
729
- })();`;
730
- }
731
- function escapeHtml(str) {
732
- return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
733
- }
734
- // src/debug/integrations.ts
735
- function recordQuery(query) {
736
- const collector = getDebugCollector();
737
- if (collector) {
738
- collector.recordQuery(query);
739
- }
740
- }
741
- function createPrismaMiddleware() {
742
- return async (params, next) => {
743
- const collector = getDebugCollector();
744
- const start = performance.now();
745
- const result = await next(params);
746
- const duration = performance.now() - start;
747
- if (collector) {
748
- const sql = `${params.action} ${params.model}`;
749
- collector.recordQuery({
750
- sql,
751
- params: params.args ? [JSON.stringify(params.args)] : undefined,
752
- duration,
753
- rows: Array.isArray(result) ? result.length : result ? 1 : 0,
754
- source: "prisma"
755
- });
756
- }
757
- return result;
758
- };
759
- }
760
- function setupPrismaLogging(prisma) {
761
- prisma.$on("query", (e) => {
762
- const collector = getDebugCollector();
763
- if (collector) {
764
- collector.recordQuery({
765
- sql: e.query,
766
- params: e.params ? JSON.parse(e.params) : undefined,
767
- duration: e.duration,
768
- source: "prisma"
769
- });
770
- }
771
- });
772
- }
773
- function createDrizzleLogger() {
774
- return {
775
- logQuery(query, params) {
776
- const collector = getDebugCollector();
777
- if (collector) {
778
- collector.recordQuery({
779
- sql: query,
780
- params,
781
- duration: 0,
782
- source: "drizzle"
783
- });
784
- }
785
- }
786
- };
787
- }
788
- async function wrapDrizzleQuery(query, sql) {
789
- const collector = getDebugCollector();
790
- const start = performance.now();
791
- const result = await query;
792
- const duration = performance.now() - start;
793
- if (collector) {
794
- collector.recordQuery({
795
- sql: sql || "Drizzle Query",
796
- duration,
797
- rows: Array.isArray(result) ? result.length : result ? 1 : 0,
798
- source: "drizzle"
799
- });
800
- }
801
- return result;
802
- }
803
- function wrapBunSQL(db) {
804
- const handler = {
805
- get(target, prop) {
806
- const value = target[prop];
807
- if (typeof value === "function") {
808
- if (prop === "query" || prop === "run" || prop === "prepare") {
809
- return function(...args) {
810
- const collector = getDebugCollector();
811
- const sql = typeof args[0] === "string" ? args[0] : "SQL Query";
812
- const start = performance.now();
813
- const result = value.apply(target, args);
814
- if (result && typeof result.all === "function") {
815
- return wrapPreparedStatement(result, sql);
816
- }
817
- const duration = performance.now() - start;
818
- if (collector) {
819
- collector.recordQuery({
820
- sql,
821
- params: args.slice(1),
822
- duration,
823
- rows: Array.isArray(result) ? result.length : undefined,
824
- source: "bun:sqlite"
825
- });
826
- }
827
- return result;
828
- };
829
- }
830
- }
831
- return value;
832
- }
833
- };
834
- return new Proxy(db, handler);
835
- }
836
- function wrapPreparedStatement(stmt, sql) {
837
- const handler = {
838
- get(target, prop) {
839
- const value = target[prop];
840
- if (typeof value === "function" && (prop === "all" || prop === "get" || prop === "run")) {
841
- return function(...args) {
842
- const collector = getDebugCollector();
843
- const start = performance.now();
844
- const result = value.apply(target, args);
845
- const duration = performance.now() - start;
846
- if (collector) {
847
- collector.recordQuery({
848
- sql,
849
- params: args,
850
- duration,
851
- rows: Array.isArray(result) ? result.length : result ? 1 : 0,
852
- source: "bun:sqlite"
853
- });
854
- }
855
- return result;
856
- };
857
- }
858
- return value;
859
- }
860
- };
861
- return new Proxy(stmt, handler);
862
- }
863
- async function wrapQuery(sql, queryFn, source = "sql") {
864
- const collector = getDebugCollector();
865
- const start = performance.now();
866
- const result = await queryFn();
867
- const duration = performance.now() - start;
868
- if (collector) {
869
- collector.recordQuery({
870
- sql,
871
- duration,
872
- rows: Array.isArray(result) ? result.length : result ? 1 : 0,
873
- source
874
- });
875
- }
876
- return result;
877
- }
878
- function createQueryWrapper(source) {
879
- return (sql, queryFn) => {
880
- return wrapQuery(sql, queryFn, source);
881
- };
882
- }
883
-
884
- // src/debug/index.ts
885
- async function renderWithDebug(env, templateName, context = {}, options = {}) {
886
- const collector = startDebugCollection();
887
- collector.captureContext(context);
888
- collector.addTemplate(templateName, "root");
889
- collector.setMode("runtime");
890
- collector.startRender();
891
- const html = await env.render(templateName, context);
892
- collector.endRender();
893
- const data = endDebugCollection();
894
- if (options.htmlOnly !== false) {
895
- const isHtml = html.includes("<html") || html.includes("<body") || html.includes("<!DOCTYPE");
896
- if (!isHtml) {
897
- return html;
898
- }
899
- }
900
- const panel = generateDebugPanel(data, options.panel);
901
- if (html.includes("</body>")) {
902
- return html.replace("</body>", `${panel}</body>`);
903
- }
904
- return html + panel;
905
- }
906
- async function renderStringWithDebug(env, source, context = {}, options = {}) {
907
- const collector = startDebugCollection();
908
- collector.captureContext(context);
909
- collector.setMode("runtime");
910
- collector.startRender();
911
- const html = await env.renderString(source, context);
912
- collector.endRender();
913
- const data = endDebugCollection();
914
- if (options.htmlOnly !== false) {
915
- const isHtml = html.includes("<html") || html.includes("<body");
916
- if (!isHtml) {
917
- return html;
918
- }
919
- }
920
- const panel = generateDebugPanel(data, options.panel);
921
- if (html.includes("</body>")) {
922
- return html.replace("</body>", `${panel}</body>`);
923
- }
924
- return html + panel;
925
- }
926
- function createDebugRenderer(env, options = {}) {
927
- return {
928
- async render(templateName, context = {}) {
929
- return renderWithDebug(env, templateName, context, options);
930
- },
931
- async renderString(source, context = {}) {
932
- return renderStringWithDebug(env, source, context, options);
933
- }
934
- };
935
- }
936
- function debugMiddleware(env, options = {}) {
937
- return {
938
- hono() {
939
- return async (c, next) => {
940
- await next();
941
- const contentType = c.res.headers.get("content-type") || "";
942
- if (!contentType.includes("text/html"))
943
- return;
944
- const body = await c.res.text();
945
- const collector = startDebugCollection();
946
- collector.captureContext({});
947
- collector.setMode("runtime");
948
- collector.endRender();
949
- const data = endDebugCollection();
950
- const panel = generateDebugPanel(data, options.panel);
951
- const newBody = body.includes("</body>") ? body.replace("</body>", `${panel}</body>`) : body + panel;
952
- c.res = new Response(newBody, {
953
- status: c.res.status,
954
- headers: c.res.headers
955
- });
956
- };
957
- },
958
- express() {
959
- return (req, res, next) => {
960
- const originalSend = res.send.bind(res);
961
- res.send = (body) => {
962
- const contentType = res.get("Content-Type") || "";
963
- if (!contentType.includes("text/html") || typeof body !== "string") {
964
- return originalSend(body);
965
- }
966
- const collector = startDebugCollection();
967
- collector.captureContext({});
968
- collector.setMode("runtime");
969
- collector.endRender();
970
- const data = endDebugCollection();
971
- const panel = generateDebugPanel(data, options.panel);
972
- const newBody = body.includes("</body>") ? body.replace("</body>", `${panel}</body>`) : body + panel;
973
- return originalSend(newBody);
974
- };
975
- next();
976
- };
977
- }
978
- };
979
- }
980
- export {
981
- wrapQuery,
982
- wrapDrizzleQuery,
983
- wrapBunSQL,
984
- startDebugCollection,
985
- setupPrismaLogging,
986
- renderWithDebug,
987
- renderStringWithDebug,
988
- recordQuery,
989
- getDebugCollector,
990
- generateDebugPanel,
991
- endDebugCollection,
992
- debugMiddleware,
993
- createQueryWrapper,
994
- createPrismaMiddleware,
995
- createDrizzleLogger,
996
- createDebugRenderer,
997
- DebugCollector
998
- };
337
+ })();`}function B(J){return String(J).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function q(J){let K=z();if(K)K.recordQuery(J)}function g(){return async(J,K)=>{let X=z(),Z=performance.now(),Y=await K(J),$=performance.now()-Z;if(X){let j=`${J.action} ${J.model}`;X.recordQuery({sql:j,params:J.args?[JSON.stringify(J.args)]:void 0,duration:$,rows:Array.isArray(Y)?Y.length:Y?1:0,source:"prisma"})}return Y}}function y(J){J.$on("query",(K)=>{let X=z();if(X)X.recordQuery({sql:K.query,params:K.params?JSON.parse(K.params):void 0,duration:K.duration,source:"prisma"})})}function m(){return{logQuery(J,K){let X=z();if(X)X.recordQuery({sql:J,params:K,duration:0,source:"drizzle"})}}}async function v(J,K){let X=z(),Z=performance.now(),Y=await J,$=performance.now()-Z;if(X)X.recordQuery({sql:K||"Drizzle Query",duration:$,rows:Array.isArray(Y)?Y.length:Y?1:0,source:"drizzle"});return Y}function p(J){return new Proxy(J,{get(X,Z){let Y=X[Z];if(typeof Y==="function"){if(Z==="query"||Z==="run"||Z==="prepare")return function(...$){let j=z(),U=typeof $[0]==="string"?$[0]:"SQL Query",_=performance.now(),G=Y.apply(X,$);if(G&&typeof G.all==="function")return n(G,U);let A=performance.now()-_;if(j)j.recordQuery({sql:U,params:$.slice(1),duration:A,rows:Array.isArray(G)?G.length:void 0,source:"bun:sqlite"});return G}}return Y}})}function n(J,K){return new Proxy(J,{get(Z,Y){let $=Z[Y];if(typeof $==="function"&&(Y==="all"||Y==="get"||Y==="run"))return function(...j){let U=z(),_=performance.now(),G=$.apply(Z,j),A=performance.now()-_;if(U)U.recordQuery({sql:K,params:j,duration:A,rows:Array.isArray(G)?G.length:G?1:0,source:"bun:sqlite"});return G};return $}})}async function f(J,K,X="sql"){let Z=z(),Y=performance.now(),$=await K(),j=performance.now()-Y;if(Z)Z.recordQuery({sql:J,duration:j,rows:Array.isArray($)?$.length:$?1:0,source:X});return $}function u(J){return(K,X)=>{return f(K,X,J)}}async function s(J,K,X={},Z={}){let Y=R();Y.captureContext(X),Y.addTemplate(K,"root"),Y.setMode("runtime"),Y.startRender();let $=await J.render(K,X);Y.endRender();let j=I();if(Z.htmlOnly!==!1){if(!($.includes("<html")||$.includes("<body")||$.includes("<!DOCTYPE")))return $}let U=O(j,Z.panel);if($.includes("</body>"))return $.replace("</body>",`${U}</body>`);return $+U}async function c(J,K,X={},Z={}){let Y=R();Y.captureContext(X),Y.setMode("runtime"),Y.startRender();let $=await J.renderString(K,X);Y.endRender();let j=I();if(Z.htmlOnly!==!1){if(!($.includes("<html")||$.includes("<body")))return $}let U=O(j,Z.panel);if($.includes("</body>"))return $.replace("</body>",`${U}</body>`);return $+U}function JJ(J,K={}){return{async render(X,Z={}){return s(J,X,Z,K)},async renderString(X,Z={}){return c(J,X,Z,K)}}}function KJ(J,K={}){return{hono(){return async(X,Z)=>{if(await Z(),!(X.res.headers.get("content-type")||"").includes("text/html"))return;let $=await X.res.text(),j=R();j.captureContext({}),j.setMode("runtime"),j.endRender();let U=I(),_=O(U,K.panel),G=$.includes("</body>")?$.replace("</body>",`${_}</body>`):$+_;X.res=new Response(G,{status:X.res.status,headers:X.res.headers})}},express(){return(X,Z,Y)=>{let $=Z.send.bind(Z);Z.send=(j)=>{if(!(Z.get("Content-Type")||"").includes("text/html")||typeof j!=="string")return $(j);let _=R();_.captureContext({}),_.setMode("runtime"),_.endRender();let G=I(),A=O(G,K.panel),Q=j.includes("</body>")?j.replace("</body>",`${A}</body>`):j+A;return $(Q)},Y()}}}}export{f as wrapQuery,v as wrapDrizzleQuery,p as wrapBunSQL,R as startDebugCollection,y as setupPrismaLogging,s as renderWithDebug,c as renderStringWithDebug,q as recordQuery,z as getDebugCollector,O as generateDebugPanel,I as endDebugCollection,KJ as debugMiddleware,u as createQueryWrapper,g as createPrismaMiddleware,m as createDrizzleLogger,JJ as createDebugRenderer,E as DebugCollector};