binja 0.9.1 → 0.9.2

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/ai/index.js CHANGED
@@ -1,14 +1,16 @@
1
1
  // @bun
2
- var j=Object.create;var{getPrototypeOf:b,defineProperty:X,getOwnPropertyNames:v}=Object;var f=Object.prototype.hasOwnProperty;var $=(A,E,O)=>{O=A!=null?j(b(A)):{};let I=E||!A||!A.__esModule?X(O,"default",{value:A,enumerable:!0}):O;for(let P of v(A))if(!f.call(I,P))X(I,P,{get:()=>A[P],enumerable:!0});return I};var F=import.meta.require;var J={and:"AND",or:"OR",not:"NOT",true:"NAME",false:"NAME",True:"NAME",False:"NAME",None:"NAME",none:"NAME",is:"NAME",in:"NAME"};var h={red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m",dim:"\x1B[2m",reset:"\x1B[0m"},x=process.stdout?.isTTY!==!1;function B(A,E){return x?`${h[A]}${E}${h.reset}`:E}class _ extends Error{line;column;source;templateName;suggestion;constructor(A,E){let O=g("TemplateSyntaxError",A,E);super(O);this.name="TemplateSyntaxError",this.line=E.line,this.column=E.column,this.source=E.source,this.templateName=E.templateName,this.suggestion=E.suggestion}}function g(A,E,O){let I=[],P=O.templateName?`${O.templateName}:${O.line}:${O.column}`:`line ${O.line}, column ${O.column}`;if(I.push(`${B("red",B("bold",A))}: ${E} at ${B("cyan",P)}`),O.source)I.push(""),I.push(u(O.source,O.line,O.column));if(O.suggestion)I.push(""),I.push(`${B("yellow","Did you mean")}: ${B("cyan",O.suggestion)}?`);if(O.availableOptions&&O.availableOptions.length>0){I.push("");let R=O.availableOptions.slice(0,8),M=O.availableOptions.length>8?` ${B("gray",`... and ${O.availableOptions.length-8} more`)}`:"";I.push(`${B("gray","Available")}: ${R.join(", ")}${M}`)}return I.join(`
3
- `)}function u(A,E,O){let I=A.split(`
4
- `),P=[],R=Math.max(1,E-2),M=Math.min(I.length,E+1),L=String(M).length;for(let N=R;N<=M;N++){let C=I[N-1]||"",K=String(N).padStart(L," ");if(N===E){P.push(`${B("red"," \u2192")} ${B("gray",K)} ${B("dim","\u2502")} ${C}`);let W=" ".repeat(L+4+Math.max(0,O-1)),V=B("red","^");P.push(`${W}${V}`)}else P.push(` ${B("gray",K)} ${B("dim","\u2502")} ${B("gray",C)}`)}return P.join(`
5
- `)}class D{state;variableStart;variableEnd;blockStart;blockEnd;commentStart;commentEnd;constructor(A,E={}){this.state={source:A,pos:0,line:1,column:1,tokens:[]},this.variableStart=E.variableStart??"{{",this.variableEnd=E.variableEnd??"}}",this.blockStart=E.blockStart??"{%",this.blockEnd=E.blockEnd??"%}",this.commentStart=E.commentStart??"{#",this.commentEnd=E.commentEnd??"#}"}tokenize(){while(!this.isAtEnd())this.scanToken();return this.addToken("EOF",""),this.state.tokens}scanToken(){if(this.match(this.variableStart)){this.addToken("VARIABLE_START",this.variableStart),this.scanExpression(this.variableEnd,"VARIABLE_END");return}if(this.match(this.blockStart)){let A=this.peek()==="-";if(A)this.advance();let E=this.state.pos;if(this.skipWhitespace(),this.checkWord("raw")||this.checkWord("verbatim")){let O=this.checkWord("raw")?"raw":"verbatim";this.scanRawBlock(O,A);return}this.state.pos=E,this.addToken("BLOCK_START",this.blockStart+(A?"-":"")),this.scanExpression(this.blockEnd,"BLOCK_END");return}if(this.match(this.commentStart)){this.scanComment();return}this.scanText()}checkWord(A){let E=this.state.pos;for(let I=0;I<A.length;I++)if(this.state.source[E+I]?.toLowerCase()!==A[I])return!1;let O=this.state.source[E+A.length];return!O||!this.isAlphaNumeric(O)}scanRawBlock(A,E){let O=this.state.line,I=this.state.column;for(let M=0;M<A.length;M++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new _(`Expected '${this.blockEnd}' after '${A}'`,{line:this.state.line,column:this.state.column,source:this.state.source});let P=`end${A}`,R=this.state.pos;while(!this.isAtEnd()){if(this.check(this.blockStart)){let M=this.state.pos,L=this.state.line,N=this.state.column;if(this.match(this.blockStart),this.peek()==="-")this.advance();if(this.skipWhitespace(),this.checkWord(P)){let C=this.state.source.slice(R,M);if(C.length>0)this.state.tokens.push({type:"TEXT",value:C,line:O,column:I});for(let K=0;K<P.length;K++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new _(`Expected '${this.blockEnd}' after '${P}'`,{line:this.state.line,column:this.state.column,source:this.state.source});return}this.state.pos=M,this.state.line=L,this.state.column=N}if(this.peek()===`
6
- `)this.state.line++,this.state.column=0;this.advance()}throw new _(`Unclosed '${A}' block`,{line:O,column:I,source:this.state.source,suggestion:`Add {% end${A} %} to close the block`})}scanText(){let A=this.state.pos,E=this.state.line,O=this.state.column;while(!this.isAtEnd()){if(this.check(this.variableStart)||this.check(this.blockStart)||this.check(this.commentStart))break;if(this.peek()===`
7
- `)this.state.line++,this.state.column=0;this.advance()}if(this.state.pos>A){let I=this.state.source.slice(A,this.state.pos);this.state.tokens.push({type:"TEXT",value:I,line:E,column:O})}}scanExpression(A,E){this.skipWhitespace();while(!this.isAtEnd()){if(this.skipWhitespace(),this.peek()==="-"&&this.check(A,1))this.advance();if(this.match(A)){this.addToken(E,A);return}this.scanExpressionToken()}throw new _("Unclosed template tag",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing delimiter '${A}'`})}scanExpressionToken(){if(this.skipWhitespace(),this.isAtEnd())return;let A=this.peek();if(A==='"'||A==="'"){this.scanString(A);return}if(this.isDigit(A)){this.scanNumber();return}if(this.isAlpha(A)||A==="_"){this.scanIdentifier();return}this.scanOperator()}scanString(A){this.advance();let E=this.state.pos;while(!this.isAtEnd()&&this.peek()!==A){if(this.peek()==="\\"&&this.peekNext()===A)this.advance();if(this.peek()===`
8
- `)this.state.line++,this.state.column=0;this.advance()}if(this.isAtEnd())throw new _("Unterminated string literal",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing quote '${A}'`});let O=this.state.source.slice(E,this.state.pos);this.advance(),this.addToken("STRING",O)}scanNumber(){let A=this.state.pos;while(this.isDigit(this.peek()))this.advance();if(this.peek()==="."&&this.isDigit(this.peekNext())){this.advance();while(this.isDigit(this.peek()))this.advance()}let E=this.state.source.slice(A,this.state.pos);this.addToken("NUMBER",E)}scanIdentifier(){let A=this.state.pos;while(this.isAlphaNumeric(this.peek())||this.peek()==="_")this.advance();let E=this.state.source.slice(A,this.state.pos),O=J[E]??"NAME";this.addToken(O,E)}scanOperator(){let A=this.advance();switch(A){case".":this.addToken("DOT",A);break;case",":this.addToken("COMMA",A);break;case":":this.addToken("COLON",A);break;case"|":this.addToken("PIPE",A);break;case"(":this.addToken("LPAREN",A);break;case")":this.addToken("RPAREN",A);break;case"[":this.addToken("LBRACKET",A);break;case"]":this.addToken("RBRACKET",A);break;case"{":this.addToken("LBRACE",A);break;case"}":this.addToken("RBRACE",A);break;case"+":this.addToken("ADD",A);break;case"-":this.addToken("SUB",A);break;case"*":this.addToken("MUL",A);break;case"/":this.addToken("DIV",A);break;case"%":this.addToken("MOD",A);break;case"~":this.addToken("TILDE",A);break;case"=":if(this.match("="))this.addToken("EQ","==");else this.addToken("ASSIGN","=");break;case"!":if(this.match("="))this.addToken("NE","!=");else throw new _("Unexpected character '!'",{line:this.state.line,column:this.state.column-1,source:this.state.source,suggestion:"Use '!=' for not-equal comparison or 'not' for negation"});break;case"<":if(this.match("="))this.addToken("LE","<=");else this.addToken("LT","<");break;case">":if(this.match("="))this.addToken("GE",">=");else this.addToken("GT",">");break;case"?":if(this.match("?"))this.addToken("NULLCOALESCE","??");else this.addToken("QUESTION","?");break;default:if(!this.isWhitespace(A))throw new _(`Unexpected character '${A}'`,{line:this.state.line,column:this.state.column-1,source:this.state.source})}}scanComment(){while(!this.isAtEnd()&&!this.check(this.commentEnd)){if(this.peek()===`
2
+ var $=import.meta.require;var F={and:"AND",or:"OR",not:"NOT",true:"NAME",false:"NAME",True:"NAME",False:"NAME",None:"NAME",none:"NAME",is:"NAME",in:"NAME"};var w={red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m",dim:"\x1B[2m",reset:"\x1B[0m"},W=process.stdout?.isTTY!==!1;function _(A,E){return W?`${w[A]}${E}${w.reset}`:E}class B extends Error{line;column;source;templateName;suggestion;constructor(A,E){let O=V("TemplateSyntaxError",A,E);super(O);this.name="TemplateSyntaxError",this.line=E.line,this.column=E.column,this.source=E.source,this.templateName=E.templateName,this.suggestion=E.suggestion}}function V(A,E,O){let I=[],P=O.templateName?`${O.templateName}:${O.line}:${O.column}`:`line ${O.line}, column ${O.column}`;if(I.push(`${_("red",_("bold",A))}: ${E} at ${_("cyan",P)}`),O.source)I.push(""),I.push(j(O.source,O.line,O.column));if(O.suggestion)I.push(""),I.push(`${_("yellow","Did you mean")}: ${_("cyan",O.suggestion)}?`);if(O.availableOptions&&O.availableOptions.length>0){I.push("");let R=O.availableOptions.slice(0,8),M=O.availableOptions.length>8?` ${_("gray",`... and ${O.availableOptions.length-8} more`)}`:"";I.push(`${_("gray","Available")}: ${R.join(", ")}${M}`)}return I.join(`
3
+ `)}function j(A,E,O){let I=A.split(`
4
+ `),P=[],R=Math.max(1,E-2),M=Math.min(I.length,E+1),L=String(M).length;for(let N=R;N<=M;N++){let C=I[N-1]||"",K=String(N).padStart(L," ");if(N===E){P.push(`${_("red"," \u2192")} ${_("gray",K)} ${_("dim","\u2502")} ${C}`);let Z=" ".repeat(L+4+Math.max(0,O-1)),q=_("red","^");P.push(`${Z}${q}`)}else P.push(` ${_("gray",K)} ${_("dim","\u2502")} ${_("gray",C)}`)}return P.join(`
5
+ `)}class D{state;variableStart;variableEnd;blockStart;blockEnd;commentStart;commentEnd;constructor(A,E={}){this.state={source:A,pos:0,line:1,column:1,tokens:[]},this.variableStart=E.variableStart??"{{",this.variableEnd=E.variableEnd??"}}",this.blockStart=E.blockStart??"{%",this.blockEnd=E.blockEnd??"%}",this.commentStart=E.commentStart??"{#",this.commentEnd=E.commentEnd??"#}"}tokenize(){while(!this.isAtEnd())this.scanToken();return this.addToken("EOF",""),this.state.tokens}scanToken(){if(this.match(this.variableStart)){this.addToken("VARIABLE_START",this.variableStart),this.scanExpression(this.variableEnd,"VARIABLE_END");return}if(this.match(this.blockStart)){let A=this.peek()==="-";if(A)this.advance();let E=this.state.pos;if(this.skipWhitespace(),this.checkWord("raw")||this.checkWord("verbatim")){let O=this.checkWord("raw")?"raw":"verbatim";this.scanRawBlock(O,A);return}this.state.pos=E,this.addToken("BLOCK_START",this.blockStart+(A?"-":"")),this.scanExpression(this.blockEnd,"BLOCK_END");return}if(this.match(this.commentStart)){this.scanComment();return}this.scanText()}checkWord(A){let E=this.state.pos;for(let I=0;I<A.length;I++)if(this.state.source[E+I]?.toLowerCase()!==A[I])return!1;let O=this.state.source[E+A.length];return!O||!this.isAlphaNumeric(O)}scanRawBlock(A,E){let O=this.state.line,I=this.state.column;for(let M=0;M<A.length;M++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new B(`Expected '${this.blockEnd}' after '${A}'`,{line:this.state.line,column:this.state.column,source:this.state.source});let P=`end${A}`,R=this.state.pos;while(!this.isAtEnd()){if(this.check(this.blockStart)){let M=this.state.pos,L=this.state.line,N=this.state.column;if(this.match(this.blockStart),this.peek()==="-")this.advance();if(this.skipWhitespace(),this.checkWord(P)){let C=this.state.source.slice(R,M);if(C.length>0)this.state.tokens.push({type:"TEXT",value:C,line:O,column:I});for(let K=0;K<P.length;K++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new B(`Expected '${this.blockEnd}' after '${P}'`,{line:this.state.line,column:this.state.column,source:this.state.source});return}this.state.pos=M,this.state.line=L,this.state.column=N}if(this.peek()===`
6
+ `)this.state.line++,this.state.column=0;this.advance()}throw new B(`Unclosed '${A}' block`,{line:O,column:I,source:this.state.source,suggestion:`Add {% end${A} %} to close the block`})}scanText(){let A=this.state.pos,E=this.state.line,O=this.state.column,I=this.state.source;while(!this.isAtEnd()){let P=I.indexOf("{",this.state.pos);if(P===-1){this.advanceToEnd();break}if(P>this.state.pos)this.advanceTo(P);if(this.check(this.variableStart)||this.check(this.blockStart)||this.check(this.commentStart))break;if(this.peek()===`
7
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}if(this.state.pos>A){let P=I.slice(A,this.state.pos);this.state.tokens.push({type:"TEXT",value:P,line:E,column:O})}}advanceTo(A){let E=this.state.source;while(this.state.pos<A){if(E[this.state.pos]===`
8
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}}advanceToEnd(){let A=this.state.source,E=A.length;while(this.state.pos<E){if(A[this.state.pos]===`
9
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}}scanExpression(A,E){this.skipWhitespace();while(!this.isAtEnd()){if(this.skipWhitespace(),this.peek()==="-"&&this.check(A,1))this.advance();if(this.match(A)){this.addToken(E,A);return}this.scanExpressionToken()}throw new B("Unclosed template tag",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing delimiter '${A}'`})}scanExpressionToken(){if(this.skipWhitespace(),this.isAtEnd())return;let A=this.peek();if(A==='"'||A==="'"){this.scanString(A);return}if(this.isDigit(A)){this.scanNumber();return}if(this.isAlpha(A)||A==="_"){this.scanIdentifier();return}this.scanOperator()}scanString(A){this.advance();let E=this.state.pos;while(!this.isAtEnd()&&this.peek()!==A){if(this.peek()==="\\"&&this.peekNext()===A)this.advance();if(this.peek()===`
10
+ `)this.state.line++,this.state.column=0;this.advance()}if(this.isAtEnd())throw new B("Unterminated string literal",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing quote '${A}'`});let O=this.state.source.slice(E,this.state.pos);this.advance(),this.addToken("STRING",O)}scanNumber(){let A=this.state.pos;while(this.isDigit(this.peek()))this.advance();if(this.peek()==="."&&this.isDigit(this.peekNext())){this.advance();while(this.isDigit(this.peek()))this.advance()}let E=this.state.source.slice(A,this.state.pos);this.addToken("NUMBER",E)}scanIdentifier(){let A=this.state.pos;while(this.isAlphaNumeric(this.peek())||this.peek()==="_")this.advance();let E=this.state.source.slice(A,this.state.pos),O=F[E]??"NAME";this.addToken(O,E)}scanOperator(){let A=this.advance();switch(A){case".":this.addToken("DOT",A);break;case",":this.addToken("COMMA",A);break;case":":this.addToken("COLON",A);break;case"|":this.addToken("PIPE",A);break;case"(":this.addToken("LPAREN",A);break;case")":this.addToken("RPAREN",A);break;case"[":this.addToken("LBRACKET",A);break;case"]":this.addToken("RBRACKET",A);break;case"{":this.addToken("LBRACE",A);break;case"}":this.addToken("RBRACE",A);break;case"+":this.addToken("ADD",A);break;case"-":this.addToken("SUB",A);break;case"*":this.addToken("MUL",A);break;case"/":this.addToken("DIV",A);break;case"%":this.addToken("MOD",A);break;case"~":this.addToken("TILDE",A);break;case"=":if(this.match("="))this.addToken("EQ","==");else this.addToken("ASSIGN","=");break;case"!":if(this.match("="))this.addToken("NE","!=");else throw new B("Unexpected character '!'",{line:this.state.line,column:this.state.column-1,source:this.state.source,suggestion:"Use '!=' for not-equal comparison or 'not' for negation"});break;case"<":if(this.match("="))this.addToken("LE","<=");else this.addToken("LT","<");break;case">":if(this.match("="))this.addToken("GE",">=");else this.addToken("GT",">");break;case"?":if(this.match("?"))this.addToken("NULLCOALESCE","??");else this.addToken("QUESTION","?");break;default:if(!this.isWhitespace(A))throw new B(`Unexpected character '${A}'`,{line:this.state.line,column:this.state.column-1,source:this.state.source})}}scanComment(){while(!this.isAtEnd()&&!this.check(this.commentEnd)){if(this.peek()===`
9
11
  `)this.state.line++,this.state.column=0;this.advance()}if(!this.isAtEnd())this.match(this.commentEnd)}isAtEnd(){return this.state.pos>=this.state.source.length}peek(){if(this.isAtEnd())return"\x00";return this.state.source[this.state.pos]}peekNext(){if(this.state.pos+1>=this.state.source.length)return"\x00";return this.state.source[this.state.pos+1]}advance(){let A=this.state.source[this.state.pos];return this.state.pos++,this.state.column++,A}match(A,E=0){let O=this.state.source,I=this.state.pos+E,P=A.length;if(I+P>O.length)return!1;for(let R=0;R<P;R++)if(O[I+R]!==A[R])return!1;if(E===0)this.state.pos+=P,this.state.column+=P;return!0}check(A,E=0){let O=this.state.source,I=this.state.pos+E,P=A.length;if(I+P>O.length)return!1;for(let R=0;R<P;R++)if(O[I+R]!==A[R])return!1;return!0}skipWhitespace(){while(!this.isAtEnd()&&this.isWhitespace(this.peek())){if(this.peek()===`
10
12
  `)this.state.line++,this.state.column=0;this.advance()}}isWhitespace(A){return A===" "||A==="\t"||A===`
11
- `||A==="\r"}isDigit(A){let E=A.charCodeAt(0);return E>=48&&E<=57}isAlpha(A){let E=A.charCodeAt(0);return E>=97&&E<=122||E>=65&&E<=90}isAlphaNumeric(A){let E=A.charCodeAt(0);return E>=48&&E<=57||E>=97&&E<=122||E>=65&&E<=90}addToken(A,E){this.state.tokens.push({type:A,value:E,line:this.state.line,column:this.state.column-E.length})}}class Q{tokens;current=0;source;constructor(A,E){this.tokens=A,this.source=E}parse(){let A=[];while(!this.isAtEnd()){let E=this.parseStatement();if(E)A.push(E)}return{type:"Template",body:A,line:1,column:1}}parseStatement(){switch(this.peek().type){case"TEXT":return this.parseText();case"VARIABLE_START":return this.parseOutput();case"BLOCK_START":return this.parseBlock();case"EOF":return null;default:return this.advance(),null}}parseText(){let A=this.advance();return{type:"Text",value:A.value,line:A.line,column:A.column}}parseOutput(){let A=this.advance(),E=this.parseExpression();return this.expect("VARIABLE_END"),{type:"Output",expression:E,line:A.line,column:A.column}}parseBlock(){let A=this.advance(),E=this.expect("NAME");switch(E.value){case"if":return this.parseIf(A);case"for":return this.parseFor(A);case"block":return this.parseBlockTag(A);case"extends":return this.parseExtends(A);case"include":return this.parseInclude(A);case"set":return this.parseSet(A);case"with":return this.parseWith(A);case"load":return this.parseLoad(A);case"url":return this.parseUrl(A);case"static":return this.parseStatic(A);case"now":return this.parseNow(A);case"comment":return this.parseComment(A);case"spaceless":case"autoescape":case"verbatim":return this.parseSimpleBlock(A,E.value);case"cycle":return this.parseCycle(A);case"firstof":return this.parseFirstof(A);case"ifchanged":return this.parseIfchanged(A);case"regroup":return this.parseRegroup(A);case"widthratio":return this.parseWidthratio(A);case"lorem":return this.parseLorem(A);case"csrf_token":return this.parseCsrfToken(A);case"debug":return this.parseDebug(A);case"templatetag":return this.parseTemplatetag(A);case"ifequal":return this.parseIfequal(A,!1);case"ifnotequal":return this.parseIfequal(A,!0);default:return this.skipToBlockEnd(),null}}parseIf(A){let E=this.parseExpression();this.expect("BLOCK_END");let O=[],I=[],P=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let R=this.parseStatement();if(R)O.push(R)}while(this.checkBlockTag("elif")){this.advance(),this.advance();let R=this.parseExpression();this.expect("BLOCK_END");let M=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let L=this.parseStatement();if(L)M.push(L)}I.push({test:R,body:M})}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endif"))break;let R=this.parseStatement();if(R)P.push(R)}}return this.expectBlockTag("endif"),{type:"If",test:E,body:O,elifs:I,else_:P,line:A.line,column:A.column}}parseFor(A){let E,O=this.expect("NAME").value;if(this.check("COMMA")){let N=[O];while(this.match("COMMA"))N.push(this.expect("NAME").value);E=N}else E=O;let I=this.expect("NAME");if(I.value!=="in")throw this.error(`Expected 'in' in for loop, got '${I.value}'`);let P=this.parseExpression(),R=this.check("NAME")&&this.peek().value==="recursive";if(R)this.advance();this.expect("BLOCK_END");let M=[],L=[];while(!this.isAtEnd()){if(this.checkBlockTag("empty")||this.checkBlockTag("else")||this.checkBlockTag("endfor"))break;let N=this.parseStatement();if(N)M.push(N)}if(this.checkBlockTag("empty")||this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endfor"))break;let N=this.parseStatement();if(N)L.push(N)}}return this.expectBlockTag("endfor"),{type:"For",target:E,iter:P,body:M,else_:L,recursive:R,line:A.line,column:A.column}}parseBlockTag(A){let E=this.expect("NAME").value,O=this.check("NAME")&&this.peek().value==="scoped";if(O)this.advance();this.expect("BLOCK_END");let I=[];while(!this.isAtEnd()){if(this.checkBlockTag("endblock"))break;let P=this.parseStatement();if(P)I.push(P)}if(this.advance(),this.advance(),this.check("NAME"))this.advance();return this.expect("BLOCK_END"),{type:"Block",name:E,body:I,scoped:O,line:A.line,column:A.column}}parseExtends(A){let E=this.parseExpression();return this.expect("BLOCK_END"),{type:"Extends",template:E,line:A.line,column:A.column}}parseInclude(A){let E=this.parseExpression(),O=null,I=!1,P=!1;while(this.check("NAME")){let R=this.peek().value;if(R==="ignore"&&this.peekNext()?.value==="missing")this.advance(),this.advance(),P=!0;else if(R==="with")this.advance(),O=this.parseKeywordArgs();else if(R==="only")this.advance(),I=!0;else if(R==="without"){if(this.advance(),this.check("NAME")&&this.peek().value==="context")this.advance(),I=!0}else break}return this.expect("BLOCK_END"),{type:"Include",template:E,context:O,only:I,ignoreMissing:P,line:A.line,column:A.column}}parseSet(A){let E=this.expect("NAME").value;this.expect("ASSIGN");let O=this.parseExpression();return this.expect("BLOCK_END"),{type:"Set",target:E,value:O,line:A.line,column:A.column}}parseWith(A){let E=[];do{let I=this.expect("NAME").value;this.expect("ASSIGN");let P=this.parseExpression();E.push({target:I,value:P})}while(this.match("COMMA")||this.check("NAME")&&this.peekNext()?.type==="ASSIGN");this.expect("BLOCK_END");let O=[];while(!this.isAtEnd()){if(this.checkBlockTag("endwith"))break;let I=this.parseStatement();if(I)O.push(I)}return this.expectBlockTag("endwith"),{type:"With",assignments:E,body:O,line:A.line,column:A.column}}parseLoad(A){let E=[];while(this.check("NAME"))E.push(this.advance().value);return this.expect("BLOCK_END"),{type:"Load",names:E,line:A.line,column:A.column}}parseUrl(A){let E=this.parseExpression(),O=[],I={},P=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),P=this.expect("NAME").value;break}if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let R=this.advance().value;this.advance(),I[R]=this.parseExpression()}else O.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Url",name:E,args:O,kwargs:I,asVar:P,line:A.line,column:A.column}}parseStatic(A){let E=this.parseExpression(),O=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),O=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Static",path:E,asVar:O,line:A.line,column:A.column}}parseNow(A){let E=this.parseExpression(),O=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),O=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Now",format:E,asVar:O,line:A.line,column:A.column}}parseComment(A){this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endcomment"))break;this.advance()}return this.expectBlockTag("endcomment"),null}parseSimpleBlock(A,E){this.skipToBlockEnd();let O=`end${E}`;while(!this.isAtEnd()){if(this.checkBlockTag(O))break;this.advance()}if(this.checkBlockTag(O))this.advance(),this.advance(),this.expect("BLOCK_END");return null}parseCycle(A){let E=[],O=null,I=!1;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){if(this.advance(),O=this.expect("NAME").value,this.check("NAME")&&this.peek().value==="silent")this.advance(),I=!0;break}E.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Cycle",values:E,asVar:O,silent:I,line:A.line,column:A.column}}parseFirstof(A){let E=[],O=null,I=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),I=this.expect("NAME").value;break}E.push(this.parseExpression())}if(E.length>0){let P=E[E.length-1];if(P.type==="Literal"&&typeof P.value==="string")O=E.pop()}return this.expect("BLOCK_END"),{type:"Firstof",values:E,fallback:O,asVar:I,line:A.line,column:A.column}}parseIfchanged(A){let E=[];while(!this.check("BLOCK_END"))E.push(this.parseExpression());this.expect("BLOCK_END");let O=[],I=[];while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag("endifchanged"))break;let P=this.parseStatement();if(P)O.push(P)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endifchanged"))break;let P=this.parseStatement();if(P)I.push(P)}}return this.expectBlockTag("endifchanged"),{type:"Ifchanged",values:E,body:O,else_:I,line:A.line,column:A.column}}parseRegroup(A){let E=this.parseExpression();this.expectName("by");let O=this.expect("NAME").value;this.expectName("as");let I=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Regroup",target:E,key:O,asVar:I,line:A.line,column:A.column}}parseWidthratio(A){let E=this.parseExpression(),O=this.parseExpression(),I=this.parseExpression(),P=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),P=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Widthratio",value:E,maxValue:O,maxWidth:I,asVar:P,line:A.line,column:A.column}}parseLorem(A){let E=null,O="p",I=!1;if(this.check("NUMBER"))E={type:"Literal",value:parseInt(this.advance().value,10),line:A.line,column:A.column};if(this.check("NAME")){let P=this.peek().value.toLowerCase();if(P==="w"||P==="p"||P==="b")O=P,this.advance()}if(this.check("NAME")&&this.peek().value==="random")I=!0,this.advance();return this.expect("BLOCK_END"),{type:"Lorem",count:E,method:O,random:I,line:A.line,column:A.column}}parseCsrfToken(A){return this.expect("BLOCK_END"),{type:"CsrfToken",line:A.line,column:A.column}}parseDebug(A){return this.expect("BLOCK_END"),{type:"Debug",line:A.line,column:A.column}}parseTemplatetag(A){let E=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Templatetag",tagType:E,line:A.line,column:A.column}}parseIfequal(A,E){let O=this.parseExpression(),I=this.parseExpression();this.expect("BLOCK_END");let P={type:"Compare",left:O,ops:[{operator:E?"!=":"==",right:I}],line:A.line,column:A.column},R=[],M=[],L=E?"endifnotequal":"endifequal";while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag(L))break;let N=this.parseStatement();if(N)R.push(N)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag(L))break;let N=this.parseStatement();if(N)M.push(N)}}return this.expectBlockTag(L),{type:"If",test:P,body:R,elifs:[],else_:M,line:A.line,column:A.column}}parseExpression(){return this.parseConditional()}parseConditional(){let A=this.parseOr();if(this.check("NAME")&&this.peek().value==="if"){this.advance();let E=this.parseOr();this.expectName("else");let O=this.parseConditional();A={type:"Conditional",test:E,trueExpr:A,falseExpr:O,line:A.line,column:A.column}}return A}parseOr(){let A=this.parseAnd();while(this.check("OR")||this.check("NAME")&&this.peek().value==="or"){this.advance();let E=this.parseAnd();A={type:"BinaryOp",operator:"or",left:A,right:E,line:A.line,column:A.column}}return A}parseAnd(){let A=this.parseNot();while(this.check("AND")||this.check("NAME")&&this.peek().value==="and"){this.advance();let E=this.parseNot();A={type:"BinaryOp",operator:"and",left:A,right:E,line:A.line,column:A.column}}return A}parseNot(){if(this.check("NOT")||this.check("NAME")&&this.peek().value==="not"){let A=this.advance();return{type:"UnaryOp",operator:"not",operand:this.parseNot(),line:A.line,column:A.column}}return this.parseCompare()}parseCompare(){let A=this.parseAddSub(),E=[];while(!0){let O=null;if(this.match("EQ"))O="==";else if(this.match("NE"))O="!=";else if(this.match("LT"))O="<";else if(this.match("GT"))O=">";else if(this.match("LE"))O="<=";else if(this.match("GE"))O=">=";else if(this.check("NAME")){let P=this.peek().value;if(P==="in")this.advance(),O="in";else if(P==="not"&&this.peekNext()?.value==="in")this.advance(),this.advance(),O="not in";else if(P==="is"){this.advance();let R=this.check("NOT");if(R)this.advance();let L=this.expect("NAME").value,N=[];if(this.match("LPAREN")){while(!this.check("RPAREN"))if(N.push(this.parseExpression()),!this.check("RPAREN"))this.expect("COMMA");this.expect("RPAREN")}A={type:"TestExpr",node:A,test:L,args:N,negated:R,line:A.line,column:A.column};continue}}if(!O)break;let I=this.parseAddSub();E.push({operator:O,right:I})}if(E.length===0)return A;return{type:"Compare",left:A,ops:E,line:A.line,column:A.column}}parseAddSub(){let A=this.parseMulDiv();while(this.check("ADD")||this.check("SUB")||this.check("TILDE")){let E=this.advance(),O=this.parseMulDiv();A={type:"BinaryOp",operator:E.value,left:A,right:O,line:A.line,column:A.column}}return A}parseMulDiv(){let A=this.parseUnary();while(this.check("MUL")||this.check("DIV")||this.check("MOD")){let E=this.advance(),O=this.parseUnary();A={type:"BinaryOp",operator:E.value,left:A,right:O,line:A.line,column:A.column}}return A}parseUnary(){if(this.check("SUB")||this.check("ADD")){let A=this.advance(),E=this.parseUnary();return{type:"UnaryOp",operator:A.value,operand:E,line:A.line,column:A.column}}return this.parseFilter()}parseFilter(){let A=this.parsePostfix();while(this.match("PIPE")){let E=this.expect("NAME").value,O=[],I={};if(this.match("COLON"))if(this.check("SUB")||this.check("ADD")){let P=this.advance(),R=this.parsePostfix();O.push({type:"UnaryOp",operator:P.value,operand:R,line:P.line,column:P.column})}else O.push(this.parsePostfix());else if(this.match("LPAREN")){while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let P=this.advance().value;this.advance(),I[P]=this.parseExpression()}else O.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN")}A={type:"FilterExpr",node:A,filter:E,args:O,kwargs:I,line:A.line,column:A.column}}return A}parsePostfix(){let A=this.parsePrimary();while(!0)if(this.match("DOT")){let E;if(this.check("NUMBER"))E=this.advance().value;else E=this.expect("NAME").value;A={type:"GetAttr",object:A,attribute:E,line:A.line,column:A.column}}else if(this.match("LBRACKET")){let E=this.parseExpression();this.expect("RBRACKET"),A={type:"GetItem",object:A,index:E,line:A.line,column:A.column}}else if(this.match("LPAREN")){let E=[],O={};while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let I=this.advance().value;this.advance(),O[I]=this.parseExpression()}else E.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN"),A={type:"FunctionCall",callee:A,args:E,kwargs:O,line:A.line,column:A.column}}else break;return A}parsePrimary(){let A=this.peek();if(this.match("STRING"))return{type:"Literal",value:A.value,line:A.line,column:A.column};if(this.match("NUMBER"))return{type:"Literal",value:A.value.includes(".")?parseFloat(A.value):parseInt(A.value,10),line:A.line,column:A.column};if(this.check("NAME")){let E=this.advance().value;if(E==="true"||E==="True")return{type:"Literal",value:!0,line:A.line,column:A.column};if(E==="false"||E==="False")return{type:"Literal",value:!1,line:A.line,column:A.column};if(E==="none"||E==="None"||E==="null")return{type:"Literal",value:null,line:A.line,column:A.column};return{type:"Name",name:E,line:A.line,column:A.column}}if(this.match("LPAREN")){let E=this.parseExpression();return this.expect("RPAREN"),E}if(this.match("LBRACKET")){let E=[];while(!this.check("RBRACKET"))if(E.push(this.parseExpression()),!this.check("RBRACKET"))this.expect("COMMA");return this.expect("RBRACKET"),{type:"Array",elements:E,line:A.line,column:A.column}}if(this.match("LBRACE")){let E=[];while(!this.check("RBRACE")){let O=this.parseExpression();this.expect("COLON");let I=this.parseExpression();if(E.push({key:O,value:I}),!this.check("RBRACE"))this.expect("COMMA")}return this.expect("RBRACE"),{type:"Object",pairs:E,line:A.line,column:A.column}}throw this.error(`Unexpected token: ${A.type} (${A.value})`)}parseKeywordArgs(){let A={};while(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let E=this.advance().value;this.advance(),A[E]=this.parseExpression()}return A}checkBlockTag(A){if(this.peek().type!=="BLOCK_START")return!1;let E=this.current+1;if(E>=this.tokens.length)return!1;let O=this.tokens[E];return O.type==="NAME"&&O.value===A}expectBlockTag(A){this.advance();let E=this.expect("NAME");if(E.value!==A)throw this.error(`Expected '${A}', got '${E.value}'`);this.expect("BLOCK_END")}expectName(A){let E=this.expect("NAME");if(E.value!==A)throw this.error(`Expected '${A}', got '${E.value}'`)}skipToBlockEnd(){while(!this.isAtEnd()&&!this.check("BLOCK_END"))this.advance();if(this.check("BLOCK_END"))this.advance()}isAtEnd(){return this.peek().type==="EOF"}peek(){return this.tokens[this.current]}peekNext(){if(this.current+1>=this.tokens.length)return null;return this.tokens[this.current+1]}advance(){if(!this.isAtEnd())this.current++;return this.tokens[this.current-1]}check(A){if(this.isAtEnd())return!1;return this.peek().type===A}match(A){if(this.check(A))return this.advance(),!0;return!1}expect(A){if(this.check(A))return this.advance();let E=this.peek();throw this.error(`Expected ${A}, got ${E.type} (${E.value})`)}error(A){let E=this.peek();return new _(A,{line:E.line,column:E.column,source:this.source})}}function G(A,E){let O=E||process.env.ANTHROPIC_API_KEY;return{name:"anthropic",async available(){return!!O},async analyze(I,P){let N=(await new(await import("@anthropic-ai/sdk")).default({apiKey:O}).messages.create({model:A||"claude-sonnet-4-20250514",max_tokens:1500,messages:[{role:"user",content:P.replace("{{TEMPLATE}}",I)}]})).content[0];if(N.type==="text")return N.text;throw Error("Unexpected response type from Anthropic")}}}function U(A,E){let O=E||process.env.OPENAI_API_KEY;return{name:"openai",async available(){return!!O},async analyze(I,P){return(await new(await import("openai")).default({apiKey:O}).chat.completions.create({model:A||"gpt-4o-mini",max_tokens:1500,messages:[{role:"user",content:P.replace("{{TEMPLATE}}",I)}]})).choices[0]?.message?.content||""}}}function S(A,E){let O=E||"http://localhost:11434";return{name:"ollama",async available(){try{return(await fetch(`${O}/api/tags`,{signal:AbortSignal.timeout(2000)})).ok}catch{return!1}},async analyze(I,P){let R=await fetch(`${O}/api/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:A||"llama3.1",prompt:P.replace("{{TEMPLATE}}",I),stream:!1})});if(!R.ok)throw Error(`Ollama error: ${R.statusText}`);return(await R.json()).response}}}function Y(A,E){let O=E||process.env.GROQ_API_KEY;return{name:"groq",async available(){return!!O},async analyze(I,P){if(!O)throw Error("Groq API key not provided");let R=await fetch("https://api.groq.com/openai/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${O}`},body:JSON.stringify({model:A||"llama-3.1-70b-versatile",max_tokens:1500,messages:[{role:"user",content:P.replace("{{TEMPLATE}}",I)}]})});if(!R.ok){let L=await R.text();throw Error(`Groq error: ${L}`)}return(await R.json()).choices[0]?.message?.content||""}}}function z(A,E={}){switch(A){case"anthropic":return G(E.model,E.apiKey);case"openai":return U(E.model,E.apiKey);case"ollama":return S(E.model,E.ollamaUrl);case"groq":return Y(E.model,E.apiKey);default:throw Error(`Unknown provider: ${A}`)}}async function Z(A={}){let E=[{name:"anthropic",create:()=>G(A.model,A.apiKey)},{name:"openai",create:()=>U(A.model,A.apiKey)},{name:"groq",create:()=>Y(A.model,A.apiKey)},{name:"ollama",create:()=>S(A.model,A.ollamaUrl)}];for(let{name:O,create:I}of E){let P=I();if(await P.available())return P}throw Error(`No AI provider available.
13
+ `||A==="\r"}isDigit(A){let E=A.charCodeAt(0);return E>=48&&E<=57}isAlpha(A){let E=A.charCodeAt(0);return E>=97&&E<=122||E>=65&&E<=90}isAlphaNumeric(A){let E=A.charCodeAt(0);return E>=48&&E<=57||E>=97&&E<=122||E>=65&&E<=90}addToken(A,E){this.state.tokens.push({type:A,value:E,line:this.state.line,column:this.state.column-E.length})}}class Q{tokens;current=0;source;constructor(A,E){this.tokens=A,this.source=E}parse(){let A=[];while(!this.isAtEnd()){let E=this.parseStatement();if(E)A.push(E)}return{type:"Template",body:A,line:1,column:1}}parseStatement(){switch(this.peek().type){case"TEXT":return this.parseText();case"VARIABLE_START":return this.parseOutput();case"BLOCK_START":return this.parseBlock();case"EOF":return null;default:return this.advance(),null}}parseText(){let A=this.advance();return{type:"Text",value:A.value,line:A.line,column:A.column}}parseOutput(){let A=this.advance(),E=this.parseExpression();return this.expect("VARIABLE_END"),{type:"Output",expression:E,line:A.line,column:A.column}}parseBlock(){let A=this.advance(),E=this.expect("NAME");switch(E.value){case"if":return this.parseIf(A);case"for":return this.parseFor(A);case"block":return this.parseBlockTag(A);case"extends":return this.parseExtends(A);case"include":return this.parseInclude(A);case"set":return this.parseSet(A);case"with":return this.parseWith(A);case"load":return this.parseLoad(A);case"url":return this.parseUrl(A);case"static":return this.parseStatic(A);case"now":return this.parseNow(A);case"comment":return this.parseComment(A);case"spaceless":case"autoescape":case"verbatim":return this.parseSimpleBlock(A,E.value);case"cycle":return this.parseCycle(A);case"firstof":return this.parseFirstof(A);case"ifchanged":return this.parseIfchanged(A);case"regroup":return this.parseRegroup(A);case"widthratio":return this.parseWidthratio(A);case"lorem":return this.parseLorem(A);case"csrf_token":return this.parseCsrfToken(A);case"debug":return this.parseDebug(A);case"templatetag":return this.parseTemplatetag(A);case"ifequal":return this.parseIfequal(A,!1);case"ifnotequal":return this.parseIfequal(A,!0);default:return this.skipToBlockEnd(),null}}parseIf(A){let E=this.parseExpression();this.expect("BLOCK_END");let O=[],I=[],P=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let R=this.parseStatement();if(R)O.push(R)}while(this.checkBlockTag("elif")){this.advance(),this.advance();let R=this.parseExpression();this.expect("BLOCK_END");let M=[];while(!this.isAtEnd()){if(this.checkBlockTag("elif")||this.checkBlockTag("else")||this.checkBlockTag("endif"))break;let L=this.parseStatement();if(L)M.push(L)}I.push({test:R,body:M})}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endif"))break;let R=this.parseStatement();if(R)P.push(R)}}return this.expectBlockTag("endif"),{type:"If",test:E,body:O,elifs:I,else_:P,line:A.line,column:A.column}}parseFor(A){let E,O=this.expect("NAME").value;if(this.check("COMMA")){let N=[O];while(this.match("COMMA"))N.push(this.expect("NAME").value);E=N}else E=O;let I=this.expect("NAME");if(I.value!=="in")throw this.error(`Expected 'in' in for loop, got '${I.value}'`);let P=this.parseExpression(),R=this.check("NAME")&&this.peek().value==="recursive";if(R)this.advance();this.expect("BLOCK_END");let M=[],L=[];while(!this.isAtEnd()){if(this.checkBlockTag("empty")||this.checkBlockTag("else")||this.checkBlockTag("endfor"))break;let N=this.parseStatement();if(N)M.push(N)}if(this.checkBlockTag("empty")||this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endfor"))break;let N=this.parseStatement();if(N)L.push(N)}}return this.expectBlockTag("endfor"),{type:"For",target:E,iter:P,body:M,else_:L,recursive:R,line:A.line,column:A.column}}parseBlockTag(A){let E=this.expect("NAME").value,O=this.check("NAME")&&this.peek().value==="scoped";if(O)this.advance();this.expect("BLOCK_END");let I=[];while(!this.isAtEnd()){if(this.checkBlockTag("endblock"))break;let P=this.parseStatement();if(P)I.push(P)}if(this.advance(),this.advance(),this.check("NAME"))this.advance();return this.expect("BLOCK_END"),{type:"Block",name:E,body:I,scoped:O,line:A.line,column:A.column}}parseExtends(A){let E=this.parseExpression();return this.expect("BLOCK_END"),{type:"Extends",template:E,line:A.line,column:A.column}}parseInclude(A){let E=this.parseExpression(),O=null,I=!1,P=!1;while(this.check("NAME")){let R=this.peek().value;if(R==="ignore"&&this.peekNext()?.value==="missing")this.advance(),this.advance(),P=!0;else if(R==="with")this.advance(),O=this.parseKeywordArgs();else if(R==="only")this.advance(),I=!0;else if(R==="without"){if(this.advance(),this.check("NAME")&&this.peek().value==="context")this.advance(),I=!0}else break}return this.expect("BLOCK_END"),{type:"Include",template:E,context:O,only:I,ignoreMissing:P,line:A.line,column:A.column}}parseSet(A){let E=this.expect("NAME").value;this.expect("ASSIGN");let O=this.parseExpression();return this.expect("BLOCK_END"),{type:"Set",target:E,value:O,line:A.line,column:A.column}}parseWith(A){let E=[];do{let I=this.expect("NAME").value;this.expect("ASSIGN");let P=this.parseExpression();E.push({target:I,value:P})}while(this.match("COMMA")||this.check("NAME")&&this.peekNext()?.type==="ASSIGN");this.expect("BLOCK_END");let O=[];while(!this.isAtEnd()){if(this.checkBlockTag("endwith"))break;let I=this.parseStatement();if(I)O.push(I)}return this.expectBlockTag("endwith"),{type:"With",assignments:E,body:O,line:A.line,column:A.column}}parseLoad(A){let E=[];while(this.check("NAME"))E.push(this.advance().value);return this.expect("BLOCK_END"),{type:"Load",names:E,line:A.line,column:A.column}}parseUrl(A){let E=this.parseExpression(),O=[],I={},P=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),P=this.expect("NAME").value;break}if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let R=this.advance().value;this.advance(),I[R]=this.parseExpression()}else O.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Url",name:E,args:O,kwargs:I,asVar:P,line:A.line,column:A.column}}parseStatic(A){let E=this.parseExpression(),O=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),O=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Static",path:E,asVar:O,line:A.line,column:A.column}}parseNow(A){let E=this.parseExpression(),O=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),O=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Now",format:E,asVar:O,line:A.line,column:A.column}}parseComment(A){this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endcomment"))break;this.advance()}return this.expectBlockTag("endcomment"),null}parseSimpleBlock(A,E){this.skipToBlockEnd();let O=`end${E}`;while(!this.isAtEnd()){if(this.checkBlockTag(O))break;this.advance()}if(this.checkBlockTag(O))this.advance(),this.advance(),this.expect("BLOCK_END");return null}parseCycle(A){let E=[],O=null,I=!1;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){if(this.advance(),O=this.expect("NAME").value,this.check("NAME")&&this.peek().value==="silent")this.advance(),I=!0;break}E.push(this.parseExpression())}return this.expect("BLOCK_END"),{type:"Cycle",values:E,asVar:O,silent:I,line:A.line,column:A.column}}parseFirstof(A){let E=[],O=null,I=null;while(!this.check("BLOCK_END")){if(this.check("NAME")&&this.peek().value==="as"){this.advance(),I=this.expect("NAME").value;break}E.push(this.parseExpression())}if(E.length>0){let P=E[E.length-1];if(P.type==="Literal"&&typeof P.value==="string")O=E.pop()}return this.expect("BLOCK_END"),{type:"Firstof",values:E,fallback:O,asVar:I,line:A.line,column:A.column}}parseIfchanged(A){let E=[];while(!this.check("BLOCK_END"))E.push(this.parseExpression());this.expect("BLOCK_END");let O=[],I=[];while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag("endifchanged"))break;let P=this.parseStatement();if(P)O.push(P)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag("endifchanged"))break;let P=this.parseStatement();if(P)I.push(P)}}return this.expectBlockTag("endifchanged"),{type:"Ifchanged",values:E,body:O,else_:I,line:A.line,column:A.column}}parseRegroup(A){let E=this.parseExpression();this.expectName("by");let O=this.expect("NAME").value;this.expectName("as");let I=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Regroup",target:E,key:O,asVar:I,line:A.line,column:A.column}}parseWidthratio(A){let E=this.parseExpression(),O=this.parseExpression(),I=this.parseExpression(),P=null;if(this.check("NAME")&&this.peek().value==="as")this.advance(),P=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Widthratio",value:E,maxValue:O,maxWidth:I,asVar:P,line:A.line,column:A.column}}parseLorem(A){let E=null,O="p",I=!1;if(this.check("NUMBER"))E={type:"Literal",value:parseInt(this.advance().value,10),line:A.line,column:A.column};if(this.check("NAME")){let P=this.peek().value.toLowerCase();if(P==="w"||P==="p"||P==="b")O=P,this.advance()}if(this.check("NAME")&&this.peek().value==="random")I=!0,this.advance();return this.expect("BLOCK_END"),{type:"Lorem",count:E,method:O,random:I,line:A.line,column:A.column}}parseCsrfToken(A){return this.expect("BLOCK_END"),{type:"CsrfToken",line:A.line,column:A.column}}parseDebug(A){return this.expect("BLOCK_END"),{type:"Debug",line:A.line,column:A.column}}parseTemplatetag(A){let E=this.expect("NAME").value;return this.expect("BLOCK_END"),{type:"Templatetag",tagType:E,line:A.line,column:A.column}}parseIfequal(A,E){let O=this.parseExpression(),I=this.parseExpression();this.expect("BLOCK_END");let P={type:"Compare",left:O,ops:[{operator:E?"!=":"==",right:I}],line:A.line,column:A.column},R=[],M=[],L=E?"endifnotequal":"endifequal";while(!this.isAtEnd()){if(this.checkBlockTag("else")||this.checkBlockTag(L))break;let N=this.parseStatement();if(N)R.push(N)}if(this.checkBlockTag("else")){this.advance(),this.advance(),this.expect("BLOCK_END");while(!this.isAtEnd()){if(this.checkBlockTag(L))break;let N=this.parseStatement();if(N)M.push(N)}}return this.expectBlockTag(L),{type:"If",test:P,body:R,elifs:[],else_:M,line:A.line,column:A.column}}parseExpression(){return this.parseConditional()}parseConditional(){let A=this.parseOr();if(this.check("NAME")&&this.peek().value==="if"){this.advance();let E=this.parseOr();this.expectName("else");let O=this.parseConditional();A={type:"Conditional",test:E,trueExpr:A,falseExpr:O,line:A.line,column:A.column}}return A}parseOr(){let A=this.parseAnd();while(this.check("OR")||this.check("NAME")&&this.peek().value==="or"){this.advance();let E=this.parseAnd();A={type:"BinaryOp",operator:"or",left:A,right:E,line:A.line,column:A.column}}return A}parseAnd(){let A=this.parseNot();while(this.check("AND")||this.check("NAME")&&this.peek().value==="and"){this.advance();let E=this.parseNot();A={type:"BinaryOp",operator:"and",left:A,right:E,line:A.line,column:A.column}}return A}parseNot(){if(this.check("NOT")||this.check("NAME")&&this.peek().value==="not"){let A=this.advance();return{type:"UnaryOp",operator:"not",operand:this.parseNot(),line:A.line,column:A.column}}return this.parseCompare()}parseCompare(){let A=this.parseAddSub(),E=[];while(!0){let O=null;if(this.match("EQ"))O="==";else if(this.match("NE"))O="!=";else if(this.match("LT"))O="<";else if(this.match("GT"))O=">";else if(this.match("LE"))O="<=";else if(this.match("GE"))O=">=";else if(this.check("NAME")){let P=this.peek().value;if(P==="in")this.advance(),O="in";else if(P==="not"&&this.peekNext()?.value==="in")this.advance(),this.advance(),O="not in";else if(P==="is"){this.advance();let R=this.check("NOT");if(R)this.advance();let L=this.expect("NAME").value,N=[];if(this.match("LPAREN")){while(!this.check("RPAREN"))if(N.push(this.parseExpression()),!this.check("RPAREN"))this.expect("COMMA");this.expect("RPAREN")}A={type:"TestExpr",node:A,test:L,args:N,negated:R,line:A.line,column:A.column};continue}}if(!O)break;let I=this.parseAddSub();E.push({operator:O,right:I})}if(E.length===0)return A;return{type:"Compare",left:A,ops:E,line:A.line,column:A.column}}parseAddSub(){let A=this.parseMulDiv();while(this.check("ADD")||this.check("SUB")||this.check("TILDE")){let E=this.advance(),O=this.parseMulDiv();A={type:"BinaryOp",operator:E.value,left:A,right:O,line:A.line,column:A.column}}return A}parseMulDiv(){let A=this.parseUnary();while(this.check("MUL")||this.check("DIV")||this.check("MOD")){let E=this.advance(),O=this.parseUnary();A={type:"BinaryOp",operator:E.value,left:A,right:O,line:A.line,column:A.column}}return A}parseUnary(){if(this.check("SUB")||this.check("ADD")){let A=this.advance(),E=this.parseUnary();return{type:"UnaryOp",operator:A.value,operand:E,line:A.line,column:A.column}}return this.parseFilter()}parseFilter(){let A=this.parsePostfix();while(this.match("PIPE")){let E=this.expect("NAME").value,O=[],I={};if(this.match("COLON"))if(this.check("SUB")||this.check("ADD")){let P=this.advance(),R=this.parsePostfix();O.push({type:"UnaryOp",operator:P.value,operand:R,line:P.line,column:P.column})}else O.push(this.parsePostfix());else if(this.match("LPAREN")){while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let P=this.advance().value;this.advance(),I[P]=this.parseExpression()}else O.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN")}A={type:"FilterExpr",node:A,filter:E,args:O,kwargs:I,line:A.line,column:A.column}}return A}parsePostfix(){let A=this.parsePrimary();while(!0)if(this.match("DOT")){let E;if(this.check("NUMBER"))E=this.advance().value;else E=this.expect("NAME").value;A={type:"GetAttr",object:A,attribute:E,line:A.line,column:A.column}}else if(this.match("LBRACKET")){let E=this.parseExpression();this.expect("RBRACKET"),A={type:"GetItem",object:A,index:E,line:A.line,column:A.column}}else if(this.match("LPAREN")){let E=[],O={};while(!this.check("RPAREN")){if(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let I=this.advance().value;this.advance(),O[I]=this.parseExpression()}else E.push(this.parseExpression());if(!this.check("RPAREN"))this.expect("COMMA")}this.expect("RPAREN"),A={type:"FunctionCall",callee:A,args:E,kwargs:O,line:A.line,column:A.column}}else break;return A}parsePrimary(){let A=this.peek();if(this.match("STRING"))return{type:"Literal",value:A.value,line:A.line,column:A.column};if(this.match("NUMBER"))return{type:"Literal",value:A.value.includes(".")?parseFloat(A.value):parseInt(A.value,10),line:A.line,column:A.column};if(this.check("NAME")){let E=this.advance().value;if(E==="true"||E==="True")return{type:"Literal",value:!0,line:A.line,column:A.column};if(E==="false"||E==="False")return{type:"Literal",value:!1,line:A.line,column:A.column};if(E==="none"||E==="None"||E==="null")return{type:"Literal",value:null,line:A.line,column:A.column};return{type:"Name",name:E,line:A.line,column:A.column}}if(this.match("LPAREN")){let E=this.parseExpression();return this.expect("RPAREN"),E}if(this.match("LBRACKET")){let E=[];while(!this.check("RBRACKET"))if(E.push(this.parseExpression()),!this.check("RBRACKET"))this.expect("COMMA");return this.expect("RBRACKET"),{type:"Array",elements:E,line:A.line,column:A.column}}if(this.match("LBRACE")){let E=[];while(!this.check("RBRACE")){let O=this.parseExpression();this.expect("COLON");let I=this.parseExpression();if(E.push({key:O,value:I}),!this.check("RBRACE"))this.expect("COMMA")}return this.expect("RBRACE"),{type:"Object",pairs:E,line:A.line,column:A.column}}throw this.error(`Unexpected token: ${A.type} (${A.value})`)}parseKeywordArgs(){let A={};while(this.check("NAME")&&this.peekNext()?.type==="ASSIGN"){let E=this.advance().value;this.advance(),A[E]=this.parseExpression()}return A}checkBlockTag(A){if(this.peek().type!=="BLOCK_START")return!1;let E=this.current+1;if(E>=this.tokens.length)return!1;let O=this.tokens[E];return O.type==="NAME"&&O.value===A}expectBlockTag(A){this.advance();let E=this.expect("NAME");if(E.value!==A)throw this.error(`Expected '${A}', got '${E.value}'`);this.expect("BLOCK_END")}expectName(A){let E=this.expect("NAME");if(E.value!==A)throw this.error(`Expected '${A}', got '${E.value}'`)}skipToBlockEnd(){while(!this.isAtEnd()&&!this.check("BLOCK_END"))this.advance();if(this.check("BLOCK_END"))this.advance()}isAtEnd(){return this.peek().type==="EOF"}peek(){return this.tokens[this.current]}peekNext(){if(this.current+1>=this.tokens.length)return null;return this.tokens[this.current+1]}advance(){if(!this.isAtEnd())this.current++;return this.tokens[this.current-1]}check(A){if(this.isAtEnd())return!1;return this.peek().type===A}match(A){if(this.check(A))return this.advance(),!0;return!1}expect(A){if(this.check(A))return this.advance();let E=this.peek();throw this.error(`Expected ${A}, got ${E.type} (${E.value})`)}error(A){let E=this.peek();return new B(A,{line:E.line,column:E.column,source:this.source})}}function G(A,E){let O=E||process.env.ANTHROPIC_API_KEY;return{name:"anthropic",async available(){return!!O},async analyze(I,P){let N=(await new(await import("@anthropic-ai/sdk")).default({apiKey:O}).messages.create({model:A||"claude-sonnet-4-20250514",max_tokens:1500,messages:[{role:"user",content:P.replace("{{TEMPLATE}}",I)}]})).content[0];if(N.type==="text")return N.text;throw Error("Unexpected response type from Anthropic")}}}function U(A,E){let O=E||process.env.OPENAI_API_KEY;return{name:"openai",async available(){return!!O},async analyze(I,P){return(await new(await import("openai")).default({apiKey:O}).chat.completions.create({model:A||"gpt-4o-mini",max_tokens:1500,messages:[{role:"user",content:P.replace("{{TEMPLATE}}",I)}]})).choices[0]?.message?.content||""}}}function S(A,E){let O=E||"http://localhost:11434";return{name:"ollama",async available(){try{return(await fetch(`${O}/api/tags`,{signal:AbortSignal.timeout(2000)})).ok}catch{return!1}},async analyze(I,P){let R=await fetch(`${O}/api/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:A||"llama3.1",prompt:P.replace("{{TEMPLATE}}",I),stream:!1})});if(!R.ok)throw Error(`Ollama error: ${R.statusText}`);return(await R.json()).response}}}function Y(A,E){let O=E||process.env.GROQ_API_KEY;return{name:"groq",async available(){return!!O},async analyze(I,P){if(!O)throw Error("Groq API key not provided");let R=await fetch("https://api.groq.com/openai/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${O}`},body:JSON.stringify({model:A||"llama-3.1-70b-versatile",max_tokens:1500,messages:[{role:"user",content:P.replace("{{TEMPLATE}}",I)}]})});if(!R.ok){let L=await R.text();throw Error(`Groq error: ${L}`)}return(await R.json()).choices[0]?.message?.content||""}}}function h(A,E={}){switch(A){case"anthropic":return G(E.model,E.apiKey);case"openai":return U(E.model,E.apiKey);case"ollama":return S(E.model,E.ollamaUrl);case"groq":return Y(E.model,E.apiKey);default:throw Error(`Unknown provider: ${A}`)}}async function H(A={}){let E=[{name:"anthropic",create:()=>G(A.model,A.apiKey)},{name:"openai",create:()=>U(A.model,A.apiKey)},{name:"groq",create:()=>Y(A.model,A.apiKey)},{name:"ollama",create:()=>S(A.model,A.ollamaUrl)}];for(let{name:O,create:I}of E){let P=I();if(await P.available())return P}throw Error(`No AI provider available.
12
14
 
13
15
  Configure one of the following:
14
16
  - ANTHROPIC_API_KEY (Claude)
@@ -18,7 +20,7 @@ Configure one of the following:
18
20
 
19
21
  Install SDK if needed:
20
22
  bun add @anthropic-ai/sdk # for Claude
21
- bun add openai # for OpenAI`)}async function w(A={}){let E=A.provider||"auto";if(E==="auto")return Z(A);let O=z(E,A);if(!await O.available())throw Error(`Provider '${E}' is not available. Check your API key or configuration.`);return O}function q(A){if(!A||A.length===0)return`Analyze this Jinja2/Django template for issues.
23
+ bun add openai # for OpenAI`)}async function J(A={}){let E=A.provider||"auto";if(E==="auto")return H(A);let O=h(E,A);if(!await O.available())throw Error(`Provider '${E}' is not available. Check your API key or configuration.`);return O}function z(A){if(!A||A.length===0)return`Analyze this Jinja2/Django template for issues.
22
24
 
23
25
  TEMPLATE:
24
26
  \`\`\`jinja
@@ -110,4 +112,4 @@ Respond ONLY with valid JSON (no markdown, no explanation):
110
112
 
111
113
  If no issues found, return: {"issues": []}`.replace(/Check for:[\s\S]*?Respond ONLY/,`Check ONLY for: ${O.join(", ")}
112
114
 
113
- Respond ONLY`)}async function c(A,E={}){let O=Date.now(),I={valid:!0,errors:[],warnings:[],suggestions:[]};try{let R=new D(A).tokenize();new Q(R,A).parse()}catch(P){return I.valid=!1,I.errors.push({line:P.line||1,type:"syntax",severity:"error",message:P.message}),I.duration=Date.now()-O,I}try{let P=await w(E);I.provider=P.name;let R=q(E.categories),M=await P.analyze(A,R),L=m(M);for(let N of L){if(E.maxIssues&&I.errors.length+I.warnings.length+I.suggestions.length>=E.maxIssues)break;switch(N.severity){case"error":I.errors.push(N);break;case"warning":I.warnings.push(N);break;case"suggestion":I.suggestions.push(N);break}}I.valid=I.errors.length===0}catch(P){I.warnings.push({line:0,type:"best-practice",severity:"warning",message:`AI analysis failed: ${P.message}`})}return I.duration=Date.now()-O,I}function m(A){try{let E=A.trim(),O=E.match(/```(?:json)?\s*([\s\S]*?)```/);if(O)E=O[1].trim();let I=E.match(/\{[\s\S]*\}/);if(I)E=I[0];let P=JSON.parse(E);if(!P.issues||!Array.isArray(P.issues))return[];return P.issues.filter((R)=>R&&typeof R==="object").map((R)=>({line:typeof R.line==="number"?R.line:1,type:l(R.type),severity:d(R.severity),message:String(R.message||"Unknown issue"),suggestion:R.suggestion?String(R.suggestion):void 0}))}catch{return[]}}function l(A){return{security:"security",performance:"performance",accessibility:"accessibility",a11y:"accessibility","best-practice":"best-practice","best-practices":"best-practice",bestpractice:"best-practice",deprecated:"deprecated",syntax:"syntax"}[String(A).toLowerCase()]||"best-practice"}function d(A){return{error:"error",warning:"warning",warn:"warning",suggestion:"suggestion",info:"suggestion",hint:"suggestion"}[String(A).toLowerCase()]||"warning"}function i(A){let E={valid:!0,errors:[],warnings:[],suggestions:[]};try{let I=new D(A).tokenize();new Q(I,A).parse()}catch(O){E.valid=!1,E.errors.push({line:O.line||1,type:"syntax",severity:"error",message:O.message})}return E}export{i as syntaxCheck,w as resolveProvider,c as lint,z as getProvider,Z as detectProvider,U as createOpenAIProvider,S as createOllamaProvider,Y as createGroqProvider,G as createAnthropicProvider};
115
+ Respond ONLY`)}async function b(A,E={}){let O=Date.now(),I={valid:!0,errors:[],warnings:[],suggestions:[]};try{let R=new D(A).tokenize();new Q(R,A).parse()}catch(P){return I.valid=!1,I.errors.push({line:P.line||1,type:"syntax",severity:"error",message:P.message}),I.duration=Date.now()-O,I}try{let P=await J(E);I.provider=P.name;let R=z(E.categories),M=await P.analyze(A,R),L=v(M);for(let N of L){if(E.maxIssues&&I.errors.length+I.warnings.length+I.suggestions.length>=E.maxIssues)break;switch(N.severity){case"error":I.errors.push(N);break;case"warning":I.warnings.push(N);break;case"suggestion":I.suggestions.push(N);break}}I.valid=I.errors.length===0}catch(P){I.warnings.push({line:0,type:"best-practice",severity:"warning",message:`AI analysis failed: ${P.message}`})}return I.duration=Date.now()-O,I}function v(A){try{let E=A.trim(),O=E.match(/```(?:json)?\s*([\s\S]*?)```/);if(O)E=O[1].trim();let I=E.match(/\{[\s\S]*\}/);if(I)E=I[0];let P=JSON.parse(E);if(!P.issues||!Array.isArray(P.issues))return[];return P.issues.filter((R)=>R&&typeof R==="object").map((R)=>({line:typeof R.line==="number"?R.line:1,type:f(R.type),severity:x(R.severity),message:String(R.message||"Unknown issue"),suggestion:R.suggestion?String(R.suggestion):void 0}))}catch{return[]}}function f(A){return{security:"security",performance:"performance",accessibility:"accessibility",a11y:"accessibility","best-practice":"best-practice","best-practices":"best-practice",bestpractice:"best-practice",deprecated:"deprecated",syntax:"syntax"}[String(A).toLowerCase()]||"best-practice"}function x(A){return{error:"error",warning:"warning",warn:"warning",suggestion:"suggestion",info:"suggestion",hint:"suggestion"}[String(A).toLowerCase()]||"warning"}function g(A){let E={valid:!0,errors:[],warnings:[],suggestions:[]};try{let I=new D(A).tokenize();new Q(I,A).parse()}catch(O){E.valid=!1,E.errors.push({line:O.line||1,type:"syntax",severity:"error",message:O.message})}return E}export{g as syntaxCheck,J as resolveProvider,b as lint,h as getProvider,H as detectProvider,U as createOpenAIProvider,S as createOllamaProvider,Y as createGroqProvider,G as createAnthropicProvider};
package/dist/cli.js CHANGED
@@ -4,8 +4,10 @@ var nZ=Object.defineProperty;var K2=($,Y)=>{for(var Z in Y)nZ($,Z,{get:Y[Z],enum
4
4
  `)}function sZ($,Y,Z){let X=$.split(`
5
5
  `),G=[],K=Math.max(1,Y-2),W=Math.min(X.length,Y+1),z=String(W).length;for(let Q=K;Q<=W;Q++){let J=X[Q-1]||"",U=String(Q).padStart(z," ");if(Q===Y){G.push(`${K0("red"," \u2192")} ${K0("gray",U)} ${K0("dim","\u2502")} ${J}`);let D=" ".repeat(z+4+Math.max(0,Z-1)),O=K0("red","^");G.push(`${D}${O}`)}else G.push(` ${K0("gray",U)} ${K0("dim","\u2502")} ${K0("gray",J)}`)}return G.join(`
6
6
  `)}var TY,aZ,q0;var Q2=H(()=>{TY={red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m",dim:"\x1B[2m",reset:"\x1B[0m"},aZ=process.stdout?.isTTY!==!1;q0=class q0 extends Error{line;column;source;templateName;suggestion;constructor($,Y){let Z=rZ("TemplateSyntaxError",$,Y);super(Z);this.name="TemplateSyntaxError",this.line=Y.line,this.column=Y.column,this.source=Y.source,this.templateName=Y.templateName,this.suggestion=Y.suggestion}}});class Q1{state;variableStart;variableEnd;blockStart;blockEnd;commentStart;commentEnd;constructor($,Y={}){this.state={source:$,pos:0,line:1,column:1,tokens:[]},this.variableStart=Y.variableStart??"{{",this.variableEnd=Y.variableEnd??"}}",this.blockStart=Y.blockStart??"{%",this.blockEnd=Y.blockEnd??"%}",this.commentStart=Y.commentStart??"{#",this.commentEnd=Y.commentEnd??"#}"}tokenize(){while(!this.isAtEnd())this.scanToken();return this.addToken("EOF",""),this.state.tokens}scanToken(){if(this.match(this.variableStart)){this.addToken("VARIABLE_START",this.variableStart),this.scanExpression(this.variableEnd,"VARIABLE_END");return}if(this.match(this.blockStart)){let $=this.peek()==="-";if($)this.advance();let Y=this.state.pos;if(this.skipWhitespace(),this.checkWord("raw")||this.checkWord("verbatim")){let Z=this.checkWord("raw")?"raw":"verbatim";this.scanRawBlock(Z,$);return}this.state.pos=Y,this.addToken("BLOCK_START",this.blockStart+($?"-":"")),this.scanExpression(this.blockEnd,"BLOCK_END");return}if(this.match(this.commentStart)){this.scanComment();return}this.scanText()}checkWord($){let Y=this.state.pos;for(let X=0;X<$.length;X++)if(this.state.source[Y+X]?.toLowerCase()!==$[X])return!1;let Z=this.state.source[Y+$.length];return!Z||!this.isAlphaNumeric(Z)}scanRawBlock($,Y){let Z=this.state.line,X=this.state.column;for(let W=0;W<$.length;W++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new q0(`Expected '${this.blockEnd}' after '${$}'`,{line:this.state.line,column:this.state.column,source:this.state.source});let G=`end${$}`,K=this.state.pos;while(!this.isAtEnd()){if(this.check(this.blockStart)){let W=this.state.pos,z=this.state.line,Q=this.state.column;if(this.match(this.blockStart),this.peek()==="-")this.advance();if(this.skipWhitespace(),this.checkWord(G)){let J=this.state.source.slice(K,W);if(J.length>0)this.state.tokens.push({type:"TEXT",value:J,line:Z,column:X});for(let U=0;U<G.length;U++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new q0(`Expected '${this.blockEnd}' after '${G}'`,{line:this.state.line,column:this.state.column,source:this.state.source});return}this.state.pos=W,this.state.line=z,this.state.column=Q}if(this.peek()===`
7
- `)this.state.line++,this.state.column=0;this.advance()}throw new q0(`Unclosed '${$}' block`,{line:Z,column:X,source:this.state.source,suggestion:`Add {% end${$} %} to close the block`})}scanText(){let $=this.state.pos,Y=this.state.line,Z=this.state.column;while(!this.isAtEnd()){if(this.check(this.variableStart)||this.check(this.blockStart)||this.check(this.commentStart))break;if(this.peek()===`
8
- `)this.state.line++,this.state.column=0;this.advance()}if(this.state.pos>$){let X=this.state.source.slice($,this.state.pos);this.state.tokens.push({type:"TEXT",value:X,line:Y,column:Z})}}scanExpression($,Y){this.skipWhitespace();while(!this.isAtEnd()){if(this.skipWhitespace(),this.peek()==="-"&&this.check($,1))this.advance();if(this.match($)){this.addToken(Y,$);return}this.scanExpressionToken()}throw new q0("Unclosed template tag",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing delimiter '${$}'`})}scanExpressionToken(){if(this.skipWhitespace(),this.isAtEnd())return;let $=this.peek();if($==='"'||$==="'"){this.scanString($);return}if(this.isDigit($)){this.scanNumber();return}if(this.isAlpha($)||$==="_"){this.scanIdentifier();return}this.scanOperator()}scanString($){this.advance();let Y=this.state.pos;while(!this.isAtEnd()&&this.peek()!==$){if(this.peek()==="\\"&&this.peekNext()===$)this.advance();if(this.peek()===`
7
+ `)this.state.line++,this.state.column=0;this.advance()}throw new q0(`Unclosed '${$}' block`,{line:Z,column:X,source:this.state.source,suggestion:`Add {% end${$} %} to close the block`})}scanText(){let $=this.state.pos,Y=this.state.line,Z=this.state.column,X=this.state.source;while(!this.isAtEnd()){let G=X.indexOf("{",this.state.pos);if(G===-1){this.advanceToEnd();break}if(G>this.state.pos)this.advanceTo(G);if(this.check(this.variableStart)||this.check(this.blockStart)||this.check(this.commentStart))break;if(this.peek()===`
8
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}if(this.state.pos>$){let G=X.slice($,this.state.pos);this.state.tokens.push({type:"TEXT",value:G,line:Y,column:Z})}}advanceTo($){let Y=this.state.source;while(this.state.pos<$){if(Y[this.state.pos]===`
9
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}}advanceToEnd(){let $=this.state.source,Y=$.length;while(this.state.pos<Y){if($[this.state.pos]===`
10
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}}scanExpression($,Y){this.skipWhitespace();while(!this.isAtEnd()){if(this.skipWhitespace(),this.peek()==="-"&&this.check($,1))this.advance();if(this.match($)){this.addToken(Y,$);return}this.scanExpressionToken()}throw new q0("Unclosed template tag",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing delimiter '${$}'`})}scanExpressionToken(){if(this.skipWhitespace(),this.isAtEnd())return;let $=this.peek();if($==='"'||$==="'"){this.scanString($);return}if(this.isDigit($)){this.scanNumber();return}if(this.isAlpha($)||$==="_"){this.scanIdentifier();return}this.scanOperator()}scanString($){this.advance();let Y=this.state.pos;while(!this.isAtEnd()&&this.peek()!==$){if(this.peek()==="\\"&&this.peekNext()===$)this.advance();if(this.peek()===`
9
11
  `)this.state.line++,this.state.column=0;this.advance()}if(this.isAtEnd())throw new q0("Unterminated string literal",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing quote '${$}'`});let Z=this.state.source.slice(Y,this.state.pos);this.advance(),this.addToken("STRING",Z)}scanNumber(){let $=this.state.pos;while(this.isDigit(this.peek()))this.advance();if(this.peek()==="."&&this.isDigit(this.peekNext())){this.advance();while(this.isDigit(this.peek()))this.advance()}let Y=this.state.source.slice($,this.state.pos);this.addToken("NUMBER",Y)}scanIdentifier(){let $=this.state.pos;while(this.isAlphaNumeric(this.peek())||this.peek()==="_")this.advance();let Y=this.state.source.slice($,this.state.pos),Z=W2[Y]??"NAME";this.addToken(Z,Y)}scanOperator(){let $=this.advance();switch($){case".":this.addToken("DOT",$);break;case",":this.addToken("COMMA",$);break;case":":this.addToken("COLON",$);break;case"|":this.addToken("PIPE",$);break;case"(":this.addToken("LPAREN",$);break;case")":this.addToken("RPAREN",$);break;case"[":this.addToken("LBRACKET",$);break;case"]":this.addToken("RBRACKET",$);break;case"{":this.addToken("LBRACE",$);break;case"}":this.addToken("RBRACE",$);break;case"+":this.addToken("ADD",$);break;case"-":this.addToken("SUB",$);break;case"*":this.addToken("MUL",$);break;case"/":this.addToken("DIV",$);break;case"%":this.addToken("MOD",$);break;case"~":this.addToken("TILDE",$);break;case"=":if(this.match("="))this.addToken("EQ","==");else this.addToken("ASSIGN","=");break;case"!":if(this.match("="))this.addToken("NE","!=");else throw new q0("Unexpected character '!'",{line:this.state.line,column:this.state.column-1,source:this.state.source,suggestion:"Use '!=' for not-equal comparison or 'not' for negation"});break;case"<":if(this.match("="))this.addToken("LE","<=");else this.addToken("LT","<");break;case">":if(this.match("="))this.addToken("GE",">=");else this.addToken("GT",">");break;case"?":if(this.match("?"))this.addToken("NULLCOALESCE","??");else this.addToken("QUESTION","?");break;default:if(!this.isWhitespace($))throw new q0(`Unexpected character '${$}'`,{line:this.state.line,column:this.state.column-1,source:this.state.source})}}scanComment(){while(!this.isAtEnd()&&!this.check(this.commentEnd)){if(this.peek()===`
10
12
  `)this.state.line++,this.state.column=0;this.advance()}if(!this.isAtEnd())this.match(this.commentEnd)}isAtEnd(){return this.state.pos>=this.state.source.length}peek(){if(this.isAtEnd())return"\x00";return this.state.source[this.state.pos]}peekNext(){if(this.state.pos+1>=this.state.source.length)return"\x00";return this.state.source[this.state.pos+1]}advance(){let $=this.state.source[this.state.pos];return this.state.pos++,this.state.column++,$}match($,Y=0){let Z=this.state.source,X=this.state.pos+Y,G=$.length;if(X+G>Z.length)return!1;for(let K=0;K<G;K++)if(Z[X+K]!==$[K])return!1;if(Y===0)this.state.pos+=G,this.state.column+=G;return!0}check($,Y=0){let Z=this.state.source,X=this.state.pos+Y,G=$.length;if(X+G>Z.length)return!1;for(let K=0;K<G;K++)if(Z[X+K]!==$[K])return!1;return!0}skipWhitespace(){while(!this.isAtEnd()&&this.isWhitespace(this.peek())){if(this.peek()===`
11
13
  `)this.state.line++,this.state.column=0;this.advance()}}isWhitespace($){return $===" "||$==="\t"||$===`
package/dist/index.js CHANGED
@@ -3,8 +3,10 @@ var s=import.meta.require;var b;((F)=>{F.TEXT="TEXT";F.EOF="EOF";F.VARIABLE_STAR
3
3
  `)}function zB(B,K,A){let Q=B.split(`
4
4
  `),O=[],U=Math.max(1,K-2),Y=Math.min(Q.length,K+1),J=String(Y).length;for(let $=U;$<=Y;$++){let Z=Q[$-1]||"",X=String($).padStart(J," ");if($===K){O.push(`${z("red"," \u2192")} ${z("gray",X)} ${z("dim","\u2502")} ${Z}`);let G=" ".repeat(J+4+Math.max(0,A-1)),E=z("red","^");O.push(`${G}${E}`)}else O.push(` ${z("gray",X)} ${z("dim","\u2502")} ${z("gray",Z)}`)}return O.join(`
5
5
  `)}function n(B,K,A=3){let Q=null,O=A+1,U=B.toLowerCase();for(let Y of K){let J=WB(U,Y.toLowerCase());if(J<O)O=J,Q=Y}return O<=A?Q:null}function WB(B,K){if(B.length===0)return K.length;if(K.length===0)return B.length;let A=[];for(let Q=0;Q<=K.length;Q++)A[Q]=[Q];for(let Q=0;Q<=B.length;Q++)A[0][Q]=Q;for(let Q=1;Q<=K.length;Q++)for(let O=1;O<=B.length;O++)if(K[Q-1]===B[O-1])A[Q][O]=A[Q-1][O-1];else A[Q][O]=Math.min(A[Q-1][O-1]+1,A[Q][O-1]+1,A[Q-1][O]+1);return A[K.length][B.length]}class C{state;variableStart;variableEnd;blockStart;blockEnd;commentStart;commentEnd;constructor(B,K={}){this.state={source:B,pos:0,line:1,column:1,tokens:[]},this.variableStart=K.variableStart??"{{",this.variableEnd=K.variableEnd??"}}",this.blockStart=K.blockStart??"{%",this.blockEnd=K.blockEnd??"%}",this.commentStart=K.commentStart??"{#",this.commentEnd=K.commentEnd??"#}"}tokenize(){while(!this.isAtEnd())this.scanToken();return this.addToken("EOF",""),this.state.tokens}scanToken(){if(this.match(this.variableStart)){this.addToken("VARIABLE_START",this.variableStart),this.scanExpression(this.variableEnd,"VARIABLE_END");return}if(this.match(this.blockStart)){let B=this.peek()==="-";if(B)this.advance();let K=this.state.pos;if(this.skipWhitespace(),this.checkWord("raw")||this.checkWord("verbatim")){let A=this.checkWord("raw")?"raw":"verbatim";this.scanRawBlock(A,B);return}this.state.pos=K,this.addToken("BLOCK_START",this.blockStart+(B?"-":"")),this.scanExpression(this.blockEnd,"BLOCK_END");return}if(this.match(this.commentStart)){this.scanComment();return}this.scanText()}checkWord(B){let K=this.state.pos;for(let Q=0;Q<B.length;Q++)if(this.state.source[K+Q]?.toLowerCase()!==B[Q])return!1;let A=this.state.source[K+B.length];return!A||!this.isAlphaNumeric(A)}scanRawBlock(B,K){let A=this.state.line,Q=this.state.column;for(let Y=0;Y<B.length;Y++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new P(`Expected '${this.blockEnd}' after '${B}'`,{line:this.state.line,column:this.state.column,source:this.state.source});let O=`end${B}`,U=this.state.pos;while(!this.isAtEnd()){if(this.check(this.blockStart)){let Y=this.state.pos,J=this.state.line,$=this.state.column;if(this.match(this.blockStart),this.peek()==="-")this.advance();if(this.skipWhitespace(),this.checkWord(O)){let Z=this.state.source.slice(U,Y);if(Z.length>0)this.state.tokens.push({type:"TEXT",value:Z,line:A,column:Q});for(let X=0;X<O.length;X++)this.advance();if(this.skipWhitespace(),this.peek()==="-")this.advance();if(!this.match(this.blockEnd))throw new P(`Expected '${this.blockEnd}' after '${O}'`,{line:this.state.line,column:this.state.column,source:this.state.source});return}this.state.pos=Y,this.state.line=J,this.state.column=$}if(this.peek()===`
6
- `)this.state.line++,this.state.column=0;this.advance()}throw new P(`Unclosed '${B}' block`,{line:A,column:Q,source:this.state.source,suggestion:`Add {% end${B} %} to close the block`})}scanText(){let B=this.state.pos,K=this.state.line,A=this.state.column;while(!this.isAtEnd()){if(this.check(this.variableStart)||this.check(this.blockStart)||this.check(this.commentStart))break;if(this.peek()===`
7
- `)this.state.line++,this.state.column=0;this.advance()}if(this.state.pos>B){let Q=this.state.source.slice(B,this.state.pos);this.state.tokens.push({type:"TEXT",value:Q,line:K,column:A})}}scanExpression(B,K){this.skipWhitespace();while(!this.isAtEnd()){if(this.skipWhitespace(),this.peek()==="-"&&this.check(B,1))this.advance();if(this.match(B)){this.addToken(K,B);return}this.scanExpressionToken()}throw new P("Unclosed template tag",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing delimiter '${B}'`})}scanExpressionToken(){if(this.skipWhitespace(),this.isAtEnd())return;let B=this.peek();if(B==='"'||B==="'"){this.scanString(B);return}if(this.isDigit(B)){this.scanNumber();return}if(this.isAlpha(B)||B==="_"){this.scanIdentifier();return}this.scanOperator()}scanString(B){this.advance();let K=this.state.pos;while(!this.isAtEnd()&&this.peek()!==B){if(this.peek()==="\\"&&this.peekNext()===B)this.advance();if(this.peek()===`
6
+ `)this.state.line++,this.state.column=0;this.advance()}throw new P(`Unclosed '${B}' block`,{line:A,column:Q,source:this.state.source,suggestion:`Add {% end${B} %} to close the block`})}scanText(){let B=this.state.pos,K=this.state.line,A=this.state.column,Q=this.state.source;while(!this.isAtEnd()){let O=Q.indexOf("{",this.state.pos);if(O===-1){this.advanceToEnd();break}if(O>this.state.pos)this.advanceTo(O);if(this.check(this.variableStart)||this.check(this.blockStart)||this.check(this.commentStart))break;if(this.peek()===`
7
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}if(this.state.pos>B){let O=Q.slice(B,this.state.pos);this.state.tokens.push({type:"TEXT",value:O,line:K,column:A})}}advanceTo(B){let K=this.state.source;while(this.state.pos<B){if(K[this.state.pos]===`
8
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}}advanceToEnd(){let B=this.state.source,K=B.length;while(this.state.pos<K){if(B[this.state.pos]===`
9
+ `)this.state.line++,this.state.column=0;this.state.pos++,this.state.column++}}scanExpression(B,K){this.skipWhitespace();while(!this.isAtEnd()){if(this.skipWhitespace(),this.peek()==="-"&&this.check(B,1))this.advance();if(this.match(B)){this.addToken(K,B);return}this.scanExpressionToken()}throw new P("Unclosed template tag",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing delimiter '${B}'`})}scanExpressionToken(){if(this.skipWhitespace(),this.isAtEnd())return;let B=this.peek();if(B==='"'||B==="'"){this.scanString(B);return}if(this.isDigit(B)){this.scanNumber();return}if(this.isAlpha(B)||B==="_"){this.scanIdentifier();return}this.scanOperator()}scanString(B){this.advance();let K=this.state.pos;while(!this.isAtEnd()&&this.peek()!==B){if(this.peek()==="\\"&&this.peekNext()===B)this.advance();if(this.peek()===`
8
10
  `)this.state.line++,this.state.column=0;this.advance()}if(this.isAtEnd())throw new P("Unterminated string literal",{line:this.state.line,column:this.state.column,source:this.state.source,suggestion:`Add closing quote '${B}'`});let A=this.state.source.slice(K,this.state.pos);this.advance(),this.addToken("STRING",A)}scanNumber(){let B=this.state.pos;while(this.isDigit(this.peek()))this.advance();if(this.peek()==="."&&this.isDigit(this.peekNext())){this.advance();while(this.isDigit(this.peek()))this.advance()}let K=this.state.source.slice(B,this.state.pos);this.addToken("NUMBER",K)}scanIdentifier(){let B=this.state.pos;while(this.isAlphaNumeric(this.peek())||this.peek()==="_")this.advance();let K=this.state.source.slice(B,this.state.pos),A=m[K]??"NAME";this.addToken(A,K)}scanOperator(){let B=this.advance();switch(B){case".":this.addToken("DOT",B);break;case",":this.addToken("COMMA",B);break;case":":this.addToken("COLON",B);break;case"|":this.addToken("PIPE",B);break;case"(":this.addToken("LPAREN",B);break;case")":this.addToken("RPAREN",B);break;case"[":this.addToken("LBRACKET",B);break;case"]":this.addToken("RBRACKET",B);break;case"{":this.addToken("LBRACE",B);break;case"}":this.addToken("RBRACE",B);break;case"+":this.addToken("ADD",B);break;case"-":this.addToken("SUB",B);break;case"*":this.addToken("MUL",B);break;case"/":this.addToken("DIV",B);break;case"%":this.addToken("MOD",B);break;case"~":this.addToken("TILDE",B);break;case"=":if(this.match("="))this.addToken("EQ","==");else this.addToken("ASSIGN","=");break;case"!":if(this.match("="))this.addToken("NE","!=");else throw new P("Unexpected character '!'",{line:this.state.line,column:this.state.column-1,source:this.state.source,suggestion:"Use '!=' for not-equal comparison or 'not' for negation"});break;case"<":if(this.match("="))this.addToken("LE","<=");else this.addToken("LT","<");break;case">":if(this.match("="))this.addToken("GE",">=");else this.addToken("GT",">");break;case"?":if(this.match("?"))this.addToken("NULLCOALESCE","??");else this.addToken("QUESTION","?");break;default:if(!this.isWhitespace(B))throw new P(`Unexpected character '${B}'`,{line:this.state.line,column:this.state.column-1,source:this.state.source})}}scanComment(){while(!this.isAtEnd()&&!this.check(this.commentEnd)){if(this.peek()===`
9
11
  `)this.state.line++,this.state.column=0;this.advance()}if(!this.isAtEnd())this.match(this.commentEnd)}isAtEnd(){return this.state.pos>=this.state.source.length}peek(){if(this.isAtEnd())return"\x00";return this.state.source[this.state.pos]}peekNext(){if(this.state.pos+1>=this.state.source.length)return"\x00";return this.state.source[this.state.pos+1]}advance(){let B=this.state.source[this.state.pos];return this.state.pos++,this.state.column++,B}match(B,K=0){let A=this.state.source,Q=this.state.pos+K,O=B.length;if(Q+O>A.length)return!1;for(let U=0;U<O;U++)if(A[Q+U]!==B[U])return!1;if(K===0)this.state.pos+=O,this.state.column+=O;return!0}check(B,K=0){let A=this.state.source,Q=this.state.pos+K,O=B.length;if(Q+O>A.length)return!1;for(let U=0;U<O;U++)if(A[Q+U]!==B[U])return!1;return!0}skipWhitespace(){while(!this.isAtEnd()&&this.isWhitespace(this.peek())){if(this.peek()===`
10
12
  `)this.state.line++,this.state.column=0;this.advance()}}isWhitespace(B){return B===" "||B==="\t"||B===`
@@ -23,7 +23,17 @@ export declare class Lexer {
23
23
  private scanToken;
24
24
  private checkWord;
25
25
  private scanRawBlock;
26
+ /**
27
+ * Optimized scanText using indexOf
28
+ * Instead of checking 3 delimiters at each character position,
29
+ * we use indexOf to jump directly to the next '{' character.
30
+ * Benchmarks show +178% average speedup, up to +393% for text-heavy templates.
31
+ */
26
32
  private scanText;
33
+ /** Advance to target position while tracking line/column */
34
+ private advanceTo;
35
+ /** Advance to end while tracking line/column */
36
+ private advanceToEnd;
27
37
  private scanExpression;
28
38
  private scanExpressionToken;
29
39
  private scanString;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "binja",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "High-performance Jinja2/Django template engine for Bun",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",