@opensip-cli/graph 0.1.10 → 0.1.12

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 (211) hide show
  1. package/README.md +4 -2
  2. package/dist/__tests__/cache/engine-version.test.js +4 -4
  3. package/dist/__tests__/cache/engine-version.test.js.map +1 -1
  4. package/dist/__tests__/cli/graph.test.d.ts.map +1 -1
  5. package/dist/__tests__/cli/graph.test.js +0 -1
  6. package/dist/__tests__/cli/graph.test.js.map +1 -1
  7. package/dist/__tests__/internal-surface.test.js +1 -1
  8. package/dist/__tests__/internal-surface.test.js.map +1 -1
  9. package/dist/__tests__/public-api.test.js +14 -0
  10. package/dist/__tests__/public-api.test.js.map +1 -1
  11. package/dist/__tests__/render/catalog-json.test.d.ts.map +1 -1
  12. package/dist/__tests__/render/catalog-json.test.js +0 -1
  13. package/dist/__tests__/render/catalog-json.test.js.map +1 -1
  14. package/dist/__tests__/rules/registry.test.js +1 -0
  15. package/dist/__tests__/rules/registry.test.js.map +1 -1
  16. package/dist/cache/engine-version.d.ts.map +1 -1
  17. package/dist/cache/engine-version.js +5 -1
  18. package/dist/cache/engine-version.js.map +1 -1
  19. package/dist/cli/__tests__/equivalence-diagnostic.test.d.ts +7 -0
  20. package/dist/cli/__tests__/equivalence-diagnostic.test.d.ts.map +1 -0
  21. package/dist/cli/__tests__/equivalence-diagnostic.test.js +136 -0
  22. package/dist/cli/__tests__/equivalence-diagnostic.test.js.map +1 -0
  23. package/dist/cli/__tests__/graph-envelope-view.test.js +9 -9
  24. package/dist/cli/__tests__/graph-envelope-view.test.js.map +1 -1
  25. package/dist/cli/__tests__/graph-runner.test.d.ts +5 -0
  26. package/dist/cli/__tests__/graph-runner.test.d.ts.map +1 -0
  27. package/dist/cli/__tests__/graph-runner.test.js +100 -0
  28. package/dist/cli/__tests__/graph-runner.test.js.map +1 -0
  29. package/dist/cli/equivalence-check-command.d.ts.map +1 -1
  30. package/dist/cli/equivalence-check-command.js +35 -6
  31. package/dist/cli/equivalence-check-command.js.map +1 -1
  32. package/dist/cli/equivalence-diagnostic.d.ts +100 -0
  33. package/dist/cli/equivalence-diagnostic.d.ts.map +1 -0
  34. package/dist/cli/equivalence-diagnostic.js +138 -0
  35. package/dist/cli/equivalence-diagnostic.js.map +1 -0
  36. package/dist/cli/graph/graph-aux-command-specs.d.ts +0 -25
  37. package/dist/cli/graph/graph-aux-command-specs.d.ts.map +1 -1
  38. package/dist/cli/graph/graph-aux-command-specs.js +6 -12
  39. package/dist/cli/graph/graph-aux-command-specs.js.map +1 -1
  40. package/dist/cli/graph/graph-command-spec.d.ts +3 -3
  41. package/dist/cli/graph/graph-command-spec.d.ts.map +1 -1
  42. package/dist/cli/graph/graph-command-spec.js +7 -4
  43. package/dist/cli/graph/graph-command-spec.js.map +1 -1
  44. package/dist/cli/graph-config-schema.d.ts +3 -0
  45. package/dist/cli/graph-config-schema.d.ts.map +1 -1
  46. package/dist/cli/graph-config-schema.js +11 -1
  47. package/dist/cli/graph-config-schema.js.map +1 -1
  48. package/dist/cli/graph-config.d.ts.map +1 -1
  49. package/dist/cli/graph-config.js +5 -10
  50. package/dist/cli/graph-config.js.map +1 -1
  51. package/dist/cli/graph-envelope-view.d.ts +7 -36
  52. package/dist/cli/graph-envelope-view.d.ts.map +1 -1
  53. package/dist/cli/graph-envelope-view.js +7 -102
  54. package/dist/cli/graph-envelope-view.js.map +1 -1
  55. package/dist/cli/graph-modes.d.ts.map +1 -1
  56. package/dist/cli/graph-modes.js +6 -5
  57. package/dist/cli/graph-modes.js.map +1 -1
  58. package/dist/cli/graph-report.d.ts +4 -6
  59. package/dist/cli/graph-report.d.ts.map +1 -1
  60. package/dist/cli/graph-report.js +2 -2
  61. package/dist/cli/graph-report.js.map +1 -1
  62. package/dist/cli/graph-result-delivery.d.ts +30 -0
  63. package/dist/cli/graph-result-delivery.d.ts.map +1 -0
  64. package/dist/cli/graph-result-delivery.js +231 -0
  65. package/dist/cli/graph-result-delivery.js.map +1 -0
  66. package/dist/cli/graph-runner.d.ts +2 -83
  67. package/dist/cli/graph-runner.d.ts.map +1 -1
  68. package/dist/cli/graph-runner.js +71 -215
  69. package/dist/cli/graph-runner.js.map +1 -1
  70. package/dist/cli/graph-session-contribution.d.ts.map +1 -1
  71. package/dist/cli/graph-session-contribution.js +6 -2
  72. package/dist/cli/graph-session-contribution.js.map +1 -1
  73. package/dist/cli/graph-sharded-engine.d.ts.map +1 -1
  74. package/dist/cli/graph-sharded-engine.js +0 -1
  75. package/dist/cli/graph-sharded-engine.js.map +1 -1
  76. package/dist/cli/graph-single-run-mode.d.ts.map +1 -1
  77. package/dist/cli/graph-single-run-mode.js +0 -4
  78. package/dist/cli/graph-single-run-mode.js.map +1 -1
  79. package/dist/cli/graph-worker.d.ts.map +1 -1
  80. package/dist/cli/graph-worker.js +0 -1
  81. package/dist/cli/graph-worker.js.map +1 -1
  82. package/dist/cli/graph.d.ts +2 -22
  83. package/dist/cli/graph.d.ts.map +1 -1
  84. package/dist/cli/graph.js +3 -256
  85. package/dist/cli/graph.js.map +1 -1
  86. package/dist/cli/list-files.d.ts.map +1 -1
  87. package/dist/cli/list-files.js +0 -2
  88. package/dist/cli/list-files.js.map +1 -1
  89. package/dist/cli/orchestrate/__tests__/cross-shard-resolve.test.js +76 -0
  90. package/dist/cli/orchestrate/__tests__/cross-shard-resolve.test.js.map +1 -1
  91. package/dist/cli/orchestrate/cache-orchestrator.d.ts.map +1 -1
  92. package/dist/cli/orchestrate/cache-orchestrator.js +0 -1
  93. package/dist/cli/orchestrate/cache-orchestrator.js.map +1 -1
  94. package/dist/cli/orchestrate/catalog-stats.d.ts.map +1 -1
  95. package/dist/cli/orchestrate/catalog-stats.js +0 -1
  96. package/dist/cli/orchestrate/catalog-stats.js.map +1 -1
  97. package/dist/cli/orchestrate/cross-shard-resolve.d.ts.map +1 -1
  98. package/dist/cli/orchestrate/cross-shard-resolve.js +77 -2
  99. package/dist/cli/orchestrate/cross-shard-resolve.js.map +1 -1
  100. package/dist/cli/orchestrate/flat-monorepo-strategy.d.ts.map +1 -1
  101. package/dist/cli/orchestrate/flat-monorepo-strategy.js +0 -1
  102. package/dist/cli/orchestrate/flat-monorepo-strategy.js.map +1 -1
  103. package/dist/cli/orchestrate/sharded-graph.js +0 -4
  104. package/dist/cli/orchestrate/sharded-graph.js.map +1 -1
  105. package/dist/cli/orchestrate.d.ts.map +1 -1
  106. package/dist/cli/orchestrate.js +0 -1
  107. package/dist/cli/orchestrate.js.map +1 -1
  108. package/dist/cli/pressure-monitor.d.ts +2 -0
  109. package/dist/cli/pressure-monitor.d.ts.map +1 -1
  110. package/dist/cli/pressure-monitor.js +15 -1
  111. package/dist/cli/pressure-monitor.js.map +1 -1
  112. package/dist/cli/report-data.d.ts.map +1 -1
  113. package/dist/cli/report-data.js +0 -1
  114. package/dist/cli/report-data.js.map +1 -1
  115. package/dist/cli/workspace-runner.d.ts.map +1 -1
  116. package/dist/cli/workspace-runner.js +0 -1
  117. package/dist/cli/workspace-runner.js.map +1 -1
  118. package/dist/identity.d.ts +5 -0
  119. package/dist/identity.d.ts.map +1 -0
  120. package/dist/identity.js +6 -0
  121. package/dist/identity.js.map +1 -0
  122. package/dist/index.d.ts +8 -2
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +14 -3
  125. package/dist/index.js.map +1 -1
  126. package/dist/internal.d.ts +1 -1
  127. package/dist/internal.d.ts.map +1 -1
  128. package/dist/internal.js +4 -6
  129. package/dist/internal.js.map +1 -1
  130. package/dist/lang-adapter/__tests__/language-of-file.test.d.ts +2 -0
  131. package/dist/lang-adapter/__tests__/language-of-file.test.d.ts.map +1 -0
  132. package/dist/lang-adapter/__tests__/language-of-file.test.js +36 -0
  133. package/dist/lang-adapter/__tests__/language-of-file.test.js.map +1 -0
  134. package/dist/lang-adapter/__tests__/near-duplicate-signature.test.d.ts +2 -0
  135. package/dist/lang-adapter/__tests__/near-duplicate-signature.test.d.ts.map +1 -0
  136. package/dist/lang-adapter/__tests__/near-duplicate-signature.test.js +78 -0
  137. package/dist/lang-adapter/__tests__/near-duplicate-signature.test.js.map +1 -0
  138. package/dist/lang-adapter/body-digest.d.ts +9 -41
  139. package/dist/lang-adapter/body-digest.d.ts.map +1 -1
  140. package/dist/lang-adapter/body-digest.js +9 -44
  141. package/dist/lang-adapter/body-digest.js.map +1 -1
  142. package/dist/lang-adapter/language-of-file.d.ts +15 -0
  143. package/dist/lang-adapter/language-of-file.d.ts.map +1 -0
  144. package/dist/lang-adapter/language-of-file.js +43 -0
  145. package/dist/lang-adapter/language-of-file.js.map +1 -0
  146. package/dist/lang-adapter/near-duplicate-signature.d.ts +12 -0
  147. package/dist/lang-adapter/near-duplicate-signature.d.ts.map +1 -0
  148. package/dist/lang-adapter/near-duplicate-signature.js +12 -0
  149. package/dist/lang-adapter/near-duplicate-signature.js.map +1 -0
  150. package/dist/lang-adapter/read-source.d.ts +20 -0
  151. package/dist/lang-adapter/read-source.d.ts.map +1 -0
  152. package/dist/lang-adapter/read-source.js +48 -0
  153. package/dist/lang-adapter/read-source.js.map +1 -0
  154. package/dist/lang-adapter/read-source.test.d.ts +2 -0
  155. package/dist/lang-adapter/read-source.test.d.ts.map +1 -0
  156. package/dist/lang-adapter/read-source.test.js +41 -0
  157. package/dist/lang-adapter/read-source.test.js.map +1 -0
  158. package/dist/lang-adapter/registry.d.ts.map +1 -1
  159. package/dist/lang-adapter/registry.js +0 -1
  160. package/dist/lang-adapter/registry.js.map +1 -1
  161. package/dist/persistence/catalog-repo.d.ts.map +1 -1
  162. package/dist/persistence/catalog-repo.js +1 -0
  163. package/dist/persistence/catalog-repo.js.map +1 -1
  164. package/dist/persistence/session-payload.d.ts.map +1 -1
  165. package/dist/persistence/session-payload.js +0 -1
  166. package/dist/persistence/session-payload.js.map +1 -1
  167. package/dist/persistence/session-replay.d.ts.map +1 -1
  168. package/dist/persistence/session-replay.js +10 -25
  169. package/dist/persistence/session-replay.js.map +1 -1
  170. package/dist/pipeline/feature-deps.d.ts.map +1 -1
  171. package/dist/pipeline/feature-deps.js +0 -1
  172. package/dist/pipeline/feature-deps.js.map +1 -1
  173. package/dist/pipeline/features.d.ts.map +1 -1
  174. package/dist/pipeline/features.js +0 -1
  175. package/dist/pipeline/features.js.map +1 -1
  176. package/dist/pipeline/indexes.d.ts.map +1 -1
  177. package/dist/pipeline/indexes.js +0 -1
  178. package/dist/pipeline/indexes.js.map +1 -1
  179. package/dist/recipes/resolve.d.ts.map +1 -1
  180. package/dist/recipes/resolve.js +0 -1
  181. package/dist/recipes/resolve.js.map +1 -1
  182. package/dist/render/rule-id-mapping.d.ts.map +1 -1
  183. package/dist/render/rule-id-mapping.js +1 -0
  184. package/dist/render/rule-id-mapping.js.map +1 -1
  185. package/dist/rules/__tests__/near-duplicate-function-body.test.d.ts +2 -0
  186. package/dist/rules/__tests__/near-duplicate-function-body.test.d.ts.map +1 -0
  187. package/dist/rules/__tests__/near-duplicate-function-body.test.js +142 -0
  188. package/dist/rules/__tests__/near-duplicate-function-body.test.js.map +1 -0
  189. package/dist/rules/_entry-points.d.ts.map +1 -1
  190. package/dist/rules/_entry-points.js +0 -1
  191. package/dist/rules/_entry-points.js.map +1 -1
  192. package/dist/rules/define-rule.d.ts.map +1 -1
  193. package/dist/rules/define-rule.js +0 -3
  194. package/dist/rules/define-rule.js.map +1 -1
  195. package/dist/rules/duplicated-function-body.d.ts +12 -34
  196. package/dist/rules/duplicated-function-body.d.ts.map +1 -1
  197. package/dist/rules/duplicated-function-body.js +83 -233
  198. package/dist/rules/duplicated-function-body.js.map +1 -1
  199. package/dist/rules/near-duplicate-function-body.d.ts +11 -0
  200. package/dist/rules/near-duplicate-function-body.d.ts.map +1 -0
  201. package/dist/rules/near-duplicate-function-body.js +74 -0
  202. package/dist/rules/near-duplicate-function-body.js.map +1 -0
  203. package/dist/rules/registry.d.ts.map +1 -1
  204. package/dist/rules/registry.js +2 -1
  205. package/dist/rules/registry.js.map +1 -1
  206. package/dist/tool.d.ts.map +1 -1
  207. package/dist/tool.js +8 -4
  208. package/dist/tool.js.map +1 -1
  209. package/dist/types.d.ts +21 -1
  210. package/dist/types.d.ts.map +1 -1
  211. package/package.json +16 -10
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- // @fitness-ignore-file module-coupling-fan-out -- Public barrel: re-exports the surface of each pipeline stage; fan-out is the contract of this file
2
1
  /**
3
2
  * @opensip-cli/graph — public barrel.
4
3
  *
5
- * The graph tool implements a strict six-stage pipeline:
6
- * discover inventory → edges → indexes → rules → render.
4
+ * The graph tool implements a strict seven-stage pipeline (the canonical
5
+ * `GRAPH_STAGES` execution order):
6
+ * discover → parse → walk → resolve → index → features → rules.
7
7
  */
8
8
  // Side-effect import: surfaces the `scope.graph` augmentation on
9
9
  // @opensip-cli/core's RunScope interface (D7 — tool subscopes via
@@ -20,6 +20,12 @@ export { GRAPH_CONTRACT_VERSION } from './tool.js';
20
20
  export { GRAPH_ENV_SPECS } from './cli/pressure-monitor.js';
21
21
  export { pickAdapter, createAdapterRegistry, currentAdapterRegistry, GraphAdapterRegistry, } from './lang-adapter/registry.js';
22
22
  export { GraphAdapterSelector } from './lang-adapter/selector.js';
23
+ // Size-guarded source-file reader for adapter parse steps (per-file 10 MB cap,
24
+ // shared with the fitness engine's FileAccessor policy). Adapters use this
25
+ // instead of a raw `readFileSync` so one pathological source file can't spike
26
+ // graph memory; oversize throws and the adapter's per-file catch records a
27
+ // ParseError + continues.
28
+ export { readSourceFileGuarded, MAX_SOURCE_FILE_BYTES } from './lang-adapter/read-source.js';
23
29
  export { truncateForCallEdge, CALL_EDGE_TEXT_MAX, CREATION_EDGE_PREFIX, CREATION_EDGE_TEXT_MAX, appendEdge, createMutableStats, pushCreationEdge, } from './lang-adapter/edge-helpers.js';
24
30
  // ── Shared body-digest primitives ─────────────────────────────────
25
31
  //
@@ -29,6 +35,11 @@ export { truncateForCallEdge, CALL_EDGE_TEXT_MAX, CREATION_EDGE_PREFIX, CREATION
29
35
  // only its language-specific comment stripper / normalizer (round-3
30
36
  // audit 2026-05-30, finding D). Mirrors the edge-helpers rationale.
31
37
  export { normalizeWhitespace, hashBody } from './lang-adapter/body-digest.js';
38
+ export { NEAR_DUP_SIGNATURE_K, NEAR_DUP_LSH_BANDS, NEAR_DUP_LSH_ROWS, shingle, bodySignature, estimateJaccard, lshBandHashes, digestCanonicalBody, } from './lang-adapter/near-duplicate-signature.js';
39
+ export { languageOfFile } from './lang-adapter/language-of-file.js';
40
+ // Canonical TS/JS test-file predicate, single-sourced in the shared substrate (ADR-0064,
41
+ // D1). Re-exported so graph-typescript reaches it via the engine barrel (no new dep).
42
+ export { isTestFilePath } from '@opensip-cli/clone-detection';
32
43
  // PR 3 of plan 2026-05-23-plan-graph-adapter-package-split.md:
33
44
  // first-party adapters live in their own graph-* packages, so the
34
45
  // engine no longer re-exports any adapter, parsed-project type, or
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qJAAqJ;AACrJ;;;;;GAKG;AAEH,iEAAiE;AACjE,kEAAkE;AAClE,wBAAwB;AACxB,OAAO,yBAAyB,CAAC;AAGjC,mEAAmE;AACnE,qEAAqE;AACrE,6DAA6D;AAC7D,qBAAqB;AACrB,OAAO,EAAE,SAAS,EAAE,SAAS,IAAI,IAAI,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,oEAAoE;AACpE,6EAA6E;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAsF5D,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,gCAAgC,CAAC;AAWxC,qEAAqE;AACrE,EAAE;AACF,oEAAoE;AACpE,qEAAqE;AACrE,wEAAwE;AACxE,oEAAoE;AACpE,oEAAoE;AACpE,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAG9E,+DAA+D;AAC/D,kEAAkE;AAClE,mEAAmE;AACnE,qEAAqE;AACrE,gBAAgB;AAEhB,4EAA4E;AAC5E,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,sEAAsE;AACtE,uEAAuE;AACvE,4EAA4E;AAC5E,0EAA0E;AAC1E,wEAAwE;AACxE,0EAA0E;AAC1E,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,kCAAkC,EAClC,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC;AAOzC,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAEnF,6EAA6E;AAC7E,qEAAqE;AACrE,6EAA6E;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,sEAAsE;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAOvD,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,iEAAiE;AACjE,kEAAkE;AAClE,wBAAwB;AACxB,OAAO,yBAAyB,CAAC;AAGjC,mEAAmE;AACnE,qEAAqE;AACrE,6DAA6D;AAC7D,qBAAqB;AACrB,OAAO,EAAE,SAAS,EAAE,SAAS,IAAI,IAAI,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,oEAAoE;AACpE,6EAA6E;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAsF5D,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,2EAA2E;AAC3E,0BAA0B;AAC1B,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,gCAAgC,CAAC;AAWxC,qEAAqE;AACrE,EAAE;AACF,oEAAoE;AACpE,qEAAqE;AACrE,wEAAwE;AACxE,oEAAoE;AACpE,oEAAoE;AACpE,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,eAAe,EACf,aAAa,EACb,mBAAmB,GACpB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,yFAAyF;AACzF,sFAAsF;AACtF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,+DAA+D;AAC/D,kEAAkE;AAClE,mEAAmE;AACnE,qEAAqE;AACrE,gBAAgB;AAEhB,4EAA4E;AAC5E,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,sEAAsE;AACtE,uEAAuE;AACvE,4EAA4E;AAC5E,0EAA0E;AAC1E,wEAAwE;AACxE,0EAA0E;AAC1E,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,kCAAkC,EAClC,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC;AAOzC,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAEnF,6EAA6E;AAC7E,qEAAqE;AACrE,6EAA6E;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,sEAAsE;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAOvD,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -18,7 +18,7 @@ export { runGraph, GRAPH_STAGES } from './cli/orchestrate.js';
18
18
  export type { GraphStage, GraphProgressEvent, GraphProgressCallback, RunGraphInput, RunGraphResult, } from './cli/orchestrate.js';
19
19
  export { executeGraph, buildUnifiedReportLines } from './cli/graph.js';
20
20
  export type { UnifiedReportInput } from './cli/graph.js';
21
- export { graphDoneTableNode } from './cli/graph-envelope-view.js';
21
+ export { envelopeToLiveRunTableRows } from './cli/graph-envelope-view.js';
22
22
  export { CatalogRepo } from './persistence/catalog-repo.js';
23
23
  export { MemoryPressureError } from './cli/pressure-monitor.js';
24
24
  export { HEAP_TARGETS, decideHeapTargetMb, systemHasMemoryFor, runHeapPreflight, totalSystemMemoryMb, } from './cli/heap-preflight.js';
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAKrD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAU9D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAMzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAQlE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAKrD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAU9D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAIzD,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAQ1E,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC"}
package/dist/internal.js CHANGED
@@ -18,7 +18,7 @@ export { duplicatedFunctionBodyRule } from './rules/duplicated-function-body.js'
18
18
  export { orphanSubtreeRule } from './rules/orphan-subtree.js';
19
19
  // ── Orchestration / CLI-handler surface (ADR-0009, Finding 3) ──────
20
20
  //
21
- // These drive the six-stage pipeline and the `graph` CLI command. The
21
+ // These drive the seven-stage pipeline and the `graph` CLI command. The
22
22
  // production path reaches them via `graphTool.commandSpecs` (host-mounted,
23
23
  // and the parent repo via the `catalog-export` subcommand), never by importing these
24
24
  // symbols — so they are private. Only the cross-package adapter and CLI
@@ -26,11 +26,9 @@ export { orphanSubtreeRule } from './rules/orphan-subtree.js';
26
26
  // end without going through Commander.
27
27
  export { runGraph, GRAPH_STAGES } from './cli/orchestrate.js';
28
28
  export { executeGraph, buildUnifiedReportLines } from './cli/graph.js';
29
- // The graph live verbose/detail per-unit table node. Internal — exported only
30
- // so the host-side live/static parity proof
31
- // (`packages/cli/src/ui/__tests__/graph-live-static-parity.test.tsx`) can assert
32
- // it renders byte-identically to the static `envelopeToTableView` table.
33
- export { graphDoneTableNode } from './cli/graph-envelope-view.js';
29
+ // Graph envelope live-run table rows. Internal — exported for the host-side
30
+ // live/static parity proof (`graph-live-static-parity.test.tsx`).
31
+ export { envelopeToLiveRunTableRows } from './cli/graph-envelope-view.js';
34
32
  // `CatalogRepo` — the engine's SQLite/Drizzle catalog persistence repo. Used
35
33
  // internally by graph (orchestrator cache, `tool.ts` report contribution,
36
34
  // `lookup`/`catalog-export`). It was briefly on the public barrel for fitness's
@@ -1 +1 @@
1
- {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,yEAAyE;AACzE,+EAA+E;AAC/E,8DAA8D;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAOrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,sEAAsE;AACtE,EAAE;AACF,sEAAsE;AACtE,2EAA2E;AAC3E,qFAAqF;AACrF,wEAAwE;AACxE,wEAAwE;AACxE,uCAAuC;AACvC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQ9D,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGvE,8EAA8E;AAC9E,4CAA4C;AAC5C,iFAAiF;AACjF,yEAAyE;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,6EAA6E;AAC7E,0EAA0E;AAC1E,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,yEAAyE;AACzE,+EAA+E;AAC/E,8DAA8D;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAOrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,sEAAsE;AACtE,EAAE;AACF,wEAAwE;AACxE,2EAA2E;AAC3E,qFAAqF;AACrF,wEAAwE;AACxE,wEAAwE;AACxE,uCAAuC;AACvC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQ9D,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGvE,8EAA8E;AAC9E,kEAAkE;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,6EAA6E;AAC7E,0EAA0E;AAC1E,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=language-of-file.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-of-file.test.d.ts","sourceRoot":"","sources":["../../../src/lang-adapter/__tests__/language-of-file.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { languageOfFile } from '../language-of-file.js';
3
+ describe('languageOfFile', () => {
4
+ it('maps TypeScript extensions', () => {
5
+ expect(languageOfFile('src/a.ts')).toBe('typescript');
6
+ expect(languageOfFile('src/a.tsx')).toBe('typescript');
7
+ expect(languageOfFile('src/a.mts')).toBe('typescript');
8
+ expect(languageOfFile('src/a.cts')).toBe('typescript');
9
+ });
10
+ it('maps JavaScript extensions to typescript (same adapter; .ts↔.js clones are real)', () => {
11
+ expect(languageOfFile('src/a.js')).toBe('typescript');
12
+ expect(languageOfFile('src/a.jsx')).toBe('typescript');
13
+ expect(languageOfFile('src/a.mjs')).toBe('typescript');
14
+ expect(languageOfFile('src/a.cjs')).toBe('typescript');
15
+ // A .ts and a .js file are the same language → clones between them are detected.
16
+ expect(languageOfFile('a.ts')).toBe(languageOfFile('b.js'));
17
+ });
18
+ it('maps Python extensions without truncating .pyi', () => {
19
+ expect(languageOfFile('mod.py')).toBe('python');
20
+ expect(languageOfFile('stub.pyi')).toBe('python');
21
+ });
22
+ it('maps Go, Java, and Rust', () => {
23
+ expect(languageOfFile('main.go')).toBe('go');
24
+ expect(languageOfFile('App.java')).toBe('java');
25
+ expect(languageOfFile('lib.rs')).toBe('rust');
26
+ });
27
+ it('returns undefined for unknown extensions', () => {
28
+ expect(languageOfFile('readme.md')).toBeUndefined();
29
+ expect(languageOfFile('noext')).toBeUndefined();
30
+ });
31
+ it('two files share a language iff languageOfFile is equal', () => {
32
+ expect(languageOfFile('a.ts')).toBe(languageOfFile('b.tsx'));
33
+ expect(languageOfFile('a.go')).not.toBe(languageOfFile('b.ts'));
34
+ });
35
+ });
36
+ //# sourceMappingURL=language-of-file.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-of-file.test.js","sourceRoot":"","sources":["../../../src/lang-adapter/__tests__/language-of-file.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,iFAAiF;QACjF,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACpD,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7D,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=near-duplicate-signature.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"near-duplicate-signature.test.d.ts","sourceRoot":"","sources":["../../../src/lang-adapter/__tests__/near-duplicate-signature.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,78 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { NEAR_DUP_LSH_BANDS, NEAR_DUP_LSH_ROWS, NEAR_DUP_SIGNATURE_K, bodySignature, digestCanonicalBody, estimateJaccard, lshBandHashes, shingle, } from '../near-duplicate-signature.js';
3
+ describe('near-duplicate-signature', () => {
4
+ const base = 'function processItems(items) { const out = []; for (const item of items) { out.push(transform(item)); } return out; }';
5
+ const nearEdit = 'function processItems(items) { const result = []; for (const item of items) { result.push(transform(item)); } return result; }';
6
+ const unrelated = 'export function validateConfig(cfg) { if (!cfg.apiKey) throw new Error("missing"); return cfg; }';
7
+ it('identical canonical text yields Jaccard 1.0', () => {
8
+ const a = bodySignature(base);
9
+ const b = bodySignature(base);
10
+ expect(estimateJaccard(a, b)).toBe(1);
11
+ });
12
+ it('one-token edit yields high but sub-1.0 Jaccard', () => {
13
+ const j = estimateJaccard(bodySignature(base), bodySignature(nearEdit));
14
+ expect(j).toBeGreaterThan(0.5);
15
+ expect(j).toBeLessThan(1);
16
+ });
17
+ it('unrelated bodies yield low Jaccard', () => {
18
+ expect(estimateJaccard(bodySignature(base), bodySignature(unrelated))).toBeLessThan(0.5);
19
+ });
20
+ it('signatures are stable across runs', () => {
21
+ expect(bodySignature(base)).toEqual(bodySignature(base));
22
+ });
23
+ it('bands × rows equals k', () => {
24
+ expect(NEAR_DUP_LSH_BANDS * NEAR_DUP_LSH_ROWS).toBe(NEAR_DUP_SIGNATURE_K);
25
+ });
26
+ it('LSH knee is near the 0.85 threshold', () => {
27
+ const knee = (1 / NEAR_DUP_LSH_BANDS) ** (1 / NEAR_DUP_LSH_ROWS);
28
+ expect(knee).toBeCloseTo(0.878, 2);
29
+ });
30
+ it('shingle emits char 5-grams', () => {
31
+ expect(shingle('abcdef').size).toBe(2);
32
+ expect(shingle('ab').size).toBe(1);
33
+ expect(shingle('').size).toBe(0);
34
+ });
35
+ it('digestCanonicalBody returns hash, size, and signature', () => {
36
+ const d = digestCanonicalBody(base);
37
+ expect(d.hash).toMatch(/^[a-f0-9]{64}$/);
38
+ expect(d.size).toBeGreaterThan(0);
39
+ expect(d.signature?.length).toBe(NEAR_DUP_SIGNATURE_K);
40
+ });
41
+ it('lshBandHashes returns one hash per band', () => {
42
+ const sig = bodySignature(base);
43
+ expect(lshBandHashes(sig, NEAR_DUP_LSH_BANDS, NEAR_DUP_LSH_ROWS).length).toBe(NEAR_DUP_LSH_BANDS);
44
+ });
45
+ it('bodySignature returns empty for empty canonical (no shingles)', () => {
46
+ expect(bodySignature('')).toEqual([]);
47
+ });
48
+ it('bodySignature with custom k derives k seeds and returns k values', () => {
49
+ const sig = bodySignature(base, 32);
50
+ expect(sig.length).toBe(32);
51
+ // Custom-k path is deterministic and stable across calls.
52
+ expect(bodySignature(base, 32)).toEqual(sig);
53
+ // Identical body still self-matches under a custom k.
54
+ expect(estimateJaccard(sig, bodySignature(base, 32))).toBe(1);
55
+ });
56
+ it('bodySignature produces a full-width signature for sub-gram bodies', () => {
57
+ // Body shorter than the gram size yields a single whole-string shingle,
58
+ // exercising the single-base MinHash loop across all k positions.
59
+ const short = bodySignature('ab');
60
+ expect(short.length).toBe(NEAR_DUP_SIGNATURE_K);
61
+ expect(short.every((v) => Number.isInteger(v))).toBe(true);
62
+ });
63
+ it('estimateJaccard returns 0 when either signature is empty', () => {
64
+ const sig = bodySignature(base);
65
+ expect(estimateJaccard([], sig)).toBe(0);
66
+ expect(estimateJaccard(sig, [])).toBe(0);
67
+ expect(estimateJaccard([], [])).toBe(0);
68
+ });
69
+ it('estimateJaccard returns 0 for mismatched signature lengths', () => {
70
+ expect(estimateJaccard(bodySignature(base), bodySignature(base, 32))).toBe(0);
71
+ });
72
+ it('digestCanonicalBody omits signature for empty canonical', () => {
73
+ const d = digestCanonicalBody('');
74
+ expect(d.hash).toMatch(/^[a-f0-9]{64}$/);
75
+ expect(d.signature).toBeUndefined();
76
+ });
77
+ });
78
+ //# sourceMappingURL=near-duplicate-signature.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"near-duplicate-signature.test.js","sourceRoot":"","sources":["../../../src/lang-adapter/__tests__/near-duplicate-signature.test.ts"],"names":[],"mappings":"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"}
@@ -1,46 +1,14 @@
1
1
  /**
2
- * @fileoverview Shared body-digest primitives for GraphLanguageAdapters.
2
+ * @fileoverview Body-digest primitives relocated to `@opensip-cli/clone-detection`.
3
3
  *
4
- * The catalog's `bodyHash` contract is "reduce a function body to a
5
- * canonical string, then SHA-256 it." The normalize-to-hash TAIL of that
6
- * pipeline the `BodyDigest` shape, the whitespace-collapse normalizer,
7
- * and the hash+size step was byte-identical across every adapter that
8
- * hashes bodies (go/java/rust real bodies, plus the synthetic-body path
9
- * in all four tree-sitter packs). Per the round-3 modular-monolith audit
10
- * (2026-05-30, finding D) it lives here on the contract layer instead of
11
- * being copy-pasted into each pack.
4
+ * The hash/normalize tail (`BodyDigest`, `normalizeWhitespace`, `hashBody`) moved to
5
+ * the shared layer-2 substrate (ADR-0064) so both graph and yagni single-source it and
6
+ * cannot diverge. This module re-exports the same symbols verbatim so every existing
7
+ * graph-engine + adapter importer of `'../lang-adapter/body-digest.js'` is unchanged
8
+ * and `bodyHash` values stay byte-identical.
12
9
  *
13
- * This mirrors edge-helpers.ts exactly: tiny utilities that used to be
14
- * duplicated under each adapter now sit in lang-adapter/, which adapters
15
- * already import types from — so importing one more helper is
16
- * structurally consistent and violates no layering rule (lang-* adapters
17
- * may not import each OTHER, but lang-adapter/ is fair game).
18
- *
19
- * What deliberately STAYS in each adapter pack: the language-specific
20
- * comment stripper (Go rune literals vs Java text blocks vs Rust nested
21
- * block comments) and any language-specific normalization. Python, for
22
- * example, substitutes its own `normalizePythonBody` for real bodies
23
- * because indentation is semantically significant there — it still
24
- * composes `hashBody` + `normalizeWhitespace` for synthetic bodies. The
25
- * primitives are intentionally low-level (not a single `digestBody(text,
26
- * stripFn)`) precisely so a pack can swap the normalization step.
27
- */
28
- /** Catalog body fingerprint: SHA-256 hex hash + canonical-text length. */
29
- export interface BodyDigest {
30
- readonly hash: string;
31
- readonly size: number;
32
- }
33
- /**
34
- * Collapse every run of whitespace to a single space and trim. The
35
- * default body normalization for whitespace-insensitive languages
36
- * (Go/Java/Rust) and for synthetic (module-init) bodies in every pack.
37
- */
38
- export declare function normalizeWhitespace(s: string): string;
39
- /**
40
- * SHA-256 an already-normalized ("canonical") body string into a
41
- * `BodyDigest`. `size` is the canonical-text length so it tracks the
42
- * hashed content, not the raw source. Callers normalize first — via
43
- * `normalizeWhitespace` or a language-specific normalizer.
10
+ * Language-specific comment strippers / normalizers still live in each adapter pack;
11
+ * only the canonical-string→hash tail is shared (see the package).
44
12
  */
45
- export declare function hashBody(canonical: string): BodyDigest;
13
+ export { normalizeWhitespace, hashBody, type BodyDigest } from '@opensip-cli/clone-detection';
46
14
  //# sourceMappingURL=body-digest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"body-digest.d.ts","sourceRoot":"","sources":["../../src/lang-adapter/body-digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,0EAA0E;AAC1E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAKtD"}
1
+ {"version":3,"file":"body-digest.d.ts","sourceRoot":"","sources":["../../src/lang-adapter/body-digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC"}
@@ -1,49 +1,14 @@
1
1
  /**
2
- * @fileoverview Shared body-digest primitives for GraphLanguageAdapters.
2
+ * @fileoverview Body-digest primitives relocated to `@opensip-cli/clone-detection`.
3
3
  *
4
- * The catalog's `bodyHash` contract is "reduce a function body to a
5
- * canonical string, then SHA-256 it." The normalize-to-hash TAIL of that
6
- * pipeline the `BodyDigest` shape, the whitespace-collapse normalizer,
7
- * and the hash+size step was byte-identical across every adapter that
8
- * hashes bodies (go/java/rust real bodies, plus the synthetic-body path
9
- * in all four tree-sitter packs). Per the round-3 modular-monolith audit
10
- * (2026-05-30, finding D) it lives here on the contract layer instead of
11
- * being copy-pasted into each pack.
4
+ * The hash/normalize tail (`BodyDigest`, `normalizeWhitespace`, `hashBody`) moved to
5
+ * the shared layer-2 substrate (ADR-0064) so both graph and yagni single-source it and
6
+ * cannot diverge. This module re-exports the same symbols verbatim so every existing
7
+ * graph-engine + adapter importer of `'../lang-adapter/body-digest.js'` is unchanged
8
+ * and `bodyHash` values stay byte-identical.
12
9
  *
13
- * This mirrors edge-helpers.ts exactly: tiny utilities that used to be
14
- * duplicated under each adapter now sit in lang-adapter/, which adapters
15
- * already import types from — so importing one more helper is
16
- * structurally consistent and violates no layering rule (lang-* adapters
17
- * may not import each OTHER, but lang-adapter/ is fair game).
18
- *
19
- * What deliberately STAYS in each adapter pack: the language-specific
20
- * comment stripper (Go rune literals vs Java text blocks vs Rust nested
21
- * block comments) and any language-specific normalization. Python, for
22
- * example, substitutes its own `normalizePythonBody` for real bodies
23
- * because indentation is semantically significant there — it still
24
- * composes `hashBody` + `normalizeWhitespace` for synthetic bodies. The
25
- * primitives are intentionally low-level (not a single `digestBody(text,
26
- * stripFn)`) precisely so a pack can swap the normalization step.
27
- */
28
- import { createHash } from 'node:crypto';
29
- /**
30
- * Collapse every run of whitespace to a single space and trim. The
31
- * default body normalization for whitespace-insensitive languages
32
- * (Go/Java/Rust) and for synthetic (module-init) bodies in every pack.
33
- */
34
- export function normalizeWhitespace(s) {
35
- return s.replaceAll(/\s+/g, ' ').trim();
36
- }
37
- /**
38
- * SHA-256 an already-normalized ("canonical") body string into a
39
- * `BodyDigest`. `size` is the canonical-text length so it tracks the
40
- * hashed content, not the raw source. Callers normalize first — via
41
- * `normalizeWhitespace` or a language-specific normalizer.
10
+ * Language-specific comment strippers / normalizers still live in each adapter pack;
11
+ * only the canonical-string→hash tail is shared (see the package).
42
12
  */
43
- export function hashBody(canonical) {
44
- return {
45
- hash: createHash('sha256').update(canonical, 'utf8').digest('hex'),
46
- size: canonical.length,
47
- };
48
- }
13
+ export { normalizeWhitespace, hashBody } from '@opensip-cli/clone-detection';
49
14
  //# sourceMappingURL=body-digest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"body-digest.js","sourceRoot":"","sources":["../../src/lang-adapter/body-digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,OAAO,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAiB;IACxC,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAClE,IAAI,EAAE,SAAS,CAAC,MAAM;KACvB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"body-digest.js","sourceRoot":"","sources":["../../src/lang-adapter/body-digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAmB,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Canonical language id by file extension — engine-homed for rule gating.
3
+ *
4
+ * The near-clone rule must gate candidate pairs to the same language. The
5
+ * equivalent filter in graph-adapter-common cannot be imported from the engine
6
+ * rule layer (adapter-common depends on the engine). This module is the
7
+ * canonical extension map consumed by rules; adapter-common may delegate here
8
+ * in a follow-up.
9
+ */
10
+ /**
11
+ * Return the canonical language id for `filePath` by extension, or `undefined`
12
+ * when the extension is not recognized.
13
+ */
14
+ export declare function languageOfFile(filePath: string): string | undefined;
15
+ //# sourceMappingURL=language-of-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-of-file.d.ts","sourceRoot":"","sources":["../../src/lang-adapter/language-of-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0BH;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKnE"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Canonical language id by file extension — engine-homed for rule gating.
3
+ *
4
+ * The near-clone rule must gate candidate pairs to the same language. The
5
+ * equivalent filter in graph-adapter-common cannot be imported from the engine
6
+ * rule layer (adapter-common depends on the engine). This module is the
7
+ * canonical extension map consumed by rules; adapter-common may delegate here
8
+ * in a follow-up.
9
+ */
10
+ /**
11
+ * Extension suffix → canonical graph language id. Longest-match first per path.
12
+ * `.js`/`.jsx`/`.mjs`/`.cjs` map to `typescript` because the graph-typescript
13
+ * adapter analyzes them and a `.ts`↔`.js` (or `.js`↔`.js`) near-clone is a real,
14
+ * actionable clone — omitting them would silently skip every JS clone pair.
15
+ */
16
+ const EXTENSION_TO_LANGUAGE = {
17
+ '.tsx': 'typescript',
18
+ '.mts': 'typescript',
19
+ '.cts': 'typescript',
20
+ '.ts': 'typescript',
21
+ '.jsx': 'typescript',
22
+ '.mjs': 'typescript',
23
+ '.cjs': 'typescript',
24
+ '.js': 'typescript',
25
+ '.pyi': 'python',
26
+ '.py': 'python',
27
+ '.java': 'java',
28
+ '.go': 'go',
29
+ '.rs': 'rust',
30
+ };
31
+ const SORTED_EXTENSIONS = Object.keys(EXTENSION_TO_LANGUAGE).sort((a, b) => b.length - a.length);
32
+ /**
33
+ * Return the canonical language id for `filePath` by extension, or `undefined`
34
+ * when the extension is not recognized.
35
+ */
36
+ export function languageOfFile(filePath) {
37
+ for (const ext of SORTED_EXTENSIONS) {
38
+ if (filePath.endsWith(ext))
39
+ return EXTENSION_TO_LANGUAGE[ext];
40
+ }
41
+ return undefined;
42
+ }
43
+ //# sourceMappingURL=language-of-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-of-file.js","sourceRoot":"","sources":["../../src/lang-adapter/language-of-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,qBAAqB,GAAqC;IAC9D,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE,YAAY;IACnB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE,YAAY;IACnB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,MAAM;IACf,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,MAAM;CACd,CAAC;AAEF,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjG;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Near-duplicate MinHash/LSH primitives — relocated to
3
+ * `@opensip-cli/clone-detection`.
4
+ *
5
+ * The signature constants + digest tail moved to the shared layer-2 substrate
6
+ * (ADR-0064) so graph and yagni single-source them. This module re-exports the same
7
+ * symbols verbatim so existing importers of `'../lang-adapter/near-duplicate-signature.js'`
8
+ * are unchanged and signature VALUES (and the `sig=` cache-key segment) stay
9
+ * byte-identical. `NEAR_DUP_SIGNATURE_VERSION` is unchanged by the move.
10
+ */
11
+ export { NEAR_DUP_SIGNATURE_K, NEAR_DUP_LSH_BANDS, NEAR_DUP_LSH_ROWS, NEAR_DUP_SIGNATURE_VERSION, shingle, bodySignature, estimateJaccard, lshBandHashes, digestCanonicalBody, type BodyDigestWithSignature, } from '@opensip-cli/clone-detection';
12
+ //# sourceMappingURL=near-duplicate-signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"near-duplicate-signature.d.ts","sourceRoot":"","sources":["../../src/lang-adapter/near-duplicate-signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,0BAA0B,EAC1B,OAAO,EACP,aAAa,EACb,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,KAAK,uBAAuB,GAC7B,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Near-duplicate MinHash/LSH primitives — relocated to
3
+ * `@opensip-cli/clone-detection`.
4
+ *
5
+ * The signature constants + digest tail moved to the shared layer-2 substrate
6
+ * (ADR-0064) so graph and yagni single-source them. This module re-exports the same
7
+ * symbols verbatim so existing importers of `'../lang-adapter/near-duplicate-signature.js'`
8
+ * are unchanged and signature VALUES (and the `sig=` cache-key segment) stay
9
+ * byte-identical. `NEAR_DUP_SIGNATURE_VERSION` is unchanged by the move.
10
+ */
11
+ export { NEAR_DUP_SIGNATURE_K, NEAR_DUP_LSH_BANDS, NEAR_DUP_LSH_ROWS, NEAR_DUP_SIGNATURE_VERSION, shingle, bodySignature, estimateJaccard, lshBandHashes, digestCanonicalBody, } from '@opensip-cli/clone-detection';
12
+ //# sourceMappingURL=near-duplicate-signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"near-duplicate-signature.js","sourceRoot":"","sources":["../../src/lang-adapter/near-duplicate-signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,0BAA0B,EAC1B,OAAO,EACP,aAAa,EACb,eAAe,EACf,aAAa,EACb,mBAAmB,GAEpB,MAAM,8BAA8B,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Maximum bytes a single source file may occupy when read for parsing (10 MB).
3
+ * Mirrors the fitness engine's `FileAccessor` `FILE_TOO_LARGE` ceiling.
4
+ */
5
+ export declare const MAX_SOURCE_FILE_BYTES = 10000000;
6
+ /**
7
+ * Read a source file as UTF-8 with a per-file size guard. Throws if the file
8
+ * exceeds `maxBytes` (default {@link MAX_SOURCE_FILE_BYTES}) — by `statSync`
9
+ * size (checked before the read, so an oversized file is never loaded) or by
10
+ * post-read length (closing the stat→read TOCTOU window). A missing/unreadable
11
+ * file surfaces the underlying `statSync` error, exactly as the prior direct
12
+ * `readFileSync` surfaced its own.
13
+ *
14
+ * @throws {Error} When the file exceeds `maxBytes` (by `statSync` size or
15
+ * post-read length), or when the underlying `statSync`/`readFileSync` fails
16
+ * (e.g. a missing or unreadable file). Adapter parse loops catch this per file
17
+ * and record a `ParseError`, so the throw is non-fatal.
18
+ */
19
+ export declare function readSourceFileGuarded(path: string, maxBytes?: number): string;
20
+ //# sourceMappingURL=read-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-source.d.ts","sourceRoot":"","sources":["../../src/lang-adapter/read-source.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,eAAO,MAAM,qBAAqB,WAAa,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAA8B,GACvC,MAAM,CAYR"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * read-source — the single size-guarded source-file reader for graph language
3
+ * adapters.
4
+ *
5
+ * Adapter parse steps read project SOURCE files into memory before handing them
6
+ * to a parser. Reading a whole file is unavoidable, but an *unbounded* read lets
7
+ * one pathological file (a multi-hundred-MB generated/minified bundle) spike
8
+ * graph memory. This wrapper caps a single source file at
9
+ * {@link MAX_SOURCE_FILE_BYTES} (10 MB — the same ceiling the fitness engine's
10
+ * `FileAccessor` enforces, so the two tools share one source-size policy), using
11
+ * a `statSync` pre-check so an oversized file is rejected WITHOUT being loaded.
12
+ *
13
+ * On oversize it THROWS. Adapter parse loops already wrap each per-file read in a
14
+ * try/catch that records a `ParseError` and continues, so an oversized file is
15
+ * skipped with a recorded diagnostic — never fatal, consistent with how a
16
+ * read/parse failure on any single file is already handled.
17
+ */
18
+ import { readFileSync, statSync } from 'node:fs';
19
+ /**
20
+ * Maximum bytes a single source file may occupy when read for parsing (10 MB).
21
+ * Mirrors the fitness engine's `FileAccessor` `FILE_TOO_LARGE` ceiling.
22
+ */
23
+ export const MAX_SOURCE_FILE_BYTES = 10_000_000;
24
+ /**
25
+ * Read a source file as UTF-8 with a per-file size guard. Throws if the file
26
+ * exceeds `maxBytes` (default {@link MAX_SOURCE_FILE_BYTES}) — by `statSync`
27
+ * size (checked before the read, so an oversized file is never loaded) or by
28
+ * post-read length (closing the stat→read TOCTOU window). A missing/unreadable
29
+ * file surfaces the underlying `statSync` error, exactly as the prior direct
30
+ * `readFileSync` surfaced its own.
31
+ *
32
+ * @throws {Error} When the file exceeds `maxBytes` (by `statSync` size or
33
+ * post-read length), or when the underlying `statSync`/`readFileSync` fails
34
+ * (e.g. a missing or unreadable file). Adapter parse loops catch this per file
35
+ * and record a `ParseError`, so the throw is non-fatal.
36
+ */
37
+ export function readSourceFileGuarded(path, maxBytes = MAX_SOURCE_FILE_BYTES) {
38
+ const { size } = statSync(path);
39
+ if (size > maxBytes) {
40
+ throw new Error(`source file exceeds ${maxBytes}-byte size guard (${size} bytes): ${path}`);
41
+ }
42
+ const text = readFileSync(path, 'utf8');
43
+ if (text.length > maxBytes) {
44
+ throw new Error(`source file exceeds ${maxBytes}-byte size guard after read (${text.length} bytes): ${path}`);
45
+ }
46
+ return text;
47
+ }
48
+ //# sourceMappingURL=read-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-source.js","sourceRoot":"","sources":["../../src/lang-adapter/read-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAY,EACZ,WAAmB,qBAAqB;IAExC,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,qBAAqB,IAAI,YAAY,IAAI,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,uBAAuB,QAAQ,gCAAgC,IAAI,CAAC,MAAM,YAAY,IAAI,EAAE,CAC7F,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=read-source.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-source.test.d.ts","sourceRoot":"","sources":["../../src/lang-adapter/read-source.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * read-source — per-file source size guard for adapter parse steps.
3
+ *
4
+ * Proves the guard rejects an oversized file (the unbounded-memory hardening)
5
+ * and otherwise behaves like a plain UTF-8 read, so the adapters' existing
6
+ * per-file try/catch records a ParseError + continues on the throw.
7
+ */
8
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
9
+ import { tmpdir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ import { afterEach, describe, expect, it } from 'vitest';
12
+ import { MAX_SOURCE_FILE_BYTES, readSourceFileGuarded } from './read-source.js';
13
+ describe('readSourceFileGuarded', () => {
14
+ let dir;
15
+ afterEach(() => {
16
+ if (dir !== undefined)
17
+ rmSync(dir, { recursive: true, force: true });
18
+ dir = undefined;
19
+ });
20
+ function tmpFile(name, content) {
21
+ dir = mkdtempSync(join(tmpdir(), 'read-source-'));
22
+ const filePath = join(dir, name);
23
+ writeFileSync(filePath, content, 'utf8');
24
+ return filePath;
25
+ }
26
+ it('reads a normal source file as UTF-8', () => {
27
+ const filePath = tmpFile('a.ts', 'export const x = 1;\n');
28
+ expect(readSourceFileGuarded(filePath)).toBe('export const x = 1;\n');
29
+ });
30
+ it('throws (does not load) when the file exceeds the byte guard', () => {
31
+ const filePath = tmpFile('big.ts', 'x'.repeat(64));
32
+ expect(() => readSourceFileGuarded(filePath, 16)).toThrow(/size guard/);
33
+ });
34
+ it('defaults to the shared 10MB ceiling', () => {
35
+ expect(MAX_SOURCE_FILE_BYTES).toBe(10_000_000);
36
+ });
37
+ it('surfaces a stat error for a missing file (so the adapter catch records a ParseError)', () => {
38
+ expect(() => readSourceFileGuarded(join(tmpdir(), 'opensip-missing-source-xyz.ts'))).toThrow();
39
+ });
40
+ });
41
+ //# sourceMappingURL=read-source.test.js.map