bonsai-js 0.2.0 → 0.3.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.
@@ -0,0 +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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"}