binja 0.9.0 → 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/README.md +231 -18
- package/dist/adapters/elysia.js +12 -10
- package/dist/adapters/hono.js +13 -11
- package/dist/ai/index.d.ts +24 -0
- package/dist/ai/index.js +115 -0
- package/dist/ai/lint.d.ts +26 -0
- package/dist/ai/prompt.d.ts +10 -0
- package/dist/ai/providers/anthropic.d.ts +6 -0
- package/dist/ai/providers/groq.d.ts +6 -0
- package/dist/ai/providers/index.d.ts +22 -0
- package/dist/ai/providers/ollama.d.ts +6 -0
- package/dist/ai/providers/openai.d.ts +6 -0
- package/dist/ai/types.d.ts +42 -0
- package/dist/cli.js +185 -30
- package/dist/index.js +4 -2
- package/dist/lexer/index.d.ts +10 -0
- package/package.json +22 -2
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.
|
|
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===`
|
package/dist/lexer/index.d.ts
CHANGED
|
@@ -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.
|
|
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",
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"bun": "./dist/index.js"
|
|
17
17
|
},
|
|
18
|
+
"./ai": {
|
|
19
|
+
"import": "./dist/ai/index.js",
|
|
20
|
+
"require": "./dist/ai/index.js",
|
|
21
|
+
"types": "./dist/ai/index.d.ts",
|
|
22
|
+
"bun": "./dist/ai/index.js"
|
|
23
|
+
},
|
|
18
24
|
"./debug": {
|
|
19
25
|
"import": "./dist/debug/index.js",
|
|
20
26
|
"require": "./dist/debug/index.js",
|
|
@@ -40,7 +46,7 @@
|
|
|
40
46
|
"LICENSE"
|
|
41
47
|
],
|
|
42
48
|
"scripts": {
|
|
43
|
-
"build": "bun build ./src/index.ts --outdir ./dist --target bun --minify && bun build ./src/cli.ts --outdir ./dist --target bun --minify && bun build ./src/debug/index.ts --outdir ./dist/debug --target bun --minify && bun build ./src/adapters/hono.ts --outdir ./dist/adapters --target bun --minify && bun build ./src/adapters/elysia.ts --outdir ./dist/adapters --target bun --minify && bun run build:types && bun run build:clean",
|
|
49
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun --minify && bun build ./src/cli.ts --outdir ./dist --target bun --minify && bun build ./src/debug/index.ts --outdir ./dist/debug --target bun --minify && bun build ./src/ai/index.ts --outdir ./dist/ai --target bun --minify --external @anthropic-ai/sdk --external openai && bun build ./src/adapters/hono.ts --outdir ./dist/adapters --target bun --minify && bun build ./src/adapters/elysia.ts --outdir ./dist/adapters --target bun --minify && bun run build:types && bun run build:clean",
|
|
44
50
|
"build:types": "tsc --declaration --emitDeclarationOnly --outDir ./dist",
|
|
45
51
|
"build:clean": "rm -rf dist/native dist/lexer/hybrid.* && find dist -name '*.d.ts.map' -delete",
|
|
46
52
|
"test": "bun test",
|
|
@@ -76,11 +82,25 @@
|
|
|
76
82
|
"author": "egeominotti",
|
|
77
83
|
"license": "BSD-3-Clause",
|
|
78
84
|
"devDependencies": {
|
|
85
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
79
86
|
"@types/bun": "latest",
|
|
80
87
|
"elysia": "^1.4.19",
|
|
81
88
|
"hono": "^4.11.3",
|
|
89
|
+
"openai": "^4.77.0",
|
|
82
90
|
"typescript": "latest"
|
|
83
91
|
},
|
|
92
|
+
"peerDependencies": {
|
|
93
|
+
"@anthropic-ai/sdk": ">=0.30.0",
|
|
94
|
+
"openai": ">=4.0.0"
|
|
95
|
+
},
|
|
96
|
+
"peerDependenciesMeta": {
|
|
97
|
+
"@anthropic-ai/sdk": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"openai": {
|
|
101
|
+
"optional": true
|
|
102
|
+
}
|
|
103
|
+
},
|
|
84
104
|
"engines": {
|
|
85
105
|
"bun": ">=1.0.0"
|
|
86
106
|
}
|