gitnexus 1.6.12-rc.2 → 1.6.12-rc.21

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.
Files changed (188) hide show
  1. package/dist/_shared/graph/types.d.ts +1 -1
  2. package/dist/_shared/graph/types.d.ts.map +1 -1
  3. package/dist/_shared/language-detection.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.js +2 -0
  5. package/dist/_shared/language-detection.js.map +1 -1
  6. package/dist/_shared/languages.d.ts +1 -0
  7. package/dist/_shared/languages.d.ts.map +1 -1
  8. package/dist/_shared/languages.js +1 -0
  9. package/dist/_shared/languages.js.map +1 -1
  10. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  11. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  12. package/dist/_shared/lbug/schema-constants.js +2 -0
  13. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  14. package/dist/_shared/scope-resolution/language-classification.d.ts +2 -1
  15. package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/language-classification.js +3 -1
  17. package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
  18. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +3 -3
  19. package/dist/_shared/scope-resolution/registries/class-registry.js +3 -3
  20. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
  21. package/dist/_shared/scope-resolution/registries/context.js +2 -0
  22. package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
  23. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/resolve-type-ref.js +2 -0
  25. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
  26. package/dist/cli/analyze-config.d.ts +2 -2
  27. package/dist/cli/analyze-config.js +10 -33
  28. package/dist/cli/doctor.js +6 -4
  29. package/dist/cli/eval-server.js +13 -1
  30. package/dist/cli/i18n/en.d.ts +4 -2
  31. package/dist/cli/i18n/en.js +4 -2
  32. package/dist/cli/i18n/resources.d.ts +6 -2
  33. package/dist/cli/i18n/zh-CN.d.ts +2 -0
  34. package/dist/cli/i18n/zh-CN.js +4 -2
  35. package/dist/core/analysis-feature-registry.d.ts +1 -1
  36. package/dist/core/analysis-feature-registry.js +2 -0
  37. package/dist/core/auto-sync/starter.js +2 -2
  38. package/dist/core/embeddings/ast-utils.js +8 -3
  39. package/dist/core/embeddings/chunker.js +67 -10
  40. package/dist/core/embeddings/embedding-pipeline.d.ts +1 -1
  41. package/dist/core/embeddings/embedding-pipeline.js +1 -1
  42. package/dist/core/embeddings/structural-extractor.js +2 -2
  43. package/dist/core/embeddings/types.d.ts +4 -2
  44. package/dist/core/embeddings/types.js +18 -0
  45. package/dist/core/git-ref.d.ts +22 -0
  46. package/dist/core/git-ref.js +114 -0
  47. package/dist/core/git-staleness.js +14 -0
  48. package/dist/core/group/extractors/graphql-extractor.d.ts +1 -1
  49. package/dist/core/group/extractors/graphql-extractor.js +149 -13
  50. package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
  51. package/dist/core/group/extractors/manifest-extractor.js +2 -2
  52. package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +5 -8
  53. package/dist/core/ingestion/content-language-classification.d.ts +10 -0
  54. package/dist/core/ingestion/content-language-classification.js +30 -0
  55. package/dist/core/ingestion/filesystem-walker.d.ts +1 -0
  56. package/dist/core/ingestion/filesystem-walker.js +1 -1
  57. package/dist/core/ingestion/import-resolvers/node-workspace-packages.js +3 -2
  58. package/dist/core/ingestion/language-config.d.ts +82 -13
  59. package/dist/core/ingestion/language-config.js +217 -13
  60. package/dist/core/ingestion/language-provider.d.ts +88 -1
  61. package/dist/core/ingestion/languages/index.d.ts +7 -0
  62. package/dist/core/ingestion/languages/index.js +30 -1
  63. package/dist/core/ingestion/languages/objective-c/analysis-features.d.ts +10 -0
  64. package/dist/core/ingestion/languages/objective-c/analysis-features.js +19 -0
  65. package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.d.ts +13 -0
  66. package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.js +124 -0
  67. package/dist/core/ingestion/languages/objective-c/facts.d.ts +148 -0
  68. package/dist/core/ingestion/languages/objective-c/facts.js +1150 -0
  69. package/dist/core/ingestion/languages/objective-c/import-target.d.ts +11 -0
  70. package/dist/core/ingestion/languages/objective-c/import-target.js +122 -0
  71. package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.d.ts +24 -0
  72. package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.js +261 -0
  73. package/dist/core/ingestion/languages/objective-c/resolution-config.d.ts +41 -0
  74. package/dist/core/ingestion/languages/objective-c/resolution-config.js +282 -0
  75. package/dist/core/ingestion/languages/objective-c/scope-resolver.d.ts +2 -0
  76. package/dist/core/ingestion/languages/objective-c/scope-resolver.js +503 -0
  77. package/dist/core/ingestion/languages/objective-c.d.ts +2 -0
  78. package/dist/core/ingestion/languages/objective-c.js +300 -0
  79. package/dist/core/ingestion/languages/typescript/query.d.ts +31 -0
  80. package/dist/core/ingestion/languages/typescript/query.js +7 -1
  81. package/dist/core/ingestion/languages/typescript/tsconfig.js +3 -2
  82. package/dist/core/ingestion/languages/typescript.js +1 -1
  83. package/dist/core/ingestion/languages/zig/query.d.ts +26 -0
  84. package/dist/core/ingestion/languages/zig/query.js +61 -1
  85. package/dist/core/ingestion/languages/zig/range-binding.d.ts +8 -0
  86. package/dist/core/ingestion/languages/zig/range-binding.js +24 -0
  87. package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +3 -2
  88. package/dist/core/ingestion/languages/zig/scope-resolver.js +18 -5
  89. package/dist/core/ingestion/languages/zig/this-alias-bindings.d.ts +58 -0
  90. package/dist/core/ingestion/languages/zig/this-alias-bindings.js +176 -0
  91. package/dist/core/ingestion/languages/zig/workspace-static-gating.d.ts +8 -0
  92. package/dist/core/ingestion/languages/zig/workspace-static-gating.js +82 -0
  93. package/dist/core/ingestion/model/registration-table.d.ts +3 -3
  94. package/dist/core/ingestion/model/registration-table.js +9 -5
  95. package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
  96. package/dist/core/ingestion/model/symbol-table.js +2 -0
  97. package/dist/core/ingestion/model/type-registry.d.ts +1 -1
  98. package/dist/core/ingestion/parsing-processor.d.ts +22 -6
  99. package/dist/core/ingestion/parsing-processor.js +39 -20
  100. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +9 -1
  101. package/dist/core/ingestion/pipeline-phases/parse-impl.js +307 -96
  102. package/dist/core/ingestion/pipeline-phases/parse-round-budget.d.ts +42 -0
  103. package/dist/core/ingestion/pipeline-phases/parse-round-budget.js +50 -0
  104. package/dist/core/ingestion/pipeline-phases/parse.d.ts +7 -1
  105. package/dist/core/ingestion/scope-extractor.js +4 -0
  106. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +13 -0
  107. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +2 -0
  108. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +2 -0
  109. package/dist/core/ingestion/scope-resolution/passes/property-dispatch.d.ts +78 -5
  110. package/dist/core/ingestion/scope-resolution/passes/property-dispatch.js +262 -7
  111. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +5 -2
  112. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
  113. package/dist/core/ingestion/scope-resolution/pipeline/run.js +10 -1
  114. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +39 -3
  115. package/dist/core/ingestion/scope-resolution/scope/walkers.js +82 -3
  116. package/dist/core/ingestion/scope-resolution/value-ref-edges.d.ts +14 -0
  117. package/dist/core/ingestion/scope-resolution/value-ref-edges.js +14 -0
  118. package/dist/core/ingestion/tree-sitter-queries.js +2 -0
  119. package/dist/core/ingestion/utils/symbol-labels.js +2 -0
  120. package/dist/core/ingestion/workers/parse-worker.d.ts +1 -1
  121. package/dist/core/ingestion/workers/parse-worker.js +63 -3
  122. package/dist/core/ingestion/workers/worker-pool.d.ts +56 -2
  123. package/dist/core/ingestion/workers/worker-pool.js +117 -26
  124. package/dist/core/lbug/csv-generator.js +2 -0
  125. package/dist/core/lbug/schema.d.ts +3 -1
  126. package/dist/core/lbug/schema.js +27 -0
  127. package/dist/core/search/fts-indexes.js +13 -4
  128. package/dist/core/search/fts-schema.js +2 -0
  129. package/dist/core/tree-sitter/parser-loader.js +10 -0
  130. package/dist/core/tree-sitter/vendored-grammars.d.ts +1 -1
  131. package/dist/core/tree-sitter/vendored-grammars.js +2 -1
  132. package/dist/mcp/local/local-backend.d.ts +33 -0
  133. package/dist/mcp/local/local-backend.js +166 -5
  134. package/dist/mcp/tools.js +6 -4
  135. package/dist/server/analyze-job.d.ts +19 -1
  136. package/dist/server/analyze-job.js +14 -3
  137. package/dist/server/analyze-launch.d.ts +14 -0
  138. package/dist/server/analyze-launch.js +59 -14
  139. package/dist/server/analyze-worker-ipc.d.ts +6 -5
  140. package/dist/server/analyze-worker-ipc.js +4 -0
  141. package/dist/server/api.js +68 -21
  142. package/dist/server/git-clone.d.ts +38 -5
  143. package/dist/server/git-clone.js +216 -38
  144. package/dist/server/repo-projection.d.ts +88 -0
  145. package/dist/server/repo-projection.js +53 -0
  146. package/dist/storage/file-lock.js +5 -3
  147. package/dist/storage/parse-cache.js +32 -1
  148. package/dist/storage/parsedfile-store.js +53 -1
  149. package/dist/storage/v8-sidecar.d.ts +11 -0
  150. package/dist/storage/v8-sidecar.js +15 -7
  151. package/dist/utils/process-identity.d.ts +13 -0
  152. package/dist/utils/process-identity.js +18 -0
  153. package/package.json +1 -1
  154. package/scripts/build-tree-sitter-grammars.cjs +4 -3
  155. package/vendor/tree-sitter-objc/LICENSE +21 -0
  156. package/vendor/tree-sitter-objc/README.md +18 -0
  157. package/vendor/tree-sitter-objc/binding.gyp +35 -0
  158. package/vendor/tree-sitter-objc/bindings/node/binding.cc +19 -0
  159. package/vendor/tree-sitter-objc/bindings/node/binding_test.js +9 -0
  160. package/vendor/tree-sitter-objc/bindings/node/index.d.ts +27 -0
  161. package/vendor/tree-sitter-objc/bindings/node/index.js +11 -0
  162. package/vendor/tree-sitter-objc/grammar.js +1286 -0
  163. package/vendor/tree-sitter-objc/package.json +56 -0
  164. package/vendor/tree-sitter-objc/prebuilds/SHA256SUMS +6 -0
  165. package/vendor/tree-sitter-objc/prebuilds/darwin-arm64/tree-sitter-objc.node +0 -0
  166. package/vendor/tree-sitter-objc/prebuilds/darwin-x64/tree-sitter-objc.node +0 -0
  167. package/vendor/tree-sitter-objc/prebuilds/linux-arm64/tree-sitter-objc.node +0 -0
  168. package/vendor/tree-sitter-objc/prebuilds/linux-x64/tree-sitter-objc.node +0 -0
  169. package/vendor/tree-sitter-objc/prebuilds/win32-arm64/tree-sitter-objc.node +0 -0
  170. package/vendor/tree-sitter-objc/prebuilds/win32-x64/tree-sitter-objc.node +0 -0
  171. package/vendor/tree-sitter-objc/queries/folds.scm +20 -0
  172. package/vendor/tree-sitter-objc/queries/highlights.scm +216 -0
  173. package/vendor/tree-sitter-objc/queries/indents.scm +1 -0
  174. package/vendor/tree-sitter-objc/queries/injections.scm +10 -0
  175. package/vendor/tree-sitter-objc/queries/locals.scm +1 -0
  176. package/vendor/tree-sitter-objc/src/grammar.json +16292 -0
  177. package/vendor/tree-sitter-objc/src/node-types.json +7524 -0
  178. package/vendor/tree-sitter-objc/src/parser.c +684023 -0
  179. package/vendor/tree-sitter-objc/src/tree_sitter/alloc.h +54 -0
  180. package/vendor/tree-sitter-objc/src/tree_sitter/array.h +291 -0
  181. package/vendor/tree-sitter-objc/src/tree_sitter/parser.h +266 -0
  182. package/vendor/tree-sitter-objc/tree-sitter-objc.wasm +0 -0
  183. package/vendor/tree-sitter-objc/tree-sitter.json +38 -0
  184. package/web/assets/{agent-BMRqXorA.js → agent-C2n33ANd.js} +1 -1
  185. package/web/assets/{index-Dj3vQOHK.js → index-CHJoW_P1.js} +18 -18
  186. package/web/assets/src-jc8Ffy3T.js +1 -0
  187. package/web/index.html +2 -2
  188. package/web/assets/src-Df5C1Nz4.js +0 -1
@@ -5,17 +5,19 @@
5
5
  * Thin wiring: Zig has no inheritance (default MRO linearization over an
6
6
  * empty heritage set), no `super`, and is statically typed (field-fallback
7
7
  * heuristic off per the contract guidance). Import resolution reuses the
8
- * same `resolveZigImportInternal` the legacy import-resolver config wraps,
9
- * with `build.zig.zon` `.path` deps threaded through `loadResolutionConfig`.
8
+ * same `resolveZigImportInternal` the legacy import-resolver config wraps, with
9
+ * the repo's Zig build packages threaded through `loadResolutionConfig` and the
10
+ * one governing each file selected per-file by `zigPackageFor`.
10
11
  */
11
12
  import { SupportedLanguages } from '../../../../_shared/index.js';
12
13
  import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js';
13
14
  import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
14
- import { loadZigBuildConfig } from '../../language-config.js';
15
+ import { loadZigWorkspaceIndex, zigPackageFor, } from '../../language-config.js';
15
16
  import { resolveZigImportInternal } from '../../import-resolvers/zig.js';
16
17
  import { zigProvider } from '../zig.js';
17
18
  import { expandZigWildcardNames, zigArityCompatibility, zigMergeBindings } from './index.js';
18
19
  import { populateZigRangeBindings } from './range-binding.js';
20
+ import { populateZigWorkspaceStaticGating } from './workspace-static-gating.js';
19
21
  export const zigScopeResolver = {
20
22
  language: SupportedLanguages.Zig,
21
23
  languageProvider: zigProvider,
@@ -40,14 +42,25 @@ export const zigScopeResolver = {
40
42
  // (`opmod.Op.lookup.event_max()`) — walked hop by hop from the verified
41
43
  // import; a one-hop split at the last dot resolved none of them.
42
44
  resolveNamespaceChains: true,
43
- loadResolutionConfig: (repoPath) => loadZigBuildConfig(repoPath),
44
- resolveImportTarget: (targetRaw, fromFile, allFilePaths, resolutionConfig) => resolveZigImportInternal(fromFile, targetRaw, allFilePaths, resolutionConfig ?? null),
45
+ // The whole workspace, not just the root package. A Zig module's import table
46
+ // is declared by the `build.zig` of the package the file belongs to, so a repo
47
+ // laying its packages out as `packages/<name>/build.zig` has as many import
48
+ // tables as packages — and reading only the root one leaves every bare
49
+ // `@import("<module>")` in such a repo unresolved. Same shape, same reason, as
50
+ // `loadTsconfigIndex` for a TypeScript monorepo.
51
+ loadResolutionConfig: (repoPath) => loadZigWorkspaceIndex(repoPath),
52
+ resolveImportTarget: (targetRaw, fromFile, allFilePaths, resolutionConfig) => resolveZigImportInternal(fromFile, targetRaw, allFilePaths,
53
+ // The package governing THIS file — `zigPackageFor` is the `tsconfigFor`
54
+ // analogue. `resolveZigImportInternal` is handed one package's config and
55
+ // is unchanged by this: which config it receives is the only difference.
56
+ zigPackageFor(resolutionConfig, fromFile)),
45
57
  // `pub usingnamespace @import("x.zig");` — target decls become local decls.
46
58
  expandsWildcardTo: (targetModuleScope, parsedFiles) => expandZigWildcardNames(targetModuleScope, parsedFiles),
47
59
  mergeBindings: zigMergeBindings,
48
60
  arityCompatibility: zigArityCompatibility,
49
61
  buildMro: (graph, parsedFiles, nodeLookup) => buildMro(graph, parsedFiles, nodeLookup, defaultLinearize),
50
62
  populateOwners: (parsed) => populateClassOwnedMembers(parsed),
63
+ populateWorkspaceReferences: populateZigWorkspaceStaticGating,
51
64
  // Payload captures — `for (items) |it|`, `if (opt) |v|`, `while (it.next())
52
65
  // |x|` — typed from the subject's binding after finalize (F6).
53
66
  populateRangeBindings: populateZigRangeBindings,
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Bind a Zig `const X = @This();` alias to the container it names (#3219
3
+ * review round 8).
4
+ *
5
+ * `@This()` IS the enclosing container, and Zig code says so constantly: the
6
+ * single most common spelling is `const Self = @This();`, followed by
7
+ * `Alias.member` wherever the explicit form is wanted (`Self.width(self)`, a
8
+ * registration `bridge.accessor(Self.width, …)`, a nested type `Self.Node`).
9
+ * A compiler resolves `Self` and the container's own name to the same type.
10
+ * This index did not, for one specific reason:
11
+ *
12
+ * - A container-level alias (`const Self = @This();` inside `struct {…}`)
13
+ * mints a `Variable` beside the `Struct`, so `Self` binds to a VALUE. Every
14
+ * class-like lookup filters on `isClassLike` and walks straight past it.
15
+ * - A FILE-level alias in a file-as-struct mints nothing at all —
16
+ * `isZigFileThisAlias` suppresses the Const deliberately, so it cannot
17
+ * shadow the type for `x: *Page`. The name is then bound to nothing, while
18
+ * the container itself is bound under the FILE STEM.
19
+ *
20
+ * So `Self.width` resolved only when the alias happened to be spelled like its
21
+ * container. Measured on the three Zig corpora on hand: 73 of ghostty's 185
22
+ * `@This()` files, 93 of tigerbeetle's 94 and 8 of mach's 42 spell it
23
+ * differently, carrying 302 `Alias.member` references between them, 96 of those
24
+ * calls. Those were not wrong edges — they were no edges, and a caller list
25
+ * missing them is exactly the false confidence #3399 is about.
26
+ *
27
+ * WHAT THIS ADDS, precisely: one binding of the alias NAME to the container's
28
+ * own definition, appended to `indexes.bindingAugmentations` (the sanctioned
29
+ * post-finalize channel, invariant I8 — `indexes.bindings` is frozen). The
30
+ * augmentation channel is consulted by `lookupBindingsAt` only AFTER a scope's
31
+ * own `Scope.bindings`, so this can never outrank a real local declaration: the
32
+ * `Variable` the container-level alias already mints still answers first for
33
+ * anything that wants a value, and the container answers for anything that
34
+ * wants a type. Nothing is replaced and nothing is removed.
35
+ *
36
+ * WHAT IT DELIBERATELY DOES NOT DO. Only aliases declared DIRECTLY in a
37
+ * container body or at file level are bound, which is the same set
38
+ * `collectZigThisAliases` recognizes for the type-rewrite path. A function-local
39
+ * `const Self = @This();` also names the enclosing container, but it belongs in
40
+ * that function's scope, not the container's, and binding it here would make it
41
+ * visible to sibling functions that never declared it.
42
+ */
43
+ import type { ParsedFile } from '../../../../_shared/index.js';
44
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
45
+ import type { SyntaxNode } from '../../utils/ast-helpers.js';
46
+ /**
47
+ * Bind every `@This()` alias declared in `parsed` at container or file level.
48
+ *
49
+ * Called per file from `populateZigRangeBindings`, which already holds the
50
+ * parsed tree — a second pass over `parsedFiles` would re-parse every file when
51
+ * the tree cache is cold. That is the only thing the two share. Ordering
52
+ * against the payload bindings that follow does not matter, twice over: the
53
+ * payload walk types its subjects through `findReceiverTypeBinding`, which
54
+ * never reads the channel written here, and a `@This()` alias is a
55
+ * container-private name no other file can import, so nothing outside this file
56
+ * reads it either.
57
+ */
58
+ export declare function bindZigThisAliases(parsed: ParsedFile, root: SyntaxNode, indexes: ScopeResolutionIndexes): void;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Bind a Zig `const X = @This();` alias to the container it names (#3219
3
+ * review round 8).
4
+ *
5
+ * `@This()` IS the enclosing container, and Zig code says so constantly: the
6
+ * single most common spelling is `const Self = @This();`, followed by
7
+ * `Alias.member` wherever the explicit form is wanted (`Self.width(self)`, a
8
+ * registration `bridge.accessor(Self.width, …)`, a nested type `Self.Node`).
9
+ * A compiler resolves `Self` and the container's own name to the same type.
10
+ * This index did not, for one specific reason:
11
+ *
12
+ * - A container-level alias (`const Self = @This();` inside `struct {…}`)
13
+ * mints a `Variable` beside the `Struct`, so `Self` binds to a VALUE. Every
14
+ * class-like lookup filters on `isClassLike` and walks straight past it.
15
+ * - A FILE-level alias in a file-as-struct mints nothing at all —
16
+ * `isZigFileThisAlias` suppresses the Const deliberately, so it cannot
17
+ * shadow the type for `x: *Page`. The name is then bound to nothing, while
18
+ * the container itself is bound under the FILE STEM.
19
+ *
20
+ * So `Self.width` resolved only when the alias happened to be spelled like its
21
+ * container. Measured on the three Zig corpora on hand: 73 of ghostty's 185
22
+ * `@This()` files, 93 of tigerbeetle's 94 and 8 of mach's 42 spell it
23
+ * differently, carrying 302 `Alias.member` references between them, 96 of those
24
+ * calls. Those were not wrong edges — they were no edges, and a caller list
25
+ * missing them is exactly the false confidence #3399 is about.
26
+ *
27
+ * WHAT THIS ADDS, precisely: one binding of the alias NAME to the container's
28
+ * own definition, appended to `indexes.bindingAugmentations` (the sanctioned
29
+ * post-finalize channel, invariant I8 — `indexes.bindings` is frozen). The
30
+ * augmentation channel is consulted by `lookupBindingsAt` only AFTER a scope's
31
+ * own `Scope.bindings`, so this can never outrank a real local declaration: the
32
+ * `Variable` the container-level alias already mints still answers first for
33
+ * anything that wants a value, and the container answers for anything that
34
+ * wants a type. Nothing is replaced and nothing is removed.
35
+ *
36
+ * WHAT IT DELIBERATELY DOES NOT DO. Only aliases declared DIRECTLY in a
37
+ * container body or at file level are bound, which is the same set
38
+ * `collectZigThisAliases` recognizes for the type-rewrite path. A function-local
39
+ * `const Self = @This();` also names the enclosing container, but it belongs in
40
+ * that function's scope, not the container's, and binding it here would make it
41
+ * visible to sibling functions that never declared it.
42
+ */
43
+ import { isClassLike } from '../../scope-resolution/scope/walkers.js';
44
+ import { isZigKeywordDeclaration, ZIG_CONTAINER_TYPES } from './captures.js';
45
+ /** `const X = @This();` — the declaration shape, wherever it sits. */
46
+ function thisAliasName(node) {
47
+ if (node.type !== 'variable_declaration' || !isZigKeywordDeclaration(node))
48
+ return undefined;
49
+ const named = node.namedChildren.filter((c) => c !== null);
50
+ if (named.length !== 2 || named[0].type !== 'identifier')
51
+ return undefined;
52
+ const value = named[1];
53
+ if (value.type !== 'builtin_function' || value.namedChild(0)?.text !== '@This')
54
+ return undefined;
55
+ return named[0].text;
56
+ }
57
+ /** The class-like definition a Class scope is the body of. */
58
+ function containerDefOf(scope) {
59
+ return scope.ownedDefs.find((d) => isClassLike(d.type));
60
+ }
61
+ /**
62
+ * The Class scope of the FILE-AS-STRUCT, if this file is one.
63
+ *
64
+ * Identified by range rather than by name: it is the only Class scope that both
65
+ * hangs directly off the module scope AND spans the same lines as it. A
66
+ * namespace-only file with a top-level `pub const Counter = struct {…}` also
67
+ * produces a Class scope under the module scope, and it also carries the file
68
+ * stem when the file is `Counter.zig` — matching on the name would bind that
69
+ * file's `@This()` alias to `Counter`, which is not what `@This()` means there.
70
+ */
71
+ function fileStructScope(scopes, moduleScope) {
72
+ return scopes.find((s) => s.kind === 'Class' &&
73
+ s.parent === moduleScope.id &&
74
+ s.range.startLine === moduleScope.range.startLine &&
75
+ s.range.endLine === moduleScope.range.endLine);
76
+ }
77
+ /**
78
+ * Innermost Class scope containing `node`.
79
+ *
80
+ * Compared on (line, column) rather than line alone, because a Zig container
81
+ * can nest inside another on ONE line — `const A = struct { const S = @This();
82
+ * const B = struct { const T = @This(); }; };` gives both scopes the same start
83
+ * line, and picking between them by line would be a coin toss that binds an
84
+ * alias to the wrong container. A wrong edge is the one outcome this whole
85
+ * change set treats as worse than no edge.
86
+ */
87
+ function enclosingClassScope(scopes, node) {
88
+ const line = node.startPosition.row + 1;
89
+ const column = node.startPosition.column;
90
+ const startsAtOrBefore = (l, c) => l < line || (l === line && c <= column);
91
+ const endsAtOrAfter = (l, c) => l > line || (l === line && c >= column);
92
+ let best;
93
+ for (const s of scopes) {
94
+ if (s.kind !== 'Class')
95
+ continue;
96
+ if (!startsAtOrBefore(s.range.startLine, s.range.startCol))
97
+ continue;
98
+ if (!endsAtOrAfter(s.range.endLine, s.range.endCol))
99
+ continue;
100
+ if (best === undefined ||
101
+ s.range.startLine > best.range.startLine ||
102
+ (s.range.startLine === best.range.startLine && s.range.startCol > best.range.startCol)) {
103
+ best = s;
104
+ }
105
+ }
106
+ return best;
107
+ }
108
+ /**
109
+ * Append `alias -> def` at `scopeId`, skipping the append when THIS def is
110
+ * already bound there. Idempotence, not precedence: a duplicate would be
111
+ * harmless for lookup (`lookupBindingsAt` dedupes by `def.nodeId`) but would
112
+ * make the channel's contents depend on how many times the hook ran. Other
113
+ * bindings under the same name are left alone — the channel is append-only per
114
+ * I8, and the whole point of the augmentation tier is that a scope's own
115
+ * `Scope.bindings` are consulted first and still win.
116
+ */
117
+ function appendBinding(indexes, scopeId, alias, def) {
118
+ const channel = indexes.bindingAugmentations;
119
+ let byName = channel.get(scopeId);
120
+ if (byName === undefined) {
121
+ byName = new Map();
122
+ channel.set(scopeId, byName);
123
+ }
124
+ const bucket = byName.get(alias);
125
+ if (bucket === undefined) {
126
+ byName.set(alias, [{ def, origin: 'local' }]);
127
+ return;
128
+ }
129
+ if (bucket.some((ref) => ref.def.nodeId === def.nodeId))
130
+ return;
131
+ bucket.push({ def, origin: 'local' });
132
+ }
133
+ /**
134
+ * Bind every `@This()` alias declared in `parsed` at container or file level.
135
+ *
136
+ * Called per file from `populateZigRangeBindings`, which already holds the
137
+ * parsed tree — a second pass over `parsedFiles` would re-parse every file when
138
+ * the tree cache is cold. That is the only thing the two share. Ordering
139
+ * against the payload bindings that follow does not matter, twice over: the
140
+ * payload walk types its subjects through `findReceiverTypeBinding`, which
141
+ * never reads the channel written here, and a `@This()` alias is a
142
+ * container-private name no other file can import, so nothing outside this file
143
+ * reads it either.
144
+ */
145
+ export function bindZigThisAliases(parsed, root, indexes) {
146
+ const moduleScope = parsed.scopes.find((s) => s.kind === 'Module');
147
+ if (moduleScope === undefined)
148
+ return;
149
+ const fileStruct = fileStructScope(parsed.scopes, moduleScope);
150
+ const visit = (node) => {
151
+ const alias = thisAliasName(node);
152
+ if (alias !== undefined) {
153
+ const parent = node.parent;
154
+ if (parent?.type === 'source_file') {
155
+ // The file-as-struct's own name. Bound at the MODULE scope, which is
156
+ // where the container is already bound under the file stem, so the
157
+ // alias and the stem are visible to exactly the same sites.
158
+ const def = fileStruct === undefined ? undefined : containerDefOf(fileStruct);
159
+ if (def !== undefined)
160
+ appendBinding(indexes, moduleScope.id, alias, def);
161
+ }
162
+ else if (parent !== null && ZIG_CONTAINER_TYPES.has(parent.type)) {
163
+ const scope = enclosingClassScope(parsed.scopes, node);
164
+ const def = scope === undefined ? undefined : containerDefOf(scope);
165
+ if (scope !== undefined && def !== undefined)
166
+ appendBinding(indexes, scope.id, alias, def);
167
+ }
168
+ }
169
+ for (let i = 0; i < node.namedChildCount; i++) {
170
+ const child = node.namedChild(i);
171
+ if (child !== null)
172
+ visit(child);
173
+ }
174
+ };
175
+ visit(root);
176
+ }
@@ -0,0 +1,8 @@
1
+ import type { ParsedFile } from '../../../../_shared/index.js';
2
+ export declare function populateZigWorkspaceStaticGating(parsedFiles: ParsedFile[], ctx: {
3
+ readonly fileContents: ReadonlyMap<string, string>;
4
+ readonly treeCache?: {
5
+ get(filePath: string): unknown;
6
+ };
7
+ readonly resolutionConfig?: unknown;
8
+ }): void;
@@ -0,0 +1,82 @@
1
+ import { getTreeSitterBufferSize } from '../../constants.js';
2
+ import { zigPackageFor } from '../../language-config.js';
3
+ import { resolveZigImportInternal } from '../../import-resolvers/zig.js';
4
+ import { buildZigBoolConstMap, collectZigStaticGatedRanges, isPositionStaticGated, } from '../../call-extractors/zig-static-gating.js';
5
+ import { parseSourceSafe, ParseTimeoutError } from '../../../tree-sitter/safe-parse.js';
6
+ import { getZigParser } from './query.js';
7
+ export function populateZigWorkspaceStaticGating(parsedFiles, ctx) {
8
+ const parser = getZigParser();
9
+ const trees = new Map();
10
+ const bools = new Map();
11
+ for (const parsed of parsedFiles) {
12
+ const source = ctx.fileContents.get(parsed.filePath);
13
+ if (source === undefined)
14
+ continue;
15
+ let tree = ctx.treeCache?.get(parsed.filePath);
16
+ if (tree === undefined) {
17
+ try {
18
+ tree = parseSourceSafe(parser, source, undefined, {
19
+ bufferSize: getTreeSitterBufferSize(source),
20
+ });
21
+ }
22
+ catch (err) {
23
+ if (err instanceof ParseTimeoutError)
24
+ continue;
25
+ throw err;
26
+ }
27
+ }
28
+ trees.set(parsed.filePath, tree);
29
+ bools.set(parsed.filePath, buildZigBoolConstMap(tree.rootNode));
30
+ }
31
+ const knownPaths = new Set(trees.keys());
32
+ for (const [index, parsed] of parsedFiles.entries()) {
33
+ const tree = trees.get(parsed.filePath);
34
+ if (tree === undefined)
35
+ continue;
36
+ const aliases = collectImportAliases(tree, parsed.filePath, knownPaths,
37
+ // `resolutionConfig` is the whole Zig workspace; the package governing
38
+ // THIS file is what `resolveZigImportInternal` takes. Selected here rather
39
+ // than hoisted out of the loop because the answer is per-file: two files
40
+ // of this pass can belong to different packages.
41
+ zigPackageFor(ctx.resolutionConfig, parsed.filePath));
42
+ if (aliases.size === 0)
43
+ continue;
44
+ const ranges = collectZigStaticGatedRanges(tree.rootNode, bools.get(parsed.filePath) ?? new Map(), aliases, (filePath) => bools.get(filePath));
45
+ if (ranges.length === 0)
46
+ continue;
47
+ const next = parsed.referenceSites.map((site) => site.kind === 'call' &&
48
+ site.staticGated !== true &&
49
+ isPositionStaticGated(site.atRange.startLine, site.atRange.startCol, ranges)
50
+ ? { ...site, staticGated: true }
51
+ : site);
52
+ parsedFiles[index] = Object.freeze({ ...parsed, referenceSites: Object.freeze(next) });
53
+ }
54
+ }
55
+ function collectImportAliases(tree, fromFile, knownPaths, packageConfig) {
56
+ const candidates = new Map();
57
+ const declarationCounts = new Map();
58
+ for (const decl of tree.rootNode.descendantsOfType('variable_declaration')) {
59
+ const names = decl.namedChildren.filter((node) => node.type === 'identifier');
60
+ const binding = names[0]?.text;
61
+ if (binding === undefined)
62
+ continue;
63
+ declarationCounts.set(binding, (declarationCounts.get(binding) ?? 0) + 1);
64
+ const builtin = decl.namedChildren.find((node) => node.type === 'builtin_function' && node.text.startsWith('@import('));
65
+ const raw = builtin?.descendantsOfType('string').at(0)?.text;
66
+ if (raw === undefined)
67
+ continue;
68
+ const specifier = raw.replace(/^['"]|['"]$/g, '');
69
+ const target = resolveZigImportInternal(fromFile, specifier, knownPaths, packageConfig);
70
+ if (target !== null)
71
+ candidates.set(binding, target);
72
+ }
73
+ const aliases = new Map();
74
+ for (const [binding, target] of candidates) {
75
+ // Alias lookup below is name-based rather than position-aware. If a name
76
+ // is redeclared in another lexical scope, fail open instead of applying
77
+ // either module's constants to every use of that spelling.
78
+ if (declarationCounts.get(binding) === 1)
79
+ aliases.set(binding, target);
80
+ }
81
+ return aliases;
82
+ }
@@ -15,11 +15,11 @@
15
15
  * └── symbols (SymbolTable) ← owns fileIndex + callableByName,
16
16
  * calls dispatch() in add()
17
17
  *
18
- * ## Behavior groups (5 hooks, 13 table entries)
18
+ * ## Behavior groups (4 hooks, 12 table entries)
19
19
  *
20
20
  * | Group | NodeLabel values | Hook | Skip callable? |
21
21
  * |---------------|---------------------------------------------------|--------------|----------------|
22
- * | class-like | Class, Struct, Interface, Enum, Record, Trait | classLikeHook | no |
22
+ * | class-like | Class, Protocol, Category, Struct, Interface, Enum, Record, Trait | classLikeHook | no |
23
23
  * | method-like | Method, Constructor | methodHook | no |
24
24
  * | property | Property | propertyHook | YES |
25
25
  * | impl-block | Impl | implHook | no |
@@ -81,7 +81,7 @@ export interface RegistrationTableDeps {
81
81
  * registry (if any) receives the symbol write during `SymbolTable.add()`:
82
82
  *
83
83
  * - `dispatch` — owner-scoped registry write via the dispatch table
84
- * (Class/Struct/Interface/Enum/Record/Trait → types.registerClass,
84
+ * (Class/Protocol/Category/Struct/Interface/Enum/Record/Trait → types.registerClass,
85
85
  * Method/Constructor → methods.register,
86
86
  * Property → fields.register,
87
87
  * Impl → types.registerImpl)
@@ -15,11 +15,11 @@
15
15
  * └── symbols (SymbolTable) ← owns fileIndex + callableByName,
16
16
  * calls dispatch() in add()
17
17
  *
18
- * ## Behavior groups (5 hooks, 13 table entries)
18
+ * ## Behavior groups (4 hooks, 12 table entries)
19
19
  *
20
20
  * | Group | NodeLabel values | Hook | Skip callable? |
21
21
  * |---------------|---------------------------------------------------|--------------|----------------|
22
- * | class-like | Class, Struct, Interface, Enum, Record, Trait | classLikeHook | no |
22
+ * | class-like | Class, Protocol, Category, Struct, Interface, Enum, Record, Trait | classLikeHook | no |
23
23
  * | method-like | Method, Constructor | methodHook | no |
24
24
  * | property | Property | propertyHook | YES |
25
25
  * | impl-block | Impl | implHook | no |
@@ -79,6 +79,8 @@ import { FREE_CALLABLE_TYPES } from './symbol-table.js';
79
79
  const LABEL_BEHAVIOR = {
80
80
  // dispatch — owner-scoped registry writes
81
81
  Class: 'dispatch',
82
+ Protocol: 'dispatch',
83
+ Category: 'dispatch',
82
84
  Struct: 'dispatch',
83
85
  Interface: 'dispatch',
84
86
  Enum: 'dispatch',
@@ -169,8 +171,8 @@ export const DISPATCH_LABELS = new Set(labelsWithBehavior('dispatch'));
169
171
  */
170
172
  export const createRegistrationTable = (deps) => {
171
173
  const { types, methods, fields } = deps;
172
- // Hook 1: class-like — Class, Struct, Interface, Enum, Record, Trait.
173
- // Shared reference — six table entries point at this one closure.
174
+ // Hook 1: class-like — Class, Protocol, Category, Struct, Interface, Enum, Record, Trait.
175
+ // Shared reference — eight table entries point at this one closure.
174
176
  const classLikeHook = (name, def) => {
175
177
  const qualifiedKey = def.qualifiedName ?? name;
176
178
  types.registerClass(name, qualifiedKey, def);
@@ -205,11 +207,13 @@ export const createRegistrationTable = (deps) => {
205
207
  // classified as 'dispatch'. This is the compile-time twin of the
206
208
  // runtime taxonomy — no drift possible.
207
209
  const dispatchByLabel = {
208
- // class-like — six labels share the single `classLikeHook` closure,
210
+ // class-like — eight labels share the single `classLikeHook` closure,
209
211
  // kept in lockstep with `CLASS_TYPES_TUPLE` via the
210
212
  // `Record<ClassLikeLabel, 'dispatch'>` cross-invariant on
211
213
  // `LABEL_BEHAVIOR`.
212
214
  Class: classLikeHook,
215
+ Protocol: classLikeHook,
216
+ Category: classLikeHook,
213
217
  Struct: classLikeHook,
214
218
  Interface: classLikeHook,
215
219
  Enum: classLikeHook,
@@ -52,7 +52,7 @@ import type { NodeLabel, ParameterTypeClass, SymbolDefinition } from '../../../_
52
52
  * `impl Trait for Struct`, and Scala traits all contribute methods to the
53
53
  * hierarchy of their using/implementing type.
54
54
  */
55
- export declare const CLASS_TYPES_TUPLE: readonly ["Class", "Struct", "Interface", "Enum", "Record", "Trait"];
55
+ export declare const CLASS_TYPES_TUPLE: readonly ["Class", "Protocol", "Category", "Struct", "Interface", "Enum", "Record", "Trait"];
56
56
  export type ClassLikeLabel = (typeof CLASS_TYPES_TUPLE)[number];
57
57
  export declare const CLASS_TYPES: ReadonlySet<NodeLabel>;
58
58
  /** Free-callable labels — single source of truth for "callables that have
@@ -53,6 +53,8 @@
53
53
  */
54
54
  export const CLASS_TYPES_TUPLE = [
55
55
  'Class',
56
+ 'Protocol',
57
+ 'Category',
56
58
  'Struct',
57
59
  'Interface',
58
60
  'Enum',
@@ -8,7 +8,7 @@
8
8
  import type { SymbolDefinition } from '../../../_shared/index.js';
9
9
  export interface TypeRegistry {
10
10
  /**
11
- * Look up class-like definitions (Class, Struct, Interface, Enum, Record, Trait)
11
+ * Look up class-like definitions (Class, Protocol, Category, Struct, Interface, Enum, Record, Trait)
12
12
  * by simple name. Returns all matching definitions across files
13
13
  * (e.g. partial classes). Returned array is a view into the live
14
14
  * internal index — do not mutate.
@@ -3,6 +3,7 @@ import type { SymbolTableWriter } from './model/index.js';
3
3
  import { type ExportedTypeMap } from './call-processor.js';
4
4
  import type { ParsedFile } from '../../_shared/index.js';
5
5
  import { WorkerPool } from './workers/worker-pool.js';
6
+ import type { DispatchGroup } from './workers/worker-pool.js';
6
7
  import type { ParseWorkerResult, ExtractedRoute, ExtractedFetchCall, ExtractedDecoratorRoute, ExtractedModuleConstants, ExtractedToolDef, FileScopeBindings, ExtractedORMQuery, FetchWrapperDef } from './workers/parse-worker.js';
7
8
  import type { ExtractedRouterConstructorPrefix, ExtractedRouterImport, ExtractedRouterInclude, ExtractedRouterModuleAlias } from './route-extractors/fastapi-router-bindings.js';
8
9
  import type { SharedSpringType } from './route-extractors/spring-shared.js';
@@ -47,12 +48,12 @@ export interface WorkerExtractedData {
47
48
  */
48
49
  export declare const mergeChunkResults: (graph: KnowledgeGraph, symbolTable: SymbolTableWriter, chunkResults: readonly ParseWorkerResult[], exportedTypeMap?: ExportedTypeMap) => WorkerExtractedData;
49
50
  /**
50
- * Dispatch a chunk's files to the worker pool and return the RAW per-worker
51
- * results, WITHOUT merging them into the graph. Split out from
52
- * {@link processParsing} so the parse loop can overlap one chunk's
53
- * merge (main-thread, via {@link mergeChunkResults}) with the NEXT chunk's
54
- * worker parse — the merge is the only remaining serial main-thread step once
55
- * ParsedFile serialization moved into the workers (#worker-idle pipelining).
51
+ * Dispatch ONE chunk's files to the worker pool and return the RAW per-worker
52
+ * results, WITHOUT merging them into the graph. A thin single-group wrapper
53
+ * over {@link dispatchChunkParseRound}, used by {@link processParsing}'s
54
+ * one-shot path. The chunk-to-chunk overlap this once described now lives in
55
+ * `parse-impl.ts` at ROUND granularity (`startRound` / `drainRound` /
56
+ * `closeRound`), which batches several chunks into one dispatch.
56
57
  * Returns `[]` for an all-unparseable chunk (the caller merges `[]` → empty).
57
58
  */
58
59
  export declare const dispatchChunkParse: (files: {
@@ -67,6 +68,21 @@ outRawResults?: ParseWorkerResult[],
67
68
  * (#2038). `undefined` ⇒ no durable write (tests / no-cache path).
68
69
  */
69
70
  chunkHash?: string) => Promise<ParseWorkerResult[]>;
71
+ /**
72
+ * Dispatch SEVERAL parse-cache chunks as one pool round and return their raw
73
+ * results, one array per input group in input order.
74
+ *
75
+ * `WorkerPool.dispatch` is a barrier, so one round-trip per chunk leaves most
76
+ * slots idle whenever a chunk is smaller than the pool — which stable
77
+ * `(language, hash(path) % 128)` packs usually are. Batching chunks into one
78
+ * `dispatchGroups` call removes those barriers; jobs are still cut at chunk
79
+ * boundaries, so every result stays attributable to the chunk whose cache key
80
+ * owns it.
81
+ */
82
+ export declare const dispatchChunkParseRound: (groups: ReadonlyArray<DispatchGroup<{
83
+ path: string;
84
+ content: string;
85
+ }>>, workerPool: WorkerPool, onFileProgress?: FileProgressCallback) => Promise<ParseWorkerResult[][]>;
70
86
  export declare const processParsing: (graph: KnowledgeGraph, files: {
71
87
  path: string;
72
88
  content: string;
@@ -1,4 +1,4 @@
1
- import { getLanguageFromFilename } from '../../_shared/index.js';
1
+ import { getLanguageForFileContent } from './languages/index.js';
2
2
  import { accumulateExportedTypesFromParsedNode } from './call-processor.js';
3
3
  import { logger } from '../logger.js';
4
4
  function compareText(left, right) {
@@ -147,12 +147,12 @@ export const mergeChunkResults = (graph, symbolTable, chunkResults, exportedType
147
147
  };
148
148
  };
149
149
  /**
150
- * Dispatch a chunk's files to the worker pool and return the RAW per-worker
151
- * results, WITHOUT merging them into the graph. Split out from
152
- * {@link processParsing} so the parse loop can overlap one chunk's
153
- * merge (main-thread, via {@link mergeChunkResults}) with the NEXT chunk's
154
- * worker parse — the merge is the only remaining serial main-thread step once
155
- * ParsedFile serialization moved into the workers (#worker-idle pipelining).
150
+ * Dispatch ONE chunk's files to the worker pool and return the RAW per-worker
151
+ * results, WITHOUT merging them into the graph. A thin single-group wrapper
152
+ * over {@link dispatchChunkParseRound}, used by {@link processParsing}'s
153
+ * one-shot path. The chunk-to-chunk overlap this once described now lives in
154
+ * `parse-impl.ts` at ROUND granularity (`startRound` / `drainRound` /
155
+ * `closeRound`), which batches several chunks into one dispatch.
156
156
  * Returns `[]` for an all-unparseable chunk (the caller merges `[]` → empty).
157
157
  */
158
158
  export const dispatchChunkParse = async (files, workerPool, onFileProgress,
@@ -164,23 +164,42 @@ outRawResults,
164
164
  * (#2038). `undefined` ⇒ no durable write (tests / no-cache path).
165
165
  */
166
166
  chunkHash) => {
167
- const parseableFiles = [];
168
- for (const file of files) {
169
- const lang = getLanguageFromFilename(file.path);
170
- if (lang)
171
- parseableFiles.push({ path: file.path, content: file.content });
172
- }
173
- if (parseableFiles.length === 0)
174
- return [];
175
- const total = files.length;
176
- const chunkResults = await workerPool.dispatch(parseableFiles, (filesProcessed) => {
177
- onFileProgress?.(Math.min(filesProcessed, total), total, 'Parsing...');
178
- }, chunkHash);
167
+ const [chunkResults = []] = await dispatchChunkParseRound([{ items: files, chunkHash }], workerPool, onFileProgress);
179
168
  // Capture raw results for the incremental parse cache before merging.
180
169
  if (outRawResults) {
181
170
  for (const r of chunkResults)
182
171
  outRawResults.push(r);
183
172
  }
173
+ return chunkResults;
174
+ };
175
+ /**
176
+ * Dispatch SEVERAL parse-cache chunks as one pool round and return their raw
177
+ * results, one array per input group in input order.
178
+ *
179
+ * `WorkerPool.dispatch` is a barrier, so one round-trip per chunk leaves most
180
+ * slots idle whenever a chunk is smaller than the pool — which stable
181
+ * `(language, hash(path) % 128)` packs usually are. Batching chunks into one
182
+ * `dispatchGroups` call removes those barriers; jobs are still cut at chunk
183
+ * boundaries, so every result stays attributable to the chunk whose cache key
184
+ * owns it.
185
+ */
186
+ export const dispatchChunkParseRound = async (groups, workerPool, onFileProgress) => {
187
+ const dispatchGroups = groups.map((group) => {
188
+ const items = [];
189
+ for (const file of group.items) {
190
+ const lang = getLanguageForFileContent(file.path, file.content);
191
+ if (lang)
192
+ items.push({ path: file.path, content: file.content });
193
+ }
194
+ return { items, chunkHash: group.chunkHash };
195
+ });
196
+ const total = groups.reduce((sum, group) => sum + group.items.length, 0);
197
+ if (dispatchGroups.every((group) => group.items.length === 0))
198
+ return groups.map(() => []);
199
+ const perGroup = await workerPool.dispatchGroups(dispatchGroups, (filesProcessed) => {
200
+ onFileProgress?.(Math.min(filesProcessed, total), total, 'Parsing...');
201
+ });
202
+ const chunkResults = perGroup.flat();
184
203
  // Skipped-language telemetry (worker output, independent of the merge).
185
204
  const skippedLanguages = new Map();
186
205
  for (const result of chunkResults) {
@@ -238,7 +257,7 @@ chunkHash) => {
238
257
  logger.warn(` Sanitized ${skippedPaths.length} file(s) with non-serializable parse output: ${shown}${more}`);
239
258
  }
240
259
  onFileProgress?.(total, total, 'done');
241
- return chunkResults;
260
+ return perGroup;
242
261
  };
243
262
  // ============================================================================
244
263
  // Public API
@@ -17,7 +17,7 @@
17
17
  import { BindingAccumulator } from '../binding-accumulator.js';
18
18
  import { type ExportedTypeMap } from '../call-processor.js';
19
19
  import { type MutableSemanticModel } from '../model/index.js';
20
- import { type PipelineProgress } from '../../../_shared/index.js';
20
+ import { type PipelineProgress, SupportedLanguages } from '../../../_shared/index.js';
21
21
  import type { ExtractedDecoratorRoute, ExtractedFetchCall, ExtractedORMQuery, ExtractedRoute, ExtractedToolDef, FetchWrapperDef } from '../workers/parse-worker.js';
22
22
  import type { ModuleConstants } from '../route-extractors/constant-resolver.js';
23
23
  import type { KnowledgeGraph } from '../../graph/types.js';
@@ -104,6 +104,14 @@ export declare function runChunkedParseAndResolve(graph: KnowledgeGraph, scanned
104
104
  * cache analyze run can skip the dominant `extractParsedFile` cost
105
105
  * (otherwise ~58s on a 1000-file repo). */
106
106
  parsedFiles: import('../../../_shared/index.js').ParsedFile[];
107
+ /**
108
+ * Content-derived language decisions for extension-ambiguous files.
109
+ *
110
+ * The source text used to classify these paths is released before parsing
111
+ * begins. Scope resolution consumes this snapshot instead of reading the
112
+ * same files again just to repeat classification.
113
+ */
114
+ contentLanguageByPath: ReadonlyMap<string, SupportedLanguages | null>;
107
115
  /** Repo-wide harvested constants, already prepared per provider. See
108
116
  * `ParseOutput.moduleConstants` for why this leaves the parse phase. */
109
117
  moduleConstants: ReadonlyMap<string, ModuleConstants>;