bonsai-js 0.3.0 → 0.4.0
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/CHANGELOG.md +19 -5
- package/README.md +67 -7
- package/dist/autocomplete/index.d.mts +1 -1
- package/dist/autocomplete/index.mjs +8 -8
- package/dist/autocomplete/index.mjs.map +1 -1
- package/dist/{errors-t5i9AEaf.mjs → errors-BONB6P_5.mjs} +2 -3
- package/dist/{errors-t5i9AEaf.mjs.map → errors-BONB6P_5.mjs.map} +1 -1
- package/dist/{execution-context-DqYC3L92.mjs → execution-context-B-5hQ92X.mjs} +2 -2
- package/dist/{execution-context-DqYC3L92.mjs.map → execution-context-B-5hQ92X.mjs.map} +1 -1
- package/dist/index.d.mts +16 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +86 -32
- package/dist/index.mjs.map +1 -1
- package/dist/stdlib/index.d.mts +1 -1
- package/dist/stdlib/index.mjs +1 -1
- package/dist/stdlib/index.mjs.map +1 -1
- package/dist/{types-BhsZZsLY.d.mts → types-C58c6SlH.d.mts} +56 -16
- package/dist/{types-BhsZZsLY.d.mts.map → types-C58c6SlH.d.mts.map} +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors-t5i9AEaf.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { SourcePosition } from './types.js'\n\nconst NEWLINE_CODE = 10\n\nexport interface ErrorLocation {\n source: string\n start: number\n end: number\n}\n\n/** Syntax error thrown during parsing. Contains source location and formatted caret display. */\nexport class ExpressionError extends Error {\n readonly rawMessage: string\n readonly start: number\n readonly end: number\n readonly source: string\n readonly suggestion?: string\n\n constructor(\n message: string,\n location: ErrorLocation,\n suggestion?: string,\n ) {\n const formatted = formatError(message, location, suggestion)\n super(formatted)\n this.name = 'ExpressionError'\n this.rawMessage = message\n this.start = location.start\n this.end = location.end\n this.source = location.source\n this.suggestion = suggestion\n }\n}\n\n/** Runtime type error when a transform, function, or method receives an unexpected value type. */\nexport class BonsaiTypeError extends Error {\n readonly transform: string\n readonly expected: string\n readonly received: string\n location?: ErrorLocation\n formatted?: string\n\n constructor(transform: string, expected: string, value: unknown) {\n let received: string\n if (value === null) received = 'null'\n else if (value instanceof Promise) received = 'Promise'\n else if (Array.isArray(value)) received = 'array'\n else received = typeof value\n super(`\"${transform}\" expects ${expected}, got ${received}`)\n this.name = 'BonsaiTypeError'\n this.transform = transform\n this.expected = expected\n this.received = received\n }\n}\n\n/** Security violation: blocked property access, timeout, depth limit, or array size limit. */\nexport class BonsaiSecurityError extends Error {\n readonly code: string\n location?: ErrorLocation\n formatted?: string\n\n constructor(code: string, message: string) {\n super(message)\n this.name = 'BonsaiSecurityError'\n this.code = code\n }\n}\n\n/** Unknown transform, function, or method name. Includes a \"did you mean?\" suggestion when possible. */\nexport class BonsaiReferenceError extends Error {\n readonly identifier: string\n readonly kind: 'transform' | 'function' | 'method'\n readonly suggestion?: string\n location?: ErrorLocation\n formatted?: string\n\n constructor(kind: 'transform' | 'function' | 'method', identifier: string, suggestion?: string) {\n const msg = suggestion\n ? `Unknown ${kind} \"${identifier}\". Did you mean \"${suggestion}\"?`\n : `Unknown ${kind} \"${identifier}\"`\n super(msg)\n this.name = 'BonsaiReferenceError'\n this.identifier = identifier\n this.kind = kind\n this.suggestion = suggestion\n }\n}\n\nexport type BonsaiRuntimeError = BonsaiTypeError | BonsaiSecurityError | BonsaiReferenceError\nexport type BonsaiError = ExpressionError | BonsaiRuntimeError\n\nfunction clampOffset(source: string, offset: number): number {\n return Math.max(0, Math.min(offset, source.length))\n}\n\nfunction getLineStart(source: string, offset: number): number {\n let index = clampOffset(source, offset)\n while (index > 0 && source.charCodeAt(index - 1) !== NEWLINE_CODE) index--\n return index\n}\n\nfunction getLineEnd(source: string, offset: number): number {\n let index = clampOffset(source, offset)\n while (index < source.length && source.charCodeAt(index) !== NEWLINE_CODE) index++\n return index\n}\n\nexport function offsetToPosition(source: string, offset: number): SourcePosition {\n const safeOffset = clampOffset(source, offset)\n let line = 1\n let column = 1\n\n for (let index = 0; index < safeOffset; index++) {\n if (source.charCodeAt(index) === NEWLINE_CODE) {\n line++\n column = 1\n } else {\n column++\n }\n }\n\n return { line, column, offset: safeOffset }\n}\n\nexport function hasErrorLocation(error: unknown): error is BonsaiRuntimeError & { location: ErrorLocation } {\n return (\n (error instanceof BonsaiTypeError\n || error instanceof BonsaiSecurityError\n || error instanceof BonsaiReferenceError)\n && error.location !== undefined\n )\n}\n\nexport function attachLocation(error: unknown, source: string, start: number, end: number): void {\n if (error instanceof BonsaiTypeError || error instanceof BonsaiSecurityError || error instanceof BonsaiReferenceError) {\n if (!error.location) {\n error.location = { source, start, end }\n error.formatted = formatError(error.message, error.location)\n }\n }\n}\n\n/** Format an error message with source context, caret display, and position info. */\nexport function formatError(\n message: string,\n location: ErrorLocation,\n suggestion?: string,\n): string {\n const { source } = location\n const start = clampOffset(source, location.start)\n const rawEnd = clampOffset(source, location.end)\n const end = Math.max(start + 1, rawEnd)\n const startPos = offsetToPosition(source, start)\n const lineStart = getLineStart(source, start)\n const lineEnd = getLineEnd(source, start)\n const lineText = source.slice(lineStart, lineEnd)\n const caretStart = start - lineStart\n const caretEnd = Math.min(end, lineEnd)\n const caretLength = Math.max(1, caretEnd - start)\n const gutter = `${startPos.line} | `\n const padding = ' '.repeat(gutter.length + caretStart)\n const carets = '^'.repeat(caretLength)\n\n let result = `${message}\\n\\n${gutter}${lineText}\\n${padding}${carets}`\n\n if (suggestion) {\n result += ` Did you mean \"${suggestion}\"?`\n }\n\n result += `\\n\\nat line ${startPos.line}, column ${startPos.column} (offset ${start}-${end})`\n\n return result\n}\n\n/** Format any Bonsai error into a human-readable string with source context when available. */\nexport function formatBonsaiError(error: unknown): string {\n if (error instanceof ExpressionError) return error.message\n if (hasErrorLocation(error)) {\n return error.formatted ?? formatError(error.message, error.location)\n }\n if (error instanceof Error) return error.message\n return String(error)\n}\n\nconst MAX_SUGGEST_LENGTH = 64\n\nexport function suggest(\n input: string,\n known: string[],\n maxDistance = 2,\n): string | undefined {\n if (input.length > MAX_SUGGEST_LENGTH) return undefined\n let best: string | undefined\n let bestDistance = maxDistance + 1\n\n for (const candidate of known) {\n const distance = levenshtein(input, candidate)\n if (distance < bestDistance) {\n bestDistance = distance\n best = candidate\n }\n }\n\n return best\n}\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length\n const n = b.length\n const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0))\n\n for (let i = 0; i <= m; i++) dp[i][0] = i\n for (let j = 0; j <= n; j++) dp[0][j] = j\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n dp[i][j] = Math.min(\n dp[i - 1][j] + 1,\n dp[i][j - 1] + 1,\n dp[i - 1][j - 1] + cost,\n )\n }\n }\n\n return dp[m][n]\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors-BONB6P_5.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { SourcePosition } from './types.js'\n\nconst NEWLINE_CODE = 10\n\nexport interface ErrorLocation {\n source: string\n start: number\n end: number\n}\n\n/** Syntax error thrown during parsing. Contains source location and formatted caret display. */\nexport class ExpressionError extends Error {\n readonly rawMessage: string\n readonly start: number\n readonly end: number\n readonly source: string\n readonly suggestion?: string\n\n constructor(\n message: string,\n location: ErrorLocation,\n suggestion?: string,\n ) {\n const formatted = formatError(message, location, suggestion)\n super(formatted)\n this.name = 'ExpressionError'\n this.rawMessage = message\n this.start = location.start\n this.end = location.end\n this.source = location.source\n this.suggestion = suggestion\n }\n}\n\n/** Runtime type error when a transform, function, or method receives an unexpected value type. */\nexport class BonsaiTypeError extends Error {\n readonly transform: string\n readonly expected: string\n readonly received: string\n location?: ErrorLocation\n formatted?: string\n\n constructor(transform: string, expected: string, value: unknown) {\n let received: string\n if (value === null) received = 'null'\n else if (value instanceof Promise) received = 'Promise'\n else if (Array.isArray(value)) received = 'array'\n else received = typeof value\n super(`\"${transform}\" expects ${expected}, got ${received}`)\n this.name = 'BonsaiTypeError'\n this.transform = transform\n this.expected = expected\n this.received = received\n }\n}\n\n/** Security violation: blocked property access, timeout, depth limit, or array size limit. */\nexport class BonsaiSecurityError extends Error {\n readonly code: string\n location?: ErrorLocation\n formatted?: string\n\n constructor(code: string, message: string) {\n super(message)\n this.name = 'BonsaiSecurityError'\n this.code = code\n }\n}\n\n/** Unknown transform, function, or method name. Includes a \"did you mean?\" suggestion when possible. */\nexport class BonsaiReferenceError extends Error {\n readonly identifier: string\n readonly kind: 'transform' | 'function' | 'method'\n readonly suggestion?: string\n location?: ErrorLocation\n formatted?: string\n\n constructor(kind: 'transform' | 'function' | 'method', identifier: string, suggestion?: string) {\n const msg = suggestion\n ? `Unknown ${kind} \"${identifier}\". Did you mean \"${suggestion}\"?`\n : `Unknown ${kind} \"${identifier}\"`\n super(msg)\n this.name = 'BonsaiReferenceError'\n this.identifier = identifier\n this.kind = kind\n this.suggestion = suggestion\n }\n}\n\nexport type BonsaiRuntimeError = BonsaiTypeError | BonsaiSecurityError | BonsaiReferenceError\nexport type BonsaiError = ExpressionError | BonsaiRuntimeError\n\nfunction clampOffset(source: string, offset: number): number {\n return Math.max(0, Math.min(offset, source.length))\n}\n\nfunction getLineStart(source: string, offset: number): number {\n let index = clampOffset(source, offset)\n while (index > 0 && source.charCodeAt(index - 1) !== NEWLINE_CODE) index--\n return index\n}\n\nfunction getLineEnd(source: string, offset: number): number {\n let index = clampOffset(source, offset)\n while (index < source.length && source.charCodeAt(index) !== NEWLINE_CODE) index++\n return index\n}\n\nexport function offsetToPosition(source: string, offset: number): SourcePosition {\n const safeOffset = clampOffset(source, offset)\n let line = 1\n let column = 1\n\n for (let index = 0; index < safeOffset; index++) {\n if (source.charCodeAt(index) === NEWLINE_CODE) {\n line++\n column = 1\n } else {\n column++\n }\n }\n\n return { line, column, offset: safeOffset }\n}\n\nexport function hasErrorLocation(error: unknown): error is BonsaiRuntimeError & { location: ErrorLocation } {\n return (\n (error instanceof BonsaiTypeError\n || error instanceof BonsaiSecurityError\n || error instanceof BonsaiReferenceError)\n && error.location !== undefined\n )\n}\n\nexport function attachLocation(error: unknown, source: string, start: number, end: number): void {\n if (error instanceof BonsaiTypeError || error instanceof BonsaiSecurityError || error instanceof BonsaiReferenceError) {\n if (!error.location) {\n error.location = { source, start, end }\n error.formatted = formatError(error.message, error.location)\n }\n }\n}\n\n/** Format an error message with source context, caret display, and position info. */\nexport function formatError(\n message: string,\n location: ErrorLocation,\n suggestion?: string,\n): string {\n const { source } = location\n const start = clampOffset(source, location.start)\n const rawEnd = clampOffset(source, location.end)\n const end = Math.max(start + 1, rawEnd)\n const startPos = offsetToPosition(source, start)\n const lineStart = getLineStart(source, start)\n const lineEnd = getLineEnd(source, start)\n const lineText = source.slice(lineStart, lineEnd)\n const caretStart = start - lineStart\n const caretEnd = Math.min(end, lineEnd)\n const caretLength = Math.max(1, caretEnd - start)\n const gutter = `${startPos.line} | `\n const padding = ' '.repeat(gutter.length + caretStart)\n const carets = '^'.repeat(caretLength)\n\n let result = `${message}\\n\\n${gutter}${lineText}\\n${padding}${carets}`\n\n if (suggestion) {\n result += ` Did you mean \"${suggestion}\"?`\n }\n\n result += `\\n\\nat line ${startPos.line}, column ${startPos.column} (offset ${start}-${end})`\n\n return result\n}\n\n/** Format any Bonsai error into a human-readable string with source context when available. */\nexport function formatBonsaiError(error: unknown): string {\n if (error instanceof ExpressionError) return error.message\n if (hasErrorLocation(error)) {\n return error.formatted ?? formatError(error.message, error.location)\n }\n if (error instanceof Error) return error.message\n return String(error)\n}\n\nconst MAX_SUGGEST_LENGTH = 64\n\nexport function suggest(\n input: string,\n known: string[],\n maxDistance = 2,\n): string | undefined {\n if (input.length > MAX_SUGGEST_LENGTH) return undefined\n let best: string | undefined\n let bestDistance = maxDistance + 1\n\n for (const candidate of known) {\n const distance = levenshtein(input, candidate)\n if (distance < bestDistance) {\n bestDistance = distance\n best = candidate\n }\n }\n\n return best\n}\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length\n const n = b.length\n const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0))\n\n for (let i = 0; i <= m; i++) dp[i][0] = i\n for (let j = 0; j <= n; j++) dp[0][j] = j\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n dp[i][j] = Math.min(\n dp[i - 1][j] + 1,\n dp[i][j - 1] + 1,\n dp[i - 1][j - 1] + cost,\n )\n }\n }\n\n return dp[m][n]\n}\n"],"mappings":";AAEA,MAAM,eAAe;;AASrB,IAAa,kBAAb,cAAqC,MAAM;CACzC;CACA;CACA;CACA;CACA;CAEA,YACE,SACA,UACA,YACA;EACA,MAAM,YAAY,YAAY,SAAS,UAAU,WAAW;AAC5D,QAAM,UAAU;AAChB,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,QAAQ,SAAS;AACtB,OAAK,MAAM,SAAS;AACpB,OAAK,SAAS,SAAS;AACvB,OAAK,aAAa;;;;AAKtB,IAAa,kBAAb,cAAqC,MAAM;CACzC;CACA;CACA;CACA;CACA;CAEA,YAAY,WAAmB,UAAkB,OAAgB;EAC/D,IAAI;AACJ,MAAI,UAAU,KAAM,YAAW;WACtB,iBAAiB,QAAS,YAAW;WACrC,MAAM,QAAQ,MAAM,CAAE,YAAW;MACrC,YAAW,OAAO;AACvB,QAAM,IAAI,UAAU,YAAY,SAAS,QAAQ,WAAW;AAC5D,OAAK,OAAO;AACZ,OAAK,YAAY;AACjB,OAAK,WAAW;AAChB,OAAK,WAAW;;;;AAKpB,IAAa,sBAAb,cAAyC,MAAM;CAC7C;CACA;CACA;CAEA,YAAY,MAAc,SAAiB;AACzC,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;;;;AAKhB,IAAa,uBAAb,cAA0C,MAAM;CAC9C;CACA;CACA;CACA;CACA;CAEA,YAAY,MAA2C,YAAoB,YAAqB;EAC9F,MAAM,MAAM,aACR,WAAW,KAAK,IAAI,WAAW,mBAAmB,WAAW,MAC7D,WAAW,KAAK,IAAI,WAAW;AACnC,QAAM,IAAI;AACV,OAAK,OAAO;AACZ,OAAK,aAAa;AAClB,OAAK,OAAO;AACZ,OAAK,aAAa;;;AAOtB,SAAS,YAAY,QAAgB,QAAwB;AAC3D,QAAO,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,OAAO,OAAO,CAAC;;AAGrD,SAAS,aAAa,QAAgB,QAAwB;CAC5D,IAAI,QAAQ,YAAY,QAAQ,OAAO;AACvC,QAAO,QAAQ,KAAK,OAAO,WAAW,QAAQ,EAAE,KAAK,aAAc;AACnE,QAAO;;AAGT,SAAS,WAAW,QAAgB,QAAwB;CAC1D,IAAI,QAAQ,YAAY,QAAQ,OAAO;AACvC,QAAO,QAAQ,OAAO,UAAU,OAAO,WAAW,MAAM,KAAK,aAAc;AAC3E,QAAO;;AAGT,SAAgB,iBAAiB,QAAgB,QAAgC;CAC/E,MAAM,aAAa,YAAY,QAAQ,OAAO;CAC9C,IAAI,OAAO;CACX,IAAI,SAAS;AAEb,MAAK,IAAI,QAAQ,GAAG,QAAQ,YAAY,QACtC,KAAI,OAAO,WAAW,MAAM,KAAK,cAAc;AAC7C;AACA,WAAS;OAET;AAIJ,QAAO;EAAE;EAAM;EAAQ,QAAQ;EAAY;;AAG7C,SAAgB,iBAAiB,OAA2E;AAC1G,SACG,iBAAiB,mBACb,iBAAiB,uBACjB,iBAAiB,yBACnB,MAAM,aAAa,KAAA;;AAI1B,SAAgB,eAAe,OAAgB,QAAgB,OAAe,KAAmB;AAC/F,KAAI,iBAAiB,mBAAmB,iBAAiB,uBAAuB,iBAAiB;MAC3F,CAAC,MAAM,UAAU;AACnB,SAAM,WAAW;IAAE;IAAQ;IAAO;IAAK;AACvC,SAAM,YAAY,YAAY,MAAM,SAAS,MAAM,SAAS;;;;;AAMlE,SAAgB,YACd,SACA,UACA,YACQ;CACR,MAAM,EAAE,WAAW;CACnB,MAAM,QAAQ,YAAY,QAAQ,SAAS,MAAM;CACjD,MAAM,SAAS,YAAY,QAAQ,SAAS,IAAI;CAChD,MAAM,MAAM,KAAK,IAAI,QAAQ,GAAG,OAAO;CACvC,MAAM,WAAW,iBAAiB,QAAQ,MAAM;CAChD,MAAM,YAAY,aAAa,QAAQ,MAAM;CAC7C,MAAM,UAAU,WAAW,QAAQ,MAAM;CACzC,MAAM,WAAW,OAAO,MAAM,WAAW,QAAQ;CACjD,MAAM,aAAa,QAAQ;CAE3B,MAAM,cAAc,KAAK,IAAI,GADZ,KAAK,IAAI,KAAK,QACS,GAAG,MAAM;CACjD,MAAM,SAAS,GAAG,SAAS,KAAK;CAIhC,IAAI,SAAS,GAAG,QAAQ,MAAM,SAAS,SAAS,IAHhC,IAAI,OAAO,OAAO,SAAS,WAGgB,GAF5C,IAAI,OAAO,YAE0C;AAEpE,KAAI,WACF,WAAU,kBAAkB,WAAW;AAGzC,WAAU,eAAe,SAAS,KAAK,WAAW,SAAS,OAAO,WAAW,MAAM,GAAG,IAAI;AAE1F,QAAO;;;AAIT,SAAgB,kBAAkB,OAAwB;AACxD,KAAI,iBAAiB,gBAAiB,QAAO,MAAM;AACnD,KAAI,iBAAiB,MAAM,CACzB,QAAO,MAAM,aAAa,YAAY,MAAM,SAAS,MAAM,SAAS;AAEtE,KAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,QAAO,OAAO,MAAM;;AAGtB,MAAM,qBAAqB;AAE3B,SAAgB,QACd,OACA,OACA,cAAc,GACM;AACpB,KAAI,MAAM,SAAS,mBAAoB,QAAO,KAAA;CAC9C,IAAI;CACJ,IAAI,eAAe,cAAc;AAEjC,MAAK,MAAM,aAAa,OAAO;EAC7B,MAAM,WAAW,YAAY,OAAO,UAAU;AAC9C,MAAI,WAAW,cAAc;AAC3B,kBAAe;AACf,UAAO;;;AAIX,QAAO;;AAGT,SAAS,YAAY,GAAW,GAAmB;CACjD,MAAM,IAAI,EAAE;CACZ,MAAM,IAAI,EAAE;CACZ,MAAM,KAAiB,MAAM,KAAK,EAAE,QAAQ,IAAI,GAAG,QAAQ,MAAM,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;AAEhF,MAAK,IAAI,IAAI,GAAG,KAAK,GAAG,IAAK,IAAG,GAAG,KAAK;AACxC,MAAK,IAAI,IAAI,GAAG,KAAK,GAAG,IAAK,IAAG,GAAG,KAAK;AAExC,MAAK,IAAI,IAAI,GAAG,KAAK,GAAG,IACtB,MAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK;EAC3B,MAAM,OAAO,EAAE,IAAI,OAAO,EAAE,IAAI,KAAK,IAAI;AACzC,KAAG,GAAG,KAAK,KAAK,IACd,GAAG,IAAI,GAAG,KAAK,GACf,GAAG,GAAG,IAAI,KAAK,GACf,GAAG,IAAI,GAAG,IAAI,KAAK,KACpB;;AAIL,QAAO,GAAG,GAAG"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as ExpressionError, n as BonsaiSecurityError } from "./errors-
|
|
1
|
+
import { i as ExpressionError, n as BonsaiSecurityError } from "./errors-BONB6P_5.mjs";
|
|
2
2
|
//#region src/lexer.ts
|
|
3
3
|
const KEYWORDS = new Map([
|
|
4
4
|
["true", {
|
|
@@ -561,4 +561,4 @@ var ExecutionContext = class {
|
|
|
561
561
|
//#endregion
|
|
562
562
|
export { tokenize as i, ExecutionContext as n, SecurityPolicy as r, BLOCKED_PROPERTIES as t };
|
|
563
563
|
|
|
564
|
-
//# sourceMappingURL=execution-context-
|
|
564
|
+
//# sourceMappingURL=execution-context-B-5hQ92X.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-context-DqYC3L92.mjs","names":[],"sources":["../src/lexer.ts","../src/execution-context.ts"],"sourcesContent":["import type { Token, PunctuationValue } from './types.js'\nimport { ExpressionError } from './errors.js'\n\nconst KEYWORDS = new Map<string, { type: Token['type']; value: string }>([\n ['true', { type: 'Boolean', value: 'true' }],\n ['false', { type: 'Boolean', value: 'false' }],\n ['null', { type: 'Null', value: 'null' }],\n ['undefined', { type: 'Undefined', value: 'undefined' }],\n ['in', { type: 'Operator', value: 'in' }],\n ['not', { type: 'Operator', value: 'not' }],\n])\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction isAlpha(ch: string): boolean {\n return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_' || ch === '$'\n}\n\nfunction isAlphaNumeric(ch: string): boolean {\n return isAlpha(ch) || isDigit(ch)\n}\n\nfunction isHexDigit(ch: string): boolean {\n return isDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')\n}\n\nexport function tokenize(source: string): Token[] {\n const tokens: Token[] = []\n let i = 0\n\n while (i < source.length) {\n const ch = source[i]\n\n // Skip whitespace\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n i++\n continue\n }\n\n // Numbers: decimal, hex, binary, octal, scientific, underscores\n if (isDigit(ch)) {\n const start = i\n\n // Hex: 0x...\n if (ch === '0' && i + 1 < source.length && (source[i + 1] === 'x' || source[i + 1] === 'X')) {\n i += 2\n while (i < source.length && (isHexDigit(source[i]) || source[i] === '_')) i++\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Binary: 0b...\n if (ch === '0' && i + 1 < source.length && (source[i + 1] === 'b' || source[i + 1] === 'B')) {\n i += 2\n while (i < source.length && (source[i] === '0' || source[i] === '1' || source[i] === '_')) i++\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Octal: 0o...\n if (ch === '0' && i + 1 < source.length && (source[i + 1] === 'o' || source[i + 1] === 'O')) {\n i += 2\n while (i < source.length && ((source[i] >= '0' && source[i] <= '7') || source[i] === '_')) i++\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Decimal (with optional underscore separators)\n while (i < source.length && (isDigit(source[i]) || source[i] === '_')) i++\n if (i < source.length && source[i] === '.' && i + 1 < source.length && isDigit(source[i + 1])) {\n i++ // skip '.'\n while (i < source.length && (isDigit(source[i]) || source[i] === '_')) i++\n }\n\n // Scientific notation: e/E followed by optional +/- and digits\n if (i < source.length && (source[i] === 'e' || source[i] === 'E')) {\n i++\n if (i < source.length && (source[i] === '+' || source[i] === '-')) i++\n while (i < source.length && (isDigit(source[i]) || source[i] === '_')) i++\n }\n\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Strings\n if (ch === '\"' || ch === \"'\") {\n const quote = ch\n const start = i\n i++ // skip opening quote\n let value = ''\n while (i < source.length && source[i] !== quote) {\n if (source[i] === '\\\\') {\n i++\n const escaped = source[i]\n switch (escaped) {\n case 'n': value += '\\n'; break\n case 't': value += '\\t'; break\n case 'r': value += '\\r'; break\n case '\\\\': value += '\\\\'; break\n case '0': value += '\\0'; break\n case 'x': {\n const hex = source.slice(i + 1, i + 3)\n if (hex.length < 2 || !isHexDigit(hex[0]) || !isHexDigit(hex[1])) {\n throw new ExpressionError('Invalid \\\\x escape: expected 2 hex digits', { source, start, end: i + 3 })\n }\n value += String.fromCharCode(parseInt(hex, 16))\n i += 2\n break\n }\n case 'u': {\n if (i + 1 < source.length && source[i + 1] === '{') {\n const closeBrace = source.indexOf('}', i + 2)\n if (closeBrace === -1) {\n throw new ExpressionError('Invalid \\\\u{} escape: missing closing brace', { source, start, end: source.length })\n }\n const hex = source.slice(i + 2, closeBrace)\n if (hex.length === 0) {\n throw new ExpressionError('Invalid \\\\u{} escape: empty code point', { source, start, end: closeBrace + 1 })\n }\n for (const c of hex) {\n if (!isHexDigit(c)) {\n throw new ExpressionError(`Invalid \\\\u{} escape: non-hex digit \"${c}\"`, { source, start, end: closeBrace + 1 })\n }\n }\n const MAX_UNICODE_CODE_POINT = 0x10FFFF\n const codePoint = parseInt(hex, 16)\n if (codePoint > MAX_UNICODE_CODE_POINT) {\n throw new ExpressionError('Invalid \\\\u{} escape: code point out of range', { source, start, end: closeBrace + 1 })\n }\n value += String.fromCodePoint(codePoint)\n i = closeBrace\n } else {\n const UNICODE_ESCAPE_LEN = 4\n const hex = source.slice(i + 1, i + 1 + UNICODE_ESCAPE_LEN)\n if (hex.length < UNICODE_ESCAPE_LEN) {\n throw new ExpressionError('Invalid \\\\u escape: expected 4 hex digits', { source, start, end: i + 1 + UNICODE_ESCAPE_LEN })\n }\n for (const c of hex) {\n if (!isHexDigit(c)) {\n throw new ExpressionError('Invalid \\\\u escape: expected 4 hex digits', { source, start, end: i + 1 + UNICODE_ESCAPE_LEN })\n }\n }\n value += String.fromCharCode(parseInt(hex, 16))\n i += UNICODE_ESCAPE_LEN\n }\n break\n }\n default: value += escaped; break\n }\n } else {\n value += source[i]\n }\n i++\n }\n if (i >= source.length) {\n throw new ExpressionError('Unterminated string', { source, start, end: i })\n }\n i++ // skip closing quote\n tokens.push({ type: 'String', value, start, end: i })\n continue\n }\n\n // Template literals\n if (ch === '`') {\n const start = i\n i++ // skip opening backtick\n let raw = '`'\n while (i < source.length && source[i] !== '`') {\n if (source[i] === '$' && i + 1 < source.length && source[i + 1] === '{') {\n raw += '${'\n i += 2\n let depth = 1\n while (i < source.length && depth > 0) {\n const ic = source[i]\n // Skip string literals inside interpolations\n if (ic === '\"' || ic === \"'\") {\n raw += ic\n i++\n while (i < source.length && source[i] !== ic) {\n if (source[i] === '\\\\' && i + 1 < source.length) {\n raw += source[i] + source[i + 1]\n i += 2\n continue\n }\n raw += source[i]\n i++\n }\n if (i < source.length) {\n raw += source[i] // closing quote\n i++\n }\n continue\n }\n if (ic === '{') depth++\n else if (ic === '}') depth--\n if (depth > 0) raw += ic\n i++\n }\n raw += '}'\n } else {\n raw += source[i]\n i++\n }\n }\n if (i >= source.length) {\n throw new ExpressionError('Unterminated template literal', { source, start, end: i })\n }\n raw += '`'\n i++ // skip closing backtick\n tokens.push({ type: 'TemplateLiteral', value: raw, start, end: i })\n continue\n }\n\n // Identifiers and keywords\n if (isAlpha(ch)) {\n const start = i\n while (i < source.length && isAlphaNumeric(source[i])) i++\n const value = source.slice(start, i)\n const kw = KEYWORDS.get(value)\n if (kw) {\n tokens.push({ type: kw.type, value: kw.value, start, end: i } as Token)\n } else {\n tokens.push({ type: 'Identifier', value, start, end: i })\n }\n continue\n }\n\n // Multi-character operators and punctuation\n const start = i\n\n // Spread operator ...\n if (ch === '.' && i + 2 < source.length && source[i + 1] === '.' && source[i + 2] === '.') {\n tokens.push({ type: 'Spread', value: '...', start, end: i + 3 })\n i += 3\n continue\n }\n\n // Dot (punctuation)\n if (ch === '.') {\n tokens.push({ type: 'Punctuation', value: '.', start, end: i + 1 })\n i++\n continue\n }\n\n // Pipe |> or logical ||\n if (ch === '|') {\n if (i + 1 < source.length && source[i + 1] === '>') {\n tokens.push({ type: 'Pipe', value: '|>', start, end: i + 2 })\n i += 2\n continue\n }\n if (i + 1 < source.length && source[i + 1] === '|') {\n tokens.push({ type: 'Operator', value: '||', start, end: i + 2 })\n i += 2\n continue\n }\n }\n\n // &&\n if (ch === '&' && i + 1 < source.length && source[i + 1] === '&') {\n tokens.push({ type: 'Operator', value: '&&', start, end: i + 2 })\n i += 2\n continue\n }\n\n // == !=\n if (ch === '=' && i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '==', start, end: i + 2 })\n i += 2\n continue\n }\n if (ch === '!' && i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '!=', start, end: i + 2 })\n i += 2\n continue\n }\n\n // ! (logical not) - must come after != check\n if (ch === '!') {\n tokens.push({ type: 'Operator', value: '!', start, end: i + 1 })\n i++\n continue\n }\n\n // <= >= < >\n if (ch === '<') {\n if (i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '<=', start, end: i + 2 })\n i += 2\n continue\n }\n tokens.push({ type: 'Operator', value: '<', start, end: i + 1 })\n i++\n continue\n }\n if (ch === '>') {\n if (i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '>=', start, end: i + 2 })\n i += 2\n continue\n }\n tokens.push({ type: 'Operator', value: '>', start, end: i + 1 })\n i++\n continue\n }\n\n // ** (exponentiation) — must come before single *\n if (ch === '*' && i + 1 < source.length && source[i + 1] === '*') {\n tokens.push({ type: 'Operator', value: '**', start, end: i + 2 })\n i += 2\n continue\n }\n\n // ?. (optional chaining) or ?? (nullish coalescing) or ? (ternary)\n if (ch === '?') {\n if (i + 1 < source.length && source[i + 1] === '.') {\n tokens.push({ type: 'OptionalChain', value: '?.', start, end: i + 2 })\n i += 2\n continue\n }\n if (i + 1 < source.length && source[i + 1] === '?') {\n tokens.push({ type: 'NullishCoalescing', value: '??', start, end: i + 2 })\n i += 2\n continue\n }\n tokens.push({ type: 'Punctuation', value: '?', start, end: i + 1 })\n i++\n continue\n }\n\n // Single-character operators\n if (ch === '+' || ch === '-' || ch === '*' || ch === '/' || ch === '%') {\n tokens.push({ type: 'Operator', value: ch, start, end: i + 1 })\n i++\n continue\n }\n\n // Punctuation\n if ('()[]{},:'.includes(ch)) {\n tokens.push({ type: 'Punctuation', value: ch as PunctuationValue, start, end: i + 1 })\n i++\n continue\n }\n\n throw new ExpressionError(`Unexpected character \"${ch}\"`, { source, start, end: i + 1 })\n }\n\n tokens.push({ type: 'EOF', value: '', start: i, end: i })\n return tokens\n}\n","import type { BonsaiOptions } from './types.js'\nimport { BonsaiSecurityError } from './errors.js'\n\n/**\n * Discriminates how a property name is being accessed so security checks can\n * apply different rules (e.g. root identifiers bypass allow/deny lists).\n */\nexport type AccessKind = 'identifier' | 'member' | 'method' | 'object-key'\n\nexport const BLOCKED_PROPERTIES: ReadonlySet<string> = new Set([\n '__proto__', 'constructor', 'prototype',\n])\n\nconst MAX_INDEX_DIGITS = 10\n\nfunction isCanonicalIndex(key: string): boolean {\n if (key.length === 0 || key.length > MAX_INDEX_DIGITS) return false\n const n = Number(key)\n return Number.isInteger(n) && n >= 0 && String(n) === key\n}\n\nconst TIMEOUT_CHECK_INTERVAL = 1000\nconst DEFAULT_MAX_DEPTH = 100\nconst DEFAULT_MAX_ARRAY_LENGTH = 100_000\n\n/** Immutable per-instance security configuration derived from BonsaiOptions. */\nexport class SecurityPolicy {\n readonly maxDepth: number\n readonly maxArrayLength: number\n readonly timeout: number\n readonly allowedProperties?: ReadonlySet<string>\n readonly deniedProperties?: ReadonlySet<string>\n\n constructor(options: BonsaiOptions = {}) {\n this.maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH\n this.maxArrayLength = options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH\n this.timeout = options.timeout ?? 0\n this.allowedProperties = options.allowedProperties\n ? new Set(options.allowedProperties)\n : undefined\n this.deniedProperties = options.deniedProperties\n ? new Set(options.deniedProperties)\n : undefined\n }\n}\n\n/** Mutable per-evaluation state: tracks depth, step count, and deadline for a single evaluation. */\nexport class ExecutionContext {\n private stepCount = 0\n private depth = 0\n private deadline: number\n readonly policy: SecurityPolicy\n private readonly now: () => number\n\n constructor(policy: SecurityPolicy, now: () => number = Date.now) {\n this.policy = policy\n this.now = now\n this.deadline = policy.timeout ? now() + policy.timeout : 0\n }\n\n /** Reset mutable state for reuse. Avoids allocating a new instance per evaluation. */\n reset(): void {\n this.stepCount = 0\n this.depth = 0\n this.deadline = this.policy.timeout ? this.now() + this.policy.timeout : 0\n }\n\n step(): void {\n if (this.deadline) {\n this.stepCount++\n if (this.stepCount % TIMEOUT_CHECK_INTERVAL === 0) {\n this.checkTimeout()\n }\n }\n }\n\n checkTimeout(): void {\n if (this.deadline && this.now() >= this.deadline) {\n throw new BonsaiSecurityError(\n 'TIMEOUT',\n `Expression timeout: exceeded ${this.policy.timeout}ms`,\n )\n }\n }\n\n enterDepth(): void {\n if (++this.depth > this.policy.maxDepth) {\n throw new BonsaiSecurityError(\n 'MAX_DEPTH',\n `Maximum expression depth (${this.policy.maxDepth}) exceeded`,\n )\n }\n }\n\n exitDepth(): void {\n this.depth--\n }\n\n withDepth<T>(fn: () => T): T {\n this.enterDepth()\n try {\n return fn()\n } finally {\n this.exitDepth()\n }\n }\n\n checkNameAccess(key: string, kind: AccessKind): void {\n if (BLOCKED_PROPERTIES.has(key)) {\n throw new BonsaiSecurityError(\n 'BLOCKED_PROPERTY',\n `Blocked: access to \"${key}\" is not allowed`,\n )\n }\n\n if (kind === 'identifier' || kind === 'object-key') return\n\n if (isCanonicalIndex(key)) return\n\n if (this.policy.allowedProperties) {\n if (!this.policy.allowedProperties.has(key)) {\n throw new BonsaiSecurityError(\n 'PROPERTY_NOT_ALLOWED',\n `Blocked: \"${key}\" is not in allowed properties`,\n )\n }\n }\n\n if (this.policy.deniedProperties) {\n if (this.policy.deniedProperties.has(key)) {\n throw new BonsaiSecurityError(\n 'PROPERTY_DENIED',\n `Blocked: \"${key}\" is in denied properties`,\n )\n }\n }\n }\n\n checkArrayLength(length: number): void {\n if (length > this.policy.maxArrayLength) {\n throw new BonsaiSecurityError(\n 'MAX_ARRAY_LENGTH',\n `Array length (${length}) exceeds maximum (${this.policy.maxArrayLength})`,\n )\n }\n }\n}\n"],"mappings":";;AAGA,MAAM,WAAW,IAAI,IAAoD;CACvE,CAAC,QAAQ;EAAE,MAAM;EAAW,OAAO;EAAQ,CAAC;CAC5C,CAAC,SAAS;EAAE,MAAM;EAAW,OAAO;EAAS,CAAC;CAC9C,CAAC,QAAQ;EAAE,MAAM;EAAQ,OAAO;EAAQ,CAAC;CACzC,CAAC,aAAa;EAAE,MAAM;EAAa,OAAO;EAAa,CAAC;CACxD,CAAC,MAAM;EAAE,MAAM;EAAY,OAAO;EAAM,CAAC;CACzC,CAAC,OAAO;EAAE,MAAM;EAAY,OAAO;EAAO,CAAC;CAC5C,CAAC;AAEF,SAAS,QAAQ,IAAqB;AACpC,QAAO,MAAM,OAAO,MAAM;;AAG5B,SAAS,QAAQ,IAAqB;AACpC,QAAQ,MAAM,OAAO,MAAM,OAAS,MAAM,OAAO,MAAM,OAAQ,OAAO,OAAO,OAAO;;AAGtF,SAAS,eAAe,IAAqB;AAC3C,QAAO,QAAQ,GAAG,IAAI,QAAQ,GAAG;;AAGnC,SAAS,WAAW,IAAqB;AACvC,QAAO,QAAQ,GAAG,IAAK,MAAM,OAAO,MAAM,OAAS,MAAM,OAAO,MAAM;;AAGxE,SAAgB,SAAS,QAAyB;CAChD,MAAM,SAAkB,EAAE;CAC1B,IAAI,IAAI;AAER,QAAO,IAAI,OAAO,QAAQ;EACxB,MAAM,KAAK,OAAO;AAGlB,MAAI,OAAO,OAAO,OAAO,OAAQ,OAAO,QAAQ,OAAO,MAAM;AAC3D;AACA;;AAIF,MAAI,QAAQ,GAAG,EAAE;GACf,MAAM,QAAQ;AAGd,OAAI,OAAO,OAAO,IAAI,IAAI,OAAO,WAAW,OAAO,IAAI,OAAO,OAAO,OAAO,IAAI,OAAO,MAAM;AAC3F,SAAK;AACL,WAAO,IAAI,OAAO,WAAW,WAAW,OAAO,GAAG,IAAI,OAAO,OAAO,KAAM;AAC1E,WAAO,KAAK;KAAE,MAAM;KAAU,OAAO,OAAO,MAAM,OAAO,EAAE;KAAE;KAAO,KAAK;KAAG,CAAC;AAC7E;;AAIF,OAAI,OAAO,OAAO,IAAI,IAAI,OAAO,WAAW,OAAO,IAAI,OAAO,OAAO,OAAO,IAAI,OAAO,MAAM;AAC3F,SAAK;AACL,WAAO,IAAI,OAAO,WAAW,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAM;AAC3F,WAAO,KAAK;KAAE,MAAM;KAAU,OAAO,OAAO,MAAM,OAAO,EAAE;KAAE;KAAO,KAAK;KAAG,CAAC;AAC7E;;AAIF,OAAI,OAAO,OAAO,IAAI,IAAI,OAAO,WAAW,OAAO,IAAI,OAAO,OAAO,OAAO,IAAI,OAAO,MAAM;AAC3F,SAAK;AACL,WAAO,IAAI,OAAO,WAAY,OAAO,MAAM,OAAO,OAAO,MAAM,OAAQ,OAAO,OAAO,KAAM;AAC3F,WAAO,KAAK;KAAE,MAAM;KAAU,OAAO,OAAO,MAAM,OAAO,EAAE;KAAE;KAAO,KAAK;KAAG,CAAC;AAC7E;;AAIF,UAAO,IAAI,OAAO,WAAW,QAAQ,OAAO,GAAG,IAAI,OAAO,OAAO,KAAM;AACvE,OAAI,IAAI,OAAO,UAAU,OAAO,OAAO,OAAO,IAAI,IAAI,OAAO,UAAU,QAAQ,OAAO,IAAI,GAAG,EAAE;AAC7F;AACA,WAAO,IAAI,OAAO,WAAW,QAAQ,OAAO,GAAG,IAAI,OAAO,OAAO,KAAM;;AAIzE,OAAI,IAAI,OAAO,WAAW,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM;AACjE;AACA,QAAI,IAAI,OAAO,WAAW,OAAO,OAAO,OAAO,OAAO,OAAO,KAAM;AACnE,WAAO,IAAI,OAAO,WAAW,QAAQ,OAAO,GAAG,IAAI,OAAO,OAAO,KAAM;;AAGzE,UAAO,KAAK;IAAE,MAAM;IAAU,OAAO,OAAO,MAAM,OAAO,EAAE;IAAE;IAAO,KAAK;IAAG,CAAC;AAC7E;;AAIF,MAAI,OAAO,QAAO,OAAO,KAAK;GAC5B,MAAM,QAAQ;GACd,MAAM,QAAQ;AACd;GACA,IAAI,QAAQ;AACZ,UAAO,IAAI,OAAO,UAAU,OAAO,OAAO,OAAO;AAC/C,QAAI,OAAO,OAAO,MAAM;AACtB;KACA,MAAM,UAAU,OAAO;AACvB,aAAQ,SAAR;MACE,KAAK;AAAK,gBAAS;AAAM;MACzB,KAAK;AAAK,gBAAS;AAAM;MACzB,KAAK;AAAK,gBAAS;AAAM;MACzB,KAAK;AAAM,gBAAS;AAAM;MAC1B,KAAK;AAAK,gBAAS;AAAM;MACzB,KAAK,KAAK;OACR,MAAM,MAAM,OAAO,MAAM,IAAI,GAAG,IAAI,EAAE;AACtC,WAAI,IAAI,SAAS,KAAK,CAAC,WAAW,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,GAAG,CAC9D,OAAM,IAAI,gBAAgB,6CAA6C;QAAE;QAAQ;QAAO,KAAK,IAAI;QAAG,CAAC;AAEvG,gBAAS,OAAO,aAAa,SAAS,KAAK,GAAG,CAAC;AAC/C,YAAK;AACL;;MAEF,KAAK;AACH,WAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;QAClD,MAAM,aAAa,OAAO,QAAQ,KAAK,IAAI,EAAE;AAC7C,YAAI,eAAe,GACjB,OAAM,IAAI,gBAAgB,+CAA+C;SAAE;SAAQ;SAAO,KAAK,OAAO;SAAQ,CAAC;QAEjH,MAAM,MAAM,OAAO,MAAM,IAAI,GAAG,WAAW;AAC3C,YAAI,IAAI,WAAW,EACjB,OAAM,IAAI,gBAAgB,0CAA0C;SAAE;SAAQ;SAAO,KAAK,aAAa;SAAG,CAAC;AAE7G,aAAK,MAAM,KAAK,IACd,KAAI,CAAC,WAAW,EAAE,CAChB,OAAM,IAAI,gBAAgB,wCAAwC,EAAE,IAAI;SAAE;SAAQ;SAAO,KAAK,aAAa;SAAG,CAAC;QAGnH,MAAM,yBAAyB;QAC/B,MAAM,YAAY,SAAS,KAAK,GAAG;AACnC,YAAI,YAAY,uBACd,OAAM,IAAI,gBAAgB,iDAAiD;SAAE;SAAQ;SAAO,KAAK,aAAa;SAAG,CAAC;AAEpH,iBAAS,OAAO,cAAc,UAAU;AACxC,YAAI;cACC;QACL,MAAM,qBAAqB;QAC3B,MAAM,MAAM,OAAO,MAAM,IAAI,GAAG,IAAI,IAAI,mBAAmB;AAC3D,YAAI,IAAI,SAAS,mBACf,OAAM,IAAI,gBAAgB,6CAA6C;SAAE;SAAQ;SAAO,KAAK,IAAI,IAAI;SAAoB,CAAC;AAE5H,aAAK,MAAM,KAAK,IACd,KAAI,CAAC,WAAW,EAAE,CAChB,OAAM,IAAI,gBAAgB,6CAA6C;SAAE;SAAQ;SAAO,KAAK,IAAI,IAAI;SAAoB,CAAC;AAG9H,iBAAS,OAAO,aAAa,SAAS,KAAK,GAAG,CAAC;AAC/C,aAAK;;AAEP;MAEF;AAAS,gBAAS;AAAS;;UAG7B,UAAS,OAAO;AAElB;;AAEF,OAAI,KAAK,OAAO,OACd,OAAM,IAAI,gBAAgB,uBAAuB;IAAE;IAAQ;IAAO,KAAK;IAAG,CAAC;AAE7E;AACA,UAAO,KAAK;IAAE,MAAM;IAAU;IAAO;IAAO,KAAK;IAAG,CAAC;AACrD;;AAIF,MAAI,OAAO,KAAK;GACd,MAAM,QAAQ;AACd;GACA,IAAI,MAAM;AACV,UAAO,IAAI,OAAO,UAAU,OAAO,OAAO,IACxC,KAAI,OAAO,OAAO,OAAO,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AACvE,WAAO;AACP,SAAK;IACL,IAAI,QAAQ;AACZ,WAAO,IAAI,OAAO,UAAU,QAAQ,GAAG;KACrC,MAAM,KAAK,OAAO;AAElB,SAAI,OAAO,QAAO,OAAO,KAAK;AAC5B,aAAO;AACP;AACA,aAAO,IAAI,OAAO,UAAU,OAAO,OAAO,IAAI;AAC5C,WAAI,OAAO,OAAO,QAAQ,IAAI,IAAI,OAAO,QAAQ;AAC/C,eAAO,OAAO,KAAK,OAAO,IAAI;AAC9B,aAAK;AACL;;AAEF,cAAO,OAAO;AACd;;AAEF,UAAI,IAAI,OAAO,QAAQ;AACrB,cAAO,OAAO;AACd;;AAEF;;AAEF,SAAI,OAAO,IAAK;cACP,OAAO,IAAK;AACrB,SAAI,QAAQ,EAAG,QAAO;AACtB;;AAEF,WAAO;UACF;AACL,WAAO,OAAO;AACd;;AAGJ,OAAI,KAAK,OAAO,OACd,OAAM,IAAI,gBAAgB,iCAAiC;IAAE;IAAQ;IAAO,KAAK;IAAG,CAAC;AAEvF,UAAO;AACP;AACA,UAAO,KAAK;IAAE,MAAM;IAAmB,OAAO;IAAK;IAAO,KAAK;IAAG,CAAC;AACnE;;AAIF,MAAI,QAAQ,GAAG,EAAE;GACf,MAAM,QAAQ;AACd,UAAO,IAAI,OAAO,UAAU,eAAe,OAAO,GAAG,CAAE;GACvD,MAAM,QAAQ,OAAO,MAAM,OAAO,EAAE;GACpC,MAAM,KAAK,SAAS,IAAI,MAAM;AAC9B,OAAI,GACF,QAAO,KAAK;IAAE,MAAM,GAAG;IAAM,OAAO,GAAG;IAAO;IAAO,KAAK;IAAG,CAAU;OAEvE,QAAO,KAAK;IAAE,MAAM;IAAc;IAAO;IAAO,KAAK;IAAG,CAAC;AAE3D;;EAIF,MAAM,QAAQ;AAGd,MAAI,OAAO,OAAO,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,OAAO,OAAO,IAAI,OAAO,KAAK;AACzF,UAAO,KAAK;IAAE,MAAM;IAAU,OAAO;IAAO;IAAO,KAAK,IAAI;IAAG,CAAC;AAChE,QAAK;AACL;;AAIF,MAAI,OAAO,KAAK;AACd,UAAO,KAAK;IAAE,MAAM;IAAe,OAAO;IAAK;IAAO,KAAK,IAAI;IAAG,CAAC;AACnE;AACA;;AAIF,MAAI,OAAO,KAAK;AACd,OAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAClD,WAAO,KAAK;KAAE,MAAM;KAAQ,OAAO;KAAM;KAAO,KAAK,IAAI;KAAG,CAAC;AAC7D,SAAK;AACL;;AAEF,OAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAClD,WAAO,KAAK;KAAE,MAAM;KAAY,OAAO;KAAM;KAAO,KAAK,IAAI;KAAG,CAAC;AACjE,SAAK;AACL;;;AAKJ,MAAI,OAAO,OAAO,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAChE,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAM;IAAO,KAAK,IAAI;IAAG,CAAC;AACjE,QAAK;AACL;;AAIF,MAAI,OAAO,OAAO,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAChE,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAM;IAAO,KAAK,IAAI;IAAG,CAAC;AACjE,QAAK;AACL;;AAEF,MAAI,OAAO,OAAO,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAChE,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAM;IAAO,KAAK,IAAI;IAAG,CAAC;AACjE,QAAK;AACL;;AAIF,MAAI,OAAO,KAAK;AACd,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAK;IAAO,KAAK,IAAI;IAAG,CAAC;AAChE;AACA;;AAIF,MAAI,OAAO,KAAK;AACd,OAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAClD,WAAO,KAAK;KAAE,MAAM;KAAY,OAAO;KAAM;KAAO,KAAK,IAAI;KAAG,CAAC;AACjE,SAAK;AACL;;AAEF,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAK;IAAO,KAAK,IAAI;IAAG,CAAC;AAChE;AACA;;AAEF,MAAI,OAAO,KAAK;AACd,OAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAClD,WAAO,KAAK;KAAE,MAAM;KAAY,OAAO;KAAM;KAAO,KAAK,IAAI;KAAG,CAAC;AACjE,SAAK;AACL;;AAEF,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAK;IAAO,KAAK,IAAI;IAAG,CAAC;AAChE;AACA;;AAIF,MAAI,OAAO,OAAO,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAChE,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAM;IAAO,KAAK,IAAI;IAAG,CAAC;AACjE,QAAK;AACL;;AAIF,MAAI,OAAO,KAAK;AACd,OAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAClD,WAAO,KAAK;KAAE,MAAM;KAAiB,OAAO;KAAM;KAAO,KAAK,IAAI;KAAG,CAAC;AACtE,SAAK;AACL;;AAEF,OAAI,IAAI,IAAI,OAAO,UAAU,OAAO,IAAI,OAAO,KAAK;AAClD,WAAO,KAAK;KAAE,MAAM;KAAqB,OAAO;KAAM;KAAO,KAAK,IAAI;KAAG,CAAC;AAC1E,SAAK;AACL;;AAEF,UAAO,KAAK;IAAE,MAAM;IAAe,OAAO;IAAK;IAAO,KAAK,IAAI;IAAG,CAAC;AACnE;AACA;;AAIF,MAAI,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;AACtE,UAAO,KAAK;IAAE,MAAM;IAAY,OAAO;IAAI;IAAO,KAAK,IAAI;IAAG,CAAC;AAC/D;AACA;;AAIF,MAAI,WAAW,SAAS,GAAG,EAAE;AAC3B,UAAO,KAAK;IAAE,MAAM;IAAe,OAAO;IAAwB;IAAO,KAAK,IAAI;IAAG,CAAC;AACtF;AACA;;AAGF,QAAM,IAAI,gBAAgB,yBAAyB,GAAG,IAAI;GAAE;GAAQ;GAAO,KAAK,IAAI;GAAG,CAAC;;AAG1F,QAAO,KAAK;EAAE,MAAM;EAAO,OAAO;EAAI,OAAO;EAAG,KAAK;EAAG,CAAC;AACzD,QAAO;;;;ACtVT,MAAa,qBAA0C,IAAI,IAAI;CAC7D;CAAa;CAAe;CAC7B,CAAC;AAEF,MAAM,mBAAmB;AAEzB,SAAS,iBAAiB,KAAsB;AAC9C,KAAI,IAAI,WAAW,KAAK,IAAI,SAAS,iBAAkB,QAAO;CAC9D,MAAM,IAAI,OAAO,IAAI;AACrB,QAAO,OAAO,UAAU,EAAE,IAAI,KAAK,KAAK,OAAO,EAAE,KAAK;;AAGxD,MAAM,yBAAyB;AAC/B,MAAM,oBAAoB;AAC1B,MAAM,2BAA2B;;AAGjC,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CACA;CACA;CAEA,YAAY,UAAyB,EAAE,EAAE;AACvC,OAAK,WAAW,QAAQ,YAAY;AACpC,OAAK,iBAAiB,QAAQ,kBAAkB;AAChD,OAAK,UAAU,QAAQ,WAAW;AAClC,OAAK,oBAAoB,QAAQ,oBAC7B,IAAI,IAAI,QAAQ,kBAAkB,GAClC,KAAA;AACJ,OAAK,mBAAmB,QAAQ,mBAC5B,IAAI,IAAI,QAAQ,iBAAiB,GACjC,KAAA;;;;AAKR,IAAa,mBAAb,MAA8B;CAC5B,YAAoB;CACpB,QAAgB;CAChB;CACA;CACA;CAEA,YAAY,QAAwB,MAAoB,KAAK,KAAK;AAChE,OAAK,SAAS;AACd,OAAK,MAAM;AACX,OAAK,WAAW,OAAO,UAAU,KAAK,GAAG,OAAO,UAAU;;;CAI5D,QAAc;AACZ,OAAK,YAAY;AACjB,OAAK,QAAQ;AACb,OAAK,WAAW,KAAK,OAAO,UAAU,KAAK,KAAK,GAAG,KAAK,OAAO,UAAU;;CAG3E,OAAa;AACX,MAAI,KAAK,UAAU;AACjB,QAAK;AACL,OAAI,KAAK,YAAY,2BAA2B,EAC9C,MAAK,cAAc;;;CAKzB,eAAqB;AACnB,MAAI,KAAK,YAAY,KAAK,KAAK,IAAI,KAAK,SACtC,OAAM,IAAI,oBACR,WACA,gCAAgC,KAAK,OAAO,QAAQ,IACrD;;CAIL,aAAmB;AACjB,MAAI,EAAE,KAAK,QAAQ,KAAK,OAAO,SAC7B,OAAM,IAAI,oBACR,aACA,6BAA6B,KAAK,OAAO,SAAS,YACnD;;CAIL,YAAkB;AAChB,OAAK;;CAGP,UAAa,IAAgB;AAC3B,OAAK,YAAY;AACjB,MAAI;AACF,UAAO,IAAI;YACH;AACR,QAAK,WAAW;;;CAIpB,gBAAgB,KAAa,MAAwB;AACnD,MAAI,mBAAmB,IAAI,IAAI,CAC7B,OAAM,IAAI,oBACR,oBACA,uBAAuB,IAAI,kBAC5B;AAGH,MAAI,SAAS,gBAAgB,SAAS,aAAc;AAEpD,MAAI,iBAAiB,IAAI,CAAE;AAE3B,MAAI,KAAK,OAAO;OACV,CAAC,KAAK,OAAO,kBAAkB,IAAI,IAAI,CACzC,OAAM,IAAI,oBACR,wBACA,aAAa,IAAI,gCAClB;;AAIL,MAAI,KAAK,OAAO;OACV,KAAK,OAAO,iBAAiB,IAAI,IAAI,CACvC,OAAM,IAAI,oBACR,mBACA,aAAa,IAAI,2BAClB;;;CAKP,iBAAiB,QAAsB;AACrC,MAAI,SAAS,KAAK,OAAO,eACvB,OAAM,IAAI,oBACR,oBACA,iBAAiB,OAAO,qBAAqB,KAAK,OAAO,eAAe,GACzE"}
|
|
1
|
+
{"version":3,"file":"execution-context-B-5hQ92X.mjs","names":[],"sources":["../src/lexer.ts","../src/execution-context.ts"],"sourcesContent":["import type { Token, PunctuationValue } from './types.js'\nimport { ExpressionError } from './errors.js'\n\nconst KEYWORDS = new Map<string, { type: Token['type']; value: string }>([\n ['true', { type: 'Boolean', value: 'true' }],\n ['false', { type: 'Boolean', value: 'false' }],\n ['null', { type: 'Null', value: 'null' }],\n ['undefined', { type: 'Undefined', value: 'undefined' }],\n ['in', { type: 'Operator', value: 'in' }],\n ['not', { type: 'Operator', value: 'not' }],\n])\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction isAlpha(ch: string): boolean {\n return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_' || ch === '$'\n}\n\nfunction isAlphaNumeric(ch: string): boolean {\n return isAlpha(ch) || isDigit(ch)\n}\n\nfunction isHexDigit(ch: string): boolean {\n return isDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')\n}\n\nexport function tokenize(source: string): Token[] {\n const tokens: Token[] = []\n let i = 0\n\n while (i < source.length) {\n const ch = source[i]\n\n // Skip whitespace\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n i++\n continue\n }\n\n // Numbers: decimal, hex, binary, octal, scientific, underscores\n if (isDigit(ch)) {\n const start = i\n\n // Hex: 0x...\n if (ch === '0' && i + 1 < source.length && (source[i + 1] === 'x' || source[i + 1] === 'X')) {\n i += 2\n while (i < source.length && (isHexDigit(source[i]) || source[i] === '_')) i++\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Binary: 0b...\n if (ch === '0' && i + 1 < source.length && (source[i + 1] === 'b' || source[i + 1] === 'B')) {\n i += 2\n while (i < source.length && (source[i] === '0' || source[i] === '1' || source[i] === '_')) i++\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Octal: 0o...\n if (ch === '0' && i + 1 < source.length && (source[i + 1] === 'o' || source[i + 1] === 'O')) {\n i += 2\n while (i < source.length && ((source[i] >= '0' && source[i] <= '7') || source[i] === '_')) i++\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Decimal (with optional underscore separators)\n while (i < source.length && (isDigit(source[i]) || source[i] === '_')) i++\n if (i < source.length && source[i] === '.' && i + 1 < source.length && isDigit(source[i + 1])) {\n i++ // skip '.'\n while (i < source.length && (isDigit(source[i]) || source[i] === '_')) i++\n }\n\n // Scientific notation: e/E followed by optional +/- and digits\n if (i < source.length && (source[i] === 'e' || source[i] === 'E')) {\n i++\n if (i < source.length && (source[i] === '+' || source[i] === '-')) i++\n while (i < source.length && (isDigit(source[i]) || source[i] === '_')) i++\n }\n\n tokens.push({ type: 'Number', value: source.slice(start, i), start, end: i })\n continue\n }\n\n // Strings\n if (ch === '\"' || ch === \"'\") {\n const quote = ch\n const start = i\n i++ // skip opening quote\n let value = ''\n while (i < source.length && source[i] !== quote) {\n if (source[i] === '\\\\') {\n i++\n const escaped = source[i]\n switch (escaped) {\n case 'n': value += '\\n'; break\n case 't': value += '\\t'; break\n case 'r': value += '\\r'; break\n case '\\\\': value += '\\\\'; break\n case '0': value += '\\0'; break\n case 'x': {\n const hex = source.slice(i + 1, i + 3)\n if (hex.length < 2 || !isHexDigit(hex[0]) || !isHexDigit(hex[1])) {\n throw new ExpressionError('Invalid \\\\x escape: expected 2 hex digits', { source, start, end: i + 3 })\n }\n value += String.fromCharCode(parseInt(hex, 16))\n i += 2\n break\n }\n case 'u': {\n if (i + 1 < source.length && source[i + 1] === '{') {\n const closeBrace = source.indexOf('}', i + 2)\n if (closeBrace === -1) {\n throw new ExpressionError('Invalid \\\\u{} escape: missing closing brace', { source, start, end: source.length })\n }\n const hex = source.slice(i + 2, closeBrace)\n if (hex.length === 0) {\n throw new ExpressionError('Invalid \\\\u{} escape: empty code point', { source, start, end: closeBrace + 1 })\n }\n for (const c of hex) {\n if (!isHexDigit(c)) {\n throw new ExpressionError(`Invalid \\\\u{} escape: non-hex digit \"${c}\"`, { source, start, end: closeBrace + 1 })\n }\n }\n const MAX_UNICODE_CODE_POINT = 0x10FFFF\n const codePoint = parseInt(hex, 16)\n if (codePoint > MAX_UNICODE_CODE_POINT) {\n throw new ExpressionError('Invalid \\\\u{} escape: code point out of range', { source, start, end: closeBrace + 1 })\n }\n value += String.fromCodePoint(codePoint)\n i = closeBrace\n } else {\n const UNICODE_ESCAPE_LEN = 4\n const hex = source.slice(i + 1, i + 1 + UNICODE_ESCAPE_LEN)\n if (hex.length < UNICODE_ESCAPE_LEN) {\n throw new ExpressionError('Invalid \\\\u escape: expected 4 hex digits', { source, start, end: i + 1 + UNICODE_ESCAPE_LEN })\n }\n for (const c of hex) {\n if (!isHexDigit(c)) {\n throw new ExpressionError('Invalid \\\\u escape: expected 4 hex digits', { source, start, end: i + 1 + UNICODE_ESCAPE_LEN })\n }\n }\n value += String.fromCharCode(parseInt(hex, 16))\n i += UNICODE_ESCAPE_LEN\n }\n break\n }\n default: value += escaped; break\n }\n } else {\n value += source[i]\n }\n i++\n }\n if (i >= source.length) {\n throw new ExpressionError('Unterminated string', { source, start, end: i })\n }\n i++ // skip closing quote\n tokens.push({ type: 'String', value, start, end: i })\n continue\n }\n\n // Template literals\n if (ch === '`') {\n const start = i\n i++ // skip opening backtick\n let raw = '`'\n while (i < source.length && source[i] !== '`') {\n if (source[i] === '$' && i + 1 < source.length && source[i + 1] === '{') {\n raw += '${'\n i += 2\n let depth = 1\n while (i < source.length && depth > 0) {\n const ic = source[i]\n // Skip string literals inside interpolations\n if (ic === '\"' || ic === \"'\") {\n raw += ic\n i++\n while (i < source.length && source[i] !== ic) {\n if (source[i] === '\\\\' && i + 1 < source.length) {\n raw += source[i] + source[i + 1]\n i += 2\n continue\n }\n raw += source[i]\n i++\n }\n if (i < source.length) {\n raw += source[i] // closing quote\n i++\n }\n continue\n }\n if (ic === '{') depth++\n else if (ic === '}') depth--\n if (depth > 0) raw += ic\n i++\n }\n raw += '}'\n } else {\n raw += source[i]\n i++\n }\n }\n if (i >= source.length) {\n throw new ExpressionError('Unterminated template literal', { source, start, end: i })\n }\n raw += '`'\n i++ // skip closing backtick\n tokens.push({ type: 'TemplateLiteral', value: raw, start, end: i })\n continue\n }\n\n // Identifiers and keywords\n if (isAlpha(ch)) {\n const start = i\n while (i < source.length && isAlphaNumeric(source[i])) i++\n const value = source.slice(start, i)\n const kw = KEYWORDS.get(value)\n if (kw) {\n tokens.push({ type: kw.type, value: kw.value, start, end: i } as Token)\n } else {\n tokens.push({ type: 'Identifier', value, start, end: i })\n }\n continue\n }\n\n // Multi-character operators and punctuation\n const start = i\n\n // Spread operator ...\n if (ch === '.' && i + 2 < source.length && source[i + 1] === '.' && source[i + 2] === '.') {\n tokens.push({ type: 'Spread', value: '...', start, end: i + 3 })\n i += 3\n continue\n }\n\n // Dot (punctuation)\n if (ch === '.') {\n tokens.push({ type: 'Punctuation', value: '.', start, end: i + 1 })\n i++\n continue\n }\n\n // Pipe |> or logical ||\n if (ch === '|') {\n if (i + 1 < source.length && source[i + 1] === '>') {\n tokens.push({ type: 'Pipe', value: '|>', start, end: i + 2 })\n i += 2\n continue\n }\n if (i + 1 < source.length && source[i + 1] === '|') {\n tokens.push({ type: 'Operator', value: '||', start, end: i + 2 })\n i += 2\n continue\n }\n }\n\n // &&\n if (ch === '&' && i + 1 < source.length && source[i + 1] === '&') {\n tokens.push({ type: 'Operator', value: '&&', start, end: i + 2 })\n i += 2\n continue\n }\n\n // == !=\n if (ch === '=' && i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '==', start, end: i + 2 })\n i += 2\n continue\n }\n if (ch === '!' && i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '!=', start, end: i + 2 })\n i += 2\n continue\n }\n\n // ! (logical not) - must come after != check\n if (ch === '!') {\n tokens.push({ type: 'Operator', value: '!', start, end: i + 1 })\n i++\n continue\n }\n\n // <= >= < >\n if (ch === '<') {\n if (i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '<=', start, end: i + 2 })\n i += 2\n continue\n }\n tokens.push({ type: 'Operator', value: '<', start, end: i + 1 })\n i++\n continue\n }\n if (ch === '>') {\n if (i + 1 < source.length && source[i + 1] === '=') {\n tokens.push({ type: 'Operator', value: '>=', start, end: i + 2 })\n i += 2\n continue\n }\n tokens.push({ type: 'Operator', value: '>', start, end: i + 1 })\n i++\n continue\n }\n\n // ** (exponentiation) — must come before single *\n if (ch === '*' && i + 1 < source.length && source[i + 1] === '*') {\n tokens.push({ type: 'Operator', value: '**', start, end: i + 2 })\n i += 2\n continue\n }\n\n // ?. (optional chaining) or ?? (nullish coalescing) or ? (ternary)\n if (ch === '?') {\n if (i + 1 < source.length && source[i + 1] === '.') {\n tokens.push({ type: 'OptionalChain', value: '?.', start, end: i + 2 })\n i += 2\n continue\n }\n if (i + 1 < source.length && source[i + 1] === '?') {\n tokens.push({ type: 'NullishCoalescing', value: '??', start, end: i + 2 })\n i += 2\n continue\n }\n tokens.push({ type: 'Punctuation', value: '?', start, end: i + 1 })\n i++\n continue\n }\n\n // Single-character operators\n if (ch === '+' || ch === '-' || ch === '*' || ch === '/' || ch === '%') {\n tokens.push({ type: 'Operator', value: ch, start, end: i + 1 })\n i++\n continue\n }\n\n // Punctuation\n if ('()[]{},:'.includes(ch)) {\n tokens.push({ type: 'Punctuation', value: ch as PunctuationValue, start, end: i + 1 })\n i++\n continue\n }\n\n throw new ExpressionError(`Unexpected character \"${ch}\"`, { source, start, end: i + 1 })\n }\n\n tokens.push({ type: 'EOF', value: '', start: i, end: i })\n return tokens\n}\n","import type { BonsaiOptions } from './types.js'\nimport { BonsaiSecurityError } from './errors.js'\n\n/**\n * Discriminates how a property name is being accessed so security checks can\n * apply different rules (e.g. root identifiers bypass allow/deny lists).\n */\nexport type AccessKind = 'identifier' | 'member' | 'method' | 'object-key'\n\nexport const BLOCKED_PROPERTIES: ReadonlySet<string> = new Set([\n '__proto__', 'constructor', 'prototype',\n])\n\nconst MAX_INDEX_DIGITS = 10\n\nfunction isCanonicalIndex(key: string): boolean {\n if (key.length === 0 || key.length > MAX_INDEX_DIGITS) return false\n const n = Number(key)\n return Number.isInteger(n) && n >= 0 && String(n) === key\n}\n\nconst TIMEOUT_CHECK_INTERVAL = 1000\nconst DEFAULT_MAX_DEPTH = 100\nconst DEFAULT_MAX_ARRAY_LENGTH = 100_000\n\n/** Immutable per-instance security configuration derived from BonsaiOptions. */\nexport class SecurityPolicy {\n readonly maxDepth: number\n readonly maxArrayLength: number\n readonly timeout: number\n readonly allowedProperties?: ReadonlySet<string>\n readonly deniedProperties?: ReadonlySet<string>\n\n constructor(options: BonsaiOptions = {}) {\n this.maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH\n this.maxArrayLength = options.maxArrayLength ?? DEFAULT_MAX_ARRAY_LENGTH\n this.timeout = options.timeout ?? 0\n this.allowedProperties = options.allowedProperties\n ? new Set(options.allowedProperties)\n : undefined\n this.deniedProperties = options.deniedProperties\n ? new Set(options.deniedProperties)\n : undefined\n }\n}\n\n/** Mutable per-evaluation state: tracks depth, step count, and deadline for a single evaluation. */\nexport class ExecutionContext {\n private stepCount = 0\n private depth = 0\n private deadline: number\n readonly policy: SecurityPolicy\n private readonly now: () => number\n\n constructor(policy: SecurityPolicy, now: () => number = Date.now) {\n this.policy = policy\n this.now = now\n this.deadline = policy.timeout ? now() + policy.timeout : 0\n }\n\n /** Reset mutable state for reuse. Avoids allocating a new instance per evaluation. */\n reset(): void {\n this.stepCount = 0\n this.depth = 0\n this.deadline = this.policy.timeout ? this.now() + this.policy.timeout : 0\n }\n\n step(): void {\n if (this.deadline) {\n this.stepCount++\n if (this.stepCount % TIMEOUT_CHECK_INTERVAL === 0) {\n this.checkTimeout()\n }\n }\n }\n\n checkTimeout(): void {\n if (this.deadline && this.now() >= this.deadline) {\n throw new BonsaiSecurityError(\n 'TIMEOUT',\n `Expression timeout: exceeded ${this.policy.timeout}ms`,\n )\n }\n }\n\n enterDepth(): void {\n if (++this.depth > this.policy.maxDepth) {\n throw new BonsaiSecurityError(\n 'MAX_DEPTH',\n `Maximum expression depth (${this.policy.maxDepth}) exceeded`,\n )\n }\n }\n\n exitDepth(): void {\n this.depth--\n }\n\n withDepth<T>(fn: () => T): T {\n this.enterDepth()\n try {\n return fn()\n } finally {\n this.exitDepth()\n }\n }\n\n checkNameAccess(key: string, kind: AccessKind): void {\n if (BLOCKED_PROPERTIES.has(key)) {\n throw new BonsaiSecurityError(\n 'BLOCKED_PROPERTY',\n `Blocked: access to \"${key}\" is not allowed`,\n )\n }\n\n if (kind === 'identifier' || kind === 'object-key') return\n\n if (isCanonicalIndex(key)) return\n\n if (this.policy.allowedProperties) {\n if (!this.policy.allowedProperties.has(key)) {\n throw new BonsaiSecurityError(\n 'PROPERTY_NOT_ALLOWED',\n `Blocked: \"${key}\" is not in allowed properties`,\n )\n }\n }\n\n if (this.policy.deniedProperties) {\n if (this.policy.deniedProperties.has(key)) {\n throw new BonsaiSecurityError(\n 'PROPERTY_DENIED',\n `Blocked: \"${key}\" is in denied properties`,\n )\n }\n }\n }\n\n checkArrayLength(length: number): void {\n if (length > this.policy.maxArrayLength) {\n throw new BonsaiSecurityError(\n 'MAX_ARRAY_LENGTH',\n `Array length (${length}) exceeds maximum (${this.policy.maxArrayLength})`,\n )\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as BonsaiPlugin, c as ExpressionReferences, d as PolicySnapshot, f as ResolveResult, g as ValidationResult, h as TransformFn, i as BonsaiOptions, l as FunctionFn, m as TokenType, n as BonsaiContext, o as CompiledExpression, p as Token, r as BonsaiInstance, s as ContextFunctionFn, t as ASTNode, u as InferredTypeName } from "./types-C58c6SlH.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/errors.d.ts
|
|
4
4
|
interface ErrorLocation {
|
|
@@ -62,16 +62,28 @@ declare function compile(ast: ASTNode): ASTNode;
|
|
|
62
62
|
declare function evaluateExpression<T = unknown>(expression: string, context?: Record<string, unknown>): T;
|
|
63
63
|
/**
|
|
64
64
|
* Create a new Bonsai instance with optional safety and caching configuration.
|
|
65
|
-
* Register transforms and functions via `.use()`, `.addTransform()`,
|
|
65
|
+
* Register transforms and functions via `.use()`, `.addTransform()`, `.addFunction()`,
|
|
66
|
+
* and `.addContextFunction()`.
|
|
67
|
+
*
|
|
68
|
+
* Pass a context type argument to get end-to-end type safety on the evaluation
|
|
69
|
+
* context: `bonsai<MyContext>()` will type-check `evaluate(expr, ctx)` calls,
|
|
70
|
+
* require a context argument when `MyContext` has required fields, and
|
|
71
|
+
* propagate `MyContext` into context-aware function signatures.
|
|
66
72
|
*
|
|
67
73
|
* @example
|
|
68
74
|
* ```ts
|
|
69
75
|
* const expr = bonsai({ timeout: 50 })
|
|
70
76
|
* expr.use(strings)
|
|
71
77
|
* expr.evaluateSync('name |> trim |> upper', { name: ' hello ' }) // "HELLO"
|
|
78
|
+
*
|
|
79
|
+
* type AppCtx = { userId: string; perms: string[] }
|
|
80
|
+
* const app = bonsai<AppCtx>()
|
|
81
|
+
* app.addContextFunction('hasPermission', (ctx, action) =>
|
|
82
|
+
* ctx.perms.includes(String(action)))
|
|
83
|
+
* app.evaluateSync('hasPermission("write")', { userId: 'u_1', perms: ['write'] })
|
|
72
84
|
* ```
|
|
73
85
|
*/
|
|
74
|
-
declare function bonsai(options?: BonsaiOptions): BonsaiInstance
|
|
86
|
+
declare function bonsai<TCtx extends BonsaiContext = Record<string, unknown>>(options?: BonsaiOptions): BonsaiInstance<TCtx>;
|
|
75
87
|
//#endregion
|
|
76
|
-
export { type ASTNode, type BonsaiInstance, type BonsaiOptions, type BonsaiPlugin, BonsaiReferenceError, BonsaiSecurityError, BonsaiTypeError, type CompiledExpression, ExpressionError, type ExpressionReferences, type FunctionFn, type InferredTypeName, type PolicySnapshot, type ResolveResult, type Token, type TokenType, type TransformFn, type ValidationResult, bonsai, compile, evaluateExpression, formatBonsaiError, formatError, parse, tokenize };
|
|
88
|
+
export { type ASTNode, type BonsaiInstance, type BonsaiOptions, type BonsaiPlugin, BonsaiReferenceError, BonsaiSecurityError, BonsaiTypeError, type CompiledExpression, type ContextFunctionFn, ExpressionError, type ExpressionReferences, type FunctionFn, type InferredTypeName, type PolicySnapshot, type ResolveResult, type Token, type TokenType, type TransformFn, type ValidationResult, bonsai, compile, evaluateExpression, formatBonsaiError, formatError, parse, tokenize };
|
|
77
89
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/lexer.ts","../src/parser.ts","../src/compiler.ts","../src/index.ts"],"mappings":";;;UAIiB,aAAA;EACf,MAAA;EACA,KAAA;EACA,GAAA;AAAA;;cAIW,eAAA,SAAwB,KAAA;EAAA,SAC1B,UAAA;EAAA,SACA,KAAA;EAAA,SACA,GAAA;EAAA,SACA,MAAA;EAAA,SACA,UAAA;cAGP,OAAA,UACA,QAAA,EAAU,aAAA,EACV,UAAA;AAAA;;cAcS,eAAA,SAAwB,KAAA;EAAA,SAC1B,SAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;EACT,QAAA,GAAW,aAAA;EACX,SAAA;cAEY,SAAA,UAAmB,QAAA,UAAkB,KAAA;AAAA;;cAetC,mBAAA,SAA4B,KAAA;EAAA,SAC9B,IAAA;EACT,QAAA,GAAW,aAAA;EACX,SAAA;cAEY,IAAA,UAAc,OAAA;AAAA;;cAQf,oBAAA,SAA6B,KAAA;EAAA,SAC/B,UAAA;EAAA,SACA,IAAA;EAAA,SACA,UAAA;EACT,QAAA,GAAW,aAAA;EACX,SAAA;cAEY,IAAA,uCAA2C,UAAA,UAAoB,UAAA;AAAA;;iBAmE7D,WAAA,CACd,OAAA,UACA,QAAA,EAAU,aAAA,EACV,UAAA;;iBA6Bc,iBAAA,CAAkB,KAAA;;;iBCpJlB,QAAA,CAAS,MAAA,WAAiB,KAAA;;;iBCS1B,KAAA,CAAM,MAAA,UAAgB,MAAA,YAAa,OAAA;;;iBCnCnC,OAAA,CAAQ,GAAA,EAAK,OAAA,GAAU,OAAA;;;;;;;iBCiDvB,kBAAA,aAAA,CAAgC,UAAA,UAAoB,OAAA,GAAU,MAAA,oBAA0B,CAAA;;AJxCxG
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/lexer.ts","../src/parser.ts","../src/compiler.ts","../src/index.ts"],"mappings":";;;UAIiB,aAAA;EACf,MAAA;EACA,KAAA;EACA,GAAA;AAAA;;cAIW,eAAA,SAAwB,KAAA;EAAA,SAC1B,UAAA;EAAA,SACA,KAAA;EAAA,SACA,GAAA;EAAA,SACA,MAAA;EAAA,SACA,UAAA;cAGP,OAAA,UACA,QAAA,EAAU,aAAA,EACV,UAAA;AAAA;;cAcS,eAAA,SAAwB,KAAA;EAAA,SAC1B,SAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;EACT,QAAA,GAAW,aAAA;EACX,SAAA;cAEY,SAAA,UAAmB,QAAA,UAAkB,KAAA;AAAA;;cAetC,mBAAA,SAA4B,KAAA;EAAA,SAC9B,IAAA;EACT,QAAA,GAAW,aAAA;EACX,SAAA;cAEY,IAAA,UAAc,OAAA;AAAA;;cAQf,oBAAA,SAA6B,KAAA;EAAA,SAC/B,UAAA;EAAA,SACA,IAAA;EAAA,SACA,UAAA;EACT,QAAA,GAAW,aAAA;EACX,SAAA;cAEY,IAAA,uCAA2C,UAAA,UAAoB,UAAA;AAAA;;iBAmE7D,WAAA,CACd,OAAA,UACA,QAAA,EAAU,aAAA,EACV,UAAA;;iBA6Bc,iBAAA,CAAkB,KAAA;;;iBCpJlB,QAAA,CAAS,MAAA,WAAiB,KAAA;;;iBCS1B,KAAA,CAAM,MAAA,UAAgB,MAAA,YAAa,OAAA;;;iBCnCnC,OAAA,CAAQ,GAAA,EAAK,OAAA,GAAU,OAAA;;;;;;;iBCiDvB,kBAAA,aAAA,CAAgC,UAAA,UAAoB,OAAA,GAAU,MAAA,oBAA0B,CAAA;;AJxCxG;;;;;;;;;;;;;;;;;AAwBA;;;;;iBI4CgB,MAAA,cAAoB,aAAA,GAAgB,MAAA,kBAAA,CAClD,OAAA,GAAS,aAAA,GACR,cAAA,CAAe,IAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as attachLocation, c as offsetToPosition, i as ExpressionError, l as suggest, n as BonsaiSecurityError, o as formatBonsaiError, r as BonsaiTypeError, s as formatError, t as BonsaiReferenceError } from "./errors-
|
|
2
|
-
import { i as tokenize, n as ExecutionContext, r as SecurityPolicy } from "./execution-context-
|
|
1
|
+
import { a as attachLocation, c as offsetToPosition, i as ExpressionError, l as suggest, n as BonsaiSecurityError, o as formatBonsaiError, r as BonsaiTypeError, s as formatError, t as BonsaiReferenceError } from "./errors-BONB6P_5.mjs";
|
|
2
|
+
import { i as tokenize, n as ExecutionContext, r as SecurityPolicy } from "./execution-context-B-5hQ92X.mjs";
|
|
3
3
|
//#region src/parser.ts
|
|
4
4
|
const PIPE_PRECEDENCE = 5;
|
|
5
5
|
const PRECEDENCE = {
|
|
@@ -259,7 +259,7 @@ function parse(source, _depth = 0) {
|
|
|
259
259
|
const tok = current();
|
|
260
260
|
if (tok.type === "Number") {
|
|
261
261
|
advance();
|
|
262
|
-
const raw = tok.value.replace(/_/
|
|
262
|
+
const raw = tok.value.replace(/_/gu, "");
|
|
263
263
|
return {
|
|
264
264
|
type: "NumberLiteral",
|
|
265
265
|
value: Number(raw),
|
|
@@ -883,9 +883,16 @@ function resolveTransform(name, transforms) {
|
|
|
883
883
|
if (!Object.hasOwn(transforms, name)) throw new BonsaiReferenceError("transform", name, suggest(name, Object.keys(transforms)));
|
|
884
884
|
return transforms[name];
|
|
885
885
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
886
|
+
/**
|
|
887
|
+
* Resolve a callable by name in the shared function namespace. Pure and context
|
|
888
|
+
* functions occupy one registry keyed by name, so a single lookup returns the
|
|
889
|
+
* tagged entry; the call site uses `kind` to decide how to invoke it (pure
|
|
890
|
+
* functions take only call args; context functions receive the evaluation
|
|
891
|
+
* context first, then call args).
|
|
892
|
+
*/
|
|
893
|
+
function resolveCallable(name, functions) {
|
|
894
|
+
if (Object.hasOwn(functions, name)) return functions[name];
|
|
895
|
+
throw new BonsaiReferenceError("function", name, suggest(name, Object.keys(functions)));
|
|
889
896
|
}
|
|
890
897
|
function getIdentifierName(node, message = "Expected identifier") {
|
|
891
898
|
if (node.type !== "Identifier") throw new Error(message);
|
|
@@ -936,11 +943,11 @@ function rejectPromise(value, kind, name) {
|
|
|
936
943
|
if (value instanceof Promise) throw new BonsaiTypeError(name, `a synchronous ${kind} result — use evaluate() instead of evaluateSync() for async`, value);
|
|
937
944
|
return value;
|
|
938
945
|
}
|
|
939
|
-
function evaluate(node, context,
|
|
946
|
+
function evaluate(node, context, bindings, guard, source) {
|
|
940
947
|
return evalNode(node, {
|
|
941
948
|
ctx: context,
|
|
942
|
-
tr: transforms,
|
|
943
|
-
fn: functions,
|
|
949
|
+
tr: bindings.transforms,
|
|
950
|
+
fn: bindings.functions,
|
|
944
951
|
g: guard,
|
|
945
952
|
s: source
|
|
946
953
|
});
|
|
@@ -1062,10 +1069,10 @@ function evalCallExpression(node, env) {
|
|
|
1062
1069
|
}
|
|
1063
1070
|
}
|
|
1064
1071
|
if (node.callee.type === "Identifier") try {
|
|
1065
|
-
const
|
|
1072
|
+
const resolved = resolveCallable(node.callee.name, fn);
|
|
1066
1073
|
const args = [];
|
|
1067
1074
|
for (const arg of node.args) pushCallArgument(args, arg, env);
|
|
1068
|
-
return rejectPromise(
|
|
1075
|
+
return rejectPromise(resolved.kind === "context" ? resolved.fn(env.ctx, ...args) : resolved.fn(...args), "function", node.callee.name);
|
|
1069
1076
|
} catch (e) {
|
|
1070
1077
|
if (s) attachLocation(e, s, node.start, node.end);
|
|
1071
1078
|
throw e;
|
|
@@ -1175,11 +1182,11 @@ function makeLambdaAccessor$1(property, guard) {
|
|
|
1175
1182
|
}
|
|
1176
1183
|
//#endregion
|
|
1177
1184
|
//#region src/evaluator-async.ts
|
|
1178
|
-
async function evaluateAsync(node, context,
|
|
1185
|
+
async function evaluateAsync(node, context, bindings, guard, source) {
|
|
1179
1186
|
const result = await evalNodeAsync(node, {
|
|
1180
1187
|
ctx: context,
|
|
1181
|
-
tr: transforms,
|
|
1182
|
-
fn: functions,
|
|
1188
|
+
tr: bindings.transforms,
|
|
1189
|
+
fn: bindings.functions,
|
|
1183
1190
|
g: guard,
|
|
1184
1191
|
s: source
|
|
1185
1192
|
});
|
|
@@ -1314,10 +1321,10 @@ async function evalCallExpressionAsync(node, env) {
|
|
|
1314
1321
|
}
|
|
1315
1322
|
}
|
|
1316
1323
|
if (node.callee.type === "Identifier") try {
|
|
1317
|
-
const
|
|
1324
|
+
const resolved = resolveCallable(node.callee.name, fn);
|
|
1318
1325
|
const args = [];
|
|
1319
1326
|
for (const arg of node.args) await pushCallArgumentAsync(args, arg, env);
|
|
1320
|
-
const result = await
|
|
1327
|
+
const result = resolved.kind === "context" ? await resolved.fn(env.ctx, ...args) : await resolved.fn(...args);
|
|
1321
1328
|
g.checkTimeout();
|
|
1322
1329
|
return result;
|
|
1323
1330
|
} catch (e) {
|
|
@@ -1527,13 +1534,24 @@ function createPluginRegistry() {
|
|
|
1527
1534
|
let functionsCache = {};
|
|
1528
1535
|
let transformsDirty = false;
|
|
1529
1536
|
let functionsDirty = false;
|
|
1537
|
+
let bindingsCache;
|
|
1530
1538
|
const registry = {
|
|
1531
1539
|
addTransform(name, fn) {
|
|
1532
1540
|
transformMap.set(name, fn);
|
|
1533
1541
|
transformsDirty = true;
|
|
1534
1542
|
},
|
|
1535
1543
|
addFunction(name, fn) {
|
|
1536
|
-
functionMap.set(name,
|
|
1544
|
+
functionMap.set(name, {
|
|
1545
|
+
kind: "pure",
|
|
1546
|
+
fn
|
|
1547
|
+
});
|
|
1548
|
+
functionsDirty = true;
|
|
1549
|
+
},
|
|
1550
|
+
addContextFunction(name, fn) {
|
|
1551
|
+
functionMap.set(name, {
|
|
1552
|
+
kind: "context",
|
|
1553
|
+
fn
|
|
1554
|
+
});
|
|
1537
1555
|
functionsDirty = true;
|
|
1538
1556
|
},
|
|
1539
1557
|
removeTransform(name) {
|
|
@@ -1552,6 +1570,12 @@ function createPluginRegistry() {
|
|
|
1552
1570
|
getFunction(name) {
|
|
1553
1571
|
return functionMap.get(name);
|
|
1554
1572
|
},
|
|
1573
|
+
isContextFunction(name) {
|
|
1574
|
+
return functionMap.get(name)?.kind === "context";
|
|
1575
|
+
},
|
|
1576
|
+
hasFunction(name) {
|
|
1577
|
+
return functionMap.has(name);
|
|
1578
|
+
},
|
|
1555
1579
|
getTransformNames() {
|
|
1556
1580
|
return [...transformMap.keys()];
|
|
1557
1581
|
},
|
|
@@ -1574,6 +1598,15 @@ function createPluginRegistry() {
|
|
|
1574
1598
|
functionsDirty = false;
|
|
1575
1599
|
}
|
|
1576
1600
|
return functionsCache;
|
|
1601
|
+
},
|
|
1602
|
+
get bindings() {
|
|
1603
|
+
const t = registry.transforms;
|
|
1604
|
+
const f = registry.functions;
|
|
1605
|
+
if (!bindingsCache || bindingsCache.transforms !== t || bindingsCache.functions !== f) bindingsCache = {
|
|
1606
|
+
transforms: t,
|
|
1607
|
+
functions: f
|
|
1608
|
+
};
|
|
1609
|
+
return bindingsCache;
|
|
1577
1610
|
}
|
|
1578
1611
|
};
|
|
1579
1612
|
return registry;
|
|
@@ -1581,24 +1614,36 @@ function createPluginRegistry() {
|
|
|
1581
1614
|
//#endregion
|
|
1582
1615
|
//#region src/index.ts
|
|
1583
1616
|
const DEFAULT_CACHE_SIZE = 256;
|
|
1584
|
-
let
|
|
1617
|
+
let sharedInstance;
|
|
1585
1618
|
/**
|
|
1586
1619
|
* Evaluate a single expression with default options. Uses a shared instance internally.
|
|
1587
1620
|
* For repeated evaluation or custom configuration, use `bonsai()` to create a dedicated instance.
|
|
1588
1621
|
*/
|
|
1589
1622
|
function evaluateExpression(expression, context) {
|
|
1590
|
-
if (!
|
|
1591
|
-
return
|
|
1623
|
+
if (!sharedInstance) sharedInstance = bonsai();
|
|
1624
|
+
return sharedInstance.evaluateSync(expression, context);
|
|
1592
1625
|
}
|
|
1593
1626
|
/**
|
|
1594
1627
|
* Create a new Bonsai instance with optional safety and caching configuration.
|
|
1595
|
-
* Register transforms and functions via `.use()`, `.addTransform()`,
|
|
1628
|
+
* Register transforms and functions via `.use()`, `.addTransform()`, `.addFunction()`,
|
|
1629
|
+
* and `.addContextFunction()`.
|
|
1630
|
+
*
|
|
1631
|
+
* Pass a context type argument to get end-to-end type safety on the evaluation
|
|
1632
|
+
* context: `bonsai<MyContext>()` will type-check `evaluate(expr, ctx)` calls,
|
|
1633
|
+
* require a context argument when `MyContext` has required fields, and
|
|
1634
|
+
* propagate `MyContext` into context-aware function signatures.
|
|
1596
1635
|
*
|
|
1597
1636
|
* @example
|
|
1598
1637
|
* ```ts
|
|
1599
1638
|
* const expr = bonsai({ timeout: 50 })
|
|
1600
1639
|
* expr.use(strings)
|
|
1601
1640
|
* expr.evaluateSync('name |> trim |> upper', { name: ' hello ' }) // "HELLO"
|
|
1641
|
+
*
|
|
1642
|
+
* type AppCtx = { userId: string; perms: string[] }
|
|
1643
|
+
* const app = bonsai<AppCtx>()
|
|
1644
|
+
* app.addContextFunction('hasPermission', (ctx, action) =>
|
|
1645
|
+
* ctx.perms.includes(String(action)))
|
|
1646
|
+
* app.evaluateSync('hasPermission("write")', { userId: 'u_1', perms: ['write'] })
|
|
1602
1647
|
* ```
|
|
1603
1648
|
*/
|
|
1604
1649
|
function bonsai(options = {}) {
|
|
@@ -1625,15 +1670,16 @@ function bonsai(options = {}) {
|
|
|
1625
1670
|
const compiled = {
|
|
1626
1671
|
ast: optimized,
|
|
1627
1672
|
source,
|
|
1628
|
-
async evaluate(
|
|
1629
|
-
return evaluateAsync(optimized,
|
|
1673
|
+
async evaluate(...args) {
|
|
1674
|
+
return evaluateAsync(optimized, args[0] ?? {}, registry.bindings, createExecutionContext(), source);
|
|
1630
1675
|
},
|
|
1631
|
-
evaluateSync(
|
|
1632
|
-
|
|
1676
|
+
evaluateSync(...args) {
|
|
1677
|
+
const ctx = args[0] ?? {};
|
|
1678
|
+
if (syncCtxInUse) return evaluate(optimized, ctx, registry.bindings, createExecutionContext(), source);
|
|
1633
1679
|
syncCtxInUse = true;
|
|
1634
1680
|
try {
|
|
1635
1681
|
syncCtx.reset();
|
|
1636
|
-
return evaluate(optimized,
|
|
1682
|
+
return evaluate(optimized, ctx, registry.bindings, syncCtx, source);
|
|
1637
1683
|
} finally {
|
|
1638
1684
|
syncCtxInUse = false;
|
|
1639
1685
|
}
|
|
@@ -1655,6 +1701,10 @@ function bonsai(options = {}) {
|
|
|
1655
1701
|
registry.addFunction(name, fn);
|
|
1656
1702
|
return instance;
|
|
1657
1703
|
},
|
|
1704
|
+
addContextFunction(name, fn) {
|
|
1705
|
+
registry.addContextFunction(name, fn);
|
|
1706
|
+
return instance;
|
|
1707
|
+
},
|
|
1658
1708
|
removeTransform(name) {
|
|
1659
1709
|
return registry.removeTransform(name);
|
|
1660
1710
|
},
|
|
@@ -1665,7 +1715,10 @@ function bonsai(options = {}) {
|
|
|
1665
1715
|
return registry.getTransform(name) !== void 0;
|
|
1666
1716
|
},
|
|
1667
1717
|
hasFunction(name) {
|
|
1668
|
-
return registry.
|
|
1718
|
+
return registry.hasFunction(name);
|
|
1719
|
+
},
|
|
1720
|
+
isContextFunction(name) {
|
|
1721
|
+
return registry.isContextFunction(name);
|
|
1669
1722
|
},
|
|
1670
1723
|
listTransforms() {
|
|
1671
1724
|
return registry.getTransformNames();
|
|
@@ -1686,16 +1739,17 @@ function bonsai(options = {}) {
|
|
|
1686
1739
|
compile(expression) {
|
|
1687
1740
|
return compileExpr(expression);
|
|
1688
1741
|
},
|
|
1689
|
-
async evaluate(expression,
|
|
1690
|
-
return evaluateAsync(getAst(expression),
|
|
1742
|
+
async evaluate(expression, ...args) {
|
|
1743
|
+
return evaluateAsync(getAst(expression), args[0] ?? {}, registry.bindings, createExecutionContext(), expression);
|
|
1691
1744
|
},
|
|
1692
|
-
evaluateSync(expression,
|
|
1745
|
+
evaluateSync(expression, ...args) {
|
|
1693
1746
|
const ast = getAst(expression);
|
|
1694
|
-
|
|
1747
|
+
const ctx = args[0] ?? {};
|
|
1748
|
+
if (syncCtxInUse) return evaluate(ast, ctx, registry.bindings, createExecutionContext(), expression);
|
|
1695
1749
|
syncCtxInUse = true;
|
|
1696
1750
|
try {
|
|
1697
1751
|
syncCtx.reset();
|
|
1698
|
-
return evaluate(ast,
|
|
1752
|
+
return evaluate(ast, ctx, registry.bindings, syncCtx, expression);
|
|
1699
1753
|
} finally {
|
|
1700
1754
|
syncCtxInUse = false;
|
|
1701
1755
|
}
|