docuvia 1.2.0 → 1.3.1
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/dist/ast-worker.js +1 -1
- package/dist/ast-worker.js.map +1 -1
- package/dist/{chunk-OLQ7AP7Q.js → chunk-3WFKL5P6.js} +4 -4
- package/dist/{chunk-OLQ7AP7Q.js.map → chunk-3WFKL5P6.js.map} +1 -1
- package/dist/chunk-BR2RIZZU.js +62 -0
- package/dist/chunk-BR2RIZZU.js.map +1 -0
- package/dist/{chunk-LWT4KGH6.js → chunk-EUTMBLVT.js} +3 -3
- package/dist/{chunk-LWT4KGH6.js.map → chunk-EUTMBLVT.js.map} +1 -1
- package/dist/{chunk-QEZOSEBR.js → chunk-KZYHFZZS.js} +2 -2
- package/dist/cli.js +119 -117
- package/dist/cli.js.map +1 -1
- package/dist/fs-utils-4AOXK4SJ.js +3 -0
- package/dist/{init-templates-7JN6UT5K.js → init-templates-VHTM5VWK.js} +2 -2
- package/dist/migrations/0011_ast_call_sites_target_idx.sql +8 -0
- package/package.json +1 -1
- package/dist/chunk-NLHGZSCF.js +0 -62
- package/dist/chunk-NLHGZSCF.js.map +0 -1
- package/dist/fs-utils-ZWCVXIRW.js +0 -3
- /package/dist/{chunk-QEZOSEBR.js.map → chunk-KZYHFZZS.js.map} +0 -0
- /package/dist/{fs-utils-ZWCVXIRW.js.map → fs-utils-4AOXK4SJ.js.map} +0 -0
- /package/dist/{init-templates-7JN6UT5K.js.map → init-templates-VHTM5VWK.js.map} +0 -0
package/dist/ast-worker.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
|
-
import{c
|
|
2
|
+
import{c,d as a,e as L,f as y,h as _}from"./chunk-BR2RIZZU.js";import{ca as P,d as R,fa as N}from"./chunk-3WFKL5P6.js";import"./chunk-KVOCOCYJ.js";import{parentPort as f}from"worker_threads";import{Parser as b,Language as k}from"web-tree-sitter";import*as O from"path";import*as w from"fs";import{createHash as U}from"crypto";function E(e){return U(P).update(e.text).digest("hex")}var h=new R(e=>f?.postMessage(e));process.on("uncaughtException",e=>{h.error(a.WORKER_UNCAUGHT_EXCEPTION,{error:e instanceof Error?e.message:String(e)})});process.on("unhandledRejection",e=>{h.error(a.WORKER_UNHANDLED_REJECTION,{error:e instanceof Error?e.message:String(e)})});var S=!1,T=null;function H(){return T??=y(),T}var C=new Map;function G(e){let t=C.get(e);return t||(t=k.load(e),C.set(e,t)),t}function Q(e){let t=[],n=e.matchAll(/import\s+{([^}]+)}\s+from\s+['"]([^'"]+)['"]/g);for(let s of n)t.push({localName:s[1].trim(),originalName:s[1].trim(),modulePath:s[2]});return t}function F(e){return e.childForFieldName("name")?.text||e.descendantsOfType("identifier")[0]?.text||a.ANONYMOUS_NAME}function Y(e){let t=e.childForFieldName("declarator")?.childForFieldName("declarator");if(t)return t.type===c.QUALIFIED_IDENTIFIER?t.childForFieldName("name")?.text??a.ANONYMOUS_NAME:t.text}function V(e){let t=e.childForFieldName("name");if(t)return t.text;let n=Y(e);if(n)return n;let s=new Set([c.VARIABLE_DECLARATOR,c.ASSIGNMENT_EXPRESSION,c.PAIR,c.PUBLIC_FIELD_DEFINITION]),r=e.parent;for(;r&&r.type!==c.ARGUMENTS;){if(s.has(r.type)){let o=r.childForFieldName("name")||r.childForFieldName("key")||r.childForFieldName("left");if(o)return o.text}r=r.parent}return a.ANONYMOUS_NAME}function W(e){return(e.childForFieldName("function")||e.childForFieldName("name")||e.childForFieldName("method"))?.text??e.text}function J(e){return((e.childForFieldName("property")||e.childForFieldName("field")||e.childForFieldName("attribute")||e.childForFieldName("name")||e.childForFieldName("method"))??e).startPosition}function g(e,t){let n=e.parent;for(;n;){if(t.has(n.id))return F(n);n=n.parent}return a.ANONYMOUS_NAME}function D(e){return e.type===c.TYPE_IDENTIFIER?e.text:e.descendantsOfType(c.TYPE_IDENTIFIER)[0]?.text}function K(e){let t=e.parent;for(;t;){if(t.type===c.IMPL_ITEM){let n=t.childForFieldName("type");return n?D(n):void 0}t=t.parent}}function j(e){let n=e.childForFieldName("receiver")?.namedChild(0)?.childForFieldName("type");return n?D(n):void 0}function B(e){let n=e.childForFieldName("declarator")?.childForFieldName("declarator");if(n?.type!==c.QUALIFIED_IDENTIFIER)return;let s=n.childForFieldName("scope");if(s)return s.type===c.QUALIFIED_IDENTIFIER?s.childForFieldName("name")?.text:s.text}function X(e){return K(e)??j(e)??B(e)}function z(e,t,n){let s=t.extractClasses(e.rootNode);for(let r of s)n.push({name:F(r),startLine:r.startPosition.row,endLine:r.endPosition.row,methods:[],contentHash:E(r)});return s}var Z=new Set(["arrow_function","function_expression"]);function $(e,t,n){let s=t.extractVariables?.(e.rootNode)??[];for(let r of s){let o=r.childForFieldName("value");o&&Z.has(o.type)||n.push({name:F(r),startLine:r.startPosition.row,endLine:r.endPosition.row,contentHash:E(r)})}return s}function q(e,t,n,s){let r=new Set(s.map(i=>i.id)),o=t.extractFunctions(e.rootNode);for(let i of o){let l=g(i,r),d=l===a.ANONYMOUS_NAME?X(i):l;n.push({name:V(i),startLine:i.startPosition.row,endLine:i.endPosition.row,contentHash:E(i),containerName:d})}return o}function ee(e,t,n,s){let r=new Set(n.map(i=>i.id)),o=t.extractCalls(e.rootNode);for(let i of o){if(s.length>=1e3)break;let l=J(i);s.push({sourceFunction:g(i,r),targetFunction:W(i),startLine:l.row,startColumn:l.column})}}function te(e,t,n,s){if(!t.extractImplements)return;let r=new Set(n.map(o=>o.id));for(let o of t.extractImplements(e.rootNode))s.push({sourceClass:g(o,r),targetInterface:o.text})}function ne(e,t,n,s){if(!t.extractExtends)return;let r=new Set(n.map(o=>o.id));for(let o of t.extractExtends(e.rootNode))s.push({sourceClass:g(o,r),targetClass:o.text})}var re=new Set([N.TYPESCRIPT,N.JAVASCRIPT]),se="Worker",oe="path",ie="resolve",ae="join",ce="__dirname";function le(e){if(e.type!=="call_expression")return!1;let t=e.childForFieldName("function");if(!t||t.type!=="member_expression")return!1;let n=t.childForFieldName("object")?.text,s=t.childForFieldName("property")?.text;return n===oe&&(s===ie||s===ae)}function de(e){let t=e.childForFieldName("arguments");return!t||!t.namedChildren.some(r=>r?.type==="identifier"&&r.text===ce)?void 0:t.descendantsOfType("string")[0]?.text.replace(/['"]/g,"")}function ue(e,t){for(let n of e.rootNode.descendantsOfType(c.ASSIGNMENT_EXPRESSION)){if(!n)continue;let s=n.childForFieldName("left"),r=n.childForFieldName("right");if(s?.text!==t||!r||!le(r))continue;let o=de(r);if(o)return o}}function me(e,t){if(t.type==="string")return t.text.replace(/['"]/g,"");if(t.type==="identifier"||t.type==="member_expression")return ue(e,t.text)}function ge(e,t,n,s){if(!re.has(t))return;let r=new Set(n.map(o=>o.id));for(let o of e.rootNode.descendantsOfType("new_expression")){if(!o||o.childForFieldName("constructor")?.text!==se)continue;let d=o.childForFieldName("arguments")?.namedChildren.find(p=>p!==null);if(!d)continue;let m=me(e,d);m&&s.push({sourceFunction:g(o,r),targetPath:m})}}function pe(e,t,n){let s=[],r=[],o=[],i=[],l=[],d=[],m=[],p=[],A=[],x=[];if(e){s.push(a.parsedViaTreeSitter(e.rootNode.childCount));try{let u=z(e,t,l),I=q(e,t,i,u),M=t.extractImports(e.rootNode);r.push(...L(M)),$(e,t,d),ee(e,t,I,m),te(e,t,u,p),ne(e,t,u,A),ge(e,n,I,x),s.push(a.QUERIED_VIA_LANGUAGE_PROVIDER)}catch(u){s.push(a.providerQueryFailed(u instanceof Error?u.message:String(u)))}}return{imports:r,exports:o,functions:i,classes:l,variables:d,calls:m,implements:p,extends:A,workerSpawns:x,decisions:s}}function fe(e,t){for(let n of e.drainQueryCompileFailures?.()??[])h.error(a.QUERY_COMPILE_FAILED,{language:t,kind:n.kind,pattern:n.pattern,message:n.message})}function Ne(e,t,n,s){let r=new b;r.setLanguage(n),t.initQueries?.(n),fe(t,s);let o=r.parse(e),i=pe(o,t,s);return o&&o.delete(),r.delete(),i}async function Ee(e){S||(await b.init(),S=!0);let t=await H(),n=O.extname(e.filePath),s=t.getProviderForExtension(n);if(!s)return{taskId:e.taskId,success:!0,data:{imports:[],exports:[],functions:[],classes:[],calls:[],decisions:[a.noLanguageProviderForExtension(n)]}};let{wasmPath:r,attemptedPaths:o}=_(s.wasm_file);if(!w.existsSync(r))return{taskId:e.taskId,success:!0,data:{imports:Q(e.code),exports:[],functions:[],classes:[],calls:[],decisions:[a.wasmNotFound(e.language,o.join(", "))]}};let i=await G(r),l=Ne(e.code,s,i,e.language);return{taskId:e.taskId,success:!0,data:l}}f?.on("message",async e=>{try{let t=await Ee(e);f?.postMessage(t)}catch(t){f?.postMessage({taskId:e.taskId,success:!1,error:t.stack||String(t)})}});export{Ee as buildParseResponse,q as collectFunctionNodes,ge as collectWorkerSpawns,fe as reportQueryCompileFailures,V as resolveCallableName,_ as resolveWasmPath};
|
|
3
3
|
//# sourceMappingURL=ast-worker.js.map
|
package/dist/ast-worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../lib/core/src/ast/ast-worker.ts"],"sourcesContent":["import { parentPort } from \"worker_threads\";\nimport { Parser, Language, type Node, type Tree } from \"web-tree-sitter\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\nimport { createHash } from \"crypto\";\nimport { resolveWasmPath } from \"./resolve-wasm-path.js\";\nimport type { LanguageProvider, LanguageRegistry } from \"@workspace/ast-core\";\nimport {\n parseImportDescriptors,\n loadDefaultRegistry,\n} from \"@workspace/ast-core\";\nimport {\n ENCODING_HEX,\n HASH_ALGO_SHA256,\n IpcLoggerClient,\n SUPPORTED_LANGUAGES,\n type AstExportKind,\n type SupportedLanguage,\n} from \"@workspace/contracts\";\nimport { AstMessages, AstNodeTypes } from \"./ast-constants.js\";\n\n/**\n * Symbol-level feature hash (STOR-005): a hash of the AST node's own exact source span\n * (`node.text`), independent of the containing file's blob hash. Lets a single-symbol edit\n * produce a one-line JSONL diff for that symbol without touching its untouched siblings' hashes.\n *\n * The algorithm/digest constants come from `@workspace/contracts` (issue #211) so the worker's\n * hashes can never drift from the main thread's (`ast-worker-pool.ts`, `file-discovery.service.ts`)\n * or `lib/schema`'s. Package-name imports resolve through node_modules and work fine inside a\n * `worker_threads` Worker (as the existing contracts/ast-core imports above prove) — only bare\n * relative `.js`-to-`.ts` sibling imports needed a tsx resolve hook that doesn't propagate into\n * workers, and even that limitation is moot in dist/ where a fully-compiled worker ships.\n */\nfunction symbolContentHash(node: Node): string {\n return createHash(HASH_ALGO_SHA256).update(node.text).digest(ENCODING_HEX);\n}\n\n/**\n * Worker threads share the host process's stdout/stderr by default, so `console.*` here would\n * corrupt MCP's stdio JSON-RPC stream exactly like it would in the main thread (see\n * docs/gitbook/architecture/logging-architecture.md). A live `ILogger` callback can't cross the\n * `postMessage` structured-clone boundary, so unexpected crashes are reported through the\n * standard IPC Logger Protocol instead (see\n * docs/gitbook/guidelines/playbook-ipc-logging.md) — `AstWorkerPool` routes it back to its own\n * injected logger via `IpcLogRouter`.\n */\nconst logger = new IpcLoggerClient((message) =>\n parentPort?.postMessage(message),\n);\n\nprocess.on(\"uncaughtException\", (err) => {\n logger.error(AstMessages.WORKER_UNCAUGHT_EXCEPTION, {\n error: err instanceof Error ? err.message : String(err),\n });\n});\nprocess.on(\"unhandledRejection\", (err) => {\n logger.error(AstMessages.WORKER_UNHANDLED_REJECTION, {\n error: err instanceof Error ? err.message : String(err),\n });\n});\n\nexport interface AstParseRequest {\n taskId: string;\n filePath: string;\n code: string;\n language: SupportedLanguage;\n}\n\nexport interface ImportDescriptor {\n localName: string;\n originalName: string;\n modulePath: string;\n}\n\nexport interface AstParseResponse {\n taskId: string;\n success: boolean;\n error?: string;\n data?: {\n imports: ImportDescriptor[];\n exports: Array<{ name: string; type: AstExportKind }>;\n functions: Array<{\n name: string;\n startLine: number;\n endLine: number;\n contentHash?: string;\n containerName?: string; // NEW — the enclosing class/struct name, or undefined for a top-level function\n }>;\n classes: Array<{\n name: string;\n startLine: number;\n endLine: number;\n methods: string[];\n contentHash?: string;\n }>;\n /** Exported variable declarators (`export const X = ...`) — TS/JS only today (issue #192\n * gap 1). Function-valued initializers (arrow functions / function expressions) are\n * excluded: they are already indexed as functions via `functions`. */\n variables?: Array<{\n name: string;\n startLine: number;\n endLine: number;\n contentHash?: string;\n }>;\n calls: Array<{\n sourceFunction: string;\n targetFunction: string;\n /** 0-based source position of the call-site's callee expression start (matches Tier A's own\n * `startLine`/`startPosition` convention). The seed Tier B forward resolution (issue #11\n * plan A, Slice 2) issues `textDocument/definition` at this position per call site to\n * resolve the callee precisely -- see\n * forward-tier-b-edge-resolution-plan.md Slice 1. */\n startLine: number;\n startColumn: number;\n }>;\n implements?: Array<{ sourceClass: string; targetInterface: string }>;\n extends?: Array<{ sourceClass: string; targetClass: string }>;\n /**\n * `new Worker(<path>)` spawn sites (TS/JS only — see `WORKER_SPAWN_LANGUAGES`), one per\n * resolved spawn call, attributing it to its enclosing function like `calls` does.\n * `persist-ast-graph.ts` resolves `targetPath` to a real file and inserts a `DEPENDS_ON`\n * edge from it — the fix for the `ast-worker-pool.ts` -> `ast-worker.ts` false-negative\n * blast-radius gap (dynamic `worker_threads` spawns are invisible to the static\n * imports/calls pipeline, since the target script is a runtime path string, not a static\n * `import`).\n */\n workerSpawns?: Array<{ sourceFunction: string; targetPath: string }>;\n decisions?: string[];\n };\n}\n\nlet parserInitialized = false;\nlet registryPromise: Promise<LanguageRegistry> | null = null;\n\nfunction getRegistry(): Promise<LanguageRegistry> {\n registryPromise ??= loadDefaultRegistry();\n return registryPromise;\n}\n\n// Worker-lifetime cache for loaded tree-sitter grammars, keyed by the resolved wasmPath\n// (deterministic per language -- see resolveWasmPath()). Language.load() is a real WASM\n// dynamic-library load (WebAssembly.instantiate + relocation into the shared runtime's\n// Table/Memory), not a cheap call -- reloading the same grammar per file (the pre-existing\n// behavior) is pure waste once a worker has already loaded it once. Mirrors registryPromise's\n// own memoization shape immediately above. web-tree-sitter's Language class has no\n// delete()/dispose method (verified against the installed 0.25.10 type defs), so there is\n// nothing to release even if this cache were bounded -- see\n// docs/ai_plans/implement_wasm-language-load-cache.md §2.1 for the full check.\nconst languageCache = new Map<string, Promise<Language>>();\n\nfunction getLanguage(wasmPath: string): Promise<Language> {\n let cached = languageCache.get(wasmPath);\n if (!cached) {\n cached = Language.load(wasmPath);\n languageCache.set(wasmPath, cached);\n }\n return cached;\n}\n\n/**\n * Re-exported for backward compatibility (existing importer: `ast-worker.fixture.unit.test.ts`)\n * — the real implementation moved to `resolve-wasm-path.ts` so it can also be imported from the\n * main thread (by `SemanticDiffAnalyzerService`) without pulling in this file's\n * `worker_threads`-only side effects (`parentPort?.on(...)`, process-level\n * `uncaughtException`/`unhandledRejection` handlers). See that file's doc comment for why.\n */\nexport { resolveWasmPath };\n\n/** Regex fallback for imports so graph edges still work when WASM fails to load (e.g. in tests). */\nfunction extractFallbackImports(code: string): ImportDescriptor[] {\n const fallbackImports: ImportDescriptor[] = [];\n const importMatches = code.matchAll(\n /import\\s+{([^}]+)}\\s+from\\s+['\"]([^'\"]+)['\"]/g,\n );\n for (const match of importMatches) {\n fallbackImports.push({\n localName: match[1].trim(),\n originalName: match[1].trim(),\n modulePath: match[2],\n });\n }\n return fallbackImports;\n}\n\nfunction getNodeName(node: Node): string {\n return (\n node.childForFieldName(\"name\")?.text ||\n node.descendantsOfType(\"identifier\")[0]?.text ||\n AstMessages.ANONYMOUS_NAME\n );\n}\n\n/**\n * For anonymous callables (arrow_function/function_expression with no own name), resolve\n * the binding name from the nearest enclosing variable_declarator / assignment_expression /\n * pair (object property) / public_field_definition (class field arrow method). Returns\n * \"anonymous\" for truly unbound cases (IIFEs, bare callback arguments).\n *\n * Named nodes (function_declaration, method_definition) already have a \"name\" field, so the\n * fast path below returns the same result getNodeName() would — safe to use uniformly for\n * every function-kind node, not just anonymous ones.\n *\n * GRPH-006 follow-up: C/C++'s `function_definition` has no direct \"name\" field at all — the name\n * sits two levels down, inside `function_declarator`'s own \"declarator\" field (`identifier` for a\n * free function, `field_identifier` for an inline method, `qualified_identifier` -- scope::name --\n * for an out-of-line one). Verified this was silently returning \"anonymous\" for every C/C++\n * function before this fix (never previously observed for C++ specifically because\n * `cppConfig`'s functions query didn't even extract methods until the GRPH-006 follow-up fix to\n * `cpp.ts` — this pass surfaced both bugs together).\n */\n/** GRPH-006 follow-up: resolves a C/C++ `function_definition`'s name from its declarator-nested\n * shape (see `resolveCallableName`'s own doc comment) -- `identifier`/`field_identifier` return\n * their own text directly, `qualified_identifier` (an out-of-line `Class::method`) returns just\n * its `name` field, not the qualifier. Extracted purely to keep `resolveCallableName`'s own\n * complexity within budget. */\nfunction resolveDeclaratorNestedName(node: Node): string | undefined {\n const declaratorName = node\n .childForFieldName(\"declarator\")\n ?.childForFieldName(\"declarator\");\n if (!declaratorName) return undefined;\n return declaratorName.type === AstNodeTypes.QUALIFIED_IDENTIFIER\n ? (declaratorName.childForFieldName(\"name\")?.text ??\n AstMessages.ANONYMOUS_NAME)\n : declaratorName.text;\n}\n\nexport function resolveCallableName(node: Node): string {\n const ownName = node.childForFieldName(\"name\");\n if (ownName) return ownName.text;\n\n const declaratorName = resolveDeclaratorNestedName(node);\n if (declaratorName) return declaratorName;\n\n const NAME_BEARING_PARENTS = new Set<string>([\n AstNodeTypes.VARIABLE_DECLARATOR,\n AstNodeTypes.ASSIGNMENT_EXPRESSION,\n AstNodeTypes.PAIR,\n AstNodeTypes.PUBLIC_FIELD_DEFINITION,\n ]);\n let current = node.parent;\n while (current) {\n // An \"arguments\" ancestor means this callable is itself passed as a call argument\n // (e.g. arr.map(x => x + 1)) rather than being the direct value of a declarator/\n // assignment/property/field. Stop here — climbing further would misattribute the\n // name of the outer binding the call happens to live inside (e.g. `results` in\n // `const results = arr.map(x => x + 1)`) to this unrelated, unbound callback.\n if (current.type === AstNodeTypes.ARGUMENTS) break;\n if (NAME_BEARING_PARENTS.has(current.type)) {\n const nameNode =\n current.childForFieldName(\"name\") ||\n current.childForFieldName(\"key\") ||\n current.childForFieldName(\"left\");\n if (nameNode) return nameNode.text;\n }\n current = current.parent;\n }\n return AstMessages.ANONYMOUS_NAME;\n}\n\n/**\n * Extracts just the callee (target function/method name) from a call node.\n * The field holding the callee expression is named differently across\n * grammars (\"function\" for TS/JS/Python/Rust/Go/C/C++, \"name\" for Java/PHP,\n * \"method\" for Ruby) — try the common ones, falling back to the whole\n * node's text if none match.\n */\nfunction getCallTargetText(node: Node): string {\n const callee =\n node.childForFieldName(\"function\") ||\n node.childForFieldName(\"name\") ||\n node.childForFieldName(\"method\");\n return callee?.text ?? node.text;\n}\n\n/** Resolves a call-site's *seed position* for Tier B forward resolution (issue #11 plan A,\n * Finding C): for a bare identifier call (`foo()`), `node` already *is* the identifier and its\n * own `startPosition` is correct. But for a compound callee -- JS/TS `member_expression`\n * (`service.doSomething()`), Go `selector_expression`, C++/Rust `field_expression` -- the\n * `calls` tree-sitter query (see `lib/ast-core/src/languages/*.ts`) captures the *whole*\n * expression, whose own `startPosition` sits on the receiver (`service`), not the callee\n * (`doSomething`). `textDocument/definition` must be issued at the callee's own position, or it\n * resolves the receiver's declaration instead -- an invented edge (IMPT-002). Verified against\n * real tree-sitter-typescript output (`property` is the correct field name for\n * `member_expression`, confirmed via a real parse, not assumed) -- see this plan's own\n * instruction not to copy field names blind.\n *\n * No-op (falls through to `node.startPosition`, i.e. itself) for languages whose query already\n * captures the callee identifier directly (Java `name:`, PHP `name:`, Ruby `method:`) or for a\n * bare identifier call. */\nfunction getCallSitePosition(node: Node): { row: number; column: number } {\n const calleeIdentifier =\n node.childForFieldName(\"property\") || // JS/TS member_expression\n node.childForFieldName(\"field\") || // Go selector_expression / C++/Rust field_expression\n node.childForFieldName(\"attribute\") || // Python attribute\n node.childForFieldName(\"name\") || // defensive; Java/PHP already capture this directly\n node.childForFieldName(\"method\"); // defensive; Ruby already captures this directly\n return (calleeIdentifier ?? node).startPosition;\n}\n\n/** Walks up from `node` to find the nearest ancestor present in `containerIds` (function/class nodes already extracted for this file), returning its name, or \"anonymous\" for top-level (file-scoped) call/implements/extends sites. */\nfunction findEnclosingContainerName(\n node: Node,\n containerIds: Set<number>,\n): string {\n let current = node.parent;\n while (current) {\n if (containerIds.has(current.id)) {\n return getNodeName(current);\n }\n current = current.parent;\n }\n return AstMessages.ANONYMOUS_NAME;\n}\n\n/** Resolves the base type name whether `node` is itself a `type_identifier` or wraps one one level\n * deep -- Rust generic impls (`impl<T> Wrapper<T>` -- field is `generic_type`) and Go pointer\n * receivers (`*Receiver` -- field is `pointer_type`) both need the unwrap; a plain `type_identifier`\n * field (the common case) is returned as-is. */\nfunction firstTypeIdentifierText(node: Node): string | undefined {\n return node.type === AstNodeTypes.TYPE_IDENTIFIER\n ? node.text\n : node.descendantsOfType(AstNodeTypes.TYPE_IDENTIFIER)[0]?.text;\n}\n\n/** GRPH-006 follow-up (Rust): a method's lexical parent is `impl_item`, which `rustConfig.classes`\n * deliberately excludes (so `findEnclosingContainerName` over `classIds` always returns\n * \"anonymous\" for a Rust method) -- the target struct/enum's name lives on the impl block's own\n * `type` field instead (the Self type, not `trait` -- `impl Trait for Type` still qualifies by\n * `Type`, the concrete struct, not whichever trait it happens to implement). */\nfunction resolveRustImplContainerName(node: Node): string | undefined {\n let current = node.parent;\n while (current) {\n if (current.type === AstNodeTypes.IMPL_ITEM) {\n const typeField = current.childForFieldName(\"type\");\n return typeField ? firstTypeIdentifierText(typeField) : undefined;\n }\n current = current.parent;\n }\n return undefined;\n}\n\n/** GRPH-006 follow-up (Go): a method's receiver type is referenced through its own `receiver:`\n * field, never as an AST ancestor (`type_declaration` never encloses a `method_declaration`), so\n * `findEnclosingContainerName` over `classIds` always returns \"anonymous\" here too -- resolve the\n * receiver parameter's type directly instead. */\nfunction resolveGoReceiverContainerName(node: Node): string | undefined {\n const receiver = node.childForFieldName(\"receiver\");\n const paramType = receiver?.namedChild(0)?.childForFieldName(\"type\");\n return paramType ? firstTypeIdentifierText(paramType) : undefined;\n}\n\n/** GRPH-006 follow-up (C++): an out-of-line `Ret Class::method(){}` definition is never lexically\n * nested inside its class (inline methods already resolve via the generic ancestor walk once\n * they're extracted at all -- see `cpp.ts`'s functions-query fix), so its declarator is a\n * `qualified_identifier` carrying the class name in its own `scope` field instead. Recurses one\n * level for a nested qualifier (`A::B::method`), taking the innermost (`B`) as the immediate\n * container -- this scheme only ever tracks one level of containment, matching every other\n * language here. */\nfunction resolveCppQualifiedContainerName(node: Node): string | undefined {\n const declarator = node.childForFieldName(\"declarator\");\n const inner = declarator?.childForFieldName(\"declarator\");\n if (inner?.type !== AstNodeTypes.QUALIFIED_IDENTIFIER) return undefined;\n const scope = inner.childForFieldName(\"scope\");\n if (!scope) return undefined;\n return scope.type === AstNodeTypes.QUALIFIED_IDENTIFIER\n ? scope.childForFieldName(\"name\")?.text\n : scope.text;\n}\n\n/** GRPH-006 follow-up: for a function whose enclosing container the generic ancestor walk\n * couldn't find (Rust/Go/C++'s deferred gaps -- see the ADR), try each language-specific\n * resolver in turn. Mutually exclusive by construction (each checks for a node shape only its own\n * grammar produces -- e.g. only Rust ever has an `impl_item` ancestor), so no explicit language\n * tag is needed; a language not covered here (or a function with no enclosing container at all)\n * falls through to `undefined`, unchanged from before this follow-up. */\nfunction resolveDeferredLanguageContainerName(node: Node): string | undefined {\n return (\n resolveRustImplContainerName(node) ??\n resolveGoReceiverContainerName(node) ??\n resolveCppQualifiedContainerName(node)\n );\n}\n\n/** Shape of a fully-populated `AstParseResponse[\"data\"]` (i.e. the non-optional variant produced once parsing has actually run). */\ntype AstExtractionResult = NonNullable<AstParseResponse[\"data\"]>;\n\n/** Extracts class declaration nodes via the provider and appends their summaries to `classes`. Returns the raw nodes so callers can derive id sets for edge extraction. */\nfunction collectClassNodes(\n tree: Tree,\n provider: LanguageProvider,\n classes: AstExtractionResult[\"classes\"],\n): Node[] {\n const classNodes = provider.extractClasses(tree.rootNode);\n for (const node of classNodes) {\n classes.push({\n name: getNodeName(node),\n startLine: node.startPosition.row,\n endLine: node.endPosition.row,\n methods: [],\n contentHash: symbolContentHash(node),\n });\n }\n return classNodes;\n}\n\n/**\n * Extracts exported variable declarators via the provider and appends their summaries to\n * `variables` (issue #192 gap 1: `export const X = ...` must be an indexable symbol so\n * `impact <X>` resolves instead of \"No matching node\"). The query only matches exported\n * `lexical_declaration` declarators with plain identifier names; this collector additionally\n * skips function-valued initializers (arrow_function/function_expression) since those are\n * already indexed as functions by the `functions` query + resolveCallableName path.\n */\nconst FUNCTION_VALUE_NODE_TYPES = new Set<string>([\n \"arrow_function\",\n \"function_expression\",\n]);\n\nfunction collectVariableNodes(\n tree: Tree,\n provider: LanguageProvider,\n variables: NonNullable<AstExtractionResult[\"variables\"]>,\n): Node[] {\n const variableNodes = provider.extractVariables?.(tree.rootNode) ?? [];\n for (const node of variableNodes) {\n const value = node.childForFieldName(\"value\");\n if (value && FUNCTION_VALUE_NODE_TYPES.has(value.type)) continue;\n variables.push({\n name: getNodeName(node),\n startLine: node.startPosition.row,\n endLine: node.endPosition.row,\n contentHash: symbolContentHash(node),\n });\n }\n return variableNodes;\n}\n\n/** Extracts function/method declaration nodes via the provider and appends their summaries to `functions`. Returns the raw nodes so callers can derive id sets for edge extraction.\n *\n * `classNodes` (GRPH-006) lets each function's enclosing class/struct be resolved via the same\n * `findEnclosingContainerName` ancestor walk `collectCallEdges`/`collectImplementsEdges`/\n * `collectExtendsEdges` already use -- `containerName` is what qualifies this symbol's `node_key`\n * down the line (`persist-ast-graph.ts`'s `buildQualifiedBaseKey`). `findEnclosingContainerName`'s\n * \"nothing found\" sentinel is the literal string `AstMessages.ANONYMOUS_NAME` (\"anonymous\") --\n * that means \"top-level, outside any class\" here, a different meaning than its existing use in\n * `collectCallEdges` (\"outside any function\"), so it must be converted to `undefined` rather than\n * stored as-is (storing it as-is would qualify every top-level function as `file#anonymous.name`).\n *\n * When the ancestor walk finds nothing, `resolveDeferredLanguageContainerName` (GRPH-006\n * follow-up) tries Rust/Go/C++'s own non-lexical containment shapes before giving up to\n * `undefined` -- see that function's own doc comment for why each needs a different mechanism. */\nexport function collectFunctionNodes(\n tree: Tree,\n provider: LanguageProvider,\n functions: AstExtractionResult[\"functions\"],\n classNodes: Node[],\n): Node[] {\n const classIds = new Set(classNodes.map((n) => n.id));\n const functionNodes = provider.extractFunctions(tree.rootNode);\n for (const node of functionNodes) {\n const container = findEnclosingContainerName(node, classIds);\n const containerName =\n container === AstMessages.ANONYMOUS_NAME\n ? resolveDeferredLanguageContainerName(node)\n : container;\n functions.push({\n name: resolveCallableName(node),\n startLine: node.startPosition.row,\n endLine: node.endPosition.row,\n contentHash: symbolContentHash(node),\n containerName,\n });\n }\n return functionNodes;\n}\n\n/** Extracts call-site edges via the provider, attributing each call to its enclosing function (or \"anonymous\"), up to the 1000-call circuit breaker. */\nfunction collectCallEdges(\n tree: Tree,\n provider: LanguageProvider,\n functionNodes: Node[],\n calls: AstExtractionResult[\"calls\"],\n): void {\n const functionIds = new Set(functionNodes.map((n) => n.id));\n const callNodes = provider.extractCalls(tree.rootNode);\n for (const node of callNodes) {\n if (calls.length >= 1000) break; // Circuit breaker limit\n const position = getCallSitePosition(node);\n calls.push({\n sourceFunction: findEnclosingContainerName(node, functionIds),\n targetFunction: getCallTargetText(node),\n startLine: position.row,\n startColumn: position.column,\n });\n }\n}\n\n/** Extracts `implements` edges via the provider, if it supports them, attributing each to its enclosing class. */\nfunction collectImplementsEdges(\n tree: Tree,\n provider: LanguageProvider,\n classNodes: Node[],\n implementsList: NonNullable<AstExtractionResult[\"implements\"]>,\n): void {\n if (!provider.extractImplements) return;\n const classIds = new Set(classNodes.map((n) => n.id));\n for (const node of provider.extractImplements(tree.rootNode)) {\n implementsList.push({\n sourceClass: findEnclosingContainerName(node, classIds),\n targetInterface: node.text,\n });\n }\n}\n\n/** Extracts `extends` edges via the provider, if it supports them, attributing each to its enclosing class. */\nfunction collectExtendsEdges(\n tree: Tree,\n provider: LanguageProvider,\n classNodes: Node[],\n extendsList: NonNullable<AstExtractionResult[\"extends\"]>,\n): void {\n if (!provider.extractExtends) return;\n const classIds = new Set(classNodes.map((n) => n.id));\n for (const node of provider.extractExtends(tree.rootNode)) {\n extendsList.push({\n sourceClass: findEnclosingContainerName(node, classIds),\n targetClass: node.text,\n });\n }\n}\n\n/** Languages `collectWorkerSpawns` runs against — the reported gap (a `new Worker(...)` spawn\n * invisible to the static imports/calls pipeline) is TS/JS-specific: `new_expression` is also a\n * real C++ grammar node (the `new` operator), so gating by language avoids misinterpreting an\n * unrelated `new Worker(...)` construction in another language as a worker_threads spawn. */\nconst WORKER_SPAWN_LANGUAGES: ReadonlySet<SupportedLanguage> = new Set([\n SUPPORTED_LANGUAGES.TYPESCRIPT,\n SUPPORTED_LANGUAGES.JAVASCRIPT,\n]);\n\nconst WORKER_CONSTRUCTOR_NAME = \"Worker\";\nconst PATH_MODULE_OBJECT_NAME = \"path\";\nconst PATH_RESOLVE_METHOD_NAME = \"resolve\";\nconst PATH_JOIN_METHOD_NAME = \"join\";\nconst DIRNAME_IDENTIFIER_NAME = \"__dirname\";\n\n/** True when `node` is a `path.resolve(...)`/`path.join(...)` call expression — the exact idiom\n * `ast-worker-pool.ts` uses to build a worker script's path from `__dirname`. */\nfunction isPathJoinOrResolveCall(node: Node): boolean {\n if (node.type !== \"call_expression\") return false;\n const fn = node.childForFieldName(\"function\");\n if (!fn || fn.type !== \"member_expression\") return false;\n const object = fn.childForFieldName(\"object\")?.text;\n const property = fn.childForFieldName(\"property\")?.text;\n return (\n object === PATH_MODULE_OBJECT_NAME &&\n (property === PATH_RESOLVE_METHOD_NAME ||\n property === PATH_JOIN_METHOD_NAME)\n );\n}\n\n/** Extracts the string-literal path segment from a `path.resolve(__dirname, \"<literal>\")` /\n * `path.join(__dirname, \"<literal>\")` call, or undefined if it doesn't reference `__dirname` or\n * carries no string-literal argument. */\nfunction extractDirnameJoinedLiteral(callNode: Node): string | undefined {\n const args = callNode.childForFieldName(\"arguments\");\n if (!args) return undefined;\n const hasDirname = args.namedChildren.some(\n (n) => n?.type === \"identifier\" && n.text === DIRNAME_IDENTIFIER_NAME,\n );\n if (!hasDirname) return undefined;\n const literal = args.descendantsOfType(\"string\")[0];\n return literal?.text.replace(/['\"]/g, \"\");\n}\n\n/** Bounded same-file scan for `<argText> = path.resolve/join(__dirname, \"<literal>\")` — the\n * exact idiom `ast-worker-pool.ts` uses for `this.wPath = path.resolve(__dirname, \"./ast-worker.js\")`,\n * needed because the `new Worker(...)` call site references the field/variable, not the literal,\n * directly. Matches by comparing the assignment's own left-hand-side text against `argText`\n * verbatim (e.g. both \"this.wPath\"), so it works uniformly whether the argument is a bare\n * identifier or a member expression. */\nfunction findDirnameJoinedAssignment(\n tree: Tree,\n argText: string,\n): string | undefined {\n for (const assignment of tree.rootNode.descendantsOfType(\n AstNodeTypes.ASSIGNMENT_EXPRESSION,\n )) {\n if (!assignment) continue;\n const left = assignment.childForFieldName(\"left\");\n const right = assignment.childForFieldName(\"right\");\n if (left?.text !== argText || !right || !isPathJoinOrResolveCall(right)) {\n continue;\n }\n const literal = extractDirnameJoinedLiteral(right);\n if (literal) return literal;\n }\n return undefined;\n}\n\n/** Resolves `new Worker(<arg>)`'s first argument to a literal relative path: a direct string\n * literal, or a same-file `path.resolve/join(__dirname, \"<literal>\")`-assigned field/variable\n * (the `ast-worker-pool.ts` idiom, via `findDirnameJoinedAssignment`). Returns undefined —\n * skip silently, no guessing — when neither shape matches. */\nfunction resolveWorkerArgumentPath(\n tree: Tree,\n argNode: Node,\n): string | undefined {\n if (argNode.type === \"string\") {\n return argNode.text.replace(/['\"]/g, \"\");\n }\n if (argNode.type === \"identifier\" || argNode.type === \"member_expression\") {\n return findDirnameJoinedAssignment(tree, argNode.text);\n }\n return undefined;\n}\n\n/** Extracts `new Worker(<arg>)` spawn sites (TS/JS only, see `WORKER_SPAWN_LANGUAGES`) and\n * appends a `{sourceFunction, targetPath}` descriptor for each one whose argument resolves to a\n * literal relative path (`resolveWorkerArgumentPath`), attributing it to its enclosing function\n * like `collectCallEdges` does. `persist-ast-graph.ts` resolves `targetPath` to a real file and\n * inserts the corresponding `DEPENDS_ON` edge. */\nexport function collectWorkerSpawns(\n tree: Tree,\n language: SupportedLanguage,\n functionNodes: Node[],\n workerSpawns: NonNullable<AstExtractionResult[\"workerSpawns\"]>,\n): void {\n if (!WORKER_SPAWN_LANGUAGES.has(language)) return;\n const functionIds = new Set(functionNodes.map((n) => n.id));\n for (const node of tree.rootNode.descendantsOfType(\"new_expression\")) {\n if (!node) continue;\n const ctor = node.childForFieldName(\"constructor\");\n if (ctor?.text !== WORKER_CONSTRUCTOR_NAME) continue;\n const args = node.childForFieldName(\"arguments\");\n const firstArg = args?.namedChildren.find((n): n is Node => n !== null);\n if (!firstArg) continue;\n const targetPath = resolveWorkerArgumentPath(tree, firstArg);\n if (!targetPath) continue;\n workerSpawns.push({\n sourceFunction: findEnclosingContainerName(node, functionIds),\n targetPath,\n });\n }\n}\n\n/**\n * Runs every provider-driven extraction against a parsed tree (or returns empty results plus a\n * decision note if parsing produced no tree). A single try/catch wraps the whole pass, matching\n * the original inline behavior of recording a `providerQueryFailed` decision instead of throwing\n * when any one extractor misbehaves.\n */\nfunction extractAstData(\n tree: Tree | null,\n provider: LanguageProvider,\n language: SupportedLanguage,\n): AstExtractionResult {\n const decisions: string[] = [];\n const imports: ImportDescriptor[] = [];\n const exports: AstExtractionResult[\"exports\"] = [];\n const functions: AstExtractionResult[\"functions\"] = [];\n const classes: AstExtractionResult[\"classes\"] = [];\n const variables: NonNullable<AstExtractionResult[\"variables\"]> = [];\n const calls: AstExtractionResult[\"calls\"] = [];\n const implementsList: NonNullable<AstExtractionResult[\"implements\"]> = [];\n const extendsList: NonNullable<AstExtractionResult[\"extends\"]> = [];\n const workerSpawns: NonNullable<AstExtractionResult[\"workerSpawns\"]> = [];\n\n if (tree) {\n decisions.push(AstMessages.parsedViaTreeSitter(tree.rootNode.childCount));\n\n try {\n const classNodes = collectClassNodes(tree, provider, classes);\n const functionNodes = collectFunctionNodes(\n tree,\n provider,\n functions,\n classNodes,\n );\n\n const importNodes = provider.extractImports(tree.rootNode);\n imports.push(...parseImportDescriptors(importNodes));\n\n collectVariableNodes(tree, provider, variables);\n\n collectCallEdges(tree, provider, functionNodes, calls);\n collectImplementsEdges(tree, provider, classNodes, implementsList);\n collectExtendsEdges(tree, provider, classNodes, extendsList);\n collectWorkerSpawns(tree, language, functionNodes, workerSpawns);\n\n decisions.push(AstMessages.QUERIED_VIA_LANGUAGE_PROVIDER);\n } catch (e) {\n decisions.push(\n AstMessages.providerQueryFailed(\n e instanceof Error ? e.message : String(e),\n ),\n );\n }\n }\n\n return {\n imports,\n exports,\n functions,\n classes,\n variables,\n calls,\n implements: implementsList,\n extends: extendsList,\n workerSpawns,\n decisions,\n };\n}\n\n/** Parses `request.code` with the resolved provider/grammar and runs the full AST extraction pass, releasing the tree-sitter tree/parser handles once done. */\nfunction parseAndExtract(\n code: string,\n provider: LanguageProvider,\n langInstance: Language,\n language: SupportedLanguage,\n): AstExtractionResult {\n const parser = new Parser();\n parser.setLanguage(langInstance);\n\n // initQueries() is idempotent (DefaultProvider caches compiledQueries after the first call)\n // and the provider instance is reused for every file of this language for the worker's whole\n // lifetime (see getRegistry() above), so this only actually compiles once. A field whose\n // pattern fails to compile against the installed grammar degrades to that field's\n // descendantsOfType fallback (DefaultProvider.compileQuery) rather than throwing.\n provider.initQueries?.(langInstance);\n\n const tree = parser.parse(code);\n const data = extractAstData(tree, provider, language);\n\n if (tree) tree.delete();\n parser.delete();\n\n return data;\n}\n\n/**\n * Resolves a language provider for `request.filePath` and produces the full parse response,\n * short-circuiting with an empty-but-successful result (plus an explanatory decision note) when\n * no provider is registered for the extension or its wasm grammar can't be located on disk.\n */\nexport async function buildParseResponse(\n request: AstParseRequest,\n): Promise<AstParseResponse> {\n if (!parserInitialized) {\n await Parser.init();\n parserInitialized = true;\n }\n\n const registry = await getRegistry();\n const ext = path.extname(request.filePath);\n const provider: LanguageProvider | undefined =\n registry.getProviderForExtension(ext);\n\n if (!provider) {\n return {\n taskId: request.taskId,\n success: true,\n data: {\n imports: [],\n exports: [],\n functions: [],\n classes: [],\n calls: [],\n decisions: [AstMessages.noLanguageProviderForExtension(ext)],\n },\n };\n }\n\n const { wasmPath, attemptedPaths } = resolveWasmPath(provider.wasm_file);\n\n if (!fs.existsSync(wasmPath)) {\n return {\n taskId: request.taskId,\n success: true,\n data: {\n imports: extractFallbackImports(request.code),\n exports: [],\n functions: [],\n classes: [],\n calls: [],\n decisions: [\n AstMessages.wasmNotFound(request.language, attemptedPaths.join(\", \")),\n ],\n },\n };\n }\n\n const langInstance = await getLanguage(wasmPath);\n const data = parseAndExtract(\n request.code,\n provider,\n langInstance,\n request.language,\n );\n\n return {\n taskId: request.taskId,\n success: true,\n data,\n };\n}\n\nparentPort?.on(\"message\", async (request: AstParseRequest) => {\n try {\n const response = await buildParseResponse(request);\n parentPort?.postMessage(response);\n } catch (err: any) {\n parentPort?.postMessage({\n taskId: request.taskId,\n success: false,\n error: err.stack || String(err),\n });\n }\n});\n"],"mappings":"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","names":["parentPort","Parser","Language","path","fs","createHash","symbolContentHash","node","createHash","HASH_ALGO_SHA256","logger","IpcLoggerClient","message","parentPort","err","AstMessages","parserInitialized","registryPromise","getRegistry","loadDefaultRegistry","languageCache","getLanguage","wasmPath","cached","Language","extractFallbackImports","code","fallbackImports","importMatches","match","getNodeName","node","AstMessages","resolveDeclaratorNestedName","declaratorName","AstNodeTypes","resolveCallableName","ownName","NAME_BEARING_PARENTS","current","nameNode","getCallTargetText","getCallSitePosition","findEnclosingContainerName","containerIds","firstTypeIdentifierText","resolveRustImplContainerName","typeField","resolveGoReceiverContainerName","paramType","resolveCppQualifiedContainerName","inner","scope","resolveDeferredLanguageContainerName","collectClassNodes","tree","provider","classes","classNodes","symbolContentHash","FUNCTION_VALUE_NODE_TYPES","collectVariableNodes","variables","variableNodes","value","collectFunctionNodes","functions","classIds","n","functionNodes","container","containerName","collectCallEdges","calls","functionIds","callNodes","position","collectImplementsEdges","implementsList","collectExtendsEdges","extendsList","WORKER_SPAWN_LANGUAGES","SUPPORTED_LANGUAGES","WORKER_CONSTRUCTOR_NAME","PATH_MODULE_OBJECT_NAME","PATH_RESOLVE_METHOD_NAME","PATH_JOIN_METHOD_NAME","DIRNAME_IDENTIFIER_NAME","isPathJoinOrResolveCall","fn","object","property","extractDirnameJoinedLiteral","callNode","args","findDirnameJoinedAssignment","argText","assignment","left","right","literal","resolveWorkerArgumentPath","argNode","collectWorkerSpawns","language","workerSpawns","firstArg","targetPath","extractAstData","decisions","imports","exports","importNodes","parseImportDescriptors","e","parseAndExtract","langInstance","parser","Parser","data","buildParseResponse","request","parserInitialized","registry","getRegistry","ext","wasmPath","attemptedPaths","resolveWasmPath","getLanguage","parentPort","response","err"]}
|
|
1
|
+
{"version":3,"sources":["../../../lib/core/src/ast/ast-worker.ts"],"sourcesContent":["import { parentPort } from \"worker_threads\";\nimport { Parser, Language, type Node, type Tree } from \"web-tree-sitter\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\nimport { createHash } from \"crypto\";\nimport { resolveWasmPath } from \"./resolve-wasm-path.js\";\nimport type { LanguageProvider, LanguageRegistry } from \"@workspace/ast-core\";\nimport {\n parseImportDescriptors,\n loadDefaultRegistry,\n} from \"@workspace/ast-core\";\nimport {\n ENCODING_HEX,\n HASH_ALGO_SHA256,\n IpcLoggerClient,\n SUPPORTED_LANGUAGES,\n type AstExportKind,\n type SupportedLanguage,\n} from \"@workspace/contracts\";\nimport { AstMessages, AstNodeTypes } from \"./ast-constants.js\";\n\n/**\n * Symbol-level feature hash (STOR-005): a hash of the AST node's own exact source span\n * (`node.text`), independent of the containing file's blob hash. Lets a single-symbol edit\n * produce a one-line JSONL diff for that symbol without touching its untouched siblings' hashes.\n *\n * The algorithm/digest constants come from `@workspace/contracts` (issue #211) so the worker's\n * hashes can never drift from the main thread's (`ast-worker-pool.ts`, `file-discovery.service.ts`)\n * or `lib/schema`'s. Package-name imports resolve through node_modules and work fine inside a\n * `worker_threads` Worker (as the existing contracts/ast-core imports above prove) — only bare\n * relative `.js`-to-`.ts` sibling imports needed a tsx resolve hook that doesn't propagate into\n * workers, and even that limitation is moot in dist/ where a fully-compiled worker ships.\n */\nfunction symbolContentHash(node: Node): string {\n return createHash(HASH_ALGO_SHA256).update(node.text).digest(ENCODING_HEX);\n}\n\n/**\n * Worker threads share the host process's stdout/stderr by default, so `console.*` here would\n * corrupt MCP's stdio JSON-RPC stream exactly like it would in the main thread (see\n * docs/gitbook/architecture/logging-architecture.md). A live `ILogger` callback can't cross the\n * `postMessage` structured-clone boundary, so unexpected crashes are reported through the\n * standard IPC Logger Protocol instead (see\n * docs/gitbook/guidelines/playbook-ipc-logging.md) — `AstWorkerPool` routes it back to its own\n * injected logger via `IpcLogRouter`.\n */\nconst logger = new IpcLoggerClient((message) =>\n parentPort?.postMessage(message),\n);\n\nprocess.on(\"uncaughtException\", (err) => {\n logger.error(AstMessages.WORKER_UNCAUGHT_EXCEPTION, {\n error: err instanceof Error ? err.message : String(err),\n });\n});\nprocess.on(\"unhandledRejection\", (err) => {\n logger.error(AstMessages.WORKER_UNHANDLED_REJECTION, {\n error: err instanceof Error ? err.message : String(err),\n });\n});\n\nexport interface AstParseRequest {\n taskId: string;\n filePath: string;\n code: string;\n language: SupportedLanguage;\n}\n\nexport interface ImportDescriptor {\n localName: string;\n originalName: string;\n modulePath: string;\n}\n\nexport interface AstParseResponse {\n taskId: string;\n success: boolean;\n error?: string;\n data?: {\n imports: ImportDescriptor[];\n exports: Array<{ name: string; type: AstExportKind }>;\n functions: Array<{\n name: string;\n startLine: number;\n endLine: number;\n contentHash?: string;\n containerName?: string; // NEW — the enclosing class/struct name, or undefined for a top-level function\n }>;\n classes: Array<{\n name: string;\n startLine: number;\n endLine: number;\n methods: string[];\n contentHash?: string;\n }>;\n /** Exported variable declarators (`export const X = ...`) — TS/JS only today (issue #192\n * gap 1). Function-valued initializers (arrow functions / function expressions) are\n * excluded: they are already indexed as functions via `functions`. */\n variables?: Array<{\n name: string;\n startLine: number;\n endLine: number;\n contentHash?: string;\n }>;\n calls: Array<{\n sourceFunction: string;\n targetFunction: string;\n /** 0-based source position of the call-site's callee expression start (matches Tier A's own\n * `startLine`/`startPosition` convention). The seed Tier B forward resolution (issue #11\n * plan A, Slice 2) issues `textDocument/definition` at this position per call site to\n * resolve the callee precisely -- see\n * forward-tier-b-edge-resolution-plan.md Slice 1. */\n startLine: number;\n startColumn: number;\n }>;\n implements?: Array<{ sourceClass: string; targetInterface: string }>;\n extends?: Array<{ sourceClass: string; targetClass: string }>;\n /**\n * `new Worker(<path>)` spawn sites (TS/JS only — see `WORKER_SPAWN_LANGUAGES`), one per\n * resolved spawn call, attributing it to its enclosing function like `calls` does.\n * `persist-ast-graph.ts` resolves `targetPath` to a real file and inserts a `DEPENDS_ON`\n * edge from it — the fix for the `ast-worker-pool.ts` -> `ast-worker.ts` false-negative\n * blast-radius gap (dynamic `worker_threads` spawns are invisible to the static\n * imports/calls pipeline, since the target script is a runtime path string, not a static\n * `import`).\n */\n workerSpawns?: Array<{ sourceFunction: string; targetPath: string }>;\n decisions?: string[];\n };\n}\n\nlet parserInitialized = false;\nlet registryPromise: Promise<LanguageRegistry> | null = null;\n\nfunction getRegistry(): Promise<LanguageRegistry> {\n registryPromise ??= loadDefaultRegistry();\n return registryPromise;\n}\n\n// Worker-lifetime cache for loaded tree-sitter grammars, keyed by the resolved wasmPath\n// (deterministic per language -- see resolveWasmPath()). Language.load() is a real WASM\n// dynamic-library load (WebAssembly.instantiate + relocation into the shared runtime's\n// Table/Memory), not a cheap call -- reloading the same grammar per file (the pre-existing\n// behavior) is pure waste once a worker has already loaded it once. Mirrors registryPromise's\n// own memoization shape immediately above. web-tree-sitter's Language class has no\n// delete()/dispose method (verified against the installed 0.25.10 type defs), so there is\n// nothing to release even if this cache were bounded -- see\n// docs/ai_plans/implement_wasm-language-load-cache.md §2.1 for the full check.\nconst languageCache = new Map<string, Promise<Language>>();\n\nfunction getLanguage(wasmPath: string): Promise<Language> {\n let cached = languageCache.get(wasmPath);\n if (!cached) {\n cached = Language.load(wasmPath);\n languageCache.set(wasmPath, cached);\n }\n return cached;\n}\n\n/**\n * Re-exported for backward compatibility (existing importer: `ast-worker.fixture.unit.test.ts`)\n * — the real implementation moved to `resolve-wasm-path.ts` so it can also be imported from the\n * main thread (by `SemanticDiffAnalyzerService`) without pulling in this file's\n * `worker_threads`-only side effects (`parentPort?.on(...)`, process-level\n * `uncaughtException`/`unhandledRejection` handlers). See that file's doc comment for why.\n */\nexport { resolveWasmPath };\n\n/** Regex fallback for imports so graph edges still work when WASM fails to load (e.g. in tests). */\nfunction extractFallbackImports(code: string): ImportDescriptor[] {\n const fallbackImports: ImportDescriptor[] = [];\n const importMatches = code.matchAll(\n /import\\s+{([^}]+)}\\s+from\\s+['\"]([^'\"]+)['\"]/g,\n );\n for (const match of importMatches) {\n fallbackImports.push({\n localName: match[1].trim(),\n originalName: match[1].trim(),\n modulePath: match[2],\n });\n }\n return fallbackImports;\n}\n\nfunction getNodeName(node: Node): string {\n return (\n node.childForFieldName(\"name\")?.text ||\n node.descendantsOfType(\"identifier\")[0]?.text ||\n AstMessages.ANONYMOUS_NAME\n );\n}\n\n/**\n * For anonymous callables (arrow_function/function_expression with no own name), resolve\n * the binding name from the nearest enclosing variable_declarator / assignment_expression /\n * pair (object property) / public_field_definition (class field arrow method). Returns\n * \"anonymous\" for truly unbound cases (IIFEs, bare callback arguments).\n *\n * Named nodes (function_declaration, method_definition) already have a \"name\" field, so the\n * fast path below returns the same result getNodeName() would — safe to use uniformly for\n * every function-kind node, not just anonymous ones.\n *\n * GRPH-006 follow-up: C/C++'s `function_definition` has no direct \"name\" field at all — the name\n * sits two levels down, inside `function_declarator`'s own \"declarator\" field (`identifier` for a\n * free function, `field_identifier` for an inline method, `qualified_identifier` -- scope::name --\n * for an out-of-line one). Verified this was silently returning \"anonymous\" for every C/C++\n * function before this fix (never previously observed for C++ specifically because\n * `cppConfig`'s functions query didn't even extract methods until the GRPH-006 follow-up fix to\n * `cpp.ts` — this pass surfaced both bugs together).\n */\n/** GRPH-006 follow-up: resolves a C/C++ `function_definition`'s name from its declarator-nested\n * shape (see `resolveCallableName`'s own doc comment) -- `identifier`/`field_identifier` return\n * their own text directly, `qualified_identifier` (an out-of-line `Class::method`) returns just\n * its `name` field, not the qualifier. Extracted purely to keep `resolveCallableName`'s own\n * complexity within budget. */\nfunction resolveDeclaratorNestedName(node: Node): string | undefined {\n const declaratorName = node\n .childForFieldName(\"declarator\")\n ?.childForFieldName(\"declarator\");\n if (!declaratorName) return undefined;\n return declaratorName.type === AstNodeTypes.QUALIFIED_IDENTIFIER\n ? (declaratorName.childForFieldName(\"name\")?.text ??\n AstMessages.ANONYMOUS_NAME)\n : declaratorName.text;\n}\n\nexport function resolveCallableName(node: Node): string {\n const ownName = node.childForFieldName(\"name\");\n if (ownName) return ownName.text;\n\n const declaratorName = resolveDeclaratorNestedName(node);\n if (declaratorName) return declaratorName;\n\n const NAME_BEARING_PARENTS = new Set<string>([\n AstNodeTypes.VARIABLE_DECLARATOR,\n AstNodeTypes.ASSIGNMENT_EXPRESSION,\n AstNodeTypes.PAIR,\n AstNodeTypes.PUBLIC_FIELD_DEFINITION,\n ]);\n let current = node.parent;\n while (current) {\n // An \"arguments\" ancestor means this callable is itself passed as a call argument\n // (e.g. arr.map(x => x + 1)) rather than being the direct value of a declarator/\n // assignment/property/field. Stop here — climbing further would misattribute the\n // name of the outer binding the call happens to live inside (e.g. `results` in\n // `const results = arr.map(x => x + 1)`) to this unrelated, unbound callback.\n if (current.type === AstNodeTypes.ARGUMENTS) break;\n if (NAME_BEARING_PARENTS.has(current.type)) {\n const nameNode =\n current.childForFieldName(\"name\") ||\n current.childForFieldName(\"key\") ||\n current.childForFieldName(\"left\");\n if (nameNode) return nameNode.text;\n }\n current = current.parent;\n }\n return AstMessages.ANONYMOUS_NAME;\n}\n\n/**\n * Extracts just the callee (target function/method name) from a call node.\n * The field holding the callee expression is named differently across\n * grammars (\"function\" for TS/JS/Python/Rust/Go/C/C++, \"name\" for Java/PHP,\n * \"method\" for Ruby) — try the common ones, falling back to the whole\n * node's text if none match.\n */\nfunction getCallTargetText(node: Node): string {\n const callee =\n node.childForFieldName(\"function\") ||\n node.childForFieldName(\"name\") ||\n node.childForFieldName(\"method\");\n return callee?.text ?? node.text;\n}\n\n/** Resolves a call-site's *seed position* for Tier B forward resolution (issue #11 plan A,\n * Finding C): for a bare identifier call (`foo()`), `node` already *is* the identifier and its\n * own `startPosition` is correct. But for a compound callee -- JS/TS `member_expression`\n * (`service.doSomething()`), Go `selector_expression`, C++/Rust `field_expression` -- the\n * `calls` tree-sitter query (see `lib/ast-core/src/languages/*.ts`) captures the *whole*\n * expression, whose own `startPosition` sits on the receiver (`service`), not the callee\n * (`doSomething`). `textDocument/definition` must be issued at the callee's own position, or it\n * resolves the receiver's declaration instead -- an invented edge (IMPT-002). Verified against\n * real tree-sitter-typescript output (`property` is the correct field name for\n * `member_expression`, confirmed via a real parse, not assumed) -- see this plan's own\n * instruction not to copy field names blind.\n *\n * No-op (falls through to `node.startPosition`, i.e. itself) for languages whose query already\n * captures the callee identifier directly (Java `name:`, PHP `name:`, Ruby `method:`) or for a\n * bare identifier call. */\nfunction getCallSitePosition(node: Node): { row: number; column: number } {\n const calleeIdentifier =\n node.childForFieldName(\"property\") || // JS/TS member_expression\n node.childForFieldName(\"field\") || // Go selector_expression / C++/Rust field_expression\n node.childForFieldName(\"attribute\") || // Python attribute\n node.childForFieldName(\"name\") || // defensive; Java/PHP already capture this directly\n node.childForFieldName(\"method\"); // defensive; Ruby already captures this directly\n return (calleeIdentifier ?? node).startPosition;\n}\n\n/** Walks up from `node` to find the nearest ancestor present in `containerIds` (function/class nodes already extracted for this file), returning its name, or \"anonymous\" for top-level (file-scoped) call/implements/extends sites. */\nfunction findEnclosingContainerName(\n node: Node,\n containerIds: Set<number>,\n): string {\n let current = node.parent;\n while (current) {\n if (containerIds.has(current.id)) {\n return getNodeName(current);\n }\n current = current.parent;\n }\n return AstMessages.ANONYMOUS_NAME;\n}\n\n/** Resolves the base type name whether `node` is itself a `type_identifier` or wraps one one level\n * deep -- Rust generic impls (`impl<T> Wrapper<T>` -- field is `generic_type`) and Go pointer\n * receivers (`*Receiver` -- field is `pointer_type`) both need the unwrap; a plain `type_identifier`\n * field (the common case) is returned as-is. */\nfunction firstTypeIdentifierText(node: Node): string | undefined {\n return node.type === AstNodeTypes.TYPE_IDENTIFIER\n ? node.text\n : node.descendantsOfType(AstNodeTypes.TYPE_IDENTIFIER)[0]?.text;\n}\n\n/** GRPH-006 follow-up (Rust): a method's lexical parent is `impl_item`, which `rustConfig.classes`\n * deliberately excludes (so `findEnclosingContainerName` over `classIds` always returns\n * \"anonymous\" for a Rust method) -- the target struct/enum's name lives on the impl block's own\n * `type` field instead (the Self type, not `trait` -- `impl Trait for Type` still qualifies by\n * `Type`, the concrete struct, not whichever trait it happens to implement). */\nfunction resolveRustImplContainerName(node: Node): string | undefined {\n let current = node.parent;\n while (current) {\n if (current.type === AstNodeTypes.IMPL_ITEM) {\n const typeField = current.childForFieldName(\"type\");\n return typeField ? firstTypeIdentifierText(typeField) : undefined;\n }\n current = current.parent;\n }\n return undefined;\n}\n\n/** GRPH-006 follow-up (Go): a method's receiver type is referenced through its own `receiver:`\n * field, never as an AST ancestor (`type_declaration` never encloses a `method_declaration`), so\n * `findEnclosingContainerName` over `classIds` always returns \"anonymous\" here too -- resolve the\n * receiver parameter's type directly instead. */\nfunction resolveGoReceiverContainerName(node: Node): string | undefined {\n const receiver = node.childForFieldName(\"receiver\");\n const paramType = receiver?.namedChild(0)?.childForFieldName(\"type\");\n return paramType ? firstTypeIdentifierText(paramType) : undefined;\n}\n\n/** GRPH-006 follow-up (C++): an out-of-line `Ret Class::method(){}` definition is never lexically\n * nested inside its class (inline methods already resolve via the generic ancestor walk once\n * they're extracted at all -- see `cpp.ts`'s functions-query fix), so its declarator is a\n * `qualified_identifier` carrying the class name in its own `scope` field instead. Recurses one\n * level for a nested qualifier (`A::B::method`), taking the innermost (`B`) as the immediate\n * container -- this scheme only ever tracks one level of containment, matching every other\n * language here. */\nfunction resolveCppQualifiedContainerName(node: Node): string | undefined {\n const declarator = node.childForFieldName(\"declarator\");\n const inner = declarator?.childForFieldName(\"declarator\");\n if (inner?.type !== AstNodeTypes.QUALIFIED_IDENTIFIER) return undefined;\n const scope = inner.childForFieldName(\"scope\");\n if (!scope) return undefined;\n return scope.type === AstNodeTypes.QUALIFIED_IDENTIFIER\n ? scope.childForFieldName(\"name\")?.text\n : scope.text;\n}\n\n/** GRPH-006 follow-up: for a function whose enclosing container the generic ancestor walk\n * couldn't find (Rust/Go/C++'s deferred gaps -- see the ADR), try each language-specific\n * resolver in turn. Mutually exclusive by construction (each checks for a node shape only its own\n * grammar produces -- e.g. only Rust ever has an `impl_item` ancestor), so no explicit language\n * tag is needed; a language not covered here (or a function with no enclosing container at all)\n * falls through to `undefined`, unchanged from before this follow-up. */\nfunction resolveDeferredLanguageContainerName(node: Node): string | undefined {\n return (\n resolveRustImplContainerName(node) ??\n resolveGoReceiverContainerName(node) ??\n resolveCppQualifiedContainerName(node)\n );\n}\n\n/** Shape of a fully-populated `AstParseResponse[\"data\"]` (i.e. the non-optional variant produced once parsing has actually run). */\ntype AstExtractionResult = NonNullable<AstParseResponse[\"data\"]>;\n\n/** Extracts class declaration nodes via the provider and appends their summaries to `classes`. Returns the raw nodes so callers can derive id sets for edge extraction. */\nfunction collectClassNodes(\n tree: Tree,\n provider: LanguageProvider,\n classes: AstExtractionResult[\"classes\"],\n): Node[] {\n const classNodes = provider.extractClasses(tree.rootNode);\n for (const node of classNodes) {\n classes.push({\n name: getNodeName(node),\n startLine: node.startPosition.row,\n endLine: node.endPosition.row,\n methods: [],\n contentHash: symbolContentHash(node),\n });\n }\n return classNodes;\n}\n\n/**\n * Extracts exported variable declarators via the provider and appends their summaries to\n * `variables` (issue #192 gap 1: `export const X = ...` must be an indexable symbol so\n * `impact <X>` resolves instead of \"No matching node\"). The query only matches exported\n * `lexical_declaration` declarators with plain identifier names; this collector additionally\n * skips function-valued initializers (arrow_function/function_expression) since those are\n * already indexed as functions by the `functions` query + resolveCallableName path.\n */\nconst FUNCTION_VALUE_NODE_TYPES = new Set<string>([\n \"arrow_function\",\n \"function_expression\",\n]);\n\nfunction collectVariableNodes(\n tree: Tree,\n provider: LanguageProvider,\n variables: NonNullable<AstExtractionResult[\"variables\"]>,\n): Node[] {\n const variableNodes = provider.extractVariables?.(tree.rootNode) ?? [];\n for (const node of variableNodes) {\n const value = node.childForFieldName(\"value\");\n if (value && FUNCTION_VALUE_NODE_TYPES.has(value.type)) continue;\n variables.push({\n name: getNodeName(node),\n startLine: node.startPosition.row,\n endLine: node.endPosition.row,\n contentHash: symbolContentHash(node),\n });\n }\n return variableNodes;\n}\n\n/** Extracts function/method declaration nodes via the provider and appends their summaries to `functions`. Returns the raw nodes so callers can derive id sets for edge extraction.\n *\n * `classNodes` (GRPH-006) lets each function's enclosing class/struct be resolved via the same\n * `findEnclosingContainerName` ancestor walk `collectCallEdges`/`collectImplementsEdges`/\n * `collectExtendsEdges` already use -- `containerName` is what qualifies this symbol's `node_key`\n * down the line (`persist-ast-graph.ts`'s `buildQualifiedBaseKey`). `findEnclosingContainerName`'s\n * \"nothing found\" sentinel is the literal string `AstMessages.ANONYMOUS_NAME` (\"anonymous\") --\n * that means \"top-level, outside any class\" here, a different meaning than its existing use in\n * `collectCallEdges` (\"outside any function\"), so it must be converted to `undefined` rather than\n * stored as-is (storing it as-is would qualify every top-level function as `file#anonymous.name`).\n *\n * When the ancestor walk finds nothing, `resolveDeferredLanguageContainerName` (GRPH-006\n * follow-up) tries Rust/Go/C++'s own non-lexical containment shapes before giving up to\n * `undefined` -- see that function's own doc comment for why each needs a different mechanism. */\nexport function collectFunctionNodes(\n tree: Tree,\n provider: LanguageProvider,\n functions: AstExtractionResult[\"functions\"],\n classNodes: Node[],\n): Node[] {\n const classIds = new Set(classNodes.map((n) => n.id));\n const functionNodes = provider.extractFunctions(tree.rootNode);\n for (const node of functionNodes) {\n const container = findEnclosingContainerName(node, classIds);\n const containerName =\n container === AstMessages.ANONYMOUS_NAME\n ? resolveDeferredLanguageContainerName(node)\n : container;\n functions.push({\n name: resolveCallableName(node),\n startLine: node.startPosition.row,\n endLine: node.endPosition.row,\n contentHash: symbolContentHash(node),\n containerName,\n });\n }\n return functionNodes;\n}\n\n/** Extracts call-site edges via the provider, attributing each call to its enclosing function (or \"anonymous\"), up to the 1000-call circuit breaker. */\nfunction collectCallEdges(\n tree: Tree,\n provider: LanguageProvider,\n functionNodes: Node[],\n calls: AstExtractionResult[\"calls\"],\n): void {\n const functionIds = new Set(functionNodes.map((n) => n.id));\n const callNodes = provider.extractCalls(tree.rootNode);\n for (const node of callNodes) {\n if (calls.length >= 1000) break; // Circuit breaker limit\n const position = getCallSitePosition(node);\n calls.push({\n sourceFunction: findEnclosingContainerName(node, functionIds),\n targetFunction: getCallTargetText(node),\n startLine: position.row,\n startColumn: position.column,\n });\n }\n}\n\n/** Extracts `implements` edges via the provider, if it supports them, attributing each to its enclosing class. */\nfunction collectImplementsEdges(\n tree: Tree,\n provider: LanguageProvider,\n classNodes: Node[],\n implementsList: NonNullable<AstExtractionResult[\"implements\"]>,\n): void {\n if (!provider.extractImplements) return;\n const classIds = new Set(classNodes.map((n) => n.id));\n for (const node of provider.extractImplements(tree.rootNode)) {\n implementsList.push({\n sourceClass: findEnclosingContainerName(node, classIds),\n targetInterface: node.text,\n });\n }\n}\n\n/** Extracts `extends` edges via the provider, if it supports them, attributing each to its enclosing class. */\nfunction collectExtendsEdges(\n tree: Tree,\n provider: LanguageProvider,\n classNodes: Node[],\n extendsList: NonNullable<AstExtractionResult[\"extends\"]>,\n): void {\n if (!provider.extractExtends) return;\n const classIds = new Set(classNodes.map((n) => n.id));\n for (const node of provider.extractExtends(tree.rootNode)) {\n extendsList.push({\n sourceClass: findEnclosingContainerName(node, classIds),\n targetClass: node.text,\n });\n }\n}\n\n/** Languages `collectWorkerSpawns` runs against — the reported gap (a `new Worker(...)` spawn\n * invisible to the static imports/calls pipeline) is TS/JS-specific: `new_expression` is also a\n * real C++ grammar node (the `new` operator), so gating by language avoids misinterpreting an\n * unrelated `new Worker(...)` construction in another language as a worker_threads spawn. */\nconst WORKER_SPAWN_LANGUAGES: ReadonlySet<SupportedLanguage> = new Set([\n SUPPORTED_LANGUAGES.TYPESCRIPT,\n SUPPORTED_LANGUAGES.JAVASCRIPT,\n]);\n\nconst WORKER_CONSTRUCTOR_NAME = \"Worker\";\nconst PATH_MODULE_OBJECT_NAME = \"path\";\nconst PATH_RESOLVE_METHOD_NAME = \"resolve\";\nconst PATH_JOIN_METHOD_NAME = \"join\";\nconst DIRNAME_IDENTIFIER_NAME = \"__dirname\";\n\n/** True when `node` is a `path.resolve(...)`/`path.join(...)` call expression — the exact idiom\n * `ast-worker-pool.ts` uses to build a worker script's path from `__dirname`. */\nfunction isPathJoinOrResolveCall(node: Node): boolean {\n if (node.type !== \"call_expression\") return false;\n const fn = node.childForFieldName(\"function\");\n if (!fn || fn.type !== \"member_expression\") return false;\n const object = fn.childForFieldName(\"object\")?.text;\n const property = fn.childForFieldName(\"property\")?.text;\n return (\n object === PATH_MODULE_OBJECT_NAME &&\n (property === PATH_RESOLVE_METHOD_NAME ||\n property === PATH_JOIN_METHOD_NAME)\n );\n}\n\n/** Extracts the string-literal path segment from a `path.resolve(__dirname, \"<literal>\")` /\n * `path.join(__dirname, \"<literal>\")` call, or undefined if it doesn't reference `__dirname` or\n * carries no string-literal argument. */\nfunction extractDirnameJoinedLiteral(callNode: Node): string | undefined {\n const args = callNode.childForFieldName(\"arguments\");\n if (!args) return undefined;\n const hasDirname = args.namedChildren.some(\n (n) => n?.type === \"identifier\" && n.text === DIRNAME_IDENTIFIER_NAME,\n );\n if (!hasDirname) return undefined;\n const literal = args.descendantsOfType(\"string\")[0];\n return literal?.text.replace(/['\"]/g, \"\");\n}\n\n/** Bounded same-file scan for `<argText> = path.resolve/join(__dirname, \"<literal>\")` — the\n * exact idiom `ast-worker-pool.ts` uses for `this.wPath = path.resolve(__dirname, \"./ast-worker.js\")`,\n * needed because the `new Worker(...)` call site references the field/variable, not the literal,\n * directly. Matches by comparing the assignment's own left-hand-side text against `argText`\n * verbatim (e.g. both \"this.wPath\"), so it works uniformly whether the argument is a bare\n * identifier or a member expression. */\nfunction findDirnameJoinedAssignment(\n tree: Tree,\n argText: string,\n): string | undefined {\n for (const assignment of tree.rootNode.descendantsOfType(\n AstNodeTypes.ASSIGNMENT_EXPRESSION,\n )) {\n if (!assignment) continue;\n const left = assignment.childForFieldName(\"left\");\n const right = assignment.childForFieldName(\"right\");\n if (left?.text !== argText || !right || !isPathJoinOrResolveCall(right)) {\n continue;\n }\n const literal = extractDirnameJoinedLiteral(right);\n if (literal) return literal;\n }\n return undefined;\n}\n\n/** Resolves `new Worker(<arg>)`'s first argument to a literal relative path: a direct string\n * literal, or a same-file `path.resolve/join(__dirname, \"<literal>\")`-assigned field/variable\n * (the `ast-worker-pool.ts` idiom, via `findDirnameJoinedAssignment`). Returns undefined —\n * skip silently, no guessing — when neither shape matches. */\nfunction resolveWorkerArgumentPath(\n tree: Tree,\n argNode: Node,\n): string | undefined {\n if (argNode.type === \"string\") {\n return argNode.text.replace(/['\"]/g, \"\");\n }\n if (argNode.type === \"identifier\" || argNode.type === \"member_expression\") {\n return findDirnameJoinedAssignment(tree, argNode.text);\n }\n return undefined;\n}\n\n/** Extracts `new Worker(<arg>)` spawn sites (TS/JS only, see `WORKER_SPAWN_LANGUAGES`) and\n * appends a `{sourceFunction, targetPath}` descriptor for each one whose argument resolves to a\n * literal relative path (`resolveWorkerArgumentPath`), attributing it to its enclosing function\n * like `collectCallEdges` does. `persist-ast-graph.ts` resolves `targetPath` to a real file and\n * inserts the corresponding `DEPENDS_ON` edge. */\nexport function collectWorkerSpawns(\n tree: Tree,\n language: SupportedLanguage,\n functionNodes: Node[],\n workerSpawns: NonNullable<AstExtractionResult[\"workerSpawns\"]>,\n): void {\n if (!WORKER_SPAWN_LANGUAGES.has(language)) return;\n const functionIds = new Set(functionNodes.map((n) => n.id));\n for (const node of tree.rootNode.descendantsOfType(\"new_expression\")) {\n if (!node) continue;\n const ctor = node.childForFieldName(\"constructor\");\n if (ctor?.text !== WORKER_CONSTRUCTOR_NAME) continue;\n const args = node.childForFieldName(\"arguments\");\n const firstArg = args?.namedChildren.find((n): n is Node => n !== null);\n if (!firstArg) continue;\n const targetPath = resolveWorkerArgumentPath(tree, firstArg);\n if (!targetPath) continue;\n workerSpawns.push({\n sourceFunction: findEnclosingContainerName(node, functionIds),\n targetPath,\n });\n }\n}\n\n/**\n * Runs every provider-driven extraction against a parsed tree (or returns empty results plus a\n * decision note if parsing produced no tree). A single try/catch wraps the whole pass, matching\n * the original inline behavior of recording a `providerQueryFailed` decision instead of throwing\n * when any one extractor misbehaves.\n */\nfunction extractAstData(\n tree: Tree | null,\n provider: LanguageProvider,\n language: SupportedLanguage,\n): AstExtractionResult {\n const decisions: string[] = [];\n const imports: ImportDescriptor[] = [];\n const exports: AstExtractionResult[\"exports\"] = [];\n const functions: AstExtractionResult[\"functions\"] = [];\n const classes: AstExtractionResult[\"classes\"] = [];\n const variables: NonNullable<AstExtractionResult[\"variables\"]> = [];\n const calls: AstExtractionResult[\"calls\"] = [];\n const implementsList: NonNullable<AstExtractionResult[\"implements\"]> = [];\n const extendsList: NonNullable<AstExtractionResult[\"extends\"]> = [];\n const workerSpawns: NonNullable<AstExtractionResult[\"workerSpawns\"]> = [];\n\n if (tree) {\n decisions.push(AstMessages.parsedViaTreeSitter(tree.rootNode.childCount));\n\n try {\n const classNodes = collectClassNodes(tree, provider, classes);\n const functionNodes = collectFunctionNodes(\n tree,\n provider,\n functions,\n classNodes,\n );\n\n const importNodes = provider.extractImports(tree.rootNode);\n imports.push(...parseImportDescriptors(importNodes));\n\n collectVariableNodes(tree, provider, variables);\n\n collectCallEdges(tree, provider, functionNodes, calls);\n collectImplementsEdges(tree, provider, classNodes, implementsList);\n collectExtendsEdges(tree, provider, classNodes, extendsList);\n collectWorkerSpawns(tree, language, functionNodes, workerSpawns);\n\n decisions.push(AstMessages.QUERIED_VIA_LANGUAGE_PROVIDER);\n } catch (e) {\n decisions.push(\n AstMessages.providerQueryFailed(\n e instanceof Error ? e.message : String(e),\n ),\n );\n }\n }\n\n return {\n imports,\n exports,\n functions,\n classes,\n variables,\n calls,\n implements: implementsList,\n extends: extendsList,\n workerSpawns,\n decisions,\n };\n}\n\n/** Logs any query pattern that failed to compile, then clears them so a later file of the same\n * language doesn't re-report the same failure. Drained rather than read because the provider is\n * reused for the worker's whole lifetime.\n *\n * Exported for the wiring test: the module-scoped `logger` posts to `parentPort`, which is\n * null outside a real worker, so this path is otherwise unobservable (same reason\n * `buildParseResponse` is exported). */\nexport function reportQueryCompileFailures(\n provider: LanguageProvider,\n language: SupportedLanguage,\n): void {\n for (const failure of provider.drainQueryCompileFailures?.() ?? []) {\n logger.error(AstMessages.QUERY_COMPILE_FAILED, {\n language,\n kind: failure.kind,\n pattern: failure.pattern,\n message: failure.message,\n });\n }\n}\n\n/** Parses `request.code` with the resolved provider/grammar and runs the full AST extraction pass, releasing the tree-sitter tree/parser handles once done. */\nfunction parseAndExtract(\n code: string,\n provider: LanguageProvider,\n langInstance: Language,\n language: SupportedLanguage,\n): AstExtractionResult {\n const parser = new Parser();\n parser.setLanguage(langInstance);\n\n // initQueries() is idempotent (DefaultProvider caches compiledQueries after the first call)\n // and the provider instance is reused for every file of this language for the worker's whole\n // lifetime (see getRegistry() above), so this only actually compiles once. A field whose\n // pattern fails to compile against the installed grammar degrades to that field's\n // descendantsOfType fallback (DefaultProvider.compileQuery) rather than throwing.\n provider.initQueries?.(langInstance);\n reportQueryCompileFailures(provider, language);\n\n const tree = parser.parse(code);\n const data = extractAstData(tree, provider, language);\n\n if (tree) tree.delete();\n parser.delete();\n\n return data;\n}\n\n/**\n * Resolves a language provider for `request.filePath` and produces the full parse response,\n * short-circuiting with an empty-but-successful result (plus an explanatory decision note) when\n * no provider is registered for the extension or its wasm grammar can't be located on disk.\n */\nexport async function buildParseResponse(\n request: AstParseRequest,\n): Promise<AstParseResponse> {\n if (!parserInitialized) {\n await Parser.init();\n parserInitialized = true;\n }\n\n const registry = await getRegistry();\n const ext = path.extname(request.filePath);\n const provider: LanguageProvider | undefined =\n registry.getProviderForExtension(ext);\n\n if (!provider) {\n return {\n taskId: request.taskId,\n success: true,\n data: {\n imports: [],\n exports: [],\n functions: [],\n classes: [],\n calls: [],\n decisions: [AstMessages.noLanguageProviderForExtension(ext)],\n },\n };\n }\n\n const { wasmPath, attemptedPaths } = resolveWasmPath(provider.wasm_file);\n\n if (!fs.existsSync(wasmPath)) {\n return {\n taskId: request.taskId,\n success: true,\n data: {\n imports: extractFallbackImports(request.code),\n exports: [],\n functions: [],\n classes: [],\n calls: [],\n decisions: [\n AstMessages.wasmNotFound(request.language, attemptedPaths.join(\", \")),\n ],\n },\n };\n }\n\n const langInstance = await getLanguage(wasmPath);\n const data = parseAndExtract(\n request.code,\n provider,\n langInstance,\n request.language,\n );\n\n return {\n taskId: request.taskId,\n success: true,\n data,\n };\n}\n\nparentPort?.on(\"message\", async (request: AstParseRequest) => {\n try {\n const response = await buildParseResponse(request);\n parentPort?.postMessage(response);\n } catch (err: any) {\n parentPort?.postMessage({\n taskId: request.taskId,\n success: false,\n error: err.stack || String(err),\n });\n }\n});\n"],"mappings":"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","names":["parentPort","Parser","Language","path","fs","createHash","symbolContentHash","node","createHash","HASH_ALGO_SHA256","logger","IpcLoggerClient","message","parentPort","err","AstMessages","parserInitialized","registryPromise","getRegistry","loadDefaultRegistry","languageCache","getLanguage","wasmPath","cached","Language","extractFallbackImports","code","fallbackImports","importMatches","match","getNodeName","node","AstMessages","resolveDeclaratorNestedName","declaratorName","AstNodeTypes","resolveCallableName","ownName","NAME_BEARING_PARENTS","current","nameNode","getCallTargetText","getCallSitePosition","findEnclosingContainerName","containerIds","firstTypeIdentifierText","resolveRustImplContainerName","typeField","resolveGoReceiverContainerName","paramType","resolveCppQualifiedContainerName","inner","scope","resolveDeferredLanguageContainerName","collectClassNodes","tree","provider","classes","classNodes","symbolContentHash","FUNCTION_VALUE_NODE_TYPES","collectVariableNodes","variables","variableNodes","value","collectFunctionNodes","functions","classIds","n","functionNodes","container","containerName","collectCallEdges","calls","functionIds","callNodes","position","collectImplementsEdges","implementsList","collectExtendsEdges","extendsList","WORKER_SPAWN_LANGUAGES","SUPPORTED_LANGUAGES","WORKER_CONSTRUCTOR_NAME","PATH_MODULE_OBJECT_NAME","PATH_RESOLVE_METHOD_NAME","PATH_JOIN_METHOD_NAME","DIRNAME_IDENTIFIER_NAME","isPathJoinOrResolveCall","fn","object","property","extractDirnameJoinedLiteral","callNode","args","findDirnameJoinedAssignment","argText","assignment","left","right","literal","resolveWorkerArgumentPath","argNode","collectWorkerSpawns","language","workerSpawns","firstArg","targetPath","extractAstData","decisions","imports","exports","importNodes","parseImportDescriptors","e","reportQueryCompileFailures","failure","logger","parseAndExtract","langInstance","parser","Parser","data","buildParseResponse","request","parserInitialized","registry","getRegistry","ext","wasmPath","attemptedPaths","resolveWasmPath","getLanguage","parentPort","response","err"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
|
-
var a={DEBUG:"debug",INFO:"info",WARN:"warn",ERROR:"error"};var u="ipc-log";function I(t){return typeof t=="object"&&t!==null&&t.type===u}var S=class{constructor(e){this.postMessageFn=e}postMessageFn;debug(e,o){this.send(a.DEBUG,e,o)}info(e,o){this.send(a.INFO,e,o)}warn(e,o){this.send(a.WARN,e,o)}error(e,o){this.send(a.ERROR,e,o)}onLog(){return()=>{}}send(e,o,r){let s=r?{type:u,level:e,message:o,context:r}:{type:u,level:e,message:o};this.postMessageFn(s)}};var _={CONTEXT_INJECTION:"context-injection",COMMIT_L3_WRITE:"commit-l3-write",TIER_B_C_PREPUSH:"tier-b-c-prepush"},Q={[_.CONTEXT_INJECTION]:!0,[_.COMMIT_L3_WRITE]:!0,[_.TIER_B_C_PREPUSH]:!0};var J=".docuvia",q="logs",z="init.log",Z="clean.log",$="status.log",ee="sync.log",te="analyze.log",oe="review.log",re="impact.log",ne="query.log",se="export-topology.log",ie="snapshot.log",ae="hydrate.log",ce="sync-knowledge.log",le="node_modules",pe="local.db",ge="init.lock",de="tierC.lock",ue="sync-state.json",me="hooks-config.json",Ee="pending-l3-decisions.json";var R="utf8";var _e="sha256";var i={TYPESCRIPT:"typescript",JAVASCRIPT:"javascript",PYTHON:"python",RUST:"rust",GO:"go",JAVA:"java",C:"c",CPP:"cpp",RUBY:"ruby",PHP:"php",CSHARP:"csharp"};var fe=".claude/hooks",
|
|
2
|
+
var a={DEBUG:"debug",INFO:"info",WARN:"warn",ERROR:"error"};var u="ipc-log";function I(t){return typeof t=="object"&&t!==null&&t.type===u}var S=class{constructor(e){this.postMessageFn=e}postMessageFn;debug(e,o){this.send(a.DEBUG,e,o)}info(e,o){this.send(a.INFO,e,o)}warn(e,o){this.send(a.WARN,e,o)}error(e,o){this.send(a.ERROR,e,o)}onLog(){return()=>{}}send(e,o,r){let s=r?{type:u,level:e,message:o,context:r}:{type:u,level:e,message:o};this.postMessageFn(s)}};var _={CONTEXT_INJECTION:"context-injection",COMMIT_L3_WRITE:"commit-l3-write",TIER_B_C_PREPUSH:"tier-b-c-prepush"},Q={[_.CONTEXT_INJECTION]:!0,[_.COMMIT_L3_WRITE]:!0,[_.TIER_B_C_PREPUSH]:!0};var J=".docuvia",q="logs",z="init.log",Z="clean.log",$="status.log",ee="sync.log",te="analyze.log",oe="review.log",re="impact.log",ne="query.log",se="export-topology.log",ie="snapshot.log",ae="hydrate.log",ce="sync-knowledge.log",le="node_modules",pe="local.db",ge="init.lock",de="tierC.lock",ue="sync-state.json",me="hooks-config.json",Ee="pending-l3-decisions.json";var R="utf8";var _e="sha256";var i={TYPESCRIPT:"typescript",JAVASCRIPT:"javascript",PYTHON:"python",RUST:"rust",GO:"go",JAVA:"java",C:"c",CPP:"cpp",RUBY:"ruby",PHP:"php",CSHARP:"csharp"};var fe=".claude/hooks",ye=".cursor/hooks",Te="docuvia-hook.js",Le="docuvia-hook.cjs";var m=class{listeners=new Set;debug(e,o){this.emit(a.DEBUG,e,o)}info(e,o){this.emit(a.INFO,e,o)}warn(e,o){this.emit(a.WARN,e,o)}error(e,o){this.emit(a.ERROR,e,o)}onLog(e){return this.listeners.add(e),()=>this.listeners.delete(e)}emit(e,o,r){let s=r?{level:e,message:o,context:r}:{level:e,message:o};for(let l of this.listeners)l(s)}};function Ie(){return new m}var h=class{constructor(e){this.logger=e}logger;handleMessage(e){return I(e)?(this.logger[e.level](e.message,e.context),!0):!1}};var g={GIT_COMMAND_FAILED:"GIT_COMMAND_FAILED",GIT_NETWORK_TIMEOUT:"GIT_NETWORK_TIMEOUT",GIT_NOT_A_REPOSITORY:"GIT_NOT_A_REPOSITORY",GIT_BRANCH_CREATE_FAILED:"GIT_BRANCH_CREATE_FAILED",GIT_HOOK_INSTALL_FAILED:"GIT_HOOK_INSTALL_FAILED",GIT_FAST_IMPORT_FAILED:"GIT_FAST_IMPORT_FAILED",DB_OPEN_FAILED:"DB_OPEN_FAILED",DB_NOT_FOUND:"DB_NOT_FOUND",DB_MIGRATION_FAILED:"DB_MIGRATION_FAILED",DB_QUERY_FAILED:"DB_QUERY_FAILED",DB_LOCKED:"DB_LOCKED",AST_WORKER_CRASHED:"AST_WORKER_CRASHED",AST_PARSE_FAILED:"AST_PARSE_FAILED",FS_READ_FAILED:"FS_READ_FAILED",FS_PATH_TRAVERSAL:"FS_PATH_TRAVERSAL",FACTORY_TOKEN_NOT_REGISTERED:"FACTORY_TOKEN_NOT_REGISTERED",FACTORY_LOCKED:"FACTORY_LOCKED",MEMORY_SCOPE_NOT_FOUND:"MEMORY_SCOPE_NOT_FOUND",SYNC_FETCH_FAILED:"SYNC_FETCH_FAILED",SYNC_PUSH_FAILED:"SYNC_PUSH_FAILED",LLM_NOT_INITIALIZED:"LLM_NOT_INITIALIZED",LLM_CHAT_COMPLETION_FAILED:"LLM_CHAT_COMPLETION_FAILED",LLM_STREAM_FAILED:"LLM_STREAM_FAILED",LLM_INVALID_RESPONSE:"LLM_INVALID_RESPONSE",INIT_WORKFLOW_FAILED:"INIT_WORKFLOW_FAILED",CLEAN_WORKFLOW_FAILED:"CLEAN_WORKFLOW_FAILED",INVALID_INPUT:"INVALID_INPUT"};var Se="DocuviaError",c=class t extends Error{code;cause;constructor(e,o,r){super(o),this.name=Se,this.code=e,this.cause=r}static wrap(e,o,r){if(r instanceof t)return r;let s=r instanceof Error?r.message:String(r);return new t(e,`${o}: ${s}`,r)}};var Re={WORKSPACE_ROOT:"workspaceRoot",API_URL:"apiUrl",PAT:"pat",PROJECT_ID:"projectId",COMMIT_SHA:"commitSha",TARGET_PATH:"targetPath",LLM_BASE_URL:"llmBaseUrl",LLM_MODEL:"llmModel",BASE_REF:"baseRef",TARGET:"target",ESCALATE_TO_LSP:"escalateToLsp",TIER_B_FULL_RESYNC:"tierBFullResync",LIMIT:"limit",COLLAPSE:"collapse",KEEP_DB:"keepDb",LSP_BINARY_OVERRIDE:"lspBinaryOverride",LSP_ARGS_OVERRIDE:"lspArgsOverride",LSP_TIMEOUT_MS:"lspTimeoutMs",LSP_MAX_PROCESSES:"lspMaxProcesses",TIER_B_COMMIT_CAP:"tierBCommitCap",TIER_C_DAILY_CALL_CAP:"tierCDailyCallCap",TIER_C_DAILY_TOKEN_CAP:"tierCDailyTokenCap",TIER_C_WALL_CLOCK_MS:"tierCWallClockMs",TIER_C_ITEM_CAP:"tierCItemCap",TIER_C_LOAD_THRESHOLD:"tierCLoadThreshold",GIT_NETWORK_TIMEOUT_MS:"gitNetworkTimeoutMs",FORCE:"force",AGENT_AUTHORED_DECISIONS:"agentAuthoredDecisions",HOOK_NAME:"hookName",HOOK_ENABLED:"hookEnabled",FLUSH_STAGED_L3:"flushStagedL3"},he={SCOPE_NOT_FOUND:(t,e)=>`Cannot set "${t}": memory scope "${e}" was never created`},f=class{scopes=new Map;createScope(e){this.scopes.has(e)||this.scopes.set(e,new Map)}set(e,o,r){let s=this.scopes.get(e);if(!s)throw new c(g.MEMORY_SCOPE_NOT_FOUND,he.SCOPE_NOT_FOUND(o,e));s.set(o,r)}get(e,o){return this.scopes.get(e)?.get(o)}deleteScope(e){this.scopes.delete(e)}hasScope(e){return this.scopes.has(e)}},Ce=new f;var x={REGISTER_LOCKED:t=>`Cannot register "${t}": factory is locked (test isolation)`,PROVIDER_NOT_REGISTERED:t=>`No provider registered for "${t}" \u2014 the implementation library that owns it was never imported for its registration side effect`},y=class{providers=new Map;locked=!1;register(e,o){if(this.locked)throw new c(g.FACTORY_LOCKED,x.REGISTER_LOCKED(String(e.description)));this.providers.set(e,o)}resolve(e,o){let r=this.providers.get(e);if(!r)throw new c(g.FACTORY_TOKEN_NOT_REGISTERED,x.PROVIDER_NOT_REGISTERED(String(e.description)));return r(this,o)}has(e){return this.providers.has(e)}lock(){this.locked=!0}unlock(){this.locked=!1}reset(){this.providers.clear(),this.locked=!1}},D=new y;function n(t){return Symbol(t)}var Ae={GitProvider:n("IGitProvider"),LineBlameProvider:n("ILineBlameProvider"),KnowledgeGitService:n("IKnowledgeGitService"),FileDiscovery:n("IFileDiscovery"),ConfigScanner:n("IConfigScanner"),VcsScanner:n("IVcsScanner"),AstProcessor:n("IAstProcessor"),GraphPersister:n("IGraphPersister"),TempFileManager:n("TempFileManager"),GraphStoreOpener:n("GraphStoreOpener"),RemoteSyncClient:n("RemoteSyncClient"),LlmClient:n("LlmClient"),QueryService:n("IQueryService"),TierBCoverageHintProvider:n("ITierBCoverageHintProvider"),ImpactService:n("IImpactService"),ChangeDetectionService:n("IChangeDetectionService"),TopologyBuilder:n("ITopologyBuilder"),SnapshotRenderer:n("ISnapshotRenderer"),HydrationService:n("IHydrationService"),DiagnosticRunnerDb:n("DiagnosticRunnerDb"),DiagnosticRunnerGit:n("DiagnosticRunnerGit"),SemanticDiffAnalyzer:n("ISemanticDiffAnalyzer"),EdgeResolutionProviders:n("EdgeResolutionProviders")};var Ne={ADDED:"added",MODIFIED:"modified",DELETED:"deleted",RENAMED:"renamed"};var Oe={LSP:"lsp",LLM_INFERRED:"llm-inferred"},Pe={TYPESCRIPT:"typescript",PYTHON:"python",GO:"go",RUST:"rust",JAVA:"java",CPP:"cpp",CSHARP:"csharp",PHP:"php",RUBY:"ruby"};var ve={NO_REMOTE:"no-remote",UP_TO_DATE:"up-to-date",FAST_FORWARDED_LOCAL:"fast-forwarded-local",PUSHED_LOCAL:"pushed-local",MERGED:"merged"};var xe={MODULE:"module",PACKAGE:"package",PCD:"pcd"},De={CONTAINS:"contains",CALLS:"calls",IMPLEMENTS:"implements",EXTENDS:"extends",IMPORTS:"imports",DEPENDS_ON:"depends_on",DECISION:"decision"};var Me={PENDING:"pending",ACTIVE:"active",DRAFT:"draft",GARBAGE:"garbage"},be={ANALYZE:"analyze",AGENT_AUTHORED:"agent-authored"};var ke={CREATE_L3:"CREATE_L3"};var Fe={SYSTEM:"system",USER:"user",ASSISTANT:"assistant",TOOL:"tool"};var we={LOW:"LOW",MEDIUM:"MEDIUM",HIGH:"HIGH",CRITICAL:"CRITICAL",UNKNOWN:"UNKNOWN"},Ge={EXACT:"exact",LOWER_BOUND:"lower-bound"},He={LSP_FALLBACK:"lsp-fallback"};var Be={L2:"l2",L3:"l3"};var Ke={FILE:"file",SYMBOL:"symbol",DECISION:"decision"},Ue={AUTO:"auto",FILE:"file",SYMBOL:"symbol"},je={L1_TAG:"l1_tag",DIRECTORY:"directory"};var Ye={TYPESCRIPT:"typescript",REACT:"react",EXPRESS:"express",VUE:"vue"},Ve={UNKNOWN:"unknown",JAVASCRIPT:"javascript",GENERIC:"generic",RUST:"rust",PYTHON:"python",GO:"go"},We="general",Xe={FRONTEND:"frontend",BACKEND:"backend",NEXTJS:"nextjs",SSR:"ssr",DRIZZLE:"drizzle",DATABASE:"database",TAILWINDCSS:"tailwindcss",CSS:"css",JEST:"jest",TESTING:"testing",VITEST:"vitest",POSTGRES:"postgres",MONOREPO:"monorepo",TOKIO:"tokio",ASYNC:"async",ACTIX:"actix",SERDE:"serde",TAURI:"tauri",DESKTOP:"desktop",DJANGO:"django",FASTAPI:"fastapi",PANDAS:"pandas",DATA:"data",GIN:"gin",STRICT_TS:"strict-ts",VITE:"vite",BUILD_TOOL:"build-tool"};var M="origin",C={KNOWLEDGE_ROOT:"docuvia-knowledge",KNOWLEDGE_DIR_NAME:"knowledge",GRAPH_DIR_NAME:"graph",L3_DIR_NAME:"_l3",NODES_JSONL_NAME:"nodes.jsonl",EDGES_JSONL_NAME:"edges.jsonl",SOURCE_COMMIT_TRAILER_KEY:"Docuvia-Source",POST_COMMIT_HOOK_NAME:"post-commit",POST_COMMIT_HOOK_MARKER:"docuvia analyze",POST_COMMIT_FLUSH_L3_MARKER:"docuvia analyze --flush-staged-l3",POST_COMMIT_NOHUP_MARKER:"nohup npx --no-install docuvia analyze",DOCUVIA_HOOK_HEADER_COMMENT:"# Docuvia Knowledge Graph Evolver Hook",POST_COMMIT_HOOK_CONTENT:`#!/bin/bash
|
|
3
3
|
# Docuvia Knowledge Graph Evolver Hook
|
|
4
4
|
# Non-intrusively extracts AST deltas in the background
|
|
5
5
|
if command -v npx > /dev/null 2>&1; then
|
|
@@ -82,6 +82,6 @@ fi
|
|
|
82
82
|
# Never blocks the push on a Tier B failure -- PLAT-007's reliability requirement (failures
|
|
83
83
|
# only ever surface via JSONL logs / doctor, never to the pushing developer).
|
|
84
84
|
exit 0
|
|
85
|
-
`,META_KEY_TIER_C_QUEUE:"tierCQueue",META_KEY_TIER_C_BUDGET:"tierCBudget",DEFAULT_TIER_C_DAILY_CALL_CAP:50,DEFAULT_TIER_C_DAILY_TOKEN_CAP:1e5,DEFAULT_TIER_C_WALL_CLOCK_MS:12e3,DEFAULT_TIER_C_ITEM_CAP:20,DEFAULT_TIER_C_LOAD_THRESHOLD:.8,DEFAULT_TIER_C_MAX_ITEM_FAILURES:3};import
|
|
86
|
-
`)){let o=e.trim();if(o.startsWith(W))return o.slice(W.length).trim()}}function
|
|
87
|
-
//# sourceMappingURL=chunk-
|
|
85
|
+
`,META_KEY_TIER_C_QUEUE:"tierCQueue",META_KEY_TIER_C_BUDGET:"tierCBudget",DEFAULT_TIER_C_DAILY_CALL_CAP:50,DEFAULT_TIER_C_DAILY_TOKEN_CAP:1e5,DEFAULT_TIER_C_WALL_CLOCK_MS:12e3,DEFAULT_TIER_C_ITEM_CAP:20,DEFAULT_TIER_C_LOAD_THRESHOLD:.8,DEFAULT_TIER_C_MAX_ITEM_FAILURES:3};import T from"path";var b=[".ts",".tsx",".mts",".cts"],k=[".js",".jsx",".mjs",".cjs"],F=[".py"],w=[".rs"],G=[".go"],H=[".java"],B=[".c",".h"],K=[".cpp",".cxx",".cc",".hpp",".hxx",".hh",".cu",".cuh"],U=[".rb",".rake",".gemspec"],j=[".php",".phtml",".php3",".php4",".php5",".phps"],Y=[".cs"],A=new Set(["Rakefile","Gemfile","Guardfile","Vagrantfile","Brewfile"]),N=(()=>{let t=new Map,e=[[i.TYPESCRIPT,b],[i.JAVASCRIPT,k],[i.PYTHON,F],[i.RUST,w],[i.GO,G],[i.JAVA,H],[i.C,B],[i.CPP,K],[i.RUBY,U],[i.PHP,j],[i.CSHARP,Y]];for(let[o,r]of e)for(let s of r)t.set(s,o);return t})();function Qe(t){let e=N.get(T.extname(t).toLowerCase());return e||(A.has(T.basename(t))?i.RUBY:void 0)}function Je(t){return N.has(T.extname(t).toLowerCase())?!0:A.has(T.basename(t))}function qe(){return Array.from(N.keys()).map(t=>t.replace(/^\./,""))}var O="EEXIST",L="EPERM",P="EACCES",v="EBUSY";import d from"fs/promises";var ze={TIMED_OUT_WAITING:t=>`Timed out waiting for the lock at ${t} \u2014 another process may be stuck`},Ze={maxWaitMs:1e4,retryIntervalMs:100,heartbeatIntervalMs:1e4,staleAfterMs:3e4};function $e(t){return new Promise(e=>setTimeout(e,t))}function et(t){try{return process.kill(t,0),!0}catch(e){return e.code===L}}async function tt(t){try{let e=Number.parseInt(await d.readFile(t,R),10);return Number.isFinite(e)?e:void 0}catch{return}}async function ot(t,e){let o=await d.stat(t).catch(()=>{});if(!o||Date.now()-o.mtimeMs<=e)return!1;let r=await tt(t);return r!==void 0&&et(r)?!1:(await d.rm(t,{force:!0}).catch(()=>{}),!0)}function rt(t){let e=t.code;return e===O||e===L||e===P||e===v}async function nt(t){try{let e=await d.open(t,"wx");return await e.writeFile(String(process.pid)),await e.close(),!0}catch(e){if(!rt(e))throw e;return!1}}async function st(t,e={}){let o={...Ze,...e},r=Date.now()+o.maxWaitMs,s=!1;for(;!await nt(t);)if(s||(s=!0,e.onWaiting?.()),!await ot(t,o.staleAfterMs)){if(Date.now()>r)throw new Error(ze.TIMED_OUT_WAITING(t));await $e(o.retryIntervalMs)}let l=setInterval(()=>{let p=new Date;d.utimes(t,p,p).catch(()=>{})},o.heartbeatIntervalMs);l.unref?.();let E=!1;return{async release(){E||(E=!0,clearInterval(l),await d.rm(t,{force:!0}).catch(()=>{}))}}}var W=`${C.SOURCE_COMMIT_TRAILER_KEY}: `;function it(t){for(let e of t.split(`
|
|
86
|
+
`)){let o=e.trim();if(o.startsWith(W))return o.slice(W.length).trim()}}function at(t,e,o,r){if(!o&&!r)return;let l=(e?t.files.getTierBFileStatus(e):void 0)?.lastProcessedAt??null,E=r&&l===null,p=t.files.getTierBCoverage(),X=o&&p.processedFiles<p.totalFiles;if(!(!E&&!X))return{ownFileLastProcessedAt:l,workspaceFilesProcessed:p.processedFiles,workspaceFilesTotal:p.totalFiles}}var ct={PASS:"pass",FAIL:"fail"};export{a,m as b,Ie as c,S as d,h as e,g as f,c as g,Re as h,Ce as i,D as j,Ae as k,Ne as l,Oe as m,Pe as n,ve as o,xe as p,De as q,Me as r,be as s,ke as t,Fe as u,we as v,Ge as w,He as x,Be as y,Ke as z,Ue as A,je as B,_ as C,Q as D,J as E,q as F,z as G,Z as H,$ as I,ee as J,te as K,oe as L,re as M,ne as N,se as O,ie as P,ae as Q,ce as R,le as S,pe as T,ge as U,de as V,ue as W,me as X,Ee as Y,Ye as Z,Ve as _,We as $,Xe as aa,R as ba,_e as ca,M as da,C as ea,i as fa,b as ga,k as ha,F as ia,w as ja,G as ka,H as la,B as ma,K as na,U as oa,j as pa,Y as qa,A as ra,Qe as sa,Je as ta,qe as ua,O as va,L as wa,P as xa,v as ya,st as za,it as Aa,at as Ba,ct as Ca,fe as Da,ye as Ea,Te as Fa,Le as Ga};
|
|
87
|
+
//# sourceMappingURL=chunk-3WFKL5P6.js.map
|