/**
 * WASM parse worker entry point.
 *
 * Runs as a Node.js `worker_threads` worker. Owns every tree-sitter WASM call
 * so that fatal V8 aborts from the grammar (#965) kill only this worker —
 * never the main build process.
 *
 * For each `parse` request:
 *   1. Pick the language by file extension.
 *   2. Lazy-load the WASM grammar (first request per language).
 *   3. Parse the source.
 *   4. Run the extractor.
 *   5. Run the unified AST visitor walk (ast-store, complexity, CFG, dataflow)
 *      so that all analysis data is pre-computed before the tree is freed.
 *   6. `tree.delete()` in a finally block to release WASM linear memory.
 *   7. Serialize ExtractorOutput to a structured-clone-safe form and respond.
 *
 * The worker does NOT import from `./parser.js` — that module owns
 * process-global parser/grammar caches that are not worker-safe. Instead
 * this file keeps its own per-worker caches and its own (local) language
 * registry. Extractors are imported directly from `../extractors/index.js`.
 */
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { parentPort } from 'node:worker_threads';
import { Language, Parser, Query } from 'web-tree-sitter';
import { computeLOCMetrics, computeMaintainabilityIndex } from '../ast-analysis/metrics.js';
import { AST_STRING_CONFIGS, AST_TYPE_MAPS, astStopRecurseKinds, CFG_RULES, COMPLEXITY_RULES, DATAFLOW_RULES, HALSTEAD_RULES, } from '../ast-analysis/rules/index.js';
import { walkWithVisitors } from '../ast-analysis/visitor.js';
import { functionName as getFuncName } from '../ast-analysis/visitor-utils.js';
import { createAstStoreVisitor } from '../ast-analysis/visitors/ast-store-visitor.js';
import { createCfgVisitor } from '../ast-analysis/visitors/cfg-visitor.js';
import { createComplexityVisitor } from '../ast-analysis/visitors/complexity-visitor.js';
import { createDataflowVisitor } from '../ast-analysis/visitors/dataflow-visitor.js';
import { extractBashSymbols, extractClojureSymbols, extractCppSymbols, extractCSharpSymbols, extractCSymbols, extractCudaSymbols, extractDartSymbols, extractElixirSymbols, extractErlangSymbols, extractFSharpSymbols, extractGleamSymbols, extractGoSymbols, extractGroovySymbols, extractHaskellSymbols, extractHCLSymbols, extractJavaSymbols, extractJuliaSymbols, extractKotlinSymbols, extractLuaSymbols, extractObjCSymbols, extractOCamlSymbols, extractPHPSymbols, extractPythonSymbols, extractRSymbols, extractRubySymbols, extractRustSymbols, extractScalaSymbols, extractSoliditySymbols, extractSwiftSymbols, extractSymbols, extractVerilogSymbols, extractZigSymbols, } from '../extractors/index.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/** Grammars ship at `<repo-root>/grammars/`. Worker file is at
 *  `src/domain/wasm-worker-entry.ts` (or `dist/domain/wasm-worker-entry.js`);
 *  both resolve to the same relative path. */
function grammarPath(name) {
    return path.join(__dirname, '..', '..', 'grammars', name);
}
// ── Shared JS/TS/TSX query patterns (mirrors parser.ts) ─────────────────────
const COMMON_QUERY_PATTERNS = [
    '(function_declaration name: (identifier) @fn_name) @fn_node',
    '(generator_function_declaration name: (identifier) @fn_name) @fn_node',
    '(variable_declarator name: (identifier) @varfn_name value: (arrow_function) @varfn_value)',
    '(variable_declarator name: (identifier) @varfn_name value: (function_expression) @varfn_value)',
    '(variable_declarator name: (identifier) @varfn_name value: (generator_function) @varfn_value)',
    '(method_definition name: (property_identifier) @meth_name) @meth_node',
    '(method_definition name: (private_property_identifier) @meth_name) @meth_node',
    '(import_statement source: (string) @imp_source) @imp_node',
    '(export_statement) @exp_node',
    '(call_expression function: (identifier) @callfn_name) @callfn_node',
    '(call_expression function: (member_expression) @callmem_fn) @callmem_node',
    '(call_expression function: (subscript_expression) @callsub_fn) @callsub_node',
    '(new_expression constructor: (identifier) @newfn_name) @newfn_node',
    '(new_expression constructor: (member_expression) @newmem_fn) @newmem_node',
    '(expression_statement (assignment_expression left: (member_expression) @assign_left right: (_) @assign_right)) @assign_node',
];
const JS_CLASS_PATTERN = '(class_declaration name: (identifier) @cls_name) @cls_node';
const TS_EXTRA_PATTERNS = [
    '(class_declaration name: (type_identifier) @cls_name) @cls_node',
    '(abstract_class_declaration name: (type_identifier) @cls_name) @cls_node',
    '(interface_declaration name: (type_identifier) @iface_name) @iface_node',
    '(type_alias_declaration name: (type_identifier) @type_name) @type_node',
];
// ── Local language registry ─────────────────────────────────────────────────
// Local copy — re-using parser.ts's registry would drag in its process-wide
// parser/grammar caches, which are not safe to share across worker threads.
const LANGUAGE_REGISTRY = [
    {
        id: 'javascript',
        extensions: ['.js', '.jsx', '.mjs', '.cjs'],
        grammarFile: 'tree-sitter-javascript.wasm',
        extractor: extractSymbols,
        required: true,
    },
    {
        id: 'typescript',
        extensions: ['.ts'],
        grammarFile: 'tree-sitter-typescript.wasm',
        extractor: extractSymbols,
        required: true,
    },
    {
        id: 'tsx',
        extensions: ['.tsx'],
        grammarFile: 'tree-sitter-tsx.wasm',
        extractor: extractSymbols,
        required: true,
    },
    {
        id: 'hcl',
        extensions: ['.tf', '.hcl'],
        grammarFile: 'tree-sitter-hcl.wasm',
        extractor: extractHCLSymbols,
        required: false,
    },
    {
        id: 'python',
        extensions: ['.py', '.pyi'],
        grammarFile: 'tree-sitter-python.wasm',
        extractor: extractPythonSymbols,
        required: false,
    },
    {
        id: 'go',
        extensions: ['.go'],
        grammarFile: 'tree-sitter-go.wasm',
        extractor: extractGoSymbols,
        required: false,
    },
    {
        id: 'rust',
        extensions: ['.rs'],
        grammarFile: 'tree-sitter-rust.wasm',
        extractor: extractRustSymbols,
        required: false,
    },
    {
        id: 'java',
        extensions: ['.java'],
        grammarFile: 'tree-sitter-java.wasm',
        extractor: extractJavaSymbols,
        required: false,
    },
    {
        id: 'csharp',
        extensions: ['.cs'],
        grammarFile: 'tree-sitter-c_sharp.wasm',
        extractor: extractCSharpSymbols,
        required: false,
    },
    {
        id: 'ruby',
        extensions: ['.rb', '.rake', '.gemspec'],
        grammarFile: 'tree-sitter-ruby.wasm',
        extractor: extractRubySymbols,
        required: false,
    },
    {
        id: 'php',
        extensions: ['.php', '.phtml'],
        grammarFile: 'tree-sitter-php.wasm',
        extractor: extractPHPSymbols,
        required: false,
    },
    {
        id: 'c',
        extensions: ['.c', '.h'],
        grammarFile: 'tree-sitter-c.wasm',
        extractor: extractCSymbols,
        required: false,
    },
    {
        id: 'cpp',
        extensions: ['.cpp', '.cc', '.cxx', '.hpp'],
        grammarFile: 'tree-sitter-cpp.wasm',
        extractor: extractCppSymbols,
        required: false,
    },
    {
        id: 'kotlin',
        extensions: ['.kt', '.kts'],
        grammarFile: 'tree-sitter-kotlin.wasm',
        extractor: extractKotlinSymbols,
        required: false,
    },
    {
        id: 'swift',
        extensions: ['.swift'],
        grammarFile: 'tree-sitter-swift.wasm',
        extractor: extractSwiftSymbols,
        required: false,
    },
    {
        id: 'scala',
        extensions: ['.scala'],
        grammarFile: 'tree-sitter-scala.wasm',
        extractor: extractScalaSymbols,
        required: false,
    },
    {
        id: 'bash',
        extensions: ['.sh', '.bash'],
        grammarFile: 'tree-sitter-bash.wasm',
        extractor: extractBashSymbols,
        required: false,
    },
    {
        id: 'elixir',
        extensions: ['.ex', '.exs'],
        grammarFile: 'tree-sitter-elixir.wasm',
        extractor: extractElixirSymbols,
        required: false,
    },
    {
        id: 'lua',
        extensions: ['.lua'],
        grammarFile: 'tree-sitter-lua.wasm',
        extractor: extractLuaSymbols,
        required: false,
    },
    {
        id: 'dart',
        extensions: ['.dart'],
        grammarFile: 'tree-sitter-dart.wasm',
        extractor: extractDartSymbols,
        required: false,
    },
    {
        id: 'zig',
        extensions: ['.zig'],
        grammarFile: 'tree-sitter-zig.wasm',
        extractor: extractZigSymbols,
        required: false,
    },
    {
        id: 'haskell',
        extensions: ['.hs'],
        grammarFile: 'tree-sitter-haskell.wasm',
        extractor: extractHaskellSymbols,
        required: false,
    },
    {
        id: 'ocaml',
        extensions: ['.ml'],
        grammarFile: 'tree-sitter-ocaml.wasm',
        extractor: extractOCamlSymbols,
        required: false,
    },
    {
        id: 'ocaml-interface',
        extensions: ['.mli'],
        grammarFile: 'tree-sitter-ocaml_interface.wasm',
        extractor: extractOCamlSymbols,
        required: false,
    },
    {
        id: 'fsharp',
        extensions: ['.fs', '.fsx'],
        grammarFile: 'tree-sitter-fsharp.wasm',
        extractor: extractFSharpSymbols,
        required: false,
    },
    {
        id: 'fsharp-signature',
        extensions: ['.fsi'],
        grammarFile: 'tree-sitter-fsharp_signature.wasm',
        extractor: extractFSharpSymbols,
        required: false,
    },
    {
        id: 'gleam',
        extensions: ['.gleam'],
        grammarFile: 'tree-sitter-gleam.wasm',
        extractor: extractGleamSymbols,
        required: false,
    },
    {
        id: 'clojure',
        extensions: ['.clj', '.cljs', '.cljc'],
        grammarFile: 'tree-sitter-clojure.wasm',
        extractor: extractClojureSymbols,
        required: false,
    },
    {
        id: 'julia',
        extensions: ['.jl'],
        grammarFile: 'tree-sitter-julia.wasm',
        extractor: extractJuliaSymbols,
        required: false,
    },
    {
        id: 'r',
        extensions: ['.r', '.R'],
        grammarFile: 'tree-sitter-r.wasm',
        extractor: extractRSymbols,
        required: false,
    },
    {
        id: 'erlang',
        extensions: ['.erl', '.hrl'],
        grammarFile: 'tree-sitter-erlang.wasm',
        extractor: extractErlangSymbols,
        required: false,
    },
    {
        id: 'solidity',
        extensions: ['.sol'],
        grammarFile: 'tree-sitter-solidity.wasm',
        extractor: extractSoliditySymbols,
        required: false,
    },
    {
        id: 'objc',
        extensions: ['.m'],
        grammarFile: 'tree-sitter-objc.wasm',
        extractor: extractObjCSymbols,
        required: false,
    },
    {
        id: 'cuda',
        extensions: ['.cu', '.cuh'],
        grammarFile: 'tree-sitter-cuda.wasm',
        extractor: extractCudaSymbols,
        required: false,
    },
    {
        id: 'groovy',
        extensions: ['.groovy', '.gvy'],
        grammarFile: 'tree-sitter-groovy.wasm',
        extractor: extractGroovySymbols,
        required: false,
    },
    {
        id: 'verilog',
        extensions: ['.v', '.sv'],
        grammarFile: 'tree-sitter-verilog.wasm',
        extractor: extractVerilogSymbols,
        required: false,
    },
];
const _extToLang = new Map();
for (const entry of LANGUAGE_REGISTRY) {
    for (const ext of entry.extensions) {
        _extToLang.set(ext.toLowerCase(), entry);
    }
}
// ── Per-worker caches (not shared across workers) ───────────────────────────
let _runtimeInitialized = false;
// Value of `null` means "tried to load, grammar is optional and failed" —
// we cache the failure so we don't retry on every file.
const _parsers = new Map();
const _queries = new Map();
async function initRuntime() {
    if (_runtimeInitialized)
        return;
    await Parser.init();
    _runtimeInitialized = true;
}
/**
 * Load the grammar for a language on demand. Returns the cached parser, or
 * `null` if the grammar is optional and failed to load (same convention as
 * parser.ts). Throws for required grammars that fail.
 */
async function loadLanguageLazy(entry) {
    if (_parsers.has(entry.id))
        return _parsers.get(entry.id) ?? null;
    try {
        const lang = await Language.load(grammarPath(entry.grammarFile));
        const parser = new Parser();
        parser.setLanguage(lang);
        _parsers.set(entry.id, parser);
        // Build the JS/TS/TSX query (mirrors parser.ts::doLoadLanguage)
        if (entry.extractor === extractSymbols && !_queries.has(entry.id)) {
            const isTS = entry.id === 'typescript' || entry.id === 'tsx';
            const patterns = isTS
                ? [...COMMON_QUERY_PATTERNS, ...TS_EXTRA_PATTERNS]
                : [...COMMON_QUERY_PATTERNS, JS_CLASS_PATTERN];
            _queries.set(entry.id, new Query(lang, patterns.join('\n')));
        }
        return parser;
    }
    catch (e) {
        if (entry.required) {
            throw new Error(`Required parser ${entry.id} failed to initialize: ${e.message}`);
        }
        _parsers.set(entry.id, null);
        return null;
    }
}
// ── Helpers mirroring engine.ts (copied, db-free) ───────────────────────────
function hasFuncBody(d) {
    return ((d.kind === 'function' || d.kind === 'method') &&
        d.line > 0 &&
        d.endLine != null &&
        d.endLine > d.line &&
        !d.name.includes('.'));
}
function indexByLine(results) {
    const byLine = new Map();
    for (const r of results) {
        if (!r.funcNode)
            continue;
        const line = r.funcNode.startPosition.row + 1;
        if (!byLine.has(line))
            byLine.set(line, []);
        byLine.get(line)?.push(r);
    }
    return byLine;
}
function matchResultToDef(candidates, defName) {
    if (!candidates)
        return undefined;
    if (candidates.length === 1)
        return candidates[0];
    return (candidates.find((r) => {
        const n = r.funcNode.childForFieldName('name');
        return n && n.text === defName;
    }) ?? candidates[0]);
}
/** Override a definition's cyclomatic complexity with a CFG-derived value and recompute MI. */
function overrideCyclomaticFromCfg(def, cfgCyclomatic) {
    if (!def.complexity)
        return;
    if (cfgCyclomatic <= 0)
        return;
    def.complexity.cyclomatic = cfgCyclomatic;
    const { loc, halstead } = def.complexity;
    const volume = halstead ? halstead.volume : 0;
    const commentRatio = loc && loc.loc > 0 ? loc.commentLines / loc.loc : 0;
    def.complexity.maintainabilityIndex = computeMaintainabilityIndex(volume, cfgCyclomatic, loc?.sloc ?? 0, commentRatio);
}
function storeComplexityResults(results, defs, langId) {
    const byLine = indexByLine((results.complexity || []));
    for (const def of defs) {
        if ((def.kind === 'function' || def.kind === 'method') && def.line && !def.complexity) {
            const funcResult = matchResultToDef(byLine.get(def.line), def.name);
            if (!funcResult)
                continue;
            const { metrics } = funcResult;
            const loc = computeLOCMetrics(funcResult.funcNode, langId);
            const volume = metrics.halstead ? metrics.halstead.volume : 0;
            const commentRatio = loc.loc > 0 ? loc.commentLines / loc.loc : 0;
            const mi = computeMaintainabilityIndex(volume, metrics.cyclomatic, loc.sloc, commentRatio);
            def.complexity = {
                cognitive: metrics.cognitive,
                cyclomatic: metrics.cyclomatic,
                maxNesting: metrics.maxNesting,
                halstead: metrics.halstead,
                loc,
                maintainabilityIndex: mi,
            };
        }
    }
}
function storeCfgResults(results, defs) {
    const byLine = indexByLine((results.cfg || []));
    for (const def of defs) {
        if ((def.kind === 'function' || def.kind === 'method') &&
            def.line &&
            !def.cfg?.blocks?.length) {
            const cfgResult = matchResultToDef(byLine.get(def.line), def.name);
            if (!cfgResult)
                continue;
            def.cfg = { blocks: cfgResult.blocks, edges: cfgResult.edges };
            if (cfgResult.cyclomatic != null) {
                overrideCyclomaticFromCfg(def, cfgResult.cyclomatic);
            }
        }
    }
}
/**
 * Build the AST-store visitor for `langId`. Returns `null` when AST is
 * disabled or the language has no AST type map. db-free — passes an empty
 * nodeIdMap. The main thread re-resolves parent node IDs in
 * `features/ast.ts::collectFileAstRows`.
 */
function buildAstVisitor(langId, defs, relPath, enabled) {
    if (!enabled)
        return null;
    const astTypeMap = AST_TYPE_MAPS.get(langId);
    if (!astTypeMap)
        return null;
    const stringConfig = AST_STRING_CONFIGS.get(langId);
    return createAstStoreVisitor(astTypeMap, defs, relPath, new Map(), stringConfig, astStopRecurseKinds(langId));
}
/**
 * Build the complexity visitor when enabled, the language has complexity
 * rules, and at least one definition still lacks a `complexity` payload.
 * Side-effect: extends `walkerOpts` with nesting-node types and a
 * `getFunctionName` resolver suitable for this language.
 */
function buildComplexityVisitor(langId, defs, enabled, walkerOpts) {
    if (!enabled)
        return null;
    const cRules = COMPLEXITY_RULES.get(langId);
    if (!cRules || !defs.some((d) => hasFuncBody(d) && !d.complexity))
        return null;
    const hRules = HALSTEAD_RULES.get(langId);
    const visitor = createComplexityVisitor(cRules, hRules, { fileLevelWalk: true, langId });
    for (const t of cRules.nestingNodes)
        walkerOpts.nestingNodeTypes?.add(t);
    const dfRules = DATAFLOW_RULES.get(langId);
    walkerOpts.getFunctionName = (node) => {
        const nameNode = node.childForFieldName('name');
        if (nameNode)
            return nameNode.text;
        // dfRules shape varies per language; visitor-utils accepts any shape
        if (dfRules)
            return getFuncName(node, dfRules);
        return null;
    };
    return visitor;
}
/** Build the CFG visitor when enabled and at least one definition still lacks blocks. */
function buildCfgVisitor(langId, defs, enabled) {
    if (!enabled)
        return null;
    const cfgRulesForLang = CFG_RULES.get(langId);
    if (!cfgRulesForLang)
        return null;
    const needsCfg = defs.some((d) => hasFuncBody(d) && d.cfg !== null && !Array.isArray(d.cfg?.blocks));
    if (!needsCfg)
        return null;
    return createCfgVisitor(cfgRulesForLang);
}
/** Build the dataflow visitor when enabled and `symbols.dataflow` is not yet populated. */
function buildDataflowVisitor(langId, symbols, enabled) {
    if (!enabled)
        return null;
    const dfRules = DATAFLOW_RULES.get(langId);
    if (!dfRules || symbols.dataflow)
        return null;
    return createDataflowVisitor(dfRules);
}
function setupVisitorsLocal(symbols, relPath, langId, opts) {
    const defs = symbols.definitions || [];
    const walkerOpts = {
        functionNodeTypes: new Set(),
        nestingNodeTypes: new Set(),
        getFunctionName: (_node) => null,
    };
    const astVisitor = buildAstVisitor(langId, defs, relPath, !!opts.ast);
    const complexityVisitor = buildComplexityVisitor(langId, defs, !!opts.complexity, walkerOpts);
    const cfgVisitor = buildCfgVisitor(langId, defs, !!opts.cfg);
    const dataflowVisitor = buildDataflowVisitor(langId, symbols, !!opts.dataflow);
    const visitors = [];
    if (astVisitor)
        visitors.push(astVisitor);
    if (complexityVisitor)
        visitors.push(complexityVisitor);
    if (cfgVisitor)
        visitors.push(cfgVisitor);
    if (dataflowVisitor)
        visitors.push(dataflowVisitor);
    return { visitors, walkerOpts, astVisitor, complexityVisitor, cfgVisitor, dataflowVisitor };
}
// ── Main parse handler ──────────────────────────────────────────────────────
/**
 * Run tree-sitter parse + extractor on `code`. Returns `null` when either
 * step yields no usable output. Throws (for the caller to report back to the
 * pool) only on a hard tree-sitter parse error.
 */
function parseAndExtract(parser, entry, filePath, code) {
    let tree;
    try {
        tree = parser.parse(code);
    }
    catch (e) {
        // Parse error — report back but keep worker alive.
        throw new Error(`parse failed: ${e.message}`);
    }
    if (!tree)
        return null;
    // Extractor — on failure, skip file (ok:true, null) to match parser.ts
    // behavior where extractor issues don't crash the build. Dispose the tree
    // before returning null so WASM linear memory doesn't accumulate in the worker.
    let symbols;
    try {
        const query = _queries.get(entry.id);
        // tree-sitter's Tree/Query are structurally compatible with
        // TreeSitterTree/TreeSitterQuery at runtime — same cast style as
        // parser.ts::wasmExtractSymbols (parser.ts:789).
        symbols = entry.extractor(tree, filePath, query) ?? null;
    }
    catch {
        disposeTree(tree);
        return null;
    }
    if (!symbols) {
        disposeTree(tree);
        return null;
    }
    return { tree, symbols };
}
/**
 * Project the visitor `ast-store` rows into the wire-safe shape returned to
 * the main thread. Strips `file` and `parentNodeId` — both are re-resolved in
 * `features/ast.ts::collectFileAstRows`. Always returns an array (even empty)
 * so `engine.ts::fileNeedsWasmTree` doesn't treat the file as un-walked and
 * trigger a full ensureWasmTrees re-parse (#1036).
 */
function projectAstNodes(results) {
    const astRows = (results['ast-store'] || []);
    return astRows.map((n) => ({
        line: n.line,
        kind: n.kind,
        name: n.name ?? '',
        text: n.text ?? undefined,
        receiver: n.receiver ?? undefined,
    }));
}
/**
 * Run the configured visitor walk over `tree.rootNode` and apply each
 * visitor's results back onto `symbols`. Returns the serialized astNodes
 * (or `undefined` when AST is disabled / no rows produced).
 *
 * Mirrors engine.ts:791-829. Runs BEFORE `tree.delete()` because
 * storeComplexityResults / storeCfgResults read `funcNode` off live nodes.
 */
function runVisitorWalk(tree, symbols, langId, setup) {
    if (setup.visitors.length === 0)
        return undefined;
    // rootNode shape matches TreeSitterNode at runtime — same cast as parser.ts:789.
    const results = walkWithVisitors(tree.rootNode, setup.visitors, langId, setup.walkerOpts);
    const defs = symbols.definitions || [];
    let serializedAstNodes;
    if (setup.astVisitor)
        serializedAstNodes = projectAstNodes(results);
    if (setup.complexityVisitor)
        storeComplexityResults(results, defs, langId);
    if (setup.cfgVisitor)
        storeCfgResults(results, defs);
    if (setup.dataflowVisitor)
        symbols.dataflow = results.dataflow;
    return serializedAstNodes;
}
/**
 * Pack the in-memory ExtractorOutput into the structured-clone-safe shape
 * sent back across the worker boundary. Converts the typeMap into a tuple
 * array and intentionally omits `_tree` (cannot cross the boundary).
 */
function serializeExtractorOutput(symbols, langId, code, astNodes) {
    return {
        definitions: symbols.definitions,
        calls: symbols.calls,
        imports: symbols.imports,
        classes: symbols.classes,
        exports: symbols.exports,
        typeMap: Array.from(symbols.typeMap.entries()),
        _langId: langId,
        _lineCount: code.split('\n').length,
        dataflow: symbols.dataflow,
        astNodes,
        ...(symbols.fnRefBindings?.length ? { fnRefBindings: symbols.fnRefBindings } : {}),
        ...(symbols.paramBindings?.length ? { paramBindings: symbols.paramBindings } : {}),
        ...(symbols.arrayElemBindings?.length ? { arrayElemBindings: symbols.arrayElemBindings } : {}),
        ...(symbols.spreadArgBindings?.length ? { spreadArgBindings: symbols.spreadArgBindings } : {}),
        ...(symbols.forOfBindings?.length ? { forOfBindings: symbols.forOfBindings } : {}),
        ...(symbols.arrayCallbackBindings?.length
            ? { arrayCallbackBindings: symbols.arrayCallbackBindings }
            : {}),
        ...(symbols.objectRestParamBindings?.length
            ? { objectRestParamBindings: symbols.objectRestParamBindings }
            : {}),
        ...(symbols.objectPropBindings?.length
            ? { objectPropBindings: symbols.objectPropBindings }
            : {}),
        ...(symbols.newExpressions?.length ? { newExpressions: symbols.newExpressions } : {}),
        ...(symbols.definePropertyReceivers?.size
            ? { definePropertyReceivers: Array.from(symbols.definePropertyReceivers.entries()) }
            : {}),
        ...(symbols.returnTypeMap?.size
            ? { returnTypeMap: Array.from(symbols.returnTypeMap.entries()) }
            : {}),
        ...(symbols.callAssignments?.length ? { callAssignments: symbols.callAssignments } : {}),
    };
}
/**
 * Release WASM linear memory backing a tree. Best-effort — swallows errors so
 * the worker keeps serving requests. Deferring this would let trees accumulate
 * in the worker's WASM heap and defeat the point of isolating parse calls.
 */
function disposeTree(tree) {
    if (!tree)
        return;
    const deletable = tree;
    if (typeof deletable.delete !== 'function')
        return;
    try {
        deletable.delete();
    }
    catch {
        // best-effort cleanup — swallow; worker continues.
    }
}
async function handleParse(msg) {
    const ext = path.extname(msg.filePath).toLowerCase();
    const entry = _extToLang.get(ext);
    if (!entry)
        return null;
    await initRuntime();
    const parser = await loadLanguageLazy(entry);
    if (!parser)
        return null;
    const parsed = parseAndExtract(parser, entry, msg.filePath, msg.code);
    if (!parsed)
        return null;
    const { tree, symbols } = parsed;
    try {
        const setup = setupVisitorsLocal(symbols, msg.filePath, entry.id, msg.opts);
        // astNodes kept in the serialized shape (without `file`/`parentNodeId`),
        // not assigned back to symbols.astNodes — ExtractorOutput.astNodes is
        // ASTNodeRow[] (DB row shape with node_id), which is a different type.
        const serializedAstNodes = runVisitorWalk(tree, symbols, entry.id, setup);
        return serializeExtractorOutput(symbols, entry.id, msg.code, serializedAstNodes);
    }
    finally {
        // ALWAYS release WASM memory before responding (see disposeTree note).
        disposeTree(tree);
    }
}
// ── Worker message loop ─────────────────────────────────────────────────────
if (!parentPort) {
    throw new Error('wasm-worker-entry must be run as a worker_thread');
}
// Test-only crash marker. When the code contains this exact magic token
// and CODEGRAPH_WASM_WORKER_TEST_CRASH=1 is set in the worker env, the
// worker calls process.exit(1) mid-parse — simulating a V8 fatal from the
// grammar so we can unit-test pool crash recovery without a real V8 abort.
const TEST_CRASH_MAGIC = '__CODEGRAPH_WASM_WORKER_TEST_CRASH__';
// The pool terminates workers via `Worker.terminate()`; no graceful-shutdown
// handshake is needed. Worker only handles `parse` messages.
parentPort.on('message', async (msg) => {
    if (msg.type !== 'parse')
        return;
    if (process.env.CODEGRAPH_WASM_WORKER_TEST_CRASH === '1' &&
        typeof msg.code === 'string' &&
        msg.code.includes(TEST_CRASH_MAGIC)) {
        // Simulate a fatal V8 abort — hard-exit the worker.
        process.exit(1);
    }
    try {
        const result = await handleParse(msg);
        const res = { type: 'result', id: msg.id, ok: true, result };
        parentPort.postMessage(res);
    }
    catch (e) {
        const res = {
            type: 'result',
            id: msg.id,
            ok: false,
            error: e.message ?? String(e),
        };
        parentPort.postMessage(res);
    }
});
//# sourceMappingURL=wasm-worker-entry.js.map