bonsai-js 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.4.0] - 2026-06-02
11
+
12
+ ### Added
13
+
14
+ - **Context-aware functions** (`addContextFunction`): register functions that receive the evaluation context as their first parameter (typed `Readonly<TCtx>`, passed by reference), enabling auth/permission/personalization patterns without threading context through expression arguments. Pure and context-aware functions share a single namespace; `isContextFunction(name)` introspects the kind.
15
+ - **Generic context typing** (`bonsai<TCtx>()`): the factory is now generic over context type, with end-to-end type safety through `evaluate`, `evaluateSync`, `compile`, and `addContextFunction`. Backward compatible: defaults to `Record<string, unknown>` when unspecified.
16
+ - New exported types: `ContextFunctionFn`, generic `BonsaiPlugin<TCtx>`, generic `CompiledExpression<TCtx>`, generic `BonsaiInstance<TCtx>`.
17
+ - Internal: pure and context-aware functions share one tagged registry, and the cached `Bindings` snapshot consolidates transforms and that registry into a single object passed to the evaluator.
18
+
19
+ ### Resolves
20
+
21
+ - #33: context access from registered functions.
22
+
23
+ ### Credits
24
+
25
+ - Thanks to @jaenyf for raising #33 and contributing PR #34.
26
+
8
27
  ## [0.3.0] - 2026-03-21
9
28
 
10
29
  ### Added
@@ -58,11 +77,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
58
77
  - **Bare dot identity lambda:** `. > 2` and `. * 10` now work as lambda shorthand for the current item itself, in both method calls and pipe transforms (e.g., `[1,2,3,4].filter(. > 2)`, `[1,2,3] |> map(. * 10)`)
59
78
  - Async-safe evaluation for all higher-order array methods — lambda predicates that return Promises are correctly resolved in both top-level and nested contexts via `evaluate()`
60
79
  - Documentation for array methods, lambda shorthand, and method chaining in README and website docs
61
-
62
- ## [Unreleased]
63
-
64
- ### Added
65
-
66
80
  - Deterministic property-based parser and evaluator invariant tests
67
81
  - Random fuzz coverage for malformed parser input
68
82
  - Adversarial regression tests for deep nesting, oversized arrays, nested blocked keys, and spread misuse
package/README.md CHANGED
@@ -125,6 +125,58 @@ expr.addFunction('lookupTier', async (userId) => {
125
125
  await expr.evaluate('lookupTier(userId) == "pro"', { userId: 'u_123' })
126
126
  ```
127
127
 
128
+ ### Context-aware functions
129
+
130
+ Register functions that read the evaluation context directly. The function
131
+ receives the evaluation context as its first parameter (typed read-only), so
132
+ you can keep expressions terse and let the function pull what it needs:
133
+
134
+ ```ts
135
+ import { bonsai } from 'bonsai-js'
136
+
137
+ interface AppContext {
138
+ currentUserId: string
139
+ perms: readonly string[]
140
+ }
141
+
142
+ const app = bonsai<AppContext>()
143
+
144
+ app.addContextFunction('lookupCurrentUserTier', async (ctx) => {
145
+ const row = await db.users.findById(ctx.currentUserId)
146
+ return row?.tier ?? 'free'
147
+ })
148
+
149
+ app.addContextFunction('hasPermission', (ctx, action) =>
150
+ ctx.perms.includes(String(action)))
151
+
152
+ await app.evaluate(
153
+ 'lookupCurrentUserTier() == "pro" && hasPermission("admin")',
154
+ { currentUserId: 'u_123', perms: ['admin', 'write'] },
155
+ )
156
+ ```
157
+
158
+ The instance is generic over the context type (`bonsai<AppContext>()`), giving
159
+ you end-to-end type safety: `ctx` is typed inside the function, and the call
160
+ site is type-checked against the same shape. If your context type has required
161
+ fields, TypeScript also requires you to pass the `context` argument to
162
+ `evaluate`, `evaluateSync`, and compiled-expression evaluation.
163
+
164
+ The context is passed to your function by reference, not copied or frozen. The
165
+ `Readonly<TCtx>` parameter type signals that you should treat it as read-only:
166
+ TypeScript flags reassigning its top-level fields. Bonsai does not deep-freeze
167
+ it, so nested mutation and writes from untyped JavaScript reach the object you
168
+ passed in. If you need isolation between evaluations, pass a fresh context
169
+ object each time.
170
+
171
+ Pure functions (`addFunction`) and context-aware functions (`addContextFunction`)
172
+ share a single namespace. Registering the same name with either method
173
+ overwrites the prior registration, so re-registering a context-aware name with
174
+ `addFunction` turns it back into a pure function (check `isContextFunction(name)`
175
+ if the kind matters). Functions registered with `addFunction` never receive the
176
+ context; reach for `addContextFunction` when a function needs it. Plugins typed
177
+ against a minimal context requirement can still be applied to instances with a
178
+ wider context shape.
179
+
128
180
  ### Editor validation
129
181
 
130
182
  ```ts
@@ -361,20 +413,25 @@ evaluateExpression<number>('x * 2', { x: 21 }) // 42
361
413
  ## Instance Methods
362
414
 
363
415
  ```ts
364
- interface BonsaiInstance {
365
- use(plugin: BonsaiPlugin): this
416
+ type EvaluationContextArgs<TCtx extends object = Record<string, unknown>> =
417
+ {} extends TCtx ? [context?: TCtx] : [context: TCtx]
418
+
419
+ interface BonsaiInstance<TCtx extends object = Record<string, unknown>> {
420
+ use(plugin: BonsaiPlugin<TCtx>): this
366
421
  addTransform(name: string, fn: TransformFn): this
367
422
  addFunction(name: string, fn: FunctionFn): this
423
+ addContextFunction(name: string, fn: ContextFunctionFn<TCtx>): this
368
424
  removeTransform(name: string): boolean
369
425
  removeFunction(name: string): boolean
370
426
  hasTransform(name: string): boolean
371
427
  hasFunction(name: string): boolean
428
+ isContextFunction(name: string): boolean
372
429
  listTransforms(): string[]
373
430
  listFunctions(): string[]
374
431
  clearCache(): void
375
- compile(expression: string): CompiledExpression
376
- evaluate<T = unknown>(expression: string, context?: Record<string, unknown>): Promise<T>
377
- evaluateSync<T = unknown>(expression: string, context?: Record<string, unknown>): T
432
+ compile(expression: string): CompiledExpression<TCtx>
433
+ evaluate<T = unknown>(expression: string, ...args: EvaluationContextArgs<TCtx>): Promise<T>
434
+ evaluateSync<T = unknown>(expression: string, ...args: EvaluationContextArgs<TCtx>): T
378
435
  validate(expression: string): ValidationResult
379
436
  }
380
437
  ```
@@ -382,9 +439,12 @@ interface BonsaiInstance {
382
439
  Method notes:
383
440
 
384
441
  - `use()` runs a plugin immediately and returns the same instance.
385
- - `addTransform()` and `addFunction()` overwrite any existing registration with the same name.
386
- - `listTransforms()` and `listFunctions()` return the currently registered names.
442
+ - `addTransform()`, `addFunction()`, and `addContextFunction()` overwrite any existing registration with the same name. Pure and context-aware functions share a single namespace.
443
+ - `addContextFunction()` registers a function that receives the live evaluation context as its first argument (typed read-only; passed by reference, not copied or frozen). See [Context-aware functions](#context-aware-functions).
444
+ - `isContextFunction()` returns `true` if the named function was registered via `addContextFunction()`.
445
+ - `listTransforms()` and `listFunctions()` return the currently registered names. `listFunctions()` includes both pure and context-aware functions.
387
446
  - `clearCache()` clears the internal AST cache and compiled-expression cache. It does not remove registered transforms/functions.
447
+ - Pass a context type generic to `bonsai<MyContext>()` for end-to-end type safety: `evaluate`, `evaluateSync`, `addContextFunction`, `compile`, and `use` all propagate the type. If `MyContext` has required fields, TypeScript requires a context argument when evaluating.
388
448
 
389
449
  ## Extending the Runtime
390
450
 
@@ -1,4 +1,4 @@
1
- import { c as InferredTypeName, n as BonsaiInstance } from "../types-BhsZZsLY.mjs";
1
+ import { r as BonsaiInstance, u as InferredTypeName } from "../types-C58c6SlH.mjs";
2
2
 
3
3
  //#region src/autocomplete/tokenizer.d.ts
4
4
  /** Known phases where errors can occur during completion. */
@@ -1,5 +1,5 @@
1
- import { i as ExpressionError, n as BonsaiSecurityError, r as BonsaiTypeError, t as BonsaiReferenceError } from "../errors-t5i9AEaf.mjs";
2
- import { i as tokenize, t as BLOCKED_PROPERTIES } from "../execution-context-DqYC3L92.mjs";
1
+ import { i as ExpressionError, n as BonsaiSecurityError, r as BonsaiTypeError, t as BonsaiReferenceError } from "../errors-BONB6P_5.mjs";
2
+ import { i as tokenize, t as BLOCKED_PROPERTIES } from "../execution-context-B-5hQ92X.mjs";
3
3
  //#region src/autocomplete/tokenizer.ts
4
4
  function tolerantTokenize(expression, cursor, onError) {
5
5
  if (expression.length === 0) return {
@@ -66,7 +66,7 @@ function isCursorInsideString(expression, cursor) {
66
66
  }
67
67
  return inSingle || inDouble || inTemplate && templateDepth === 0;
68
68
  }
69
- const TOKEN_RE = /\?\.|\.\.\.|\|>|\?\?|&&|\|\||[!=<>]=?|[+\-*/%]|\*\*|[.(){}[\],?:]|"(?:[^"\\]|\\.)*"?|'(?:[^'\\]|\\.)*'?|`(?:[^`\\$]|\\.|\$(?!\{))*`?|\d[\d_]*(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?|[a-zA-Z_$][\w$]*/g;
69
+ const TOKEN_RE = /\?\.|\.\.\.|\|>|\?\?|&&|\|\||[!=<>]=?|[+\-*/%]|\*\*|[.(){}[\],?:]|"(?:[^"\\]|\\.)*"?|'(?:[^'\\]|\\.)*'?|`(?:[^`\\$]|\\.|\$(?!\{))*`?|\d[\d_]*(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?|[a-zA-Z_$][\w$]*/gu;
70
70
  function regexScan(expression, cursor) {
71
71
  const tokens = [];
72
72
  const text = expression.slice(0, cursor);
@@ -121,13 +121,13 @@ function classifyToken(value, start, end) {
121
121
  start,
122
122
  end
123
123
  };
124
- if (/^[a-zA-Z_$]/.test(value)) return {
124
+ if (/^[a-zA-Z_$]/u.test(value)) return {
125
125
  type: "Identifier",
126
126
  value,
127
127
  start,
128
128
  end
129
129
  };
130
- if (/^\d/.test(value)) return {
130
+ if (/^\d/u.test(value)) return {
131
131
  type: "Number",
132
132
  value,
133
133
  start,
@@ -686,7 +686,7 @@ function comparator(a, b) {
686
686
  }
687
687
  //#endregion
688
688
  //#region src/autocomplete/index.ts
689
- const VALID_IDENTIFIER = /^[a-zA-Z_$][\w$]*$/;
689
+ const VALID_IDENTIFIER = /^[a-zA-Z_$][\w$]*$/u;
690
690
  /** Check if an error is an expected Bonsai error (syntax, security, type, or reference). */
691
691
  function isExpectedError(err) {
692
692
  return err instanceof ExpressionError || err instanceof BonsaiSecurityError || err instanceof BonsaiTypeError || err instanceof BonsaiReferenceError;
@@ -962,9 +962,9 @@ function probeAcceptedTransforms(instance, inputType, cache, currentTransforms,
962
962
  return accepted;
963
963
  }
964
964
  function inferPipeInputType(expression, cursor, instance, context, onError) {
965
- const pipeMatch = expression.slice(0, cursor).match(/^(.*)\|>\s*\w*\s*$/s);
965
+ const pipeMatch = expression.slice(0, cursor).match(/^(?<input>.*)\|>\s*\w*\s*$/su);
966
966
  if (!pipeMatch) return void 0;
967
- const exprBefore = pipeMatch[1].trim();
967
+ const exprBefore = (pipeMatch.groups?.input ?? "").trim();
968
968
  if (!exprBefore) return void 0;
969
969
  try {
970
970
  const result = instance.evaluateSync(exprBefore, context);
@@ -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$]*/g\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_$]/.test(value)) return { type: 'Identifier', value, start, end }\n if (/^\\d/.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$]*$/\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(/^(.*)\\|>\\s*\\w*\\s*$/s)\n if (!pipeMatch) return undefined\n\n const exprBefore = pipeMatch[1].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 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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"}
@@ -118,8 +118,7 @@ function formatError(message, location, suggestion) {
118
118
  const lineEnd = getLineEnd(source, start);
119
119
  const lineText = source.slice(lineStart, lineEnd);
120
120
  const caretStart = start - lineStart;
121
- const caretEnd = Math.min(end, lineEnd);
122
- const caretLength = Math.max(1, caretEnd - start);
121
+ const caretLength = Math.max(1, Math.min(end, lineEnd) - start);
123
122
  const gutter = `${startPos.line} | `;
124
123
  let result = `${message}\n\n${gutter}${lineText}\n${" ".repeat(gutter.length + caretStart)}${"^".repeat(caretLength)}`;
125
124
  if (suggestion) result += ` Did you mean "${suggestion}"?`;
@@ -162,4 +161,4 @@ function levenshtein(a, b) {
162
161
  //#endregion
163
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 };
164
163
 
165
- //# sourceMappingURL=errors-t5i9AEaf.mjs.map
164
+ //# sourceMappingURL=errors-BONB6P_5.mjs.map