bonsai-js 0.4.0 → 0.5.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 +22 -0
- package/README.md +17 -6
- package/dist/autocomplete/index.d.mts +1 -1
- package/dist/autocomplete/index.d.mts.map +1 -1
- package/dist/autocomplete/index.mjs +16 -55
- package/dist/autocomplete/index.mjs.map +1 -1
- package/dist/{errors-BONB6P_5.mjs → errors-CCZDd1gK.mjs} +1 -1
- package/dist/errors-CCZDd1gK.mjs.map +1 -0
- package/dist/{execution-context-B-5hQ92X.mjs → execution-context-b6FBfEdv.mjs} +147 -16
- package/dist/execution-context-b6FBfEdv.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +144 -81
- package/dist/index.mjs.map +1 -1
- package/dist/stdlib/index.d.mts +1 -1
- package/dist/stdlib/index.d.mts.map +1 -1
- package/dist/stdlib/index.mjs +43 -11
- package/dist/stdlib/index.mjs.map +1 -1
- package/dist/{types-C58c6SlH.d.mts → types-DkXfy5IJ.d.mts} +4 -2
- package/dist/types-DkXfy5IJ.d.mts.map +1 -0
- package/package.json +37 -11
- package/dist/errors-BONB6P_5.mjs.map +0 -1
- package/dist/execution-context-B-5hQ92X.mjs.map +0 -1
- package/dist/types-C58c6SlH.d.mts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-06-02
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- Bounded parser recursion: pathologically nested input (deep parentheses, unary chains) now fails closed with a typed `ExpressionError` ("Maximum expression nesting depth exceeded") instead of overflowing the native call stack with an uncaught `RangeError`. `validate()` reports it as a normal syntax error rather than a stack-overflow message.
|
|
15
|
+
- `maxArrayLength` is now enforced on array-returning methods (`split`, `map`, `flatMap`, `flat`, `concat`, `toSorted`, `toReversed`, `with`, `toSpliced`), not just array literals and spread, in both the sync and async evaluators.
|
|
16
|
+
- Added `maxStringLength` (default 100,000) and enforced it as a ceiling on every string produced by a method call, in both the sync and async evaluators. `padStart`/`padEnd`/`repeat` are checked before allocation (capping the produced length, not just the repeat count); all other string-returning methods (`join`, `concat`, `slice`, `toUpperCase`, ...) are checked on their output. This closes single-operation amplifiers such as `arr.join(sep)`, whose output length is array length times separator length in one native call that the cooperative timeout cannot interrupt.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `maxStringLength` option on `bonsai(options)` and a `BonsaiSecurityError('MAX_STRING_LENGTH', ...)` error code.
|
|
21
|
+
- `bonsai(options)` now validates its options and throws a `RangeError`/`TypeError` at construction for out-of-range values (negative `cacheSize`, non-positive `maxDepth`, negative `maxArrayLength`/`maxStringLength`, negative or non-finite `timeout`, allow/deny lists that are not arrays of strings). Previously a negative `cacheSize` silently disabled caching and other bad values failed later with confusing errors.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Async/sync parity for higher-order array methods (`map`, `filter`, `find`, `some`, `every`, `findIndex`, `flatMap`). Predicates now evaluate sequentially in the async evaluator, matching the synchronous one: `some`/`every`/`find`/`findIndex` short-circuit at the first decisive element, and `maxDepth` is no longer inflated by array length (an expression that succeeded with `evaluateSync` could previously throw a spurious `Maximum expression depth` error with `evaluate`).
|
|
26
|
+
- Malformed numeric literals now raise a parse-time `ExpressionError` instead of silently evaluating to `NaN` or coercing: a base prefix or exponent with no digits (`1e`, `1e+`, `0x`, `0b`, `0o`), and misplaced numeric separators (`1_`, `1__0`, `0xff_`), which are rejected the way JavaScript rejects them.
|
|
27
|
+
- Template interpolations containing string literals or nested template literals with braces (for example `` `${ "x}" }` `` or `` `${ `a}b` }` ``) now parse correctly instead of failing with an "Unterminated string"/"Unterminated template literal" error.
|
|
28
|
+
- Higher-order array helpers now fail loud on a non-function callback instead of silently doing the wrong thing. The lambda shorthand (`.x`, `. > 0`) is a function value; passing it to a function (for example `map(inc(.x))`) evaluates to a plain value, which previously made the stdlib `map`/`filter`/`find`/`some`/`every` transforms silently return the input unchanged and the `.map`/`.filter`/`.find`/`.findIndex`/`.some`/`.every`/`.flatMap` methods throw a raw `TypeError`. Both now throw a typed `BonsaiTypeError` naming the helper. No-argument forms (`|> map`, `|> filter`) are unaffected.
|
|
29
|
+
- Autocomplete no longer suggests `array.toString`, which the evaluator rejects (the method allowlist and the autocomplete catalog now derive from one shared source, so they cannot drift). Method completions also respect `allowedProperties`, matching the evaluator, so a method that would be rejected at runtime is no longer offered.
|
|
30
|
+
- stdlib correctness: `formatDate` now replaces every occurrence of a token (not just the first, so `"YYYY/YYYY"` works); `min`/`max` validate their arguments are numbers and return `undefined` for no arguments instead of leaking `Infinity`/`-Infinity`; `clamp` rejects non-finite bounds and `min > max`; `sort` orders strings by code point for deterministic, locale-independent results. NaN passthrough in the numeric transforms is now documented.
|
|
31
|
+
|
|
10
32
|
## [0.4.0] - 2026-06-02
|
|
11
33
|
|
|
12
34
|
### Added
|
package/README.md
CHANGED
|
@@ -203,6 +203,8 @@ Inside array methods, `.` refers to the current item:
|
|
|
203
203
|
|
|
204
204
|
Compound predicates work too: `.age >= 18 && .active`
|
|
205
205
|
|
|
206
|
+
A lambda is built from the accessor plus operators, member access, and methods. The shorthand is itself a function value, so it cannot be passed *into* another function call: `map(myFn(.x))` does not mean `map(item => myFn(item.x))` and will throw a `BonsaiTypeError`. To transform each item through a function, chain instead: `items |> map(.x) |> map(myFn)` (or `items.map(.x).map(myFn)`).
|
|
207
|
+
|
|
206
208
|
### Pipe transforms (via stdlib)
|
|
207
209
|
|
|
208
210
|
```ts
|
|
@@ -296,13 +298,15 @@ const expr = bonsai(options?: BonsaiOptions)
|
|
|
296
298
|
| --- | --- | --- | --- |
|
|
297
299
|
| `timeout` | `number` | `0` | Evaluation timeout in milliseconds. `0` disables the timeout check. |
|
|
298
300
|
| `maxDepth` | `number` | `100` | Maximum evaluation depth before throwing `BonsaiSecurityError('MAX_DEPTH', ...)`. |
|
|
299
|
-
| `maxArrayLength` | `number` | `100000` | Maximum array
|
|
300
|
-
| `
|
|
301
|
+
| `maxArrayLength` | `number` | `100000` | Maximum array size produced during evaluation, including array literals, expanded spread, and array-returning methods (`split`, `map`, `flat`, `concat`, ...). Exceeding it throws `BonsaiSecurityError('MAX_ARRAY_LENGTH', ...)`. |
|
|
302
|
+
| `maxStringLength` | `number` | `100000` | Maximum string size produced by a string-returning method (`padStart`, `padEnd`, `repeat`, `join`, `concat`, `slice`, ...). Applies to the produced length (e.g. `arr.join(sep)` is bounded by its full output, not just the inputs). Exceeding it throws `BonsaiSecurityError('MAX_STRING_LENGTH', ...)`. |
|
|
303
|
+
| `cacheSize` | `number` | `256` | Per-instance cache size for compiled expressions and parsed AST reuse. `0` disables caching. |
|
|
301
304
|
| `allowedProperties` | `string[]` | `undefined` | Whitelist of allowed member/method names. Does not apply to root identifiers or object-literal keys. |
|
|
302
305
|
| `deniedProperties` | `string[]` | `undefined` | Denylist of blocked member/method names. Does not apply to root identifiers or object-literal keys. |
|
|
303
306
|
|
|
304
307
|
Important notes:
|
|
305
308
|
|
|
309
|
+
- Options are validated at construction: out-of-range values (a negative `cacheSize`, a non-positive `maxDepth`, a negative size limit, a negative/non-finite `timeout`) throw a `RangeError`/`TypeError` immediately rather than failing silently later.
|
|
306
310
|
- `allowedProperties` and `deniedProperties` apply to **member access** (`obj.name`) and **method calls** (`str.slice()`), not root identifiers (`name`) or object-literal keys (`{ name: value }`).
|
|
307
311
|
- If you whitelist `user.name`, you must allow both `user` and `name` as member names.
|
|
308
312
|
- Numeric array indices (e.g., `items[0]`) bypass allow/deny lists automatically.
|
|
@@ -530,6 +534,12 @@ Modules:
|
|
|
530
534
|
| `dates` | function `now`; transforms `formatDate`, `diffDays` |
|
|
531
535
|
| `all` | registers every stdlib module above |
|
|
532
536
|
|
|
537
|
+
Notes on stdlib semantics:
|
|
538
|
+
|
|
539
|
+
- **`sort`** orders strings by code point (not locale), so results are deterministic across runtimes and locales. Numbers sort numerically.
|
|
540
|
+
- **`min`/`max`** validate that every argument is a number and return `undefined` for no arguments (rather than `Infinity`/`-Infinity`). **`clamp`** requires finite bounds with `min <= max`.
|
|
541
|
+
- **`NaN` is passed through** by the numeric transforms (`sum`, `avg`, `round`, etc.): a `NaN` input yields a `NaN` result (which serializes to `null` in JSON). Validate inputs upstream if you need to reject non-finite numbers.
|
|
542
|
+
|
|
533
543
|
## Error Handling
|
|
534
544
|
|
|
535
545
|
Runtime exports:
|
|
@@ -592,7 +602,8 @@ Bonsai is designed to safely evaluate expressions, but it is not a process sandb
|
|
|
592
602
|
What Bonsai does:
|
|
593
603
|
|
|
594
604
|
- blocks access to `__proto__`, `constructor`, and `prototype` at every access level, even if explicitly allowed
|
|
595
|
-
- enforces `maxDepth`, `maxArrayLength`, and optional `timeout`
|
|
605
|
+
- enforces `maxDepth`, `maxArrayLength`, `maxStringLength`, and optional `timeout`
|
|
606
|
+
- bounds parser recursion so pathologically nested input fails closed with a typed `ExpressionError` instead of overflowing the call stack
|
|
596
607
|
- lets you allowlist or denylist member/method names via `allowedProperties`/`deniedProperties`
|
|
597
608
|
- prevents expressions from reaching globals or importing modules
|
|
598
609
|
- looks up root identifiers via own-property checks only (`Object.hasOwn`), so context prototype chains cannot leak
|
|
@@ -604,7 +615,7 @@ What Bonsai does:
|
|
|
604
615
|
Important operational caveats:
|
|
605
616
|
|
|
606
617
|
- `allowedProperties` and `deniedProperties` apply to member access (`obj.name`) and method calls (`str.slice()`), not root identifiers (`name`) or object-literal keys (`{ name: value }`)
|
|
607
|
-
- `timeout` is cooperative and checked
|
|
618
|
+
- `timeout` is cooperative and checked at evaluator step boundaries (not inside a single native operation); it cannot forcibly interrupt arbitrary synchronous code inside your own custom transforms/functions, so the size limits (`maxArrayLength`, `maxStringLength`) are the primary guard against single-operation resource exhaustion
|
|
608
619
|
- async transforms/functions are bounded only at awaited boundaries
|
|
609
620
|
- custom transforms/functions/plugins are trusted host code
|
|
610
621
|
|
|
@@ -785,8 +796,8 @@ interface Completion {
|
|
|
785
796
|
|
|
786
797
|
Bonsai follows SemVer for the documented package entrypoints `bonsai-js`, `bonsai-js/stdlib`, and `bonsai-js/autocomplete`.
|
|
787
798
|
|
|
788
|
-
- Supported runtimes are Node
|
|
789
|
-
- The packed npm artifact is smoke-tested on Node
|
|
799
|
+
- Supported runtimes are Node 22+ and current Bun releases.
|
|
800
|
+
- The packed npm artifact is smoke-tested on Node 22 and 24.
|
|
790
801
|
- Internal modules under `src/*` are not public API.
|
|
791
802
|
|
|
792
803
|
See [stability policy](./docs/stability-policy.md) for the compatibility boundary and release rules.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as BonsaiInstance, u as InferredTypeName } from "../types-
|
|
1
|
+
import { r as BonsaiInstance, u as InferredTypeName } from "../types-DkXfy5IJ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/autocomplete/tokenizer.d.ts
|
|
4
4
|
/** Known phases where errors can occur during completion. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/autocomplete/tokenizer.ts","../../src/autocomplete/completions.ts","../../src/autocomplete/index.ts"],"mappings":";;;;KAcY,eAAA;AAAA,KAOA,YAAA,IAAgB,KAAA,WAAgB,KAAA,EAAO,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/autocomplete/tokenizer.ts","../../src/autocomplete/completions.ts","../../src/autocomplete/index.ts"],"mappings":";;;;KAcY,eAAA;AAAA,KAOA,YAAA,IAAgB,KAAA,WAAgB,KAAA,EAAO,eAAe;;;KCd7D,gBAAA;EACC,UAAA;EAAwB,YAAA;AAAA;EACxB,UAAA;EAAoB,YAAA;AAAA;AAAA,UAEhB,cAAA;EACR,KAAA;EACA,IAAA;EACA,MAAA;EDO0C;ECL1C,YAAA;AAAA;AAAA,KAGU,UAAA,GAAa,cAAA,GAAiB,gBAAgB;;;UCTzC,mBAAA;EACf,OAAA,GAAU,MAAA;EFGe;AAAA;AAO3B;;;;EEHE,cAAA,GAAiB,MAAA,SAAe,gBAAA;EFGiB;;;EECjD,OAAA,GAAU,YAAA;AAAA;AAAA,UAGK,oBAAA;EACf,QAAA,CAAS,UAAA,UAAoB,MAAA,WAAiB,UAAA;EAC9C,UAAA,CAAW,OAAA,EAAS,MAAM;AAAA;AAAA,iBAcZ,kBAAA,CACd,QAAA,EAAU,cAAA,EACV,OAAA,GAAS,mBAAA,GACR,oBAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as ExpressionError, n as BonsaiSecurityError, r as BonsaiTypeError, t as BonsaiReferenceError } from "../errors-
|
|
2
|
-
import {
|
|
1
|
+
import { i as ExpressionError, n as BonsaiSecurityError, r as BonsaiTypeError, t as BonsaiReferenceError } from "../errors-CCZDd1gK.mjs";
|
|
2
|
+
import { a as methodsForReceiverType, o as tokenize, t as BLOCKED_PROPERTIES } from "../execution-context-b6FBfEdv.mjs";
|
|
3
3
|
//#region src/autocomplete/tokenizer.ts
|
|
4
4
|
function tolerantTokenize(expression, cursor, onError) {
|
|
5
5
|
if (expression.length === 0) return {
|
|
@@ -279,56 +279,15 @@ function buildLambdaState(tokens) {
|
|
|
279
279
|
}
|
|
280
280
|
//#endregion
|
|
281
281
|
//#region src/autocomplete/catalog.ts
|
|
282
|
-
/**
|
|
282
|
+
/**
|
|
283
|
+
* Methods offered per receiver type, derived from the shared SAFE_METHODS
|
|
284
|
+
* allowlist (src/safe-methods.ts) so autocomplete and the evaluator can never
|
|
285
|
+
* disagree about what is callable.
|
|
286
|
+
*/
|
|
283
287
|
const METHODS_BY_TYPE = {
|
|
284
|
-
string:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
"trimEnd",
|
|
288
|
-
"toLowerCase",
|
|
289
|
-
"toUpperCase",
|
|
290
|
-
"startsWith",
|
|
291
|
-
"endsWith",
|
|
292
|
-
"includes",
|
|
293
|
-
"indexOf",
|
|
294
|
-
"lastIndexOf",
|
|
295
|
-
"slice",
|
|
296
|
-
"substring",
|
|
297
|
-
"at",
|
|
298
|
-
"replace",
|
|
299
|
-
"replaceAll",
|
|
300
|
-
"split",
|
|
301
|
-
"padStart",
|
|
302
|
-
"padEnd",
|
|
303
|
-
"charAt",
|
|
304
|
-
"charCodeAt",
|
|
305
|
-
"repeat",
|
|
306
|
-
"concat",
|
|
307
|
-
"toString"
|
|
308
|
-
],
|
|
309
|
-
array: [
|
|
310
|
-
"filter",
|
|
311
|
-
"map",
|
|
312
|
-
"find",
|
|
313
|
-
"findIndex",
|
|
314
|
-
"some",
|
|
315
|
-
"every",
|
|
316
|
-
"flatMap",
|
|
317
|
-
"join",
|
|
318
|
-
"flat",
|
|
319
|
-
"includes",
|
|
320
|
-
"indexOf",
|
|
321
|
-
"lastIndexOf",
|
|
322
|
-
"slice",
|
|
323
|
-
"at",
|
|
324
|
-
"concat",
|
|
325
|
-
"toReversed",
|
|
326
|
-
"toSorted",
|
|
327
|
-
"toSpliced",
|
|
328
|
-
"with",
|
|
329
|
-
"toString"
|
|
330
|
-
],
|
|
331
|
-
number: ["toFixed", "toString"]
|
|
288
|
+
string: methodsForReceiverType("string"),
|
|
289
|
+
array: methodsForReceiverType("array"),
|
|
290
|
+
number: methodsForReceiverType("number")
|
|
332
291
|
};
|
|
333
292
|
/** Return type keyed by "receiverType.method" to disambiguate shared method names. */
|
|
334
293
|
const RETURN_TYPES = {
|
|
@@ -374,7 +333,6 @@ const RETURN_TYPES = {
|
|
|
374
333
|
"array.includes": "boolean",
|
|
375
334
|
"array.at": "unknown",
|
|
376
335
|
"array.with": "array",
|
|
377
|
-
"array.toString": "string",
|
|
378
336
|
"number.toFixed": "string",
|
|
379
337
|
"number.toString": "string"
|
|
380
338
|
};
|
|
@@ -560,9 +518,12 @@ function addMethodCompletions(results, env) {
|
|
|
560
518
|
if (!type || !isMethodReceiverType(type)) return;
|
|
561
519
|
const cached = METHOD_COMPLETIONS_BY_TYPE[type];
|
|
562
520
|
if (!cached) return;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
521
|
+
const { allowedProperties, deniedProperties } = env.policy;
|
|
522
|
+
for (const c of cached) {
|
|
523
|
+
if (allowedProperties && !allowedProperties.has(c.label)) continue;
|
|
524
|
+
if (deniedProperties && deniedProperties.has(c.label)) continue;
|
|
525
|
+
results.push(c);
|
|
526
|
+
}
|
|
566
527
|
}
|
|
567
528
|
function addLambdaPropertyCompletions(results, env) {
|
|
568
529
|
if (!env.lambda?.elementProperties) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/autocomplete/tokenizer.ts","../../src/autocomplete/context.ts","../../src/autocomplete/catalog.ts","../../src/autocomplete/inference.ts","../../src/autocomplete/completions.ts","../../src/autocomplete/index.ts"],"sourcesContent":["import { tokenize } from '../lexer.js'\nimport { ExpressionError } from '../errors.js'\nimport type { Token, PunctuationValue, OperatorValue } from '../types.js'\n\n/** Token type that excludes EOF — all tokens returned by tolerantTokenize. */\nexport type NonEofToken = Exclude<Token, { type: 'EOF' }>\n\nexport interface TolerantTokenResult {\n tokens: NonEofToken[]\n partial: boolean\n insideString: boolean\n}\n\n/** Known phases where errors can occur during completion. */\nexport type CompletionPhase =\n | 'tokenize'\n | 'tryEvalPrefix'\n | 'inferPipeInputType'\n | `probeTransform:${string}`\n | 'complete'\n\nexport type ErrorHandler = (error: unknown, phase: CompletionPhase) => void\n\nexport function tolerantTokenize(\n expression: string,\n cursor: number,\n onError?: ErrorHandler,\n): TolerantTokenResult {\n if (expression.length === 0) {\n return { tokens: [], partial: false, insideString: false }\n }\n\n const insideString = isCursorInsideString(expression, cursor)\n\n try {\n const tokens = tokenize(expression).filter((t): t is NonEofToken => t.type !== 'EOF')\n return { tokens, partial: false, insideString }\n } catch (err: unknown) {\n if (!(err instanceof ExpressionError || err instanceof SyntaxError)) {\n onError?.(err, 'tokenize')\n }\n const tokens = regexScan(expression, cursor)\n return { tokens, partial: true, insideString }\n }\n}\n\nexport function isCursorInsideString(expression: string, cursor: number): boolean {\n let inSingle = false\n let inDouble = false\n let inTemplate = false\n let templateDepth = 0\n const braceStack: number[] = [] // per-depth brace counter for nested interpolations\n let escaped = false\n\n for (let i = 0; i < expression.length && i < cursor; i++) {\n const ch = expression[i]\n\n if (escaped) {\n escaped = false\n continue\n }\n\n if (ch === '\\\\' && (inSingle || inDouble || inTemplate)) {\n escaped = true\n continue\n }\n\n if (!inSingle && !inDouble && !inTemplate) {\n if (ch === \"'\") inSingle = true\n else if (ch === '\"') inDouble = true\n else if (ch === '`') { inTemplate = true; templateDepth = 0; braceStack.length = 0 }\n } else if (inSingle && ch === \"'\") {\n inSingle = false\n } else if (inDouble && ch === '\"') {\n inDouble = false\n } else if (inTemplate) {\n if (ch === '`' && templateDepth === 0) {\n inTemplate = false\n } else if (ch === '$' && i + 1 < expression.length && expression[i + 1] === '{') {\n templateDepth++\n braceStack.push(0)\n i++\n } else if (templateDepth > 0 && ch === '{') {\n braceStack[braceStack.length - 1]++\n } else if (templateDepth > 0 && ch === '}') {\n if (braceStack.length > 0 && braceStack[braceStack.length - 1] > 0) {\n braceStack[braceStack.length - 1]--\n } else {\n braceStack.pop()\n templateDepth--\n }\n }\n }\n }\n\n return inSingle || inDouble || (inTemplate && templateDepth === 0)\n}\n\nconst TOKEN_RE = /\\?\\.|\\.\\.\\.|\\|>|\\?\\?|&&|\\|\\||[!=<>]=?|[+\\-*/%]|\\*\\*|[.(){}[\\],?:]|\"(?:[^\"\\\\]|\\\\.)*\"?|'(?:[^'\\\\]|\\\\.)*'?|`(?:[^`\\\\$]|\\\\.|\\$(?!\\{))*`?|\\d[\\d_]*(?:\\.\\d[\\d_]*)?(?:[eE][+-]?\\d+)?|[a-zA-Z_$][\\w$]*/gu\n\nfunction regexScan(expression: string, cursor: number): NonEofToken[] {\n const tokens: NonEofToken[] = []\n const text = expression.slice(0, cursor)\n\n for (const match of text.matchAll(TOKEN_RE)) {\n const value = match[0]\n const start = match.index\n const end = start + value.length\n const token = classifyToken(value, start, end)\n if (token) tokens.push(token)\n }\n\n return tokens\n}\n\nfunction classifyToken(value: string, start: number, end: number): NonEofToken | null {\n if (value === '|>') return { type: 'Pipe', value, start, end }\n if (value === '?.') return { type: 'OptionalChain', value, start, end }\n if (value === '??') return { type: 'NullishCoalescing', value, start, end }\n if (value === '...') return { type: 'Spread', value, start, end }\n if (value === 'true' || value === 'false') return { type: 'Boolean', value, start, end }\n if (value === 'null') return { type: 'Null', value, start, end }\n if (value === 'undefined') return { type: 'Undefined', value, start, end }\n if (/^[a-zA-Z_$]/u.test(value)) return { type: 'Identifier', value, start, end }\n if (/^\\d/u.test(value)) return { type: 'Number', value, start, end }\n if (value.startsWith('\"') || value.startsWith(\"'\")) return { type: 'String', value, start, end }\n if (value.startsWith('`')) return { type: 'TemplateLiteral', value, start, end }\n if (value.length === 1 && '(){}[],:?.'.includes(value)) return { type: 'Punctuation', value: value as PunctuationValue, start, end }\n if ('+-*/%!=<>&|'.includes(value[0]) || value === '**') return { type: 'Operator', value: value as OperatorValue, start, end }\n return null\n}\n","import type { Token } from '../types.js'\n\ninterface CursorContextBase {\n prefix: string\n}\n\nexport type CursorContext =\n | CursorContextBase & { kind: 'top-level-member'; precedingTokens: Token[] }\n | CursorContextBase & { kind: 'lambda-start'; depth: number }\n | CursorContextBase & { kind: 'lambda-member'; chain: string[]; depth: number }\n | CursorContextBase & { kind: 'pipe-transform' }\n | CursorContextBase & { kind: 'identifier' }\n | { kind: 'none' }\n\n/** Extract prefix from any non-none CursorContext. */\nexport function getPrefix(ctx: CursorContext): string {\n return ctx.kind !== 'none' ? ctx.prefix : ''\n}\n\nconst OPERATOR_TYPES = new Set(['Operator', 'NullishCoalescing'])\n\nfunction tokenKey(t: Token): string {\n return t.type === 'Punctuation' ? `${t.type}:${t.value}` : t.type\n}\n\nconst LAMBDA_TRIGGERS = new Set([\n 'Punctuation:(', 'Punctuation:,',\n 'Punctuation:?', 'Punctuation::',\n])\n\nconst VALUE_PRODUCERS = new Set([\n 'Identifier', 'Number', 'String', 'Boolean',\n 'Punctuation:)', 'Punctuation:]',\n])\n\nexport function classifyCursor(tokens: Token[], cursor: number): CursorContext {\n const before = tokens.filter(t => t.start < cursor)\n if (before.length === 0) return { kind: 'identifier', prefix: '' }\n\n const last = before[before.length - 1]\n const secondLast = before.length >= 2 ? before[before.length - 2] : null\n\n // Is last token a partial identifier at cursor?\n const lastIsPartial = last.type === 'Identifier' && last.end >= cursor\n\n // Pipe context\n if (last.type === 'Pipe') return { kind: 'pipe-transform', prefix: '' }\n if (lastIsPartial && secondLast?.type === 'Pipe') {\n return { kind: 'pipe-transform', prefix: last.value }\n }\n\n // Build lambda scope state\n const state = buildLambdaState(before)\n\n // Check for dot\n const dotToken = lastIsPartial ? secondLast : last\n const prefix = lastIsPartial ? last.value : ''\n\n if (dotToken && isDot(dotToken)) {\n const beforeDotIdx = before.indexOf(dotToken)\n const prevToken = beforeDotIdx > 0 ? before[beforeDotIdx - 1] : null\n\n if (state.depth > 0) {\n // Inside a call scope\n const prevKey = prevToken ? tokenKey(prevToken) : ''\n if (!prevToken || LAMBDA_TRIGGERS.has(prevKey) || OPERATOR_TYPES.has(prevToken.type)) {\n return { kind: 'lambda-start', prefix, depth: state.depth }\n }\n if (state.lambdaActive[state.depth] && prevToken && VALUE_PRODUCERS.has(tokenKey(prevToken))) {\n return { kind: 'lambda-member', prefix, chain: [...state.currentChain], depth: state.depth }\n }\n }\n\n // Top-level member access\n return { kind: 'top-level-member', prefix, precedingTokens: before.slice(0, before.indexOf(dotToken)) }\n }\n\n // Identifier context\n if (OPERATOR_TYPES.has(last.type) ||\n (last.type === 'Punctuation' && '(,'.includes(last.value))) {\n return { kind: 'identifier', prefix: '' }\n }\n\n if (lastIsPartial) {\n if (!secondLast || OPERATOR_TYPES.has(secondLast.type) ||\n (secondLast.type === 'Punctuation' && '(,'.includes(secondLast.value))) {\n // Check if inside lambda scope — if so and secondLast is lambda trigger, it's lambda start\n if (state.depth > 0 && secondLast && (LAMBDA_TRIGGERS.has(tokenKey(secondLast)) || OPERATOR_TYPES.has(secondLast.type))) {\n return { kind: 'lambda-start', prefix: last.value, depth: state.depth }\n }\n return { kind: 'identifier', prefix: last.value }\n }\n }\n\n return { kind: 'none' }\n}\n\nfunction isDot(t: Token): boolean {\n return (t.type === 'Punctuation' && t.value === '.') || t.type === 'OptionalChain'\n}\n\ninterface LambdaState {\n depth: number\n lambdaActive: Record<number, boolean>\n currentChain: string[]\n}\n\nfunction buildLambdaState(tokens: Token[]): LambdaState {\n let depth = 0\n const lambdaActive: Record<number, boolean> = {}\n const currentChain: string[] = []\n\n for (let i = 0; i < tokens.length; i++) {\n const t = tokens[i]\n\n if (t.type === 'Punctuation' && t.value === '(') {\n depth++\n lambdaActive[depth] = false\n } else if (t.type === 'Punctuation' && t.value === ')') {\n delete lambdaActive[depth]\n depth = Math.max(0, depth - 1)\n } else if (depth > 0 && isDot(t)) {\n const prev = i > 0 ? tokens[i - 1] : null\n const prevKey = prev ? tokenKey(prev) : ''\n if (!prev || LAMBDA_TRIGGERS.has(prevKey) || OPERATOR_TYPES.has(prev?.type ?? '')) {\n lambdaActive[depth] = true\n currentChain.length = 0\n } else if (lambdaActive[depth] && prev && VALUE_PRODUCERS.has(tokenKey(prev))) {\n // Only push identifiers onto the chain — not ) or ] which are value-producers\n // but not property names (e.g., .name.trim(). — the ) should not be in the chain)\n if (prev.type === 'Identifier') {\n currentChain.push(prev.value)\n }\n }\n }\n }\n\n return { depth, lambdaActive, currentChain }\n}\n","import { BLOCKED_PROPERTIES } from '../execution-context.js'\nimport type { InferredTypeName } from '../types.js'\n\n/** Types that have method catalogs. */\nexport type MethodReceiverType = 'string' | 'array' | 'number'\n\n/** Key format for the return type map: \"receiverType.methodName\". */\ntype ReturnTypeKey = `${MethodReceiverType}.${string}`\n\n/** Return type values include InferredTypeName plus 'unknown' for unresolvable cases. */\ntype ReturnTypeValue = InferredTypeName | 'unknown'\n\n/** Methods allowed per receiver type. Must stay in sync with isAllowedReceiver in eval-ops.ts. */\nexport const METHODS_BY_TYPE: Record<MethodReceiverType, readonly string[]> = {\n string: [\n 'trim', 'trimStart', 'trimEnd', 'toLowerCase', 'toUpperCase',\n 'startsWith', 'endsWith', 'includes', 'indexOf', 'lastIndexOf',\n 'slice', 'substring', 'at', 'replace', 'replaceAll',\n 'split', 'padStart', 'padEnd', 'charAt', 'charCodeAt',\n 'repeat', 'concat', 'toString',\n ],\n array: [\n 'filter', 'map', 'find', 'findIndex', 'some', 'every', 'flatMap',\n 'join', 'flat', 'includes', 'indexOf', 'lastIndexOf',\n 'slice', 'at', 'concat', 'toReversed', 'toSorted', 'toSpliced', 'with',\n 'toString',\n ],\n number: ['toFixed', 'toString'],\n}\n\n/** Return type keyed by \"receiverType.method\" to disambiguate shared method names. */\nconst RETURN_TYPES: Record<ReturnTypeKey, ReturnTypeValue> = {\n // String methods\n 'string.trim': 'string', 'string.trimStart': 'string', 'string.trimEnd': 'string',\n 'string.toLowerCase': 'string', 'string.toUpperCase': 'string',\n 'string.slice': 'string', 'string.substring': 'string',\n 'string.replace': 'string', 'string.replaceAll': 'string',\n 'string.padStart': 'string', 'string.padEnd': 'string',\n 'string.charAt': 'string', 'string.repeat': 'string', 'string.concat': 'string',\n 'string.split': 'array',\n 'string.indexOf': 'number', 'string.lastIndexOf': 'number', 'string.charCodeAt': 'number',\n 'string.startsWith': 'boolean', 'string.endsWith': 'boolean', 'string.includes': 'boolean',\n 'string.at': 'string', 'string.toString': 'string',\n\n // Array methods\n 'array.filter': 'array', 'array.map': 'array', 'array.flat': 'array',\n 'array.flatMap': 'array', 'array.concat': 'array', 'array.slice': 'array',\n 'array.toReversed': 'array', 'array.toSorted': 'array', 'array.toSpliced': 'array',\n 'array.find': 'unknown', 'array.findIndex': 'number',\n 'array.some': 'boolean', 'array.every': 'boolean',\n 'array.join': 'string', 'array.indexOf': 'number', 'array.lastIndexOf': 'number',\n 'array.includes': 'boolean', 'array.at': 'unknown', 'array.with': 'array',\n 'array.toString': 'string',\n\n // Number methods\n 'number.toFixed': 'string', 'number.toString': 'string',\n}\n\n/** Look up the return type for a method call on a given receiver type. */\nexport function getMethodReturnType(receiverType: MethodReceiverType, method: string): ReturnTypeValue | undefined {\n const key: ReturnTypeKey = `${receiverType}.${method}`\n return key in RETURN_TYPES ? RETURN_TYPES[key] : undefined\n}\n\n/** Type guard: narrows an InferredTypeName to MethodReceiverType (types that have method catalogs). */\nexport function isMethodReceiverType(t: string): t is MethodReceiverType {\n return t in METHODS_BY_TYPE\n}\n\n/** Re-export shared blocked names for use in completion filtering. */\nexport const BLOCKED_NAMES: ReadonlySet<string> = BLOCKED_PROPERTIES\n\nexport const KEYWORDS = ['true', 'false', 'null', 'undefined', 'in', 'not in'] as const\n","import { BLOCKED_PROPERTIES } from '../execution-context.js'\nimport type { InferredTypeName, ResolveResult } from '../types.js'\nimport { getMethodReturnType, type MethodReceiverType } from './catalog.js'\n\nexport function inferType(value: unknown): InferredTypeName {\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (Array.isArray(value)) return 'array'\n const t = typeof value\n if (t === 'string' || t === 'number' || t === 'boolean') return t\n // function, symbol, bigint → treat as object for completion purposes\n return 'object'\n}\n\n/** Shared policy shape for property allow/deny filtering. */\nexport interface PropertyPolicy {\n allowedProperties?: ReadonlySet<string>\n deniedProperties?: ReadonlySet<string>\n}\n\n/** Policy options for property chain resolution. */\nexport type ResolveOptions = PropertyPolicy\n\nexport function resolvePropertyChain(\n context: Record<string, unknown>,\n chain: string[],\n policy?: ResolveOptions,\n): ResolveResult {\n let current: unknown = context\n for (const key of chain) {\n if (BLOCKED_PROPERTIES.has(key)) return { found: false, reason: 'blocked' }\n if (policy?.allowedProperties && !policy.allowedProperties.has(key)) return { found: false, reason: 'blocked' }\n if (policy?.deniedProperties?.has(key)) return { found: false, reason: 'blocked' }\n if (current == null || typeof current !== 'object') return { found: false, reason: 'not-object' }\n const obj = current as Record<string, unknown>\n if (!(key in obj)) return { found: false, reason: 'not-found' }\n current = obj[key]\n }\n return { found: true, value: current }\n}\n\n// ── Element type info ──────────────────────────────────────────\n\nexport type ElementTypeInfo =\n | { type: 'unknown'; properties: []; value: undefined }\n | { type: 'object'; properties: string[]; value: Record<string, unknown> }\n | { type: 'string'; properties: []; value: string }\n | { type: 'number'; properties: []; value: number }\n | { type: 'boolean'; properties: []; value: boolean }\n | { type: 'array'; properties: []; value: unknown[] }\n\nexport function inferElementType(array: unknown[]): ElementTypeInfo {\n const first = array.find(el => el != null)\n if (first === undefined) return { type: 'unknown', properties: [], value: undefined }\n\n const type = inferType(first)\n if (type === 'object' && first !== null && typeof first === 'object') {\n return {\n type: 'object',\n properties: Object.keys(first as Record<string, unknown>),\n value: first as Record<string, unknown>,\n }\n }\n if (type === 'array') return { type: 'array', properties: [], value: first as unknown[] }\n if (type === 'string') return { type: 'string', properties: [], value: first as string }\n if (type === 'number') return { type: 'number', properties: [], value: first as number }\n if (type === 'boolean') return { type: 'boolean', properties: [], value: first as boolean }\n return { type: 'unknown', properties: [], value: undefined }\n}\n\nexport function inferMethodReturnType(receiverType: MethodReceiverType, method: string): InferredTypeName | 'unknown' {\n return getMethodReturnType(receiverType, method) ?? 'unknown'\n}\n\nexport function enumerateProperties(value: unknown): string[] {\n if (value == null || typeof value !== 'object') return []\n return Object.keys(value as Record<string, unknown>)\n}\n","import type { InferredTypeName } from '../types.js'\nimport { METHODS_BY_TYPE, BLOCKED_NAMES, KEYWORDS, getMethodReturnType, isMethodReceiverType, type MethodReceiverType } from './catalog.js'\nimport { getPrefix, type CursorContext } from './context.js'\nimport { inferType, type PropertyPolicy } from './inference.js'\n\n// ── Completion type ────────────────────────────────────────────\n\ntype CompletionInsert =\n | { insertText?: undefined; cursorOffset?: undefined }\n | { insertText: string; cursorOffset?: number }\n\ninterface CompletionBase {\n label: string\n kind: 'variable' | 'property' | 'method' | 'transform' | 'function' | 'keyword'\n detail?: string\n /** Lower values sort first. Can become negative after fuzzy-match boosting. */\n sortPriority: number\n}\n\nexport type Completion = CompletionBase & CompletionInsert\n\n// ── CompletionEnv ──────────────────────────────────────────────\n\n/** Info for member-access contexts (top-level-member, lambda-member). */\nexport interface MemberInfo {\n resolvedValue?: unknown\n resolvedType: InferredTypeName\n}\n\n/** Info for lambda-start context. */\nexport interface LambdaInfo {\n elementProperties?: string[]\n elementValue?: Record<string, unknown>\n}\n\n/** Info for identifier context. */\nexport interface IdentifierInfo {\n contextKeys: string[]\n contextValues: Record<string, unknown>\n}\n\n/** Info for pipe-transform context. */\nexport interface PipeInfo {\n inputType?: InferredTypeName\n transformTypes?: Record<string, InferredTypeName[]>\n}\n\nexport interface CompletionEnv {\n transforms: string[]\n functions: string[]\n policy: PropertyPolicy\n member?: MemberInfo\n lambda?: LambdaInfo\n identifier?: IdentifierInfo\n pipe?: PipeInfo\n}\n\n// Higher-order methods that take a lambda predicate\nconst LAMBDA_METHODS = new Set(['filter', 'map', 'find', 'findIndex', 'some', 'every', 'flatMap'])\nconst PREVIEW_MAX_LENGTH = 20\n\n// Pre-computed method completions per receiver type (static catalog, built once at module load)\nconst METHOD_COMPLETIONS_BY_TYPE: Record<string, Completion[]> = {}\nfor (const [type, methods] of Object.entries(METHODS_BY_TYPE)) {\n METHOD_COMPLETIONS_BY_TYPE[type] = methods\n .filter(m => !BLOCKED_NAMES.has(m))\n .map(method => {\n const returnType = getMethodReturnType(type as MethodReceiverType, method)\n const detail = returnType ? `${type} → ${returnType}` : type\n const isLambda = LAMBDA_METHODS.has(method)\n const insert = isLambda ? `${method}(.)` : `${method}()`\n return {\n label: method,\n kind: 'method' as const,\n detail,\n insertText: insert,\n cursorOffset: isLambda ? insert.length - 2 : insert.length - 1,\n sortPriority: 1,\n }\n })\n}\n\nexport function generateCompletions(ctx: CursorContext, env: CompletionEnv): Completion[] {\n const results: Completion[] = []\n\n switch (ctx.kind) {\n case 'top-level-member':\n case 'lambda-member':\n addPropertyCompletions(results, env)\n addMethodCompletions(results, env)\n break\n case 'lambda-start':\n addLambdaPropertyCompletions(results, env)\n break\n case 'pipe-transform':\n for (const name of env.transforms) {\n if (env.pipe?.inputType && env.pipe.transformTypes) {\n const accepts = env.pipe.transformTypes[name]\n if (Array.isArray(accepts) && !accepts.includes(env.pipe.inputType)) continue\n }\n results.push({ label: name, kind: 'transform', sortPriority: 0 })\n }\n break\n case 'identifier':\n addIdentifierCompletions(results, env)\n break\n case 'none':\n break\n }\n\n return filterAndRank(results, getPrefix(ctx))\n}\n\nfunction addPropertyCompletions(results: Completion[], env: CompletionEnv): void {\n const value = env.member?.resolvedValue\n if (value == null || typeof value !== 'object') return\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj)\n for (const key of keys) {\n if (isBlocked(key, env.policy)) continue\n const propValue = obj[key]\n const valueType = inferType(propValue)\n results.push({\n label: key,\n kind: 'property',\n detail: valueType,\n sortPriority: 0,\n })\n }\n}\n\nfunction addMethodCompletions(results: Completion[], env: CompletionEnv): void {\n const type = env.member?.resolvedType\n if (!type || !isMethodReceiverType(type)) return\n const cached = METHOD_COMPLETIONS_BY_TYPE[type]\n if (!cached) return\n // BLOCKED_NAMES is already filtered in the pre-computed cache.\n // Only deniedProperties needs runtime checking (it varies per instance).\n if (env.policy.deniedProperties) {\n for (const c of cached) {\n if (!env.policy.deniedProperties.has(c.label)) results.push(c)\n }\n } else {\n for (const c of cached) results.push(c)\n }\n}\n\nfunction addLambdaPropertyCompletions(results: Completion[], env: CompletionEnv): void {\n if (!env.lambda?.elementProperties) return\n for (const prop of env.lambda.elementProperties) {\n if (isBlocked(prop, env.policy)) continue\n const valueType = env.lambda.elementValue ? inferType(env.lambda.elementValue[prop]) : undefined\n results.push({\n label: prop,\n kind: 'property',\n detail: valueType,\n sortPriority: 0,\n })\n }\n}\n\nfunction addIdentifierCompletions(results: Completion[], env: CompletionEnv): void {\n // Note: allowedProperties/deniedProperties are NOT checked for context variable names.\n // This matches the evaluator's behavior — ExecutionContext.checkNameAccess skips\n // allow/deny checks for kind === 'identifier'. Root identifiers are never blocked.\n if (env.identifier) {\n for (const key of env.identifier.contextKeys) {\n const value = env.identifier.contextValues[key]\n const valueType = inferType(value)\n let detail: string = valueType\n // Show a brief preview for simple values\n if (valueType === 'string' && typeof value === 'string') {\n detail = value.length > PREVIEW_MAX_LENGTH\n ? `\"${value.slice(0, PREVIEW_MAX_LENGTH)}…\"`\n : `\"${value}\"`\n } else if (valueType === 'number') {\n detail = String(value)\n } else if (valueType === 'boolean') {\n detail = String(value)\n } else if (valueType === 'array' && Array.isArray(value)) {\n detail = `array(${value.length})`\n }\n results.push({ label: key, kind: 'variable', detail, sortPriority: 0 })\n }\n }\n for (const name of env.functions) {\n const insert = `${name}()`\n results.push({ label: name, kind: 'function', insertText: insert, cursorOffset: insert.length - 1, sortPriority: 1 })\n }\n for (const kw of KEYWORDS) {\n results.push({ label: kw, kind: 'keyword', sortPriority: 2 })\n }\n}\n\nfunction isBlocked(name: string, policy: CompletionEnv['policy']): boolean {\n if (BLOCKED_NAMES.has(name)) return true\n if (policy.allowedProperties && !policy.allowedProperties.has(name)) return true\n if (policy.deniedProperties && policy.deniedProperties.has(name)) return true\n return false\n}\n\nconst EXACT_MATCH_BOOST = 20\nconst PREFIX_MATCH_BOOST = 15\nconst FUZZY_MATCH_BOOST = 5\n\n// Fuzzy scoring constants\nconst SCORE_EXACT = 1000\nconst SCORE_PREFIX_BASE = 500\nconst SCORE_PREFIX_PER_CHAR = 10\nconst SCORE_CONTAINS_BASE = 200\nconst SCORE_CONTAINS_PER_CHAR = 5\nconst SCORE_CHAR_MATCH = 10\nconst SCORE_CONSECUTIVE_BONUS = 15\nconst SCORE_BOUNDARY_BONUS = 10\n\n/**\n * Fuzzy match score. Returns 0 for no match, higher = better match.\n * Rewards: consecutive matches, matches at word boundaries (camelCase), matches at start.\n */\nfunction fuzzyScore(label: string, query: string): number {\n const labelLower = label.toLowerCase()\n const queryLower = query.toLowerCase()\n\n if (label === query) return SCORE_EXACT\n if (labelLower.startsWith(queryLower)) return SCORE_PREFIX_BASE + (query.length * SCORE_PREFIX_PER_CHAR)\n if (labelLower.includes(queryLower)) return SCORE_CONTAINS_BASE + (query.length * SCORE_CONTAINS_PER_CHAR)\n\n // Fuzzy: every character in query must appear in label in order\n let qi = 0\n let score = 0\n let prevMatchIdx = -2\n for (let li = 0; li < label.length && qi < queryLower.length; li++) {\n if (labelLower[li] === queryLower[qi]) {\n qi++\n score += SCORE_CHAR_MATCH\n if (li === prevMatchIdx + 1) score += SCORE_CONSECUTIVE_BONUS\n if (li === 0 || label[li] === label[li].toUpperCase() && label[li] !== label[li].toLowerCase()) score += SCORE_BOUNDARY_BONUS\n prevMatchIdx = li\n }\n }\n\n if (qi < queryLower.length) return 0\n return score\n}\n\nfunction filterAndRank(results: Completion[], prefix: string): Completion[] {\n if (!prefix) return results.sort(comparator)\n\n const scored: Array<{ completion: Completion; score: number }> = []\n\n for (const c of results) {\n const score = fuzzyScore(c.label, prefix)\n if (score === 0) continue\n\n let boost = 0\n if (score >= SCORE_EXACT) boost = EXACT_MATCH_BOOST\n else if (score >= SCORE_PREFIX_BASE) boost = PREFIX_MATCH_BOOST\n else if (score >= SCORE_CONTAINS_BASE) boost = FUZZY_MATCH_BOOST\n\n scored.push({\n completion: { ...c, sortPriority: c.sortPriority - boost },\n score,\n })\n }\n\n // Sort by fuzzy score descending, then by base priority, then by label length, then alphabetically\n scored.sort((a, b) => {\n if (a.score !== b.score) return b.score - a.score\n if (a.completion.sortPriority !== b.completion.sortPriority) return a.completion.sortPriority - b.completion.sortPriority\n if (a.completion.label.length !== b.completion.label.length) return a.completion.label.length - b.completion.label.length\n if (a.completion.label < b.completion.label) return -1\n if (a.completion.label > b.completion.label) return 1\n return 0\n })\n\n return scored.map(s => s.completion)\n}\n\nfunction comparator(a: Completion, b: Completion): number {\n if (a.sortPriority !== b.sortPriority) return a.sortPriority - b.sortPriority\n if (a.label.length !== b.label.length) return a.label.length - b.label.length\n if (a.label < b.label) return -1\n if (a.label > b.label) return 1\n return 0\n}\n","import type { BonsaiInstance, Token, InferredTypeName } from '../types.js'\nimport { ExpressionError, BonsaiSecurityError, BonsaiTypeError, BonsaiReferenceError } from '../errors.js'\nimport { tolerantTokenize, isCursorInsideString, type ErrorHandler } from './tokenizer.js'\nimport { classifyCursor } from './context.js'\nimport { generateCompletions, type Completion, type CompletionEnv } from './completions.js'\nimport { inferType, resolvePropertyChain, inferElementType, inferMethodReturnType, type ResolveOptions } from './inference.js'\nimport { isMethodReceiverType } from './catalog.js'\n\nexport type { Completion }\n\nexport interface AutocompleteOptions {\n context?: Record<string, unknown>\n /** Map of transform name → accepted input types (e.g., { upper: ['string'], filter: ['array'] }).\n * When set, pipe-transform completions are filtered by the inferred input type.\n * When omitted, auto-probing discovers type compatibility by calling each transform\n * with a sample value — this runs once per type and is cached per instance.\n * For performance-sensitive contexts (e.g., large transform registries), pass this\n * explicitly to avoid the cold-start probe cost. */\n transformTypes?: Record<string, InferredTypeName[]>\n /** Called when an unexpected internal error occurs during completion.\n * Expected errors (e.g., syntax errors, security blocks, type mismatches) are not reported.\n * Useful for debugging missing or incorrect completions. */\n onError?: ErrorHandler\n}\n\nexport interface AutocompleteInstance {\n complete(expression: string, cursor: number): Completion[]\n setContext(context: Record<string, unknown>): void\n}\n\n// Valid identifier pattern for transform name validation\nconst VALID_IDENTIFIER = /^[a-zA-Z_$][\\w$]*$/u\n\n/** Check if an error is an expected Bonsai error (syntax, security, type, or reference). */\nfunction isExpectedError(err: unknown): boolean {\n return err instanceof ExpressionError\n || err instanceof BonsaiSecurityError\n || err instanceof BonsaiTypeError\n || err instanceof BonsaiReferenceError\n}\n\nexport function createAutocomplete(\n instance: BonsaiInstance,\n options: AutocompleteOptions = {},\n): AutocompleteInstance {\n let context: Record<string, unknown> = options.context ?? {}\n const probeCache = new Map<string, Set<string>>()\n const onError = options.onError\n\n // Cache policy at construction — it's immutable per-instance\n const rawPolicy = instance.getPolicy()\n const policy = {\n allowedProperties: rawPolicy.allowedProperties ? new Set(rawPolicy.allowedProperties) : undefined,\n deniedProperties: rawPolicy.deniedProperties ? new Set(rawPolicy.deniedProperties) : undefined,\n }\n // Resolve policy for property chain resolution (shared across calls)\n const resolvePolicy: ResolveOptions = {\n allowedProperties: policy.allowedProperties,\n deniedProperties: policy.deniedProperties,\n }\n\n return {\n complete(expression: string, cursor: number): Completion[] {\n try {\n return completeInner(expression, cursor)\n } catch (err: unknown) {\n onError?.(err, 'complete')\n return []\n }\n },\n\n setContext(newContext: Record<string, unknown>): void {\n context = newContext ?? {}\n },\n }\n\n function completeInner(expression: string, cursor: number): Completion[] {\n // Clamp cursor to valid range\n cursor = Math.max(0, Math.min(cursor, expression.length))\n\n // Fast path: check if cursor is inside a string before expensive tokenization\n if (isCursorInsideString(expression, cursor)) return []\n\n const { tokens } = tolerantTokenize(expression, cursor, onError)\n\n const cursorCtx = classifyCursor(tokens, cursor)\n if (cursorCtx.kind === 'none') return []\n\n // Defer listTransforms/listFunctions to branches that need them (avoids allocation on every call)\n const env: CompletionEnv = {\n transforms: [], functions: [], policy,\n }\n\n if (cursorCtx.kind === 'pipe-transform') {\n const transforms = instance.listTransforms()\n env.transforms = transforms\n const inputType = inferPipeInputType(expression, cursor, instance, context, onError)\n env.pipe = { inputType, transformTypes: options.transformTypes }\n if (inputType && !options.transformTypes) {\n const accepted = probeAcceptedTransforms(instance, inputType, probeCache, transforms, onError)\n env.transforms = transforms.filter(name => accepted.has(name))\n }\n } else if (cursorCtx.kind === 'top-level-member') {\n // Fast path: try static chain resolution first (avoids evaluateSync for simple chains)\n const chain = extractChainFromTokens(cursorCtx.precedingTokens)\n if (chain.length > 0) {\n const result = resolvePropertyChain(context, chain, resolvePolicy)\n if (result.found) {\n env.member = { resolvedValue: result.value, resolvedType: inferType(result.value) }\n } else if (cursorCtx.prefix === '') {\n // Static resolution failed (method chain like trim().) — try eval\n const evalResult = tryEvalPrefix(expression, cursor, instance, context, tokens, onError)\n if (evalResult !== undefined) {\n env.member = { resolvedValue: evalResult, resolvedType: inferType(evalResult) }\n }\n } else {\n // Static failed with prefix — try type inference from token chain\n const inferred = inferTypeFromTokenChain(cursorCtx.precedingTokens, context, resolvePolicy)\n if (inferred) {\n env.member = { resolvedType: inferred }\n }\n }\n } else if (cursorCtx.prefix === '') {\n // No simple chain (e.g., after `)` or complex expression) — try eval\n const evalResult = tryEvalPrefix(expression, cursor, instance, context, tokens, onError)\n if (evalResult !== undefined) {\n env.member = { resolvedValue: evalResult, resolvedType: inferType(evalResult) }\n }\n }\n } else if (cursorCtx.kind === 'lambda-member') {\n const arrTokens = extractChainBeforeCall(tokens, cursor)\n const arrResult = resolvePropertyChain(context, arrTokens, resolvePolicy)\n if (arrResult.found && Array.isArray(arrResult.value)) {\n const elemInfo = inferElementType(arrResult.value)\n if (elemInfo.type === 'object') {\n const resolved = resolvePropertyChain(elemInfo.value, cursorCtx.chain, resolvePolicy)\n if (resolved.found) {\n env.member = { resolvedValue: resolved.value, resolvedType: inferType(resolved.value) }\n }\n } else if (elemInfo.type !== 'unknown') {\n env.member = { resolvedType: inferType(elemInfo.value) }\n }\n }\n } else if (cursorCtx.kind === 'lambda-start') {\n const arrTokens = extractChainBeforeCall(tokens, cursor)\n let resolved = false\n\n // First try: static chain resolution from context\n if (arrTokens.length > 0) {\n const arrResult = resolvePropertyChain(context, arrTokens, resolvePolicy)\n if (arrResult.found && Array.isArray(arrResult.value)) {\n const elemInfo = inferElementType(arrResult.value)\n env.lambda = {\n elementProperties: elemInfo.properties,\n elementValue: elemInfo.type === 'object' ? elemInfo.value : undefined,\n }\n resolved = true\n }\n }\n\n // Second try: for nested lambdas, try eval-based inference\n // e.g., groups.map(.users.filter(. → evaluate groups[0].users to get the array\n if (!resolved) {\n const nestedArr = tryResolveNestedLambdaArray(tokens, cursor, context, resolvePolicy)\n if (nestedArr) {\n const elemInfo = inferElementType(nestedArr)\n env.lambda = {\n elementProperties: elemInfo.properties,\n elementValue: elemInfo.type === 'object' ? elemInfo.value : undefined,\n }\n }\n }\n } else if (cursorCtx.kind === 'identifier') {\n env.functions = instance.listFunctions()\n env.identifier = { contextKeys: Object.keys(context), contextValues: context }\n }\n\n return generateCompletions(cursorCtx, env)\n }\n}\n\n// ── Evaluation-based type inference ────────────────────────────\n\n/**\n * Try to evaluate the expression up to the cursor to get the actual runtime type.\n * This handles method chains like `user.name.trim().` where static resolution can't\n * know that trim() returns a string.\n */\nfunction tryEvalPrefix(\n expression: string,\n cursor: number,\n instance: BonsaiInstance,\n context: Record<string, unknown>,\n tokens: Token[],\n onError?: ErrorHandler,\n): unknown | undefined {\n // Find the expression prefix before the final dot\n const before = tokens.filter(t => t.end <= cursor)\n if (before.length === 0) return undefined\n\n // Walk backward to find where the expression starts (skip the trailing dot)\n let endIdx = before.length - 1\n if (before[endIdx].type === 'Punctuation' && before[endIdx].value === '.' || before[endIdx].type === 'OptionalChain') {\n endIdx--\n }\n if (endIdx < 0) return undefined\n\n // Extract the expression text up to (but not including) the final dot\n const exprEnd = before[endIdx].end\n const exprText = expression.slice(0, exprEnd)\n if (!exprText.trim()) return undefined\n\n try {\n return instance.evaluateSync(exprText, context)\n } catch (err: unknown) {\n if (!isExpectedError(err)) {\n onError?.(err, 'tryEvalPrefix')\n }\n return undefined\n }\n}\n\n/**\n * Infer the type by walking the token chain and using the return type map.\n * Handles: user.name.trim(). → string (because trim returns string)\n */\nfunction inferTypeFromTokenChain(\n tokens: Token[],\n context: Record<string, unknown>,\n resolveOpts?: ResolveOptions,\n): InferredTypeName | undefined {\n // Walk the chain: resolve context properties, then use return type map for method calls\n let currentType: InferredTypeName | undefined\n\n // First resolve as much as we can from context\n const chain: string[] = []\n let i = 0\n for (; i < tokens.length; i++) {\n if (tokens[i].type === 'Identifier') {\n chain.push(tokens[i].value)\n } else if ((tokens[i].type === 'Punctuation' && tokens[i].value === '.') || tokens[i].type === 'OptionalChain') {\n continue\n } else if (tokens[i].type === 'Punctuation' && tokens[i].value === '(') {\n // Method call — use the last identifier as method name\n const methodName = chain.pop()\n if (!methodName) break\n\n // Resolve what we have so far to get the receiver type\n if (currentType === undefined && chain.length > 0) {\n const result = resolvePropertyChain(context, chain, resolveOpts)\n currentType = result.found ? inferType(result.value) : undefined\n } else if (currentType !== undefined && chain.length > 0) {\n // Property access on a method return type — can't resolve statically\n // (e.g., trim().foo.bar( — foo/bar are not context properties)\n currentType = undefined\n }\n // Always clear chain after consuming it for a method call\n chain.length = 0\n\n if (currentType && isMethodReceiverType(currentType)) {\n const returned = inferMethodReturnType(currentType, methodName)\n currentType = returned === 'unknown' ? undefined : returned as InferredTypeName\n } else {\n currentType = undefined\n }\n\n // Skip past the closing paren\n let depth = 1\n i++\n while (i < tokens.length && depth > 0) {\n if (tokens[i].type === 'Punctuation' && tokens[i].value === '(') depth++\n if (tokens[i].type === 'Punctuation' && tokens[i].value === ')') depth--\n i++\n }\n i-- // will be incremented by for loop\n continue\n } else {\n break\n }\n }\n\n // If we have a resolved type but there are unresolved identifiers remaining\n // (e.g., user.name.trim().nonExistent), we can't resolve further statically\n if (currentType !== undefined && chain.length > 0) {\n return undefined\n }\n\n // Resolve remaining chain from context if we haven't resolved type yet\n if (currentType === undefined && chain.length > 0) {\n const result = resolvePropertyChain(context, chain, resolveOpts)\n currentType = result.found ? inferType(result.value) : undefined\n }\n\n return currentType\n}\n\n// ── Nested lambda resolution ───────────────────────────────────\n\n/**\n * For nested lambdas like `groups.map(.users.filter(.`, resolve the inner array\n * by walking the call stack: find the outermost array from context, get its element,\n * then follow the lambda property chain to find the nested array.\n */\nfunction tryResolveNestedLambdaArray(\n tokens: Token[],\n cursor: number,\n context: Record<string, unknown>,\n resolveOpts?: ResolveOptions,\n): unknown[] | undefined {\n const before = tokens.filter(t => t.start < cursor)\n\n // Collect lambda chains at each depth level\n // For `groups.map(.users.filter(.`:\n // depth 1 (after map(): lambda chain = ['users']\n // depth 2 (after filter(): lambda chain = [] (we're at the start)\n const lambdaChainsByDepth = new Map<number, string[]>()\n let depth = 0\n let currentChain: string[] = []\n let inLambda = false\n\n for (let i = 0; i < before.length; i++) {\n const t = before[i]\n if (t.type === 'Punctuation' && t.value === '(') {\n if (inLambda && currentChain.length > 0) {\n // The last identifier before ( is a method name, not a property — remove it\n const chain = [...currentChain]\n chain.pop() // remove the method name (e.g., 'filter' from ['users', 'filter'])\n if (chain.length > 0) {\n lambdaChainsByDepth.set(depth, chain)\n }\n }\n depth++\n inLambda = false\n currentChain = []\n } else if (t.type === 'Punctuation' && t.value === ')') {\n depth = Math.max(0, depth - 1)\n } else if (depth > 0 && t.type === 'Punctuation' && t.value === '.') {\n const prev = i > 0 ? before[i - 1] : null\n if (prev && (prev.type === 'Punctuation' && (prev.value === '(' || prev.value === ','))) {\n inLambda = true\n currentChain = []\n }\n } else if (inLambda && t.type === 'Identifier') {\n currentChain.push(t.value)\n }\n }\n\n // Save current chain for the current depth\n if (inLambda && currentChain.length > 0) {\n lambdaChainsByDepth.set(depth, [...currentChain])\n }\n\n // Find the outermost method-call ( — must be preceded by `identifier` with a `.`/`?.`/`|>` before it\n // This skips leading function calls like fn(arg) or grouping parens like (expr)\n let outerParenIdx = -1\n for (let i = 0; i < before.length; i++) {\n if (before[i].type === 'Punctuation' && before[i].value === '(' && i >= 2) {\n const prev = before[i - 1]\n const prevPrev = before[i - 2]\n if (prev.type === 'Identifier' &&\n ((prevPrev.type === 'Punctuation' && prevPrev.value === '.') ||\n prevPrev.type === 'OptionalChain' || prevPrev.type === 'Pipe')) {\n outerParenIdx = i\n break\n }\n }\n }\n\n if (outerParenIdx <= 0) return undefined\n\n // Extract the context array chain before the outermost call.\n const chain = extractChainBeforeOuterCall(before, outerParenIdx)\n if (chain.length === 0) return undefined\n\n // Resolve the outermost array from context\n const outerResult = resolvePropertyChain(context, chain, resolveOpts)\n if (!outerResult.found || !Array.isArray(outerResult.value)) return undefined\n let currentValue: unknown = outerResult.value\n\n // Walk through each depth's lambda chain to resolve nested arrays\n for (let d = 1; d <= depth; d++) {\n const elemInfo = inferElementType(currentValue as unknown[])\n if (elemInfo.type !== 'object') return undefined\n\n const lambdaChain = lambdaChainsByDepth.get(d)\n if (!lambdaChain || lambdaChain.length === 0) {\n // No lambda chain at this depth — we're at the start of the lambda\n // The current array is what we want\n return currentValue as unknown[]\n }\n\n // Follow the lambda chain on the element\n const nestedResult = resolvePropertyChain(elemInfo.value, lambdaChain, resolveOpts)\n if (!nestedResult.found || !Array.isArray(nestedResult.value)) return undefined\n currentValue = nestedResult.value\n }\n\n return Array.isArray(currentValue) ? currentValue as unknown[] : undefined\n}\n\n// ── Transform probing ──────────────────────────────────────────\n\nconst PROBE_CACHE_MAX = 32\n\nconst TYPE_SAMPLES: Partial<Record<InferredTypeName, unknown>> = {\n string: 'sample',\n number: 42,\n boolean: true,\n array: [0],\n object: {},\n}\n\nfunction probeAcceptedTransforms(\n instance: BonsaiInstance,\n inputType: string,\n cache: Map<string, Set<string>>,\n currentTransforms: string[],\n onError?: ErrorHandler,\n): Set<string> {\n // Key includes the current transform set to auto-invalidate when transforms change\n const cacheKey = `${inputType}:${[...currentTransforms].sort().join(',')}`\n const cached = cache.get(cacheKey)\n if (cached) return cached\n\n const sample = TYPE_SAMPLES[inputType as InferredTypeName]\n if (sample === undefined) return new Set(currentTransforms)\n\n const accepted = new Set<string>()\n for (const name of currentTransforms) {\n // Validate transform name is a safe identifier before interpolating\n if (!VALID_IDENTIFIER.test(name)) continue\n try {\n instance.evaluateSync(`x |> ${name}`, { x: sample })\n accepted.add(name)\n } catch (err: unknown) {\n if (!isExpectedError(err)) {\n onError?.(err, `probeTransform:${name}`)\n }\n }\n }\n\n // Evict oldest entry if cache exceeds max size\n if (cache.size >= PROBE_CACHE_MAX) {\n const first = cache.keys().next()\n if (!first.done) cache.delete(first.value)\n }\n cache.set(cacheKey, accepted)\n return accepted\n}\n\nfunction inferPipeInputType(\n expression: string,\n cursor: number,\n instance: BonsaiInstance,\n context: Record<string, unknown>,\n onError?: ErrorHandler,\n): InferredTypeName | undefined {\n const before = expression.slice(0, cursor)\n const pipeMatch = before.match(/^(?<input>.*)\\|>\\s*\\w*\\s*$/su)\n if (!pipeMatch) return undefined\n\n const exprBefore = (pipeMatch.groups?.input ?? '').trim()\n if (!exprBefore) return undefined\n\n try {\n const result = instance.evaluateSync(exprBefore, context)\n if (result === null || result === undefined) return undefined\n return inferType(result)\n } catch (err: unknown) {\n if (!isExpectedError(err)) {\n onError?.(err, 'inferPipeInputType')\n }\n return undefined\n }\n}\n\n// ── Token chain extraction ─────────────────────────────────────\n\n/** Extract trailing identifier.dot chain from a token array (walking backward). */\nfunction extractChainFromTokens(tokens: Token[]): string[] {\n const chain: string[] = []\n for (let i = tokens.length - 1; i >= 0; i--) {\n const t = tokens[i]\n if (t.type === 'Identifier') {\n chain.unshift(t.value)\n } else if ((t.type === 'Punctuation' && t.value === '.') || t.type === 'OptionalChain') {\n continue\n } else {\n break\n }\n }\n return chain\n}\n\n/** Extract the identifier chain before the outermost call's open paren. */\nfunction extractChainBeforeOuterCall(before: Token[], outerParenIdx: number): string[] {\n const chain: string[] = []\n let walkIdx = outerParenIdx - 1\n\n // Skip the method name\n if (walkIdx >= 0 && before[walkIdx].type === 'Identifier') walkIdx--\n\n // Skip the separator (dot, optional chain, or pipe)\n if (walkIdx >= 0) {\n const sep = before[walkIdx]\n if ((sep.type === 'Punctuation' && sep.value === '.') || sep.type === 'OptionalChain' || sep.type === 'Pipe') {\n walkIdx--\n }\n }\n\n // Skip past closing paren and its matching open paren (call-chain receiver)\n if (walkIdx >= 0 && before[walkIdx].type === 'Punctuation' && before[walkIdx].value === ')') {\n let parenDepth = 1\n walkIdx--\n while (walkIdx >= 0 && parenDepth > 0) {\n if (before[walkIdx].type === 'Punctuation' && before[walkIdx].value === ')') parenDepth++\n if (before[walkIdx].type === 'Punctuation' && before[walkIdx].value === '(') parenDepth--\n walkIdx--\n }\n }\n\n // Collect the identifier.identifier chain (reuses extractChainFromTokens logic)\n for (let i = walkIdx; i >= 0; i--) {\n if (before[i].type === 'Identifier') {\n chain.unshift(before[i].value)\n } else if ((before[i].type === 'Punctuation' && before[i].value === '.') || before[i].type === 'OptionalChain') {\n continue\n } else {\n break\n }\n }\n\n return chain\n}\n\nfunction extractChainBeforeCall(tokens: Token[], cursor: number): string[] {\n const before = tokens.filter(t => t.start < cursor)\n let parenIdx = -1\n let depth = 0\n for (let i = before.length - 1; i >= 0; i--) {\n if (before[i].type === 'Punctuation' && before[i].value === ')') depth++\n if (before[i].type === 'Punctuation' && before[i].value === '(') {\n if (depth === 0) { parenIdx = i; break }\n depth--\n }\n }\n\n if (parenIdx <= 0) return []\n\n const methodToken = before[parenIdx - 1]\n if (!methodToken || methodToken.type !== 'Identifier') return []\n\n const preMethodIdx = parenIdx - 2\n if (preMethodIdx < 0) return []\n const preMethodToken = before[preMethodIdx]\n\n if ((preMethodToken.type === 'Punctuation' && preMethodToken.value === '.') ||\n preMethodToken.type === 'OptionalChain' ||\n preMethodToken.type === 'Pipe') {\n return extractChainFromTokens(before.slice(0, preMethodIdx))\n }\n\n return []\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/autocomplete/tokenizer.ts","../../src/autocomplete/context.ts","../../src/autocomplete/catalog.ts","../../src/autocomplete/inference.ts","../../src/autocomplete/completions.ts","../../src/autocomplete/index.ts"],"sourcesContent":["import { tokenize } from '../lexer.js'\nimport { ExpressionError } from '../errors.js'\nimport type { Token, PunctuationValue, OperatorValue } from '../types.js'\n\n/** Token type that excludes EOF — all tokens returned by tolerantTokenize. */\nexport type NonEofToken = Exclude<Token, { type: 'EOF' }>\n\nexport interface TolerantTokenResult {\n tokens: NonEofToken[]\n partial: boolean\n insideString: boolean\n}\n\n/** Known phases where errors can occur during completion. */\nexport type CompletionPhase =\n | 'tokenize'\n | 'tryEvalPrefix'\n | 'inferPipeInputType'\n | `probeTransform:${string}`\n | 'complete'\n\nexport type ErrorHandler = (error: unknown, phase: CompletionPhase) => void\n\nexport function tolerantTokenize(\n expression: string,\n cursor: number,\n onError?: ErrorHandler,\n): TolerantTokenResult {\n if (expression.length === 0) {\n return { tokens: [], partial: false, insideString: false }\n }\n\n const insideString = isCursorInsideString(expression, cursor)\n\n try {\n const tokens = tokenize(expression).filter((t): t is NonEofToken => t.type !== 'EOF')\n return { tokens, partial: false, insideString }\n } catch (err: unknown) {\n if (!(err instanceof ExpressionError || err instanceof SyntaxError)) {\n onError?.(err, 'tokenize')\n }\n const tokens = regexScan(expression, cursor)\n return { tokens, partial: true, insideString }\n }\n}\n\nexport function isCursorInsideString(expression: string, cursor: number): boolean {\n let inSingle = false\n let inDouble = false\n let inTemplate = false\n let templateDepth = 0\n const braceStack: number[] = [] // per-depth brace counter for nested interpolations\n let escaped = false\n\n for (let i = 0; i < expression.length && i < cursor; i++) {\n const ch = expression[i]\n\n if (escaped) {\n escaped = false\n continue\n }\n\n if (ch === '\\\\' && (inSingle || inDouble || inTemplate)) {\n escaped = true\n continue\n }\n\n if (!inSingle && !inDouble && !inTemplate) {\n if (ch === \"'\") inSingle = true\n else if (ch === '\"') inDouble = true\n else if (ch === '`') { inTemplate = true; templateDepth = 0; braceStack.length = 0 }\n } else if (inSingle && ch === \"'\") {\n inSingle = false\n } else if (inDouble && ch === '\"') {\n inDouble = false\n } else if (inTemplate) {\n if (ch === '`' && templateDepth === 0) {\n inTemplate = false\n } else if (ch === '$' && i + 1 < expression.length && expression[i + 1] === '{') {\n templateDepth++\n braceStack.push(0)\n i++\n } else if (templateDepth > 0 && ch === '{') {\n braceStack[braceStack.length - 1]++\n } else if (templateDepth > 0 && ch === '}') {\n if (braceStack.length > 0 && braceStack[braceStack.length - 1] > 0) {\n braceStack[braceStack.length - 1]--\n } else {\n braceStack.pop()\n templateDepth--\n }\n }\n }\n }\n\n return inSingle || inDouble || (inTemplate && templateDepth === 0)\n}\n\nconst TOKEN_RE = /\\?\\.|\\.\\.\\.|\\|>|\\?\\?|&&|\\|\\||[!=<>]=?|[+\\-*/%]|\\*\\*|[.(){}[\\],?:]|\"(?:[^\"\\\\]|\\\\.)*\"?|'(?:[^'\\\\]|\\\\.)*'?|`(?:[^`\\\\$]|\\\\.|\\$(?!\\{))*`?|\\d[\\d_]*(?:\\.\\d[\\d_]*)?(?:[eE][+-]?\\d+)?|[a-zA-Z_$][\\w$]*/gu\n\nfunction regexScan(expression: string, cursor: number): NonEofToken[] {\n const tokens: NonEofToken[] = []\n const text = expression.slice(0, cursor)\n\n for (const match of text.matchAll(TOKEN_RE)) {\n const value = match[0]\n const start = match.index\n const end = start + value.length\n const token = classifyToken(value, start, end)\n if (token) tokens.push(token)\n }\n\n return tokens\n}\n\nfunction classifyToken(value: string, start: number, end: number): NonEofToken | null {\n if (value === '|>') return { type: 'Pipe', value, start, end }\n if (value === '?.') return { type: 'OptionalChain', value, start, end }\n if (value === '??') return { type: 'NullishCoalescing', value, start, end }\n if (value === '...') return { type: 'Spread', value, start, end }\n if (value === 'true' || value === 'false') return { type: 'Boolean', value, start, end }\n if (value === 'null') return { type: 'Null', value, start, end }\n if (value === 'undefined') return { type: 'Undefined', value, start, end }\n if (/^[a-zA-Z_$]/u.test(value)) return { type: 'Identifier', value, start, end }\n if (/^\\d/u.test(value)) return { type: 'Number', value, start, end }\n if (value.startsWith('\"') || value.startsWith(\"'\")) return { type: 'String', value, start, end }\n if (value.startsWith('`')) return { type: 'TemplateLiteral', value, start, end }\n if (value.length === 1 && '(){}[],:?.'.includes(value)) return { type: 'Punctuation', value: value as PunctuationValue, start, end }\n if ('+-*/%!=<>&|'.includes(value[0]) || value === '**') return { type: 'Operator', value: value as OperatorValue, start, end }\n return null\n}\n","import type { Token } from '../types.js'\n\ninterface CursorContextBase {\n prefix: string\n}\n\nexport type CursorContext =\n | CursorContextBase & { kind: 'top-level-member'; precedingTokens: Token[] }\n | CursorContextBase & { kind: 'lambda-start'; depth: number }\n | CursorContextBase & { kind: 'lambda-member'; chain: string[]; depth: number }\n | CursorContextBase & { kind: 'pipe-transform' }\n | CursorContextBase & { kind: 'identifier' }\n | { kind: 'none' }\n\n/** Extract prefix from any non-none CursorContext. */\nexport function getPrefix(ctx: CursorContext): string {\n return ctx.kind !== 'none' ? ctx.prefix : ''\n}\n\nconst OPERATOR_TYPES = new Set(['Operator', 'NullishCoalescing'])\n\nfunction tokenKey(t: Token): string {\n return t.type === 'Punctuation' ? `${t.type}:${t.value}` : t.type\n}\n\nconst LAMBDA_TRIGGERS = new Set([\n 'Punctuation:(', 'Punctuation:,',\n 'Punctuation:?', 'Punctuation::',\n])\n\nconst VALUE_PRODUCERS = new Set([\n 'Identifier', 'Number', 'String', 'Boolean',\n 'Punctuation:)', 'Punctuation:]',\n])\n\nexport function classifyCursor(tokens: Token[], cursor: number): CursorContext {\n const before = tokens.filter(t => t.start < cursor)\n if (before.length === 0) return { kind: 'identifier', prefix: '' }\n\n const last = before[before.length - 1]\n const secondLast = before.length >= 2 ? before[before.length - 2] : null\n\n // Is last token a partial identifier at cursor?\n const lastIsPartial = last.type === 'Identifier' && last.end >= cursor\n\n // Pipe context\n if (last.type === 'Pipe') return { kind: 'pipe-transform', prefix: '' }\n if (lastIsPartial && secondLast?.type === 'Pipe') {\n return { kind: 'pipe-transform', prefix: last.value }\n }\n\n // Build lambda scope state\n const state = buildLambdaState(before)\n\n // Check for dot\n const dotToken = lastIsPartial ? secondLast : last\n const prefix = lastIsPartial ? last.value : ''\n\n if (dotToken && isDot(dotToken)) {\n const beforeDotIdx = before.indexOf(dotToken)\n const prevToken = beforeDotIdx > 0 ? before[beforeDotIdx - 1] : null\n\n if (state.depth > 0) {\n // Inside a call scope\n const prevKey = prevToken ? tokenKey(prevToken) : ''\n if (!prevToken || LAMBDA_TRIGGERS.has(prevKey) || OPERATOR_TYPES.has(prevToken.type)) {\n return { kind: 'lambda-start', prefix, depth: state.depth }\n }\n if (state.lambdaActive[state.depth] && prevToken && VALUE_PRODUCERS.has(tokenKey(prevToken))) {\n return { kind: 'lambda-member', prefix, chain: [...state.currentChain], depth: state.depth }\n }\n }\n\n // Top-level member access\n return { kind: 'top-level-member', prefix, precedingTokens: before.slice(0, before.indexOf(dotToken)) }\n }\n\n // Identifier context\n if (OPERATOR_TYPES.has(last.type) ||\n (last.type === 'Punctuation' && '(,'.includes(last.value))) {\n return { kind: 'identifier', prefix: '' }\n }\n\n if (lastIsPartial) {\n if (!secondLast || OPERATOR_TYPES.has(secondLast.type) ||\n (secondLast.type === 'Punctuation' && '(,'.includes(secondLast.value))) {\n // Check if inside lambda scope — if so and secondLast is lambda trigger, it's lambda start\n if (state.depth > 0 && secondLast && (LAMBDA_TRIGGERS.has(tokenKey(secondLast)) || OPERATOR_TYPES.has(secondLast.type))) {\n return { kind: 'lambda-start', prefix: last.value, depth: state.depth }\n }\n return { kind: 'identifier', prefix: last.value }\n }\n }\n\n return { kind: 'none' }\n}\n\nfunction isDot(t: Token): boolean {\n return (t.type === 'Punctuation' && t.value === '.') || t.type === 'OptionalChain'\n}\n\ninterface LambdaState {\n depth: number\n lambdaActive: Record<number, boolean>\n currentChain: string[]\n}\n\nfunction buildLambdaState(tokens: Token[]): LambdaState {\n let depth = 0\n const lambdaActive: Record<number, boolean> = {}\n const currentChain: string[] = []\n\n for (let i = 0; i < tokens.length; i++) {\n const t = tokens[i]\n\n if (t.type === 'Punctuation' && t.value === '(') {\n depth++\n lambdaActive[depth] = false\n } else if (t.type === 'Punctuation' && t.value === ')') {\n delete lambdaActive[depth]\n depth = Math.max(0, depth - 1)\n } else if (depth > 0 && isDot(t)) {\n const prev = i > 0 ? tokens[i - 1] : null\n const prevKey = prev ? tokenKey(prev) : ''\n if (!prev || LAMBDA_TRIGGERS.has(prevKey) || OPERATOR_TYPES.has(prev?.type ?? '')) {\n lambdaActive[depth] = true\n currentChain.length = 0\n } else if (lambdaActive[depth] && prev && VALUE_PRODUCERS.has(tokenKey(prev))) {\n // Only push identifiers onto the chain — not ) or ] which are value-producers\n // but not property names (e.g., .name.trim(). — the ) should not be in the chain)\n if (prev.type === 'Identifier') {\n currentChain.push(prev.value)\n }\n }\n }\n }\n\n return { depth, lambdaActive, currentChain }\n}\n","import { BLOCKED_PROPERTIES } from '../execution-context.js'\nimport { methodsForReceiverType, type MethodReceiverType } from '../safe-methods.js'\nimport type { InferredTypeName } from '../types.js'\n\n/** Types that have method catalogs. Re-exported from the shared allowlist. */\nexport type { MethodReceiverType }\n\n/** Key format for the return type map: \"receiverType.methodName\". */\ntype ReturnTypeKey = `${MethodReceiverType}.${string}`\n\n/** Return type values include InferredTypeName plus 'unknown' for unresolvable cases. */\ntype ReturnTypeValue = InferredTypeName | 'unknown'\n\n/**\n * Methods offered per receiver type, derived from the shared SAFE_METHODS\n * allowlist (src/safe-methods.ts) so autocomplete and the evaluator can never\n * disagree about what is callable.\n */\nexport const METHODS_BY_TYPE: Record<MethodReceiverType, readonly string[]> = {\n string: methodsForReceiverType('string'),\n array: methodsForReceiverType('array'),\n number: methodsForReceiverType('number'),\n}\n\n/** Return type keyed by \"receiverType.method\" to disambiguate shared method names. */\nconst RETURN_TYPES: Record<ReturnTypeKey, ReturnTypeValue> = {\n // String methods\n 'string.trim': 'string', 'string.trimStart': 'string', 'string.trimEnd': 'string',\n 'string.toLowerCase': 'string', 'string.toUpperCase': 'string',\n 'string.slice': 'string', 'string.substring': 'string',\n 'string.replace': 'string', 'string.replaceAll': 'string',\n 'string.padStart': 'string', 'string.padEnd': 'string',\n 'string.charAt': 'string', 'string.repeat': 'string', 'string.concat': 'string',\n 'string.split': 'array',\n 'string.indexOf': 'number', 'string.lastIndexOf': 'number', 'string.charCodeAt': 'number',\n 'string.startsWith': 'boolean', 'string.endsWith': 'boolean', 'string.includes': 'boolean',\n 'string.at': 'string', 'string.toString': 'string',\n\n // Array methods\n 'array.filter': 'array', 'array.map': 'array', 'array.flat': 'array',\n 'array.flatMap': 'array', 'array.concat': 'array', 'array.slice': 'array',\n 'array.toReversed': 'array', 'array.toSorted': 'array', 'array.toSpliced': 'array',\n 'array.find': 'unknown', 'array.findIndex': 'number',\n 'array.some': 'boolean', 'array.every': 'boolean',\n 'array.join': 'string', 'array.indexOf': 'number', 'array.lastIndexOf': 'number',\n 'array.includes': 'boolean', 'array.at': 'unknown', 'array.with': 'array',\n\n // Number methods\n 'number.toFixed': 'string', 'number.toString': 'string',\n}\n\n/** Look up the return type for a method call on a given receiver type. */\nexport function getMethodReturnType(receiverType: MethodReceiverType, method: string): ReturnTypeValue | undefined {\n const key: ReturnTypeKey = `${receiverType}.${method}`\n return key in RETURN_TYPES ? RETURN_TYPES[key] : undefined\n}\n\n/** Type guard: narrows an InferredTypeName to MethodReceiverType (types that have method catalogs). */\nexport function isMethodReceiverType(t: string): t is MethodReceiverType {\n return t in METHODS_BY_TYPE\n}\n\n/** Re-export shared blocked names for use in completion filtering. */\nexport const BLOCKED_NAMES: ReadonlySet<string> = BLOCKED_PROPERTIES\n\nexport const KEYWORDS = ['true', 'false', 'null', 'undefined', 'in', 'not in'] as const\n","import { BLOCKED_PROPERTIES } from '../execution-context.js'\nimport type { InferredTypeName, ResolveResult } from '../types.js'\nimport { getMethodReturnType, type MethodReceiverType } from './catalog.js'\n\nexport function inferType(value: unknown): InferredTypeName {\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (Array.isArray(value)) return 'array'\n const t = typeof value\n if (t === 'string' || t === 'number' || t === 'boolean') return t\n // function, symbol, bigint → treat as object for completion purposes\n return 'object'\n}\n\n/** Shared policy shape for property allow/deny filtering. */\nexport interface PropertyPolicy {\n allowedProperties?: ReadonlySet<string>\n deniedProperties?: ReadonlySet<string>\n}\n\n/** Policy options for property chain resolution. */\nexport type ResolveOptions = PropertyPolicy\n\nexport function resolvePropertyChain(\n context: Record<string, unknown>,\n chain: string[],\n policy?: ResolveOptions,\n): ResolveResult {\n let current: unknown = context\n for (const key of chain) {\n if (BLOCKED_PROPERTIES.has(key)) return { found: false, reason: 'blocked' }\n if (policy?.allowedProperties && !policy.allowedProperties.has(key)) return { found: false, reason: 'blocked' }\n if (policy?.deniedProperties?.has(key)) return { found: false, reason: 'blocked' }\n if (current == null || typeof current !== 'object') return { found: false, reason: 'not-object' }\n const obj = current as Record<string, unknown>\n if (!(key in obj)) return { found: false, reason: 'not-found' }\n current = obj[key]\n }\n return { found: true, value: current }\n}\n\n// ── Element type info ──────────────────────────────────────────\n\nexport type ElementTypeInfo =\n | { type: 'unknown'; properties: []; value: undefined }\n | { type: 'object'; properties: string[]; value: Record<string, unknown> }\n | { type: 'string'; properties: []; value: string }\n | { type: 'number'; properties: []; value: number }\n | { type: 'boolean'; properties: []; value: boolean }\n | { type: 'array'; properties: []; value: unknown[] }\n\nexport function inferElementType(array: unknown[]): ElementTypeInfo {\n const first = array.find(el => el != null)\n if (first === undefined) return { type: 'unknown', properties: [], value: undefined }\n\n const type = inferType(first)\n if (type === 'object' && first !== null && typeof first === 'object') {\n return {\n type: 'object',\n properties: Object.keys(first as Record<string, unknown>),\n value: first as Record<string, unknown>,\n }\n }\n if (type === 'array') return { type: 'array', properties: [], value: first as unknown[] }\n if (type === 'string') return { type: 'string', properties: [], value: first as string }\n if (type === 'number') return { type: 'number', properties: [], value: first as number }\n if (type === 'boolean') return { type: 'boolean', properties: [], value: first as boolean }\n return { type: 'unknown', properties: [], value: undefined }\n}\n\nexport function inferMethodReturnType(receiverType: MethodReceiverType, method: string): InferredTypeName | 'unknown' {\n return getMethodReturnType(receiverType, method) ?? 'unknown'\n}\n\nexport function enumerateProperties(value: unknown): string[] {\n if (value == null || typeof value !== 'object') return []\n return Object.keys(value as Record<string, unknown>)\n}\n","import type { InferredTypeName } from '../types.js'\nimport { METHODS_BY_TYPE, BLOCKED_NAMES, KEYWORDS, getMethodReturnType, isMethodReceiverType, type MethodReceiverType } from './catalog.js'\nimport { getPrefix, type CursorContext } from './context.js'\nimport { inferType, type PropertyPolicy } from './inference.js'\n\n// ── Completion type ────────────────────────────────────────────\n\ntype CompletionInsert =\n | { insertText?: undefined; cursorOffset?: undefined }\n | { insertText: string; cursorOffset?: number }\n\ninterface CompletionBase {\n label: string\n kind: 'variable' | 'property' | 'method' | 'transform' | 'function' | 'keyword'\n detail?: string\n /** Lower values sort first. Can become negative after fuzzy-match boosting. */\n sortPriority: number\n}\n\nexport type Completion = CompletionBase & CompletionInsert\n\n// ── CompletionEnv ──────────────────────────────────────────────\n\n/** Info for member-access contexts (top-level-member, lambda-member). */\nexport interface MemberInfo {\n resolvedValue?: unknown\n resolvedType: InferredTypeName\n}\n\n/** Info for lambda-start context. */\nexport interface LambdaInfo {\n elementProperties?: string[]\n elementValue?: Record<string, unknown>\n}\n\n/** Info for identifier context. */\nexport interface IdentifierInfo {\n contextKeys: string[]\n contextValues: Record<string, unknown>\n}\n\n/** Info for pipe-transform context. */\nexport interface PipeInfo {\n inputType?: InferredTypeName\n transformTypes?: Record<string, InferredTypeName[]>\n}\n\nexport interface CompletionEnv {\n transforms: string[]\n functions: string[]\n policy: PropertyPolicy\n member?: MemberInfo\n lambda?: LambdaInfo\n identifier?: IdentifierInfo\n pipe?: PipeInfo\n}\n\n// Higher-order methods that take a lambda predicate\nconst LAMBDA_METHODS = new Set(['filter', 'map', 'find', 'findIndex', 'some', 'every', 'flatMap'])\nconst PREVIEW_MAX_LENGTH = 20\n\n// Pre-computed method completions per receiver type (static catalog, built once at module load)\nconst METHOD_COMPLETIONS_BY_TYPE: Record<string, Completion[]> = {}\nfor (const [type, methods] of Object.entries(METHODS_BY_TYPE)) {\n METHOD_COMPLETIONS_BY_TYPE[type] = methods\n .filter(m => !BLOCKED_NAMES.has(m))\n .map(method => {\n const returnType = getMethodReturnType(type as MethodReceiverType, method)\n const detail = returnType ? `${type} → ${returnType}` : type\n const isLambda = LAMBDA_METHODS.has(method)\n const insert = isLambda ? `${method}(.)` : `${method}()`\n return {\n label: method,\n kind: 'method' as const,\n detail,\n insertText: insert,\n cursorOffset: isLambda ? insert.length - 2 : insert.length - 1,\n sortPriority: 1,\n }\n })\n}\n\nexport function generateCompletions(ctx: CursorContext, env: CompletionEnv): Completion[] {\n const results: Completion[] = []\n\n switch (ctx.kind) {\n case 'top-level-member':\n case 'lambda-member':\n addPropertyCompletions(results, env)\n addMethodCompletions(results, env)\n break\n case 'lambda-start':\n addLambdaPropertyCompletions(results, env)\n break\n case 'pipe-transform':\n for (const name of env.transforms) {\n if (env.pipe?.inputType && env.pipe.transformTypes) {\n const accepts = env.pipe.transformTypes[name]\n if (Array.isArray(accepts) && !accepts.includes(env.pipe.inputType)) continue\n }\n results.push({ label: name, kind: 'transform', sortPriority: 0 })\n }\n break\n case 'identifier':\n addIdentifierCompletions(results, env)\n break\n case 'none':\n break\n }\n\n return filterAndRank(results, getPrefix(ctx))\n}\n\nfunction addPropertyCompletions(results: Completion[], env: CompletionEnv): void {\n const value = env.member?.resolvedValue\n if (value == null || typeof value !== 'object') return\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj)\n for (const key of keys) {\n if (isBlocked(key, env.policy)) continue\n const propValue = obj[key]\n const valueType = inferType(propValue)\n results.push({\n label: key,\n kind: 'property',\n detail: valueType,\n sortPriority: 0,\n })\n }\n}\n\nfunction addMethodCompletions(results: Completion[], env: CompletionEnv): void {\n const type = env.member?.resolvedType\n if (!type || !isMethodReceiverType(type)) return\n const cached = METHOD_COMPLETIONS_BY_TYPE[type]\n if (!cached) return\n // BLOCKED_NAMES is already filtered in the pre-computed cache. allow/deny\n // lists vary per instance and the evaluator applies BOTH to method names\n // (ExecutionContext.checkNameAccess, kind === 'method'), so completions must\n // mirror that or it would suggest methods that then throw.\n const { allowedProperties, deniedProperties } = env.policy\n for (const c of cached) {\n if (allowedProperties && !allowedProperties.has(c.label)) continue\n if (deniedProperties && deniedProperties.has(c.label)) continue\n results.push(c)\n }\n}\n\nfunction addLambdaPropertyCompletions(results: Completion[], env: CompletionEnv): void {\n if (!env.lambda?.elementProperties) return\n for (const prop of env.lambda.elementProperties) {\n if (isBlocked(prop, env.policy)) continue\n const valueType = env.lambda.elementValue ? inferType(env.lambda.elementValue[prop]) : undefined\n results.push({\n label: prop,\n kind: 'property',\n detail: valueType,\n sortPriority: 0,\n })\n }\n}\n\nfunction addIdentifierCompletions(results: Completion[], env: CompletionEnv): void {\n // Note: allowedProperties/deniedProperties are NOT checked for context variable names.\n // This matches the evaluator's behavior — ExecutionContext.checkNameAccess skips\n // allow/deny checks for kind === 'identifier'. Root identifiers are never blocked.\n if (env.identifier) {\n for (const key of env.identifier.contextKeys) {\n const value = env.identifier.contextValues[key]\n const valueType = inferType(value)\n let detail: string = valueType\n // Show a brief preview for simple values\n if (valueType === 'string' && typeof value === 'string') {\n detail = value.length > PREVIEW_MAX_LENGTH\n ? `\"${value.slice(0, PREVIEW_MAX_LENGTH)}…\"`\n : `\"${value}\"`\n } else if (valueType === 'number') {\n detail = String(value)\n } else if (valueType === 'boolean') {\n detail = String(value)\n } else if (valueType === 'array' && Array.isArray(value)) {\n detail = `array(${value.length})`\n }\n results.push({ label: key, kind: 'variable', detail, sortPriority: 0 })\n }\n }\n for (const name of env.functions) {\n const insert = `${name}()`\n results.push({ label: name, kind: 'function', insertText: insert, cursorOffset: insert.length - 1, sortPriority: 1 })\n }\n for (const kw of KEYWORDS) {\n results.push({ label: kw, kind: 'keyword', sortPriority: 2 })\n }\n}\n\nfunction isBlocked(name: string, policy: CompletionEnv['policy']): boolean {\n if (BLOCKED_NAMES.has(name)) return true\n if (policy.allowedProperties && !policy.allowedProperties.has(name)) return true\n if (policy.deniedProperties && policy.deniedProperties.has(name)) return true\n return false\n}\n\nconst EXACT_MATCH_BOOST = 20\nconst PREFIX_MATCH_BOOST = 15\nconst FUZZY_MATCH_BOOST = 5\n\n// Fuzzy scoring constants\nconst SCORE_EXACT = 1000\nconst SCORE_PREFIX_BASE = 500\nconst SCORE_PREFIX_PER_CHAR = 10\nconst SCORE_CONTAINS_BASE = 200\nconst SCORE_CONTAINS_PER_CHAR = 5\nconst SCORE_CHAR_MATCH = 10\nconst SCORE_CONSECUTIVE_BONUS = 15\nconst SCORE_BOUNDARY_BONUS = 10\n\n/**\n * Fuzzy match score. Returns 0 for no match, higher = better match.\n * Rewards: consecutive matches, matches at word boundaries (camelCase), matches at start.\n */\nfunction fuzzyScore(label: string, query: string): number {\n const labelLower = label.toLowerCase()\n const queryLower = query.toLowerCase()\n\n if (label === query) return SCORE_EXACT\n if (labelLower.startsWith(queryLower)) return SCORE_PREFIX_BASE + (query.length * SCORE_PREFIX_PER_CHAR)\n if (labelLower.includes(queryLower)) return SCORE_CONTAINS_BASE + (query.length * SCORE_CONTAINS_PER_CHAR)\n\n // Fuzzy: every character in query must appear in label in order\n let qi = 0\n let score = 0\n let prevMatchIdx = -2\n for (let li = 0; li < label.length && qi < queryLower.length; li++) {\n if (labelLower[li] === queryLower[qi]) {\n qi++\n score += SCORE_CHAR_MATCH\n if (li === prevMatchIdx + 1) score += SCORE_CONSECUTIVE_BONUS\n if (li === 0 || label[li] === label[li].toUpperCase() && label[li] !== label[li].toLowerCase()) score += SCORE_BOUNDARY_BONUS\n prevMatchIdx = li\n }\n }\n\n if (qi < queryLower.length) return 0\n return score\n}\n\nfunction filterAndRank(results: Completion[], prefix: string): Completion[] {\n if (!prefix) return results.sort(comparator)\n\n const scored: Array<{ completion: Completion; score: number }> = []\n\n for (const c of results) {\n const score = fuzzyScore(c.label, prefix)\n if (score === 0) continue\n\n let boost = 0\n if (score >= SCORE_EXACT) boost = EXACT_MATCH_BOOST\n else if (score >= SCORE_PREFIX_BASE) boost = PREFIX_MATCH_BOOST\n else if (score >= SCORE_CONTAINS_BASE) boost = FUZZY_MATCH_BOOST\n\n scored.push({\n completion: { ...c, sortPriority: c.sortPriority - boost },\n score,\n })\n }\n\n // Sort by fuzzy score descending, then by base priority, then by label length, then alphabetically\n scored.sort((a, b) => {\n if (a.score !== b.score) return b.score - a.score\n if (a.completion.sortPriority !== b.completion.sortPriority) return a.completion.sortPriority - b.completion.sortPriority\n if (a.completion.label.length !== b.completion.label.length) return a.completion.label.length - b.completion.label.length\n if (a.completion.label < b.completion.label) return -1\n if (a.completion.label > b.completion.label) return 1\n return 0\n })\n\n return scored.map(s => s.completion)\n}\n\nfunction comparator(a: Completion, b: Completion): number {\n if (a.sortPriority !== b.sortPriority) return a.sortPriority - b.sortPriority\n if (a.label.length !== b.label.length) return a.label.length - b.label.length\n if (a.label < b.label) return -1\n if (a.label > b.label) return 1\n return 0\n}\n","import type { BonsaiInstance, Token, InferredTypeName } from '../types.js'\nimport { ExpressionError, BonsaiSecurityError, BonsaiTypeError, BonsaiReferenceError } from '../errors.js'\nimport { tolerantTokenize, isCursorInsideString, type ErrorHandler } from './tokenizer.js'\nimport { classifyCursor } from './context.js'\nimport { generateCompletions, type Completion, type CompletionEnv } from './completions.js'\nimport { inferType, resolvePropertyChain, inferElementType, inferMethodReturnType, type ResolveOptions } from './inference.js'\nimport { isMethodReceiverType } from './catalog.js'\n\nexport type { Completion }\n\nexport interface AutocompleteOptions {\n context?: Record<string, unknown>\n /** Map of transform name → accepted input types (e.g., { upper: ['string'], filter: ['array'] }).\n * When set, pipe-transform completions are filtered by the inferred input type.\n * When omitted, auto-probing discovers type compatibility by calling each transform\n * with a sample value — this runs once per type and is cached per instance.\n * For performance-sensitive contexts (e.g., large transform registries), pass this\n * explicitly to avoid the cold-start probe cost. */\n transformTypes?: Record<string, InferredTypeName[]>\n /** Called when an unexpected internal error occurs during completion.\n * Expected errors (e.g., syntax errors, security blocks, type mismatches) are not reported.\n * Useful for debugging missing or incorrect completions. */\n onError?: ErrorHandler\n}\n\nexport interface AutocompleteInstance {\n complete(expression: string, cursor: number): Completion[]\n setContext(context: Record<string, unknown>): void\n}\n\n// Valid identifier pattern for transform name validation\nconst VALID_IDENTIFIER = /^[a-zA-Z_$][\\w$]*$/u\n\n/** Check if an error is an expected Bonsai error (syntax, security, type, or reference). */\nfunction isExpectedError(err: unknown): boolean {\n return err instanceof ExpressionError\n || err instanceof BonsaiSecurityError\n || err instanceof BonsaiTypeError\n || err instanceof BonsaiReferenceError\n}\n\nexport function createAutocomplete(\n instance: BonsaiInstance,\n options: AutocompleteOptions = {},\n): AutocompleteInstance {\n let context: Record<string, unknown> = options.context ?? {}\n const probeCache = new Map<string, Set<string>>()\n const onError = options.onError\n\n // Cache policy at construction — it's immutable per-instance\n const rawPolicy = instance.getPolicy()\n const policy = {\n allowedProperties: rawPolicy.allowedProperties ? new Set(rawPolicy.allowedProperties) : undefined,\n deniedProperties: rawPolicy.deniedProperties ? new Set(rawPolicy.deniedProperties) : undefined,\n }\n // Resolve policy for property chain resolution (shared across calls)\n const resolvePolicy: ResolveOptions = {\n allowedProperties: policy.allowedProperties,\n deniedProperties: policy.deniedProperties,\n }\n\n return {\n complete(expression: string, cursor: number): Completion[] {\n try {\n return completeInner(expression, cursor)\n } catch (err: unknown) {\n onError?.(err, 'complete')\n return []\n }\n },\n\n setContext(newContext: Record<string, unknown>): void {\n context = newContext ?? {}\n },\n }\n\n function completeInner(expression: string, cursor: number): Completion[] {\n // Clamp cursor to valid range\n cursor = Math.max(0, Math.min(cursor, expression.length))\n\n // Fast path: check if cursor is inside a string before expensive tokenization\n if (isCursorInsideString(expression, cursor)) return []\n\n const { tokens } = tolerantTokenize(expression, cursor, onError)\n\n const cursorCtx = classifyCursor(tokens, cursor)\n if (cursorCtx.kind === 'none') return []\n\n // Defer listTransforms/listFunctions to branches that need them (avoids allocation on every call)\n const env: CompletionEnv = {\n transforms: [], functions: [], policy,\n }\n\n if (cursorCtx.kind === 'pipe-transform') {\n const transforms = instance.listTransforms()\n env.transforms = transforms\n const inputType = inferPipeInputType(expression, cursor, instance, context, onError)\n env.pipe = { inputType, transformTypes: options.transformTypes }\n if (inputType && !options.transformTypes) {\n const accepted = probeAcceptedTransforms(instance, inputType, probeCache, transforms, onError)\n env.transforms = transforms.filter(name => accepted.has(name))\n }\n } else if (cursorCtx.kind === 'top-level-member') {\n // Fast path: try static chain resolution first (avoids evaluateSync for simple chains)\n const chain = extractChainFromTokens(cursorCtx.precedingTokens)\n if (chain.length > 0) {\n const result = resolvePropertyChain(context, chain, resolvePolicy)\n if (result.found) {\n env.member = { resolvedValue: result.value, resolvedType: inferType(result.value) }\n } else if (cursorCtx.prefix === '') {\n // Static resolution failed (method chain like trim().) — try eval\n const evalResult = tryEvalPrefix(expression, cursor, instance, context, tokens, onError)\n if (evalResult !== undefined) {\n env.member = { resolvedValue: evalResult, resolvedType: inferType(evalResult) }\n }\n } else {\n // Static failed with prefix — try type inference from token chain\n const inferred = inferTypeFromTokenChain(cursorCtx.precedingTokens, context, resolvePolicy)\n if (inferred) {\n env.member = { resolvedType: inferred }\n }\n }\n } else if (cursorCtx.prefix === '') {\n // No simple chain (e.g., after `)` or complex expression) — try eval\n const evalResult = tryEvalPrefix(expression, cursor, instance, context, tokens, onError)\n if (evalResult !== undefined) {\n env.member = { resolvedValue: evalResult, resolvedType: inferType(evalResult) }\n }\n }\n } else if (cursorCtx.kind === 'lambda-member') {\n const arrTokens = extractChainBeforeCall(tokens, cursor)\n const arrResult = resolvePropertyChain(context, arrTokens, resolvePolicy)\n if (arrResult.found && Array.isArray(arrResult.value)) {\n const elemInfo = inferElementType(arrResult.value)\n if (elemInfo.type === 'object') {\n const resolved = resolvePropertyChain(elemInfo.value, cursorCtx.chain, resolvePolicy)\n if (resolved.found) {\n env.member = { resolvedValue: resolved.value, resolvedType: inferType(resolved.value) }\n }\n } else if (elemInfo.type !== 'unknown') {\n env.member = { resolvedType: inferType(elemInfo.value) }\n }\n }\n } else if (cursorCtx.kind === 'lambda-start') {\n const arrTokens = extractChainBeforeCall(tokens, cursor)\n let resolved = false\n\n // First try: static chain resolution from context\n if (arrTokens.length > 0) {\n const arrResult = resolvePropertyChain(context, arrTokens, resolvePolicy)\n if (arrResult.found && Array.isArray(arrResult.value)) {\n const elemInfo = inferElementType(arrResult.value)\n env.lambda = {\n elementProperties: elemInfo.properties,\n elementValue: elemInfo.type === 'object' ? elemInfo.value : undefined,\n }\n resolved = true\n }\n }\n\n // Second try: for nested lambdas, try eval-based inference\n // e.g., groups.map(.users.filter(. → evaluate groups[0].users to get the array\n if (!resolved) {\n const nestedArr = tryResolveNestedLambdaArray(tokens, cursor, context, resolvePolicy)\n if (nestedArr) {\n const elemInfo = inferElementType(nestedArr)\n env.lambda = {\n elementProperties: elemInfo.properties,\n elementValue: elemInfo.type === 'object' ? elemInfo.value : undefined,\n }\n }\n }\n } else if (cursorCtx.kind === 'identifier') {\n env.functions = instance.listFunctions()\n env.identifier = { contextKeys: Object.keys(context), contextValues: context }\n }\n\n return generateCompletions(cursorCtx, env)\n }\n}\n\n// ── Evaluation-based type inference ────────────────────────────\n\n/**\n * Try to evaluate the expression up to the cursor to get the actual runtime type.\n * This handles method chains like `user.name.trim().` where static resolution can't\n * know that trim() returns a string.\n */\nfunction tryEvalPrefix(\n expression: string,\n cursor: number,\n instance: BonsaiInstance,\n context: Record<string, unknown>,\n tokens: Token[],\n onError?: ErrorHandler,\n): unknown | undefined {\n // Find the expression prefix before the final dot\n const before = tokens.filter(t => t.end <= cursor)\n if (before.length === 0) return undefined\n\n // Walk backward to find where the expression starts (skip the trailing dot)\n let endIdx = before.length - 1\n if (before[endIdx].type === 'Punctuation' && before[endIdx].value === '.' || before[endIdx].type === 'OptionalChain') {\n endIdx--\n }\n if (endIdx < 0) return undefined\n\n // Extract the expression text up to (but not including) the final dot\n const exprEnd = before[endIdx].end\n const exprText = expression.slice(0, exprEnd)\n if (!exprText.trim()) return undefined\n\n try {\n return instance.evaluateSync(exprText, context)\n } catch (err: unknown) {\n if (!isExpectedError(err)) {\n onError?.(err, 'tryEvalPrefix')\n }\n return undefined\n }\n}\n\n/**\n * Infer the type by walking the token chain and using the return type map.\n * Handles: user.name.trim(). → string (because trim returns string)\n */\nfunction inferTypeFromTokenChain(\n tokens: Token[],\n context: Record<string, unknown>,\n resolveOpts?: ResolveOptions,\n): InferredTypeName | undefined {\n // Walk the chain: resolve context properties, then use return type map for method calls\n let currentType: InferredTypeName | undefined\n\n // First resolve as much as we can from context\n const chain: string[] = []\n let i = 0\n for (; i < tokens.length; i++) {\n if (tokens[i].type === 'Identifier') {\n chain.push(tokens[i].value)\n } else if ((tokens[i].type === 'Punctuation' && tokens[i].value === '.') || tokens[i].type === 'OptionalChain') {\n continue\n } else if (tokens[i].type === 'Punctuation' && tokens[i].value === '(') {\n // Method call — use the last identifier as method name\n const methodName = chain.pop()\n if (!methodName) break\n\n // Resolve what we have so far to get the receiver type\n if (currentType === undefined && chain.length > 0) {\n const result = resolvePropertyChain(context, chain, resolveOpts)\n currentType = result.found ? inferType(result.value) : undefined\n } else if (currentType !== undefined && chain.length > 0) {\n // Property access on a method return type — can't resolve statically\n // (e.g., trim().foo.bar( — foo/bar are not context properties)\n currentType = undefined\n }\n // Always clear chain after consuming it for a method call\n chain.length = 0\n\n if (currentType && isMethodReceiverType(currentType)) {\n const returned = inferMethodReturnType(currentType, methodName)\n currentType = returned === 'unknown' ? undefined : returned as InferredTypeName\n } else {\n currentType = undefined\n }\n\n // Skip past the closing paren\n let depth = 1\n i++\n while (i < tokens.length && depth > 0) {\n if (tokens[i].type === 'Punctuation' && tokens[i].value === '(') depth++\n if (tokens[i].type === 'Punctuation' && tokens[i].value === ')') depth--\n i++\n }\n i-- // will be incremented by for loop\n continue\n } else {\n break\n }\n }\n\n // If we have a resolved type but there are unresolved identifiers remaining\n // (e.g., user.name.trim().nonExistent), we can't resolve further statically\n if (currentType !== undefined && chain.length > 0) {\n return undefined\n }\n\n // Resolve remaining chain from context if we haven't resolved type yet\n if (currentType === undefined && chain.length > 0) {\n const result = resolvePropertyChain(context, chain, resolveOpts)\n currentType = result.found ? inferType(result.value) : undefined\n }\n\n return currentType\n}\n\n// ── Nested lambda resolution ───────────────────────────────────\n\n/**\n * For nested lambdas like `groups.map(.users.filter(.`, resolve the inner array\n * by walking the call stack: find the outermost array from context, get its element,\n * then follow the lambda property chain to find the nested array.\n */\nfunction tryResolveNestedLambdaArray(\n tokens: Token[],\n cursor: number,\n context: Record<string, unknown>,\n resolveOpts?: ResolveOptions,\n): unknown[] | undefined {\n const before = tokens.filter(t => t.start < cursor)\n\n // Collect lambda chains at each depth level\n // For `groups.map(.users.filter(.`:\n // depth 1 (after map(): lambda chain = ['users']\n // depth 2 (after filter(): lambda chain = [] (we're at the start)\n const lambdaChainsByDepth = new Map<number, string[]>()\n let depth = 0\n let currentChain: string[] = []\n let inLambda = false\n\n for (let i = 0; i < before.length; i++) {\n const t = before[i]\n if (t.type === 'Punctuation' && t.value === '(') {\n if (inLambda && currentChain.length > 0) {\n // The last identifier before ( is a method name, not a property — remove it\n const chain = [...currentChain]\n chain.pop() // remove the method name (e.g., 'filter' from ['users', 'filter'])\n if (chain.length > 0) {\n lambdaChainsByDepth.set(depth, chain)\n }\n }\n depth++\n inLambda = false\n currentChain = []\n } else if (t.type === 'Punctuation' && t.value === ')') {\n depth = Math.max(0, depth - 1)\n } else if (depth > 0 && t.type === 'Punctuation' && t.value === '.') {\n const prev = i > 0 ? before[i - 1] : null\n if (prev && (prev.type === 'Punctuation' && (prev.value === '(' || prev.value === ','))) {\n inLambda = true\n currentChain = []\n }\n } else if (inLambda && t.type === 'Identifier') {\n currentChain.push(t.value)\n }\n }\n\n // Save current chain for the current depth\n if (inLambda && currentChain.length > 0) {\n lambdaChainsByDepth.set(depth, [...currentChain])\n }\n\n // Find the outermost method-call ( — must be preceded by `identifier` with a `.`/`?.`/`|>` before it\n // This skips leading function calls like fn(arg) or grouping parens like (expr)\n let outerParenIdx = -1\n for (let i = 0; i < before.length; i++) {\n if (before[i].type === 'Punctuation' && before[i].value === '(' && i >= 2) {\n const prev = before[i - 1]\n const prevPrev = before[i - 2]\n if (prev.type === 'Identifier' &&\n ((prevPrev.type === 'Punctuation' && prevPrev.value === '.') ||\n prevPrev.type === 'OptionalChain' || prevPrev.type === 'Pipe')) {\n outerParenIdx = i\n break\n }\n }\n }\n\n if (outerParenIdx <= 0) return undefined\n\n // Extract the context array chain before the outermost call.\n const chain = extractChainBeforeOuterCall(before, outerParenIdx)\n if (chain.length === 0) return undefined\n\n // Resolve the outermost array from context\n const outerResult = resolvePropertyChain(context, chain, resolveOpts)\n if (!outerResult.found || !Array.isArray(outerResult.value)) return undefined\n let currentValue: unknown = outerResult.value\n\n // Walk through each depth's lambda chain to resolve nested arrays\n for (let d = 1; d <= depth; d++) {\n const elemInfo = inferElementType(currentValue as unknown[])\n if (elemInfo.type !== 'object') return undefined\n\n const lambdaChain = lambdaChainsByDepth.get(d)\n if (!lambdaChain || lambdaChain.length === 0) {\n // No lambda chain at this depth — we're at the start of the lambda\n // The current array is what we want\n return currentValue as unknown[]\n }\n\n // Follow the lambda chain on the element\n const nestedResult = resolvePropertyChain(elemInfo.value, lambdaChain, resolveOpts)\n if (!nestedResult.found || !Array.isArray(nestedResult.value)) return undefined\n currentValue = nestedResult.value\n }\n\n return Array.isArray(currentValue) ? currentValue as unknown[] : undefined\n}\n\n// ── Transform probing ──────────────────────────────────────────\n\nconst PROBE_CACHE_MAX = 32\n\nconst TYPE_SAMPLES: Partial<Record<InferredTypeName, unknown>> = {\n string: 'sample',\n number: 42,\n boolean: true,\n array: [0],\n object: {},\n}\n\nfunction probeAcceptedTransforms(\n instance: BonsaiInstance,\n inputType: string,\n cache: Map<string, Set<string>>,\n currentTransforms: string[],\n onError?: ErrorHandler,\n): Set<string> {\n // Key includes the current transform set to auto-invalidate when transforms change\n const cacheKey = `${inputType}:${[...currentTransforms].sort().join(',')}`\n const cached = cache.get(cacheKey)\n if (cached) return cached\n\n const sample = TYPE_SAMPLES[inputType as InferredTypeName]\n if (sample === undefined) return new Set(currentTransforms)\n\n const accepted = new Set<string>()\n for (const name of currentTransforms) {\n // Validate transform name is a safe identifier before interpolating\n if (!VALID_IDENTIFIER.test(name)) continue\n try {\n instance.evaluateSync(`x |> ${name}`, { x: sample })\n accepted.add(name)\n } catch (err: unknown) {\n if (!isExpectedError(err)) {\n onError?.(err, `probeTransform:${name}`)\n }\n }\n }\n\n // Evict oldest entry if cache exceeds max size\n if (cache.size >= PROBE_CACHE_MAX) {\n const first = cache.keys().next()\n if (!first.done) cache.delete(first.value)\n }\n cache.set(cacheKey, accepted)\n return accepted\n}\n\nfunction inferPipeInputType(\n expression: string,\n cursor: number,\n instance: BonsaiInstance,\n context: Record<string, unknown>,\n onError?: ErrorHandler,\n): InferredTypeName | undefined {\n const before = expression.slice(0, cursor)\n const pipeMatch = before.match(/^(?<input>.*)\\|>\\s*\\w*\\s*$/su)\n if (!pipeMatch) return undefined\n\n const exprBefore = (pipeMatch.groups?.input ?? '').trim()\n if (!exprBefore) return undefined\n\n try {\n const result = instance.evaluateSync(exprBefore, context)\n if (result === null || result === undefined) return undefined\n return inferType(result)\n } catch (err: unknown) {\n if (!isExpectedError(err)) {\n onError?.(err, 'inferPipeInputType')\n }\n return undefined\n }\n}\n\n// ── Token chain extraction ─────────────────────────────────────\n\n/** Extract trailing identifier.dot chain from a token array (walking backward). */\nfunction extractChainFromTokens(tokens: Token[]): string[] {\n const chain: string[] = []\n for (let i = tokens.length - 1; i >= 0; i--) {\n const t = tokens[i]\n if (t.type === 'Identifier') {\n chain.unshift(t.value)\n } else if ((t.type === 'Punctuation' && t.value === '.') || t.type === 'OptionalChain') {\n continue\n } else {\n break\n }\n }\n return chain\n}\n\n/** Extract the identifier chain before the outermost call's open paren. */\nfunction extractChainBeforeOuterCall(before: Token[], outerParenIdx: number): string[] {\n const chain: string[] = []\n let walkIdx = outerParenIdx - 1\n\n // Skip the method name\n if (walkIdx >= 0 && before[walkIdx].type === 'Identifier') walkIdx--\n\n // Skip the separator (dot, optional chain, or pipe)\n if (walkIdx >= 0) {\n const sep = before[walkIdx]\n if ((sep.type === 'Punctuation' && sep.value === '.') || sep.type === 'OptionalChain' || sep.type === 'Pipe') {\n walkIdx--\n }\n }\n\n // Skip past closing paren and its matching open paren (call-chain receiver)\n if (walkIdx >= 0 && before[walkIdx].type === 'Punctuation' && before[walkIdx].value === ')') {\n let parenDepth = 1\n walkIdx--\n while (walkIdx >= 0 && parenDepth > 0) {\n if (before[walkIdx].type === 'Punctuation' && before[walkIdx].value === ')') parenDepth++\n if (before[walkIdx].type === 'Punctuation' && before[walkIdx].value === '(') parenDepth--\n walkIdx--\n }\n }\n\n // Collect the identifier.identifier chain (reuses extractChainFromTokens logic)\n for (let i = walkIdx; i >= 0; i--) {\n if (before[i].type === 'Identifier') {\n chain.unshift(before[i].value)\n } else if ((before[i].type === 'Punctuation' && before[i].value === '.') || before[i].type === 'OptionalChain') {\n continue\n } else {\n break\n }\n }\n\n return chain\n}\n\nfunction extractChainBeforeCall(tokens: Token[], cursor: number): string[] {\n const before = tokens.filter(t => t.start < cursor)\n let parenIdx = -1\n let depth = 0\n for (let i = before.length - 1; i >= 0; i--) {\n if (before[i].type === 'Punctuation' && before[i].value === ')') depth++\n if (before[i].type === 'Punctuation' && before[i].value === '(') {\n if (depth === 0) { parenIdx = i; break }\n depth--\n }\n }\n\n if (parenIdx <= 0) return []\n\n const methodToken = before[parenIdx - 1]\n if (!methodToken || methodToken.type !== 'Identifier') return []\n\n const preMethodIdx = parenIdx - 2\n if (preMethodIdx < 0) return []\n const preMethodToken = before[preMethodIdx]\n\n if ((preMethodToken.type === 'Punctuation' && preMethodToken.value === '.') ||\n preMethodToken.type === 'OptionalChain' ||\n preMethodToken.type === 'Pipe') {\n return extractChainFromTokens(before.slice(0, preMethodIdx))\n }\n\n return []\n}\n"],"mappings":"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"}
|
|
@@ -161,4 +161,4 @@ function levenshtein(a, b) {
|
|
|
161
161
|
//#endregion
|
|
162
162
|
export { attachLocation as a, offsetToPosition as c, ExpressionError as i, suggest as l, BonsaiSecurityError as n, formatBonsaiError as o, BonsaiTypeError as r, formatError as s, BonsaiReferenceError as t };
|
|
163
163
|
|
|
164
|
-
//# sourceMappingURL=errors-
|
|
164
|
+
//# sourceMappingURL=errors-CCZDd1gK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-CCZDd1gK.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\nfunction 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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"}
|