binja 0.5.2 → 0.5.3

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.
package/dist/index.js CHANGED
@@ -153,6 +153,159 @@ function tokenizeNative(source) {
153
153
  return tokens;
154
154
  }
155
155
 
156
+ // src/errors/index.ts
157
+ var colors = {
158
+ red: "\x1B[31m",
159
+ yellow: "\x1B[33m",
160
+ cyan: "\x1B[36m",
161
+ gray: "\x1B[90m",
162
+ bold: "\x1B[1m",
163
+ dim: "\x1B[2m",
164
+ reset: "\x1B[0m"
165
+ };
166
+ var useColors = process.stdout?.isTTY !== false;
167
+ function c(color, text) {
168
+ return useColors ? `${colors[color]}${text}${colors.reset}` : text;
169
+ }
170
+
171
+ class TemplateError extends Error {
172
+ line;
173
+ column;
174
+ source;
175
+ templateName;
176
+ suggestion;
177
+ availableOptions;
178
+ constructor(message, options) {
179
+ const formatted = formatError("TemplateError", message, options);
180
+ super(formatted);
181
+ this.name = "TemplateError";
182
+ this.line = options.line;
183
+ this.column = options.column;
184
+ this.source = options.source;
185
+ this.templateName = options.templateName;
186
+ this.suggestion = options.suggestion;
187
+ this.availableOptions = options.availableOptions;
188
+ }
189
+ }
190
+
191
+ class TemplateSyntaxError extends Error {
192
+ line;
193
+ column;
194
+ source;
195
+ templateName;
196
+ suggestion;
197
+ constructor(message, options) {
198
+ const formatted = formatError("TemplateSyntaxError", message, options);
199
+ super(formatted);
200
+ this.name = "TemplateSyntaxError";
201
+ this.line = options.line;
202
+ this.column = options.column;
203
+ this.source = options.source;
204
+ this.templateName = options.templateName;
205
+ this.suggestion = options.suggestion;
206
+ }
207
+ }
208
+
209
+ class TemplateRuntimeError extends Error {
210
+ line;
211
+ column;
212
+ source;
213
+ templateName;
214
+ suggestion;
215
+ availableOptions;
216
+ constructor(message, options) {
217
+ const formatted = formatError("TemplateRuntimeError", message, options);
218
+ super(formatted);
219
+ this.name = "TemplateRuntimeError";
220
+ this.line = options.line;
221
+ this.column = options.column;
222
+ this.source = options.source;
223
+ this.templateName = options.templateName;
224
+ this.suggestion = options.suggestion;
225
+ this.availableOptions = options.availableOptions;
226
+ }
227
+ }
228
+ function formatError(type, message, options) {
229
+ const parts = [];
230
+ const location = options.templateName ? `${options.templateName}:${options.line}:${options.column}` : `line ${options.line}, column ${options.column}`;
231
+ parts.push(`${c("red", c("bold", type))}: ${message} at ${c("cyan", location)}`);
232
+ if (options.source) {
233
+ parts.push("");
234
+ parts.push(generateSnippet(options.source, options.line, options.column));
235
+ }
236
+ if (options.suggestion) {
237
+ parts.push("");
238
+ parts.push(`${c("yellow", "Did you mean")}: ${c("cyan", options.suggestion)}?`);
239
+ }
240
+ if (options.availableOptions && options.availableOptions.length > 0) {
241
+ parts.push("");
242
+ const truncated = options.availableOptions.slice(0, 8);
243
+ const more = options.availableOptions.length > 8 ? ` ${c("gray", `... and ${options.availableOptions.length - 8} more`)}` : "";
244
+ parts.push(`${c("gray", "Available")}: ${truncated.join(", ")}${more}`);
245
+ }
246
+ return parts.join(`
247
+ `);
248
+ }
249
+ function generateSnippet(source, errorLine, errorColumn) {
250
+ const lines = source.split(`
251
+ `);
252
+ const parts = [];
253
+ const startLine = Math.max(1, errorLine - 2);
254
+ const endLine = Math.min(lines.length, errorLine + 1);
255
+ const gutterWidth = String(endLine).length;
256
+ for (let i = startLine;i <= endLine; i++) {
257
+ const lineContent = lines[i - 1] || "";
258
+ const lineNum = String(i).padStart(gutterWidth, " ");
259
+ const isErrorLine = i === errorLine;
260
+ if (isErrorLine) {
261
+ parts.push(`${c("red", " \u2192")} ${c("gray", lineNum)} ${c("dim", "\u2502")} ${lineContent}`);
262
+ const caretPadding = " ".repeat(gutterWidth + 4 + Math.max(0, errorColumn - 1));
263
+ const caret = c("red", "^");
264
+ parts.push(`${caretPadding}${caret}`);
265
+ } else {
266
+ parts.push(` ${c("gray", lineNum)} ${c("dim", "\u2502")} ${c("gray", lineContent)}`);
267
+ }
268
+ }
269
+ return parts.join(`
270
+ `);
271
+ }
272
+ function findSimilar(input, candidates, maxDistance = 3) {
273
+ let bestMatch = null;
274
+ let bestDistance = maxDistance + 1;
275
+ const inputLower = input.toLowerCase();
276
+ for (const candidate of candidates) {
277
+ const distance = levenshteinDistance(inputLower, candidate.toLowerCase());
278
+ if (distance < bestDistance) {
279
+ bestDistance = distance;
280
+ bestMatch = candidate;
281
+ }
282
+ }
283
+ return bestDistance <= maxDistance ? bestMatch : null;
284
+ }
285
+ function levenshteinDistance(a, b) {
286
+ if (a.length === 0)
287
+ return b.length;
288
+ if (b.length === 0)
289
+ return a.length;
290
+ const matrix = [];
291
+ for (let i = 0;i <= b.length; i++) {
292
+ matrix[i] = [i];
293
+ }
294
+ for (let j = 0;j <= a.length; j++) {
295
+ matrix[0][j] = j;
296
+ }
297
+ for (let i = 1;i <= b.length; i++) {
298
+ for (let j = 1;j <= a.length; j++) {
299
+ if (b[i - 1] === a[j - 1]) {
300
+ matrix[i][j] = matrix[i - 1][j - 1];
301
+ } else {
302
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
303
+ }
304
+ }
305
+ }
306
+ return matrix[b.length][a.length];
307
+ }
308
+
156
309
  // src/lexer/index.ts
157
310
  class Lexer {
158
311
  state;
@@ -240,7 +393,11 @@ class Lexer {
240
393
  if (this.peek() === "-")
241
394
  this.advance();
242
395
  if (!this.match(this.blockEnd)) {
243
- throw new Error(`Expected ${this.blockEnd} after ${tagName} at line ${this.state.line}`);
396
+ throw new TemplateSyntaxError(`Expected '${this.blockEnd}' after '${tagName}'`, {
397
+ line: this.state.line,
398
+ column: this.state.column,
399
+ source: this.state.source
400
+ });
244
401
  }
245
402
  const endTag = `end${tagName}`;
246
403
  const contentStart = this.state.pos;
@@ -270,7 +427,11 @@ class Lexer {
270
427
  if (this.peek() === "-")
271
428
  this.advance();
272
429
  if (!this.match(this.blockEnd)) {
273
- throw new Error(`Expected ${this.blockEnd} after ${endTag} at line ${this.state.line}`);
430
+ throw new TemplateSyntaxError(`Expected '${this.blockEnd}' after '${endTag}'`, {
431
+ line: this.state.line,
432
+ column: this.state.column,
433
+ source: this.state.source
434
+ });
274
435
  }
275
436
  return;
276
437
  }
@@ -285,7 +446,12 @@ class Lexer {
285
446
  }
286
447
  this.advance();
287
448
  }
288
- throw new Error(`Unclosed ${tagName} block starting at line ${startLine}`);
449
+ throw new TemplateSyntaxError(`Unclosed '${tagName}' block`, {
450
+ line: startLine,
451
+ column: startColumn,
452
+ source: this.state.source,
453
+ suggestion: `Add {% end${tagName} %} to close the block`
454
+ });
289
455
  }
290
456
  scanText() {
291
457
  const start = this.state.pos;
@@ -325,22 +491,27 @@ class Lexer {
325
491
  }
326
492
  this.scanExpressionToken();
327
493
  }
328
- throw new Error(`Unclosed template tag at line ${this.state.line}`);
494
+ throw new TemplateSyntaxError(`Unclosed template tag`, {
495
+ line: this.state.line,
496
+ column: this.state.column,
497
+ source: this.state.source,
498
+ suggestion: `Add closing delimiter '${endDelimiter}'`
499
+ });
329
500
  }
330
501
  scanExpressionToken() {
331
502
  this.skipWhitespace();
332
503
  if (this.isAtEnd())
333
504
  return;
334
- const c = this.peek();
335
- if (c === '"' || c === "'") {
336
- this.scanString(c);
505
+ const c2 = this.peek();
506
+ if (c2 === '"' || c2 === "'") {
507
+ this.scanString(c2);
337
508
  return;
338
509
  }
339
- if (this.isDigit(c)) {
510
+ if (this.isDigit(c2)) {
340
511
  this.scanNumber();
341
512
  return;
342
513
  }
343
- if (this.isAlpha(c) || c === "_") {
514
+ if (this.isAlpha(c2) || c2 === "_") {
344
515
  this.scanIdentifier();
345
516
  return;
346
517
  }
@@ -361,7 +532,12 @@ class Lexer {
361
532
  this.advance();
362
533
  }
363
534
  if (this.isAtEnd()) {
364
- throw new Error(`Unterminated string at line ${this.state.line}`);
535
+ throw new TemplateSyntaxError(`Unterminated string literal`, {
536
+ line: this.state.line,
537
+ column: this.state.column,
538
+ source: this.state.source,
539
+ suggestion: `Add closing quote '${quote}'`
540
+ });
365
541
  }
366
542
  const value = this.state.source.slice(start, this.state.pos);
367
543
  this.advance();
@@ -391,55 +567,55 @@ class Lexer {
391
567
  this.addToken(type, value);
392
568
  }
393
569
  scanOperator() {
394
- const c = this.advance();
395
- switch (c) {
570
+ const c2 = this.advance();
571
+ switch (c2) {
396
572
  case ".":
397
- this.addToken("DOT" /* DOT */, c);
573
+ this.addToken("DOT" /* DOT */, c2);
398
574
  break;
399
575
  case ",":
400
- this.addToken("COMMA" /* COMMA */, c);
576
+ this.addToken("COMMA" /* COMMA */, c2);
401
577
  break;
402
578
  case ":":
403
- this.addToken("COLON" /* COLON */, c);
579
+ this.addToken("COLON" /* COLON */, c2);
404
580
  break;
405
581
  case "|":
406
- this.addToken("PIPE" /* PIPE */, c);
582
+ this.addToken("PIPE" /* PIPE */, c2);
407
583
  break;
408
584
  case "(":
409
- this.addToken("LPAREN" /* LPAREN */, c);
585
+ this.addToken("LPAREN" /* LPAREN */, c2);
410
586
  break;
411
587
  case ")":
412
- this.addToken("RPAREN" /* RPAREN */, c);
588
+ this.addToken("RPAREN" /* RPAREN */, c2);
413
589
  break;
414
590
  case "[":
415
- this.addToken("LBRACKET" /* LBRACKET */, c);
591
+ this.addToken("LBRACKET" /* LBRACKET */, c2);
416
592
  break;
417
593
  case "]":
418
- this.addToken("RBRACKET" /* RBRACKET */, c);
594
+ this.addToken("RBRACKET" /* RBRACKET */, c2);
419
595
  break;
420
596
  case "{":
421
- this.addToken("LBRACE" /* LBRACE */, c);
597
+ this.addToken("LBRACE" /* LBRACE */, c2);
422
598
  break;
423
599
  case "}":
424
- this.addToken("RBRACE" /* RBRACE */, c);
600
+ this.addToken("RBRACE" /* RBRACE */, c2);
425
601
  break;
426
602
  case "+":
427
- this.addToken("ADD" /* ADD */, c);
603
+ this.addToken("ADD" /* ADD */, c2);
428
604
  break;
429
605
  case "-":
430
- this.addToken("SUB" /* SUB */, c);
606
+ this.addToken("SUB" /* SUB */, c2);
431
607
  break;
432
608
  case "*":
433
- this.addToken("MUL" /* MUL */, c);
609
+ this.addToken("MUL" /* MUL */, c2);
434
610
  break;
435
611
  case "/":
436
- this.addToken("DIV" /* DIV */, c);
612
+ this.addToken("DIV" /* DIV */, c2);
437
613
  break;
438
614
  case "%":
439
- this.addToken("MOD" /* MOD */, c);
615
+ this.addToken("MOD" /* MOD */, c2);
440
616
  break;
441
617
  case "~":
442
- this.addToken("TILDE" /* TILDE */, c);
618
+ this.addToken("TILDE" /* TILDE */, c2);
443
619
  break;
444
620
  case "=":
445
621
  if (this.match("=")) {
@@ -452,7 +628,12 @@ class Lexer {
452
628
  if (this.match("=")) {
453
629
  this.addToken("NE" /* NE */, "!=");
454
630
  } else {
455
- throw new Error(`Unexpected character '!' at line ${this.state.line}`);
631
+ throw new TemplateSyntaxError(`Unexpected character '!'`, {
632
+ line: this.state.line,
633
+ column: this.state.column - 1,
634
+ source: this.state.source,
635
+ suggestion: `Use '!=' for not-equal comparison or 'not' for negation`
636
+ });
456
637
  }
457
638
  break;
458
639
  case "<":
@@ -470,8 +651,12 @@ class Lexer {
470
651
  }
471
652
  break;
472
653
  default:
473
- if (!this.isWhitespace(c)) {
474
- throw new Error(`Unexpected character '${c}' at line ${this.state.line}`);
654
+ if (!this.isWhitespace(c2)) {
655
+ throw new TemplateSyntaxError(`Unexpected character '${c2}'`, {
656
+ line: this.state.line,
657
+ column: this.state.column - 1,
658
+ source: this.state.source
659
+ });
475
660
  }
476
661
  }
477
662
  }
@@ -502,10 +687,10 @@ class Lexer {
502
687
  return this.state.source[this.state.pos + 1];
503
688
  }
504
689
  advance() {
505
- const c = this.state.source[this.state.pos];
690
+ const c2 = this.state.source[this.state.pos];
506
691
  this.state.pos++;
507
692
  this.state.column++;
508
- return c;
693
+ return c2;
509
694
  }
510
695
  match(expected, offset = 0) {
511
696
  const source = this.state.source;
@@ -545,20 +730,20 @@ class Lexer {
545
730
  this.advance();
546
731
  }
547
732
  }
548
- isWhitespace(c) {
549
- return c === " " || c === "\t" || c === `
550
- ` || c === "\r";
733
+ isWhitespace(c2) {
734
+ return c2 === " " || c2 === "\t" || c2 === `
735
+ ` || c2 === "\r";
551
736
  }
552
- isDigit(c) {
553
- const code = c.charCodeAt(0);
737
+ isDigit(c2) {
738
+ const code = c2.charCodeAt(0);
554
739
  return code >= 48 && code <= 57;
555
740
  }
556
- isAlpha(c) {
557
- const code = c.charCodeAt(0);
741
+ isAlpha(c2) {
742
+ const code = c2.charCodeAt(0);
558
743
  return code >= 97 && code <= 122 || code >= 65 && code <= 90;
559
744
  }
560
- isAlphaNumeric(c) {
561
- const code = c.charCodeAt(0);
745
+ isAlphaNumeric(c2) {
746
+ const code = c2.charCodeAt(0);
562
747
  return code >= 48 && code <= 57 || code >= 97 && code <= 122 || code >= 65 && code <= 90;
563
748
  }
564
749
  addToken(type, value) {
@@ -575,8 +760,10 @@ class Lexer {
575
760
  class Parser {
576
761
  tokens;
577
762
  current = 0;
578
- constructor(tokens) {
763
+ source;
764
+ constructor(tokens, source) {
579
765
  this.tokens = tokens;
766
+ this.source = source;
580
767
  }
581
768
  parse() {
582
769
  const body = [];
@@ -1668,7 +1855,11 @@ class Parser {
1668
1855
  }
1669
1856
  error(message) {
1670
1857
  const token = this.peek();
1671
- return new Error(`Parse error at line ${token.line}, column ${token.column}: ${message}`);
1858
+ return new TemplateSyntaxError(message, {
1859
+ line: token.line,
1860
+ column: token.column,
1861
+ source: this.source
1862
+ });
1672
1863
  }
1673
1864
  }
1674
1865
 
@@ -1830,7 +2021,7 @@ var capfirst = (value) => {
1830
2021
  const str = String(value);
1831
2022
  return str.charAt(0).toUpperCase() + str.slice(1);
1832
2023
  };
1833
- var title = (value) => String(value).replace(TITLE_REGEX, (c) => c.toUpperCase());
2024
+ var title = (value) => String(value).replace(TITLE_REGEX, (c2) => c2.toUpperCase());
1834
2025
  var trim = (value) => String(value).trim();
1835
2026
  var striptags = (value) => String(value).replace(STRIPTAGS_REGEX, "");
1836
2027
  var escape = (value) => {
@@ -2943,6 +3134,7 @@ class Runtime {
2943
3134
  tests;
2944
3135
  blocks = new Map;
2945
3136
  parentTemplate = null;
3137
+ source;
2946
3138
  constructor(options = {}) {
2947
3139
  this.options = {
2948
3140
  autoescape: options.autoescape ?? true,
@@ -3679,8 +3871,17 @@ class Runtime {
3679
3871
  }
3680
3872
  const kwargs = this.evalObjectSync(node.kwargs, ctx);
3681
3873
  const filter = this.filters[node.filter];
3682
- if (!filter)
3683
- throw new Error(`Unknown filter: ${node.filter}`);
3874
+ if (!filter) {
3875
+ const available = Object.keys(this.filters);
3876
+ const suggestion = findSimilar(node.filter, available);
3877
+ throw new TemplateRuntimeError(`Unknown filter '${node.filter}'`, {
3878
+ line: node.line,
3879
+ column: node.column,
3880
+ source: this.source,
3881
+ suggestion: suggestion || undefined,
3882
+ availableOptions: available.slice(0, 15)
3883
+ });
3884
+ }
3684
3885
  return filter(value, ...args, ...Object.values(kwargs));
3685
3886
  }
3686
3887
  evalBinaryOp(node, ctx) {
@@ -4117,6 +4318,9 @@ class Runtime {
4117
4318
  addGlobal(name, value) {
4118
4319
  this.options.globals[name] = value;
4119
4320
  }
4321
+ setSource(source) {
4322
+ this.source = source;
4323
+ }
4120
4324
  }
4121
4325
 
4122
4326
  // src/compiler/index.ts
@@ -5053,13 +5257,13 @@ var DEFAULT_OPTIONS = {
5053
5257
  function generateDebugPanel(data, options = {}) {
5054
5258
  const opts = { ...DEFAULT_OPTIONS, ...options };
5055
5259
  const id = `binja-debug-${Date.now()}`;
5056
- const colors = opts.dark ? darkTheme : lightTheme;
5260
+ const colors2 = opts.dark ? darkTheme : lightTheme;
5057
5261
  return `
5058
5262
  <!-- Binja Debug Panel -->
5059
5263
  <div id="${id}" class="binja-debugger" data-theme="${opts.dark ? "dark" : "light"}">
5060
- <style>${generateStyles(id, colors, opts)}</style>
5061
- ${generateToggle(id, data, colors)}
5062
- ${generatePanel(id, data, colors, opts)}
5264
+ <style>${generateStyles(id, colors2, opts)}</style>
5265
+ ${generateToggle(id, data, colors2)}
5266
+ ${generatePanel(id, data, colors2, opts)}
5063
5267
  <script>${generateScript(id)}</script>
5064
5268
  </div>
5065
5269
  <!-- /Binja Debug Panel -->
@@ -5097,84 +5301,84 @@ var lightTheme = {
5097
5301
  error: "#dc2626",
5098
5302
  info: "#0891b2"
5099
5303
  };
5100
- function generateStyles(id, c, opts) {
5304
+ function generateStyles(id, c2, opts) {
5101
5305
  const pos = getPosition(opts.position);
5102
5306
  return `
5103
5307
  #${id} { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; font-size: 13px; line-height: 1.5; position: fixed; ${pos} z-index: 2147483647; }
5104
5308
  #${id} * { box-sizing: border-box; margin: 0; padding: 0; }
5105
- #${id} .dbg-toggle { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; background: ${c.bg}; border: 1px solid ${c.border}; border-radius: 8px; color: ${c.text}; cursor: pointer; font-size: 12px; font-weight: 500; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; }
5106
- #${id} .dbg-toggle:hover { border-color: ${c.accent}; box-shadow: 0 4px 16px rgba(0,0,0,0.2); }
5309
+ #${id} .dbg-toggle { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; background: ${c2.bg}; border: 1px solid ${c2.border}; border-radius: 8px; color: ${c2.text}; cursor: pointer; font-size: 12px; font-weight: 500; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; }
5310
+ #${id} .dbg-toggle:hover { border-color: ${c2.accent}; box-shadow: 0 4px 16px rgba(0,0,0,0.2); }
5107
5311
  #${id} .dbg-toggle svg { width: 16px; height: 16px; }
5108
- #${id} .dbg-toggle .dbg-time { font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 11px; padding: 2px 8px; background: ${c.bgTertiary}; border-radius: 4px; color: ${c.success}; }
5109
- #${id} .dbg-panel { display: none; width: ${opts.width}px; max-height: 85vh; background: ${c.bg}; border: 1px solid ${c.border}; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.24); overflow: hidden; margin-top: 8px; }
5312
+ #${id} .dbg-toggle .dbg-time { font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 11px; padding: 2px 8px; background: ${c2.bgTertiary}; border-radius: 4px; color: ${c2.success}; }
5313
+ #${id} .dbg-panel { display: none; width: ${opts.width}px; max-height: 85vh; background: ${c2.bg}; border: 1px solid ${c2.border}; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.24); overflow: hidden; margin-top: 8px; }
5110
5314
  #${id} .dbg-panel.open { display: block; }
5111
- #${id} .dbg-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: ${c.bgSecondary}; border-bottom: 1px solid ${c.border}; }
5112
- #${id} .dbg-logo { display: flex; align-items: center; gap: 10px; font-weight: 600; color: ${c.text}; }
5113
- #${id} .dbg-logo svg { width: 20px; height: 20px; color: ${c.accent}; }
5315
+ #${id} .dbg-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: ${c2.bgSecondary}; border-bottom: 1px solid ${c2.border}; }
5316
+ #${id} .dbg-logo { display: flex; align-items: center; gap: 10px; font-weight: 600; color: ${c2.text}; }
5317
+ #${id} .dbg-logo svg { width: 20px; height: 20px; color: ${c2.accent}; }
5114
5318
  #${id} .dbg-meta { display: flex; align-items: center; gap: 12px; }
5115
5319
  #${id} .dbg-badge { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; padding: 3px 10px; border-radius: 4px; font-weight: 500; }
5116
- #${id} .dbg-badge.time { background: rgba(34,197,94,0.1); color: ${c.success}; }
5117
- #${id} .dbg-badge.mode { background: rgba(59,130,246,0.1); color: ${c.accent}; }
5118
- #${id} .dbg-close { background: none; border: none; color: ${c.textMuted}; cursor: pointer; padding: 4px; border-radius: 4px; display: flex; }
5119
- #${id} .dbg-close:hover { background: ${c.bgTertiary}; color: ${c.text}; }
5320
+ #${id} .dbg-badge.time { background: rgba(34,197,94,0.1); color: ${c2.success}; }
5321
+ #${id} .dbg-badge.mode { background: rgba(59,130,246,0.1); color: ${c2.accent}; }
5322
+ #${id} .dbg-close { background: none; border: none; color: ${c2.textMuted}; cursor: pointer; padding: 4px; border-radius: 4px; display: flex; }
5323
+ #${id} .dbg-close:hover { background: ${c2.bgTertiary}; color: ${c2.text}; }
5120
5324
  #${id} .dbg-close svg { width: 18px; height: 18px; }
5121
5325
  #${id} .dbg-body { max-height: calc(85vh - 52px); overflow-y: auto; }
5122
- #${id} .dbg-section { border-bottom: 1px solid ${c.border}; }
5326
+ #${id} .dbg-section { border-bottom: 1px solid ${c2.border}; }
5123
5327
  #${id} .dbg-section:last-child { border-bottom: none; }
5124
5328
  #${id} .dbg-section-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; cursor: pointer; user-select: none; transition: background 0.15s; }
5125
- #${id} .dbg-section-header:hover { background: ${c.bgSecondary}; }
5126
- #${id} .dbg-section-title { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: ${c.textSecondary}; }
5329
+ #${id} .dbg-section-header:hover { background: ${c2.bgSecondary}; }
5330
+ #${id} .dbg-section-title { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: ${c2.textSecondary}; }
5127
5331
  #${id} .dbg-section-title svg { width: 14px; height: 14px; opacity: 0.7; }
5128
- #${id} .dbg-section-meta { font-size: 11px; color: ${c.textMuted}; font-family: 'SF Mono', Monaco, monospace; }
5129
- #${id} .dbg-section-content { display: none; padding: 12px 16px; background: ${c.bgSecondary}; }
5332
+ #${id} .dbg-section-meta { font-size: 11px; color: ${c2.textMuted}; font-family: 'SF Mono', Monaco, monospace; }
5333
+ #${id} .dbg-section-content { display: none; padding: 12px 16px; background: ${c2.bgSecondary}; }
5130
5334
  #${id} .dbg-section.open .dbg-section-content { display: block; }
5131
- #${id} .dbg-section .dbg-chevron { transition: transform 0.2s; color: ${c.textMuted}; }
5335
+ #${id} .dbg-section .dbg-chevron { transition: transform 0.2s; color: ${c2.textMuted}; }
5132
5336
  #${id} .dbg-section.open .dbg-chevron { transform: rotate(90deg); }
5133
- #${id} .dbg-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid ${c.border}; }
5337
+ #${id} .dbg-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid ${c2.border}; }
5134
5338
  #${id} .dbg-row:last-child { border-bottom: none; }
5135
- #${id} .dbg-label { color: ${c.textSecondary}; font-size: 12px; }
5136
- #${id} .dbg-value { color: ${c.text}; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; text-align: right; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
5137
- #${id} .dbg-bar { height: 3px; background: ${c.bgTertiary}; border-radius: 2px; margin-top: 4px; overflow: hidden; }
5339
+ #${id} .dbg-label { color: ${c2.textSecondary}; font-size: 12px; }
5340
+ #${id} .dbg-value { color: ${c2.text}; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; text-align: right; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
5341
+ #${id} .dbg-bar { height: 3px; background: ${c2.bgTertiary}; border-radius: 2px; margin-top: 4px; overflow: hidden; }
5138
5342
  #${id} .dbg-bar-fill { height: 100%; border-radius: 2px; transition: width 0.3s ease; }
5139
- #${id} .dbg-bar-fill.lexer { background: ${c.info}; }
5140
- #${id} .dbg-bar-fill.parser { background: ${c.warning}; }
5141
- #${id} .dbg-bar-fill.render { background: ${c.success}; }
5343
+ #${id} .dbg-bar-fill.lexer { background: ${c2.info}; }
5344
+ #${id} .dbg-bar-fill.parser { background: ${c2.warning}; }
5345
+ #${id} .dbg-bar-fill.render { background: ${c2.success}; }
5142
5346
  #${id} .dbg-templates { display: flex; flex-direction: column; gap: 6px; }
5143
- #${id} .dbg-template { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: ${c.bg}; border-radius: 6px; font-size: 12px; }
5144
- #${id} .dbg-template-icon { width: 16px; height: 16px; color: ${c.textMuted}; flex-shrink: 0; }
5145
- #${id} .dbg-template-name { color: ${c.text}; font-family: 'SF Mono', Monaco, monospace; }
5347
+ #${id} .dbg-template { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: ${c2.bg}; border-radius: 6px; font-size: 12px; }
5348
+ #${id} .dbg-template-icon { width: 16px; height: 16px; color: ${c2.textMuted}; flex-shrink: 0; }
5349
+ #${id} .dbg-template-name { color: ${c2.text}; font-family: 'SF Mono', Monaco, monospace; }
5146
5350
  #${id} .dbg-template-tag { font-size: 10px; padding: 2px 6px; border-radius: 3px; font-weight: 500; text-transform: uppercase; }
5147
- #${id} .dbg-template-tag.root { background: rgba(59,130,246,0.15); color: ${c.accent}; }
5351
+ #${id} .dbg-template-tag.root { background: rgba(59,130,246,0.15); color: ${c2.accent}; }
5148
5352
  #${id} .dbg-template-tag.extends { background: rgba(168,85,247,0.15); color: #a855f7; }
5149
- #${id} .dbg-template-tag.include { background: rgba(34,197,94,0.15); color: ${c.success}; }
5353
+ #${id} .dbg-template-tag.include { background: rgba(34,197,94,0.15); color: ${c2.success}; }
5150
5354
  #${id} .dbg-ctx-grid { display: flex; flex-direction: column; gap: 4px; }
5151
- #${id} .dbg-ctx-item { background: ${c.bg}; border-radius: 6px; overflow: hidden; }
5355
+ #${id} .dbg-ctx-item { background: ${c2.bg}; border-radius: 6px; overflow: hidden; }
5152
5356
  #${id} .dbg-ctx-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; cursor: default; }
5153
5357
  #${id} .dbg-ctx-row.expandable { cursor: pointer; }
5154
- #${id} .dbg-ctx-row.expandable:hover { background: ${c.bgTertiary}; }
5358
+ #${id} .dbg-ctx-row.expandable:hover { background: ${c2.bgTertiary}; }
5155
5359
  #${id} .dbg-ctx-key { display: flex; align-items: center; gap: 6px; }
5156
- #${id} .dbg-ctx-arrow { width: 12px; height: 12px; color: ${c.textMuted}; transition: transform 0.15s; flex-shrink: 0; }
5360
+ #${id} .dbg-ctx-arrow { width: 12px; height: 12px; color: ${c2.textMuted}; transition: transform 0.15s; flex-shrink: 0; }
5157
5361
  #${id} .dbg-ctx-item.open > .dbg-ctx-row .dbg-ctx-arrow { transform: rotate(90deg); }
5158
- #${id} .dbg-ctx-name { color: ${c.text}; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; }
5159
- #${id} .dbg-ctx-type { font-size: 10px; color: ${c.accent}; background: rgba(59,130,246,0.1); padding: 1px 5px; border-radius: 3px; }
5160
- #${id} .dbg-ctx-preview { color: ${c.textSecondary}; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
5161
- #${id} .dbg-ctx-children { display: none; padding-left: 16px; border-left: 1px solid ${c.border}; margin-left: 10px; }
5362
+ #${id} .dbg-ctx-name { color: ${c2.text}; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; }
5363
+ #${id} .dbg-ctx-type { font-size: 10px; color: ${c2.accent}; background: rgba(59,130,246,0.1); padding: 1px 5px; border-radius: 3px; }
5364
+ #${id} .dbg-ctx-preview { color: ${c2.textSecondary}; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
5365
+ #${id} .dbg-ctx-children { display: none; padding-left: 16px; border-left: 1px solid ${c2.border}; margin-left: 10px; }
5162
5366
  #${id} .dbg-ctx-item.open > .dbg-ctx-children { display: block; }
5163
5367
  #${id} .dbg-ctx-children .dbg-ctx-item { background: transparent; }
5164
5368
  #${id} .dbg-ctx-children .dbg-ctx-row { padding: 4px 8px; }
5165
5369
  #${id} .dbg-filters { display: flex; flex-wrap: wrap; gap: 6px; }
5166
- #${id} .dbg-filter { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: ${c.bg}; border-radius: 5px; font-size: 12px; font-family: 'SF Mono', Monaco, monospace; color: ${c.text}; }
5167
- #${id} .dbg-filter-count { font-size: 10px; color: ${c.accent}; font-weight: 600; }
5370
+ #${id} .dbg-filter { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: ${c2.bg}; border-radius: 5px; font-size: 12px; font-family: 'SF Mono', Monaco, monospace; color: ${c2.text}; }
5371
+ #${id} .dbg-filter-count { font-size: 10px; color: ${c2.accent}; font-weight: 600; }
5168
5372
  #${id} .dbg-cache { display: flex; gap: 16px; }
5169
- #${id} .dbg-cache-stat { flex: 1; padding: 12px; background: ${c.bg}; border-radius: 6px; text-align: center; }
5373
+ #${id} .dbg-cache-stat { flex: 1; padding: 12px; background: ${c2.bg}; border-radius: 6px; text-align: center; }
5170
5374
  #${id} .dbg-cache-num { font-size: 24px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
5171
- #${id} .dbg-cache-num.hit { color: ${c.success}; }
5172
- #${id} .dbg-cache-num.miss { color: ${c.error}; }
5173
- #${id} .dbg-cache-label { font-size: 11px; color: ${c.textMuted}; margin-top: 4px; }
5375
+ #${id} .dbg-cache-num.hit { color: ${c2.success}; }
5376
+ #${id} .dbg-cache-num.miss { color: ${c2.error}; }
5377
+ #${id} .dbg-cache-label { font-size: 11px; color: ${c2.textMuted}; margin-top: 4px; }
5174
5378
  #${id} .dbg-warnings { display: flex; flex-direction: column; gap: 6px; }
5175
- #${id} .dbg-warning { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; background: rgba(234,179,8,0.1); border-radius: 6px; border-left: 3px solid ${c.warning}; }
5176
- #${id} .dbg-warning-icon { color: ${c.warning}; flex-shrink: 0; margin-top: 1px; }
5177
- #${id} .dbg-warning-text { color: ${c.text}; font-size: 12px; }
5379
+ #${id} .dbg-warning { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; background: rgba(234,179,8,0.1); border-radius: 6px; border-left: 3px solid ${c2.warning}; }
5380
+ #${id} .dbg-warning-icon { color: ${c2.warning}; flex-shrink: 0; margin-top: 1px; }
5381
+ #${id} .dbg-warning-text { color: ${c2.text}; font-size: 12px; }
5178
5382
  `;
5179
5383
  }
5180
5384
  function getPosition(pos) {
@@ -5201,7 +5405,7 @@ var icons = {
5201
5405
  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>`,
5202
5406
  file: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"/></svg>`
5203
5407
  };
5204
- function generateToggle(id, data, c) {
5408
+ function generateToggle(id, data, c2) {
5205
5409
  const time2 = (data.totalTime || 0).toFixed(1);
5206
5410
  return `
5207
5411
  <button class="dbg-toggle" onclick="document.querySelector('#${id} .dbg-panel').classList.add('open');this.style.display='none'">
@@ -5210,7 +5414,7 @@ function generateToggle(id, data, c) {
5210
5414
  <span class="dbg-time">${time2}ms</span>
5211
5415
  </button>`;
5212
5416
  }
5213
- function generatePanel(id, data, c, opts) {
5417
+ function generatePanel(id, data, c2, opts) {
5214
5418
  const time2 = (data.totalTime || 0).toFixed(2);
5215
5419
  const mode = data.mode === "aot" ? "AOT" : "Runtime";
5216
5420
  return `
@@ -5484,12 +5688,12 @@ function createDebugRenderer(env, options = {}) {
5484
5688
  function debugMiddleware(env, options = {}) {
5485
5689
  return {
5486
5690
  hono() {
5487
- return async (c, next) => {
5691
+ return async (c2, next) => {
5488
5692
  await next();
5489
- const contentType = c.res.headers.get("content-type") || "";
5693
+ const contentType = c2.res.headers.get("content-type") || "";
5490
5694
  if (!contentType.includes("text/html"))
5491
5695
  return;
5492
- const body = await c.res.text();
5696
+ const body = await c2.res.text();
5493
5697
  const collector = startDebugCollection();
5494
5698
  collector.captureContext({});
5495
5699
  collector.setMode("runtime");
@@ -5497,9 +5701,9 @@ function debugMiddleware(env, options = {}) {
5497
5701
  const data = endDebugCollection();
5498
5702
  const panel = generateDebugPanel(data, options.panel);
5499
5703
  const newBody = body.includes("</body>") ? body.replace("</body>", `${panel}</body>`) : body + panel;
5500
- c.res = new Response(newBody, {
5501
- status: c.res.status,
5502
- headers: c.res.headers
5704
+ c2.res = new Response(newBody, {
5705
+ status: c2.res.status,
5706
+ headers: c2.res.headers
5503
5707
  });
5504
5708
  };
5505
5709
  },
@@ -5620,7 +5824,7 @@ class Environment {
5620
5824
  compile(source) {
5621
5825
  const lexer = new Lexer(source);
5622
5826
  const tokens = lexer.tokenize();
5623
- const parser = new Parser(tokens);
5827
+ const parser = new Parser(tokens, source);
5624
5828
  return parser.parse();
5625
5829
  }
5626
5830
  async loadTemplate(templateName) {
@@ -5741,7 +5945,7 @@ function Template(source, options = {}) {
5741
5945
  function compile(source, options = {}) {
5742
5946
  const lexer = new Lexer(source);
5743
5947
  const tokens = lexer.tokenize();
5744
- const parser = new Parser(tokens);
5948
+ const parser = new Parser(tokens, source);
5745
5949
  const ast = parser.parse();
5746
5950
  return compileToFunction(ast, options);
5747
5951
  }
@@ -5764,7 +5968,7 @@ async function compileWithInheritance(templateName, options) {
5764
5968
  parse(source2) {
5765
5969
  const lexer = new Lexer(source2);
5766
5970
  const tokens = lexer.tokenize();
5767
- const parser = new Parser(tokens);
5971
+ const parser = new Parser(tokens, source2);
5768
5972
  return parser.parse();
5769
5973
  }
5770
5974
  };
@@ -5796,7 +6000,7 @@ async function compileWithInheritanceToCode(templateName, options) {
5796
6000
  parse(source2) {
5797
6001
  const lexer = new Lexer(source2);
5798
6002
  const tokens = lexer.tokenize();
5799
- const parser = new Parser(tokens);
6003
+ const parser = new Parser(tokens, source2);
5800
6004
  return parser.parse();
5801
6005
  }
5802
6006
  };
@@ -5812,7 +6016,7 @@ async function compileWithInheritanceToCode(templateName, options) {
5812
6016
  function compileToCode(source, options = {}) {
5813
6017
  const lexer = new Lexer(source);
5814
6018
  const tokens = lexer.tokenize();
5815
- const parser = new Parser(tokens);
6019
+ const parser = new Parser(tokens, source);
5816
6020
  const ast = parser.parse();
5817
6021
  return compileToString(ast, options);
5818
6022
  }
@@ -5832,6 +6036,9 @@ export {
5832
6036
  builtinTests,
5833
6037
  builtinFilters,
5834
6038
  TokenType,
6039
+ TemplateSyntaxError,
6040
+ TemplateRuntimeError,
6041
+ TemplateError,
5835
6042
  Template,
5836
6043
  Runtime,
5837
6044
  Parser,