@descryy/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (366) hide show
  1. package/LICENSE +21 -0
  2. package/dist/capabilities/index.d.ts +3 -0
  3. package/dist/capabilities/index.d.ts.map +1 -0
  4. package/dist/capabilities/index.js +2 -0
  5. package/dist/capabilities/index.js.map +1 -0
  6. package/dist/capabilities/preconditions.d.ts +99 -0
  7. package/dist/capabilities/preconditions.d.ts.map +1 -0
  8. package/dist/capabilities/preconditions.js +94 -0
  9. package/dist/capabilities/preconditions.js.map +1 -0
  10. package/dist/contracts/engine.d.ts +90 -0
  11. package/dist/contracts/engine.d.ts.map +1 -0
  12. package/dist/contracts/engine.js +291 -0
  13. package/dist/contracts/engine.js.map +1 -0
  14. package/dist/contracts/index.d.ts +9 -0
  15. package/dist/contracts/index.d.ts.map +1 -0
  16. package/dist/contracts/index.js +5 -0
  17. package/dist/contracts/index.js.map +1 -0
  18. package/dist/contracts/orphans.d.ts +96 -0
  19. package/dist/contracts/orphans.d.ts.map +1 -0
  20. package/dist/contracts/orphans.js +241 -0
  21. package/dist/contracts/orphans.js.map +1 -0
  22. package/dist/contracts/paths.d.ts +132 -0
  23. package/dist/contracts/paths.d.ts.map +1 -0
  24. package/dist/contracts/paths.js +245 -0
  25. package/dist/contracts/paths.js.map +1 -0
  26. package/dist/contracts/shapes.d.ts +99 -0
  27. package/dist/contracts/shapes.d.ts.map +1 -0
  28. package/dist/contracts/shapes.js +196 -0
  29. package/dist/contracts/shapes.js.map +1 -0
  30. package/dist/governance/budget.d.ts +89 -0
  31. package/dist/governance/budget.d.ts.map +1 -0
  32. package/dist/governance/budget.js +101 -0
  33. package/dist/governance/budget.js.map +1 -0
  34. package/dist/governance/candidate-boundary.d.ts +49 -0
  35. package/dist/governance/candidate-boundary.d.ts.map +1 -0
  36. package/dist/governance/candidate-boundary.js +51 -0
  37. package/dist/governance/candidate-boundary.js.map +1 -0
  38. package/dist/governance/escalation.d.ts +63 -0
  39. package/dist/governance/escalation.d.ts.map +1 -0
  40. package/dist/governance/escalation.js +72 -0
  41. package/dist/governance/escalation.js.map +1 -0
  42. package/dist/governance/fact-boundary.d.ts +77 -0
  43. package/dist/governance/fact-boundary.d.ts.map +1 -0
  44. package/dist/governance/fact-boundary.js +91 -0
  45. package/dist/governance/fact-boundary.js.map +1 -0
  46. package/dist/governance/finding-funnel.d.ts +109 -0
  47. package/dist/governance/finding-funnel.d.ts.map +1 -0
  48. package/dist/governance/finding-funnel.js +137 -0
  49. package/dist/governance/finding-funnel.js.map +1 -0
  50. package/dist/governance/index.d.ts +11 -0
  51. package/dist/governance/index.d.ts.map +1 -0
  52. package/dist/governance/index.js +6 -0
  53. package/dist/governance/index.js.map +1 -0
  54. package/dist/graph/build.d.ts +127 -0
  55. package/dist/graph/build.d.ts.map +1 -0
  56. package/dist/graph/build.js +0 -0
  57. package/dist/graph/build.js.map +1 -0
  58. package/dist/graph/cross-language.d.ts +70 -0
  59. package/dist/graph/cross-language.d.ts.map +1 -0
  60. package/dist/graph/cross-language.js +104 -0
  61. package/dist/graph/cross-language.js.map +1 -0
  62. package/dist/graph/index.d.ts +11 -0
  63. package/dist/graph/index.d.ts.map +1 -0
  64. package/dist/graph/index.js +6 -0
  65. package/dist/graph/index.js.map +1 -0
  66. package/dist/graph/merge.d.ts +95 -0
  67. package/dist/graph/merge.d.ts.map +1 -0
  68. package/dist/graph/merge.js +131 -0
  69. package/dist/graph/merge.js.map +1 -0
  70. package/dist/graph/persist.d.ts +42 -0
  71. package/dist/graph/persist.d.ts.map +1 -0
  72. package/dist/graph/persist.js +52 -0
  73. package/dist/graph/persist.js.map +1 -0
  74. package/dist/graph/runtime-confirmation.d.ts +169 -0
  75. package/dist/graph/runtime-confirmation.d.ts.map +1 -0
  76. package/dist/graph/runtime-confirmation.js +318 -0
  77. package/dist/graph/runtime-confirmation.js.map +1 -0
  78. package/dist/index.d.ts +18 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +19 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/ledger/index.d.ts +5 -0
  83. package/dist/ledger/index.d.ts.map +1 -0
  84. package/dist/ledger/index.js +3 -0
  85. package/dist/ledger/index.js.map +1 -0
  86. package/dist/ledger/mint.d.ts +81 -0
  87. package/dist/ledger/mint.d.ts.map +1 -0
  88. package/dist/ledger/mint.js +138 -0
  89. package/dist/ledger/mint.js.map +1 -0
  90. package/dist/ledger/resolve.d.ts +124 -0
  91. package/dist/ledger/resolve.d.ts.map +1 -0
  92. package/dist/ledger/resolve.js +290 -0
  93. package/dist/ledger/resolve.js.map +1 -0
  94. package/dist/multipr/fingerprint.d.ts +56 -0
  95. package/dist/multipr/fingerprint.d.ts.map +1 -0
  96. package/dist/multipr/fingerprint.js +169 -0
  97. package/dist/multipr/fingerprint.js.map +1 -0
  98. package/dist/multipr/hidden-dependency.d.ts +120 -0
  99. package/dist/multipr/hidden-dependency.d.ts.map +1 -0
  100. package/dist/multipr/hidden-dependency.js +185 -0
  101. package/dist/multipr/hidden-dependency.js.map +1 -0
  102. package/dist/multipr/index.d.ts +14 -0
  103. package/dist/multipr/index.d.ts.map +1 -0
  104. package/dist/multipr/index.js +8 -0
  105. package/dist/multipr/index.js.map +1 -0
  106. package/dist/multipr/mechanical.d.ts +239 -0
  107. package/dist/multipr/mechanical.d.ts.map +1 -0
  108. package/dist/multipr/mechanical.js +333 -0
  109. package/dist/multipr/mechanical.js.map +1 -0
  110. package/dist/multipr/migration-heads.d.ts +160 -0
  111. package/dist/multipr/migration-heads.d.ts.map +1 -0
  112. package/dist/multipr/migration-heads.js +212 -0
  113. package/dist/multipr/migration-heads.js.map +1 -0
  114. package/dist/multipr/overlap.d.ts +186 -0
  115. package/dist/multipr/overlap.d.ts.map +1 -0
  116. package/dist/multipr/overlap.js +254 -0
  117. package/dist/multipr/overlap.js.map +1 -0
  118. package/dist/multipr/scope-store.d.ts +105 -0
  119. package/dist/multipr/scope-store.d.ts.map +1 -0
  120. package/dist/multipr/scope-store.js +182 -0
  121. package/dist/multipr/scope-store.js.map +1 -0
  122. package/dist/multipr/superseded.d.ts +133 -0
  123. package/dist/multipr/superseded.d.ts.map +1 -0
  124. package/dist/multipr/superseded.js +128 -0
  125. package/dist/multipr/superseded.js.map +1 -0
  126. package/dist/query/confirmed-facts.d.ts +56 -0
  127. package/dist/query/confirmed-facts.d.ts.map +1 -0
  128. package/dist/query/confirmed-facts.js +64 -0
  129. package/dist/query/confirmed-facts.js.map +1 -0
  130. package/dist/query/declared-value-closure.d.ts +126 -0
  131. package/dist/query/declared-value-closure.d.ts.map +1 -0
  132. package/dist/query/declared-value-closure.js +156 -0
  133. package/dist/query/declared-value-closure.js.map +1 -0
  134. package/dist/query/index.d.ts +28 -0
  135. package/dist/query/index.d.ts.map +1 -0
  136. package/dist/query/index.js +15 -0
  137. package/dist/query/index.js.map +1 -0
  138. package/dist/query/memory.d.ts +20 -0
  139. package/dist/query/memory.d.ts.map +1 -0
  140. package/dist/query/memory.js +55 -0
  141. package/dist/query/memory.js.map +1 -0
  142. package/dist/query/provider.d.ts +127 -0
  143. package/dist/query/provider.d.ts.map +1 -0
  144. package/dist/query/provider.js +54 -0
  145. package/dist/query/provider.js.map +1 -0
  146. package/dist/query/queries.d.ts +119 -0
  147. package/dist/query/queries.d.ts.map +1 -0
  148. package/dist/query/queries.js +267 -0
  149. package/dist/query/queries.js.map +1 -0
  150. package/dist/query/refusal-fetch.d.ts +114 -0
  151. package/dist/query/refusal-fetch.d.ts.map +1 -0
  152. package/dist/query/refusal-fetch.js +200 -0
  153. package/dist/query/refusal-fetch.js.map +1 -0
  154. package/dist/query/refusal-questions.d.ts +123 -0
  155. package/dist/query/refusal-questions.d.ts.map +1 -0
  156. package/dist/query/refusal-questions.js +122 -0
  157. package/dist/query/refusal-questions.js.map +1 -0
  158. package/dist/query/root-cause-score.d.ts +218 -0
  159. package/dist/query/root-cause-score.d.ts.map +1 -0
  160. package/dist/query/root-cause-score.js +251 -0
  161. package/dist/query/root-cause-score.js.map +1 -0
  162. package/dist/query/row-closure-picture.d.ts +108 -0
  163. package/dist/query/row-closure-picture.d.ts.map +1 -0
  164. package/dist/query/row-closure-picture.js +131 -0
  165. package/dist/query/row-closure-picture.js.map +1 -0
  166. package/dist/query/similar-incidents.d.ts +76 -0
  167. package/dist/query/similar-incidents.d.ts.map +1 -0
  168. package/dist/query/similar-incidents.js +169 -0
  169. package/dist/query/similar-incidents.js.map +1 -0
  170. package/dist/query/sqlite.d.ts +13 -0
  171. package/dist/query/sqlite.d.ts.map +1 -0
  172. package/dist/query/sqlite.js +54 -0
  173. package/dist/query/sqlite.js.map +1 -0
  174. package/dist/query/traverse.d.ts +110 -0
  175. package/dist/query/traverse.d.ts.map +1 -0
  176. package/dist/query/traverse.js +0 -0
  177. package/dist/query/traverse.js.map +1 -0
  178. package/dist/query/unresolved.d.ts +195 -0
  179. package/dist/query/unresolved.d.ts.map +1 -0
  180. package/dist/query/unresolved.js +225 -0
  181. package/dist/query/unresolved.js.map +1 -0
  182. package/dist/query/verification-status.d.ts +134 -0
  183. package/dist/query/verification-status.d.ts.map +1 -0
  184. package/dist/query/verification-status.js +163 -0
  185. package/dist/query/verification-status.js.map +1 -0
  186. package/dist/recording/index.d.ts +26 -0
  187. package/dist/recording/index.d.ts.map +1 -0
  188. package/dist/recording/index.js +30 -0
  189. package/dist/recording/index.js.map +1 -0
  190. package/dist/recording/migrate.d.ts +22 -0
  191. package/dist/recording/migrate.d.ts.map +1 -0
  192. package/dist/recording/migrate.js +51 -0
  193. package/dist/recording/migrate.js.map +1 -0
  194. package/dist/recording/reader.d.ts +27 -0
  195. package/dist/recording/reader.d.ts.map +1 -0
  196. package/dist/recording/reader.js +55 -0
  197. package/dist/recording/reader.js.map +1 -0
  198. package/dist/recording/redact.d.ts +63 -0
  199. package/dist/recording/redact.d.ts.map +1 -0
  200. package/dist/recording/redact.js +98 -0
  201. package/dist/recording/redact.js.map +1 -0
  202. package/dist/recording/schema.d.ts +41 -0
  203. package/dist/recording/schema.d.ts.map +1 -0
  204. package/dist/recording/schema.js +104 -0
  205. package/dist/recording/schema.js.map +1 -0
  206. package/dist/recording/writer.d.ts +120 -0
  207. package/dist/recording/writer.d.ts.map +1 -0
  208. package/dist/recording/writer.js +81 -0
  209. package/dist/recording/writer.js.map +1 -0
  210. package/dist/scoping/fanout.d.ts +86 -0
  211. package/dist/scoping/fanout.d.ts.map +1 -0
  212. package/dist/scoping/fanout.js +95 -0
  213. package/dist/scoping/fanout.js.map +1 -0
  214. package/dist/scoping/index.d.ts +10 -0
  215. package/dist/scoping/index.d.ts.map +1 -0
  216. package/dist/scoping/index.js +6 -0
  217. package/dist/scoping/index.js.map +1 -0
  218. package/dist/scoping/score.d.ts +83 -0
  219. package/dist/scoping/score.d.ts.map +1 -0
  220. package/dist/scoping/score.js +125 -0
  221. package/dist/scoping/score.js.map +1 -0
  222. package/dist/scoping/tiers.d.ts +32 -0
  223. package/dist/scoping/tiers.d.ts.map +1 -0
  224. package/dist/scoping/tiers.js +35 -0
  225. package/dist/scoping/tiers.js.map +1 -0
  226. package/dist/scoping/traverse.d.ts +60 -0
  227. package/dist/scoping/traverse.d.ts.map +1 -0
  228. package/dist/scoping/traverse.js +172 -0
  229. package/dist/scoping/traverse.js.map +1 -0
  230. package/dist/scoping/weights.d.ts +97 -0
  231. package/dist/scoping/weights.d.ts.map +1 -0
  232. package/dist/scoping/weights.js +167 -0
  233. package/dist/scoping/weights.js.map +1 -0
  234. package/dist/sources/git/diff.d.ts +53 -0
  235. package/dist/sources/git/diff.d.ts.map +1 -0
  236. package/dist/sources/git/diff.js +140 -0
  237. package/dist/sources/git/diff.js.map +1 -0
  238. package/dist/sources/git/env.d.ts +2 -0
  239. package/dist/sources/git/env.d.ts.map +1 -0
  240. package/dist/sources/git/env.js +21 -0
  241. package/dist/sources/git/env.js.map +1 -0
  242. package/dist/sources/git/history.d.ts +140 -0
  243. package/dist/sources/git/history.d.ts.map +1 -0
  244. package/dist/sources/git/history.js +314 -0
  245. package/dist/sources/git/history.js.map +1 -0
  246. package/dist/sources/git/index.d.ts +7 -0
  247. package/dist/sources/git/index.d.ts.map +1 -0
  248. package/dist/sources/git/index.js +4 -0
  249. package/dist/sources/git/index.js.map +1 -0
  250. package/dist/sources/git/source.d.ts +95 -0
  251. package/dist/sources/git/source.d.ts.map +1 -0
  252. package/dist/sources/git/source.js +0 -0
  253. package/dist/sources/git/source.js.map +1 -0
  254. package/dist/sources/migrations/dialects.d.ts +59 -0
  255. package/dist/sources/migrations/dialects.d.ts.map +1 -0
  256. package/dist/sources/migrations/dialects.js +84 -0
  257. package/dist/sources/migrations/dialects.js.map +1 -0
  258. package/dist/sources/migrations/index.d.ts +3 -0
  259. package/dist/sources/migrations/index.d.ts.map +1 -0
  260. package/dist/sources/migrations/index.js +2 -0
  261. package/dist/sources/migrations/index.js.map +1 -0
  262. package/dist/sources/migrations/read.d.ts +113 -0
  263. package/dist/sources/migrations/read.d.ts.map +1 -0
  264. package/dist/sources/migrations/read.js +270 -0
  265. package/dist/sources/migrations/read.js.map +1 -0
  266. package/dist/sources/workspace/index.d.ts +3 -0
  267. package/dist/sources/workspace/index.d.ts.map +1 -0
  268. package/dist/sources/workspace/index.js +2 -0
  269. package/dist/sources/workspace/index.js.map +1 -0
  270. package/dist/sources/workspace/workspace.d.ts +64 -0
  271. package/dist/sources/workspace/workspace.d.ts.map +1 -0
  272. package/dist/sources/workspace/workspace.js +294 -0
  273. package/dist/sources/workspace/workspace.js.map +1 -0
  274. package/dist/store/driver/driver.d.ts +82 -0
  275. package/dist/store/driver/driver.d.ts.map +1 -0
  276. package/dist/store/driver/driver.js +31 -0
  277. package/dist/store/driver/driver.js.map +1 -0
  278. package/dist/store/driver/node-sqlite.d.ts +28 -0
  279. package/dist/store/driver/node-sqlite.d.ts.map +1 -0
  280. package/dist/store/driver/node-sqlite.js +188 -0
  281. package/dist/store/driver/node-sqlite.js.map +1 -0
  282. package/dist/store/index.d.ts +33 -0
  283. package/dist/store/index.d.ts.map +1 -0
  284. package/dist/store/index.js +46 -0
  285. package/dist/store/index.js.map +1 -0
  286. package/dist/store/migrate.d.ts +68 -0
  287. package/dist/store/migrate.d.ts.map +1 -0
  288. package/dist/store/migrate.js +140 -0
  289. package/dist/store/migrate.js.map +1 -0
  290. package/dist/store/reader.d.ts +127 -0
  291. package/dist/store/reader.d.ts.map +1 -0
  292. package/dist/store/reader.js +269 -0
  293. package/dist/store/reader.js.map +1 -0
  294. package/dist/store/schema.d.ts +87 -0
  295. package/dist/store/schema.d.ts.map +1 -0
  296. package/dist/store/schema.js +322 -0
  297. package/dist/store/schema.js.map +1 -0
  298. package/dist/store/writer.d.ts +116 -0
  299. package/dist/store/writer.d.ts.map +1 -0
  300. package/dist/store/writer.js +279 -0
  301. package/dist/store/writer.js.map +1 -0
  302. package/dist/tiers/certify.d.ts +375 -0
  303. package/dist/tiers/certify.d.ts.map +1 -0
  304. package/dist/tiers/certify.js +434 -0
  305. package/dist/tiers/certify.js.map +1 -0
  306. package/dist/tiers/index.d.ts +5 -0
  307. package/dist/tiers/index.d.ts.map +1 -0
  308. package/dist/tiers/index.js +3 -0
  309. package/dist/tiers/index.js.map +1 -0
  310. package/dist/tiers/ladder.d.ts +73 -0
  311. package/dist/tiers/ladder.d.ts.map +1 -0
  312. package/dist/tiers/ladder.js +197 -0
  313. package/dist/tiers/ladder.js.map +1 -0
  314. package/dist/validation/config-graph.d.ts +104 -0
  315. package/dist/validation/config-graph.d.ts.map +1 -0
  316. package/dist/validation/config-graph.js +205 -0
  317. package/dist/validation/config-graph.js.map +1 -0
  318. package/dist/validation/env.d.ts +243 -0
  319. package/dist/validation/env.d.ts.map +1 -0
  320. package/dist/validation/env.js +447 -0
  321. package/dist/validation/env.js.map +1 -0
  322. package/dist/validation/index.d.ts +16 -0
  323. package/dist/validation/index.d.ts.map +1 -0
  324. package/dist/validation/index.js +9 -0
  325. package/dist/validation/index.js.map +1 -0
  326. package/dist/validation/join-substitution.d.ts +186 -0
  327. package/dist/validation/join-substitution.d.ts.map +1 -0
  328. package/dist/validation/join-substitution.js +191 -0
  329. package/dist/validation/join-substitution.js.map +1 -0
  330. package/dist/validation/migrations.d.ts +133 -0
  331. package/dist/validation/migrations.d.ts.map +1 -0
  332. package/dist/validation/migrations.js +315 -0
  333. package/dist/validation/migrations.js.map +1 -0
  334. package/dist/validation/nodes.d.ts +32 -0
  335. package/dist/validation/nodes.d.ts.map +1 -0
  336. package/dist/validation/nodes.js +45 -0
  337. package/dist/validation/nodes.js.map +1 -0
  338. package/dist/validation/rollback.d.ts +124 -0
  339. package/dist/validation/rollback.d.ts.map +1 -0
  340. package/dist/validation/rollback.js +302 -0
  341. package/dist/validation/rollback.js.map +1 -0
  342. package/dist/validation/route-drift.d.ts +108 -0
  343. package/dist/validation/route-drift.d.ts.map +1 -0
  344. package/dist/validation/route-drift.js +103 -0
  345. package/dist/validation/route-drift.js.map +1 -0
  346. package/dist/validation/sources.d.ts +153 -0
  347. package/dist/validation/sources.d.ts.map +1 -0
  348. package/dist/validation/sources.js +409 -0
  349. package/dist/validation/sources.js.map +1 -0
  350. package/dist/worker/index.d.ts +5 -0
  351. package/dist/worker/index.d.ts.map +1 -0
  352. package/dist/worker/index.js +3 -0
  353. package/dist/worker/index.js.map +1 -0
  354. package/dist/worker/pool.d.ts +73 -0
  355. package/dist/worker/pool.d.ts.map +1 -0
  356. package/dist/worker/pool.js +203 -0
  357. package/dist/worker/pool.js.map +1 -0
  358. package/dist/worker/protocol.d.ts +91 -0
  359. package/dist/worker/protocol.d.ts.map +1 -0
  360. package/dist/worker/protocol.js +19 -0
  361. package/dist/worker/protocol.js.map +1 -0
  362. package/dist/worker/traversal.worker.d.ts +25 -0
  363. package/dist/worker/traversal.worker.d.ts.map +1 -0
  364. package/dist/worker/traversal.worker.js +124 -0
  365. package/dist/worker/traversal.worker.js.map +1 -0
  366. package/package.json +29 -0
@@ -0,0 +1,9 @@
1
+ export { nearestMiss, sameEndpoint } from "./paths.ts";
2
+ export type { EndpointKey, NearMiss, NearMissKind } from "./paths.ts";
3
+ export { readShape, compareShapes } from "./shapes.ts";
4
+ export type { Shape, ShapeField, ShapeComparison, ShapeDifference, ShapeDifferenceKind, } from "./shapes.ts";
5
+ export { findContractOrphans } from "./orphans.ts";
6
+ export type { ContractFinding, ContractFindingKind, ContractReport, OrphanOptions, } from "./orphans.ts";
7
+ export { checkContracts } from "./engine.ts";
8
+ export type { ContractCheck, ContractOptions, ShapeCheck, ShapeDirection, ShapeMismatch, } from "./engine.ts";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EACV,KAAK,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,aAAa,EACb,eAAe,EACf,UAAU,EACV,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { nearestMiss, sameEndpoint } from "./paths.js";
2
+ export { readShape, compareShapes } from "./shapes.js";
3
+ export { findContractOrphans } from "./orphans.js";
4
+ export { checkContracts } from "./engine.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGvD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAQnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The contract engine's R2 half: who calls what, and who serves it.
3
+ *
4
+ * §13's mechanism is shape comparison at R3. This is the part that works
5
+ * **below** it, and the architecture is explicit that it should exist: at R2,
6
+ * report path-level orphans only, and say so. Splitting the engine this way
7
+ * means half the value lands on graphs no type checker has touched, and the R3
8
+ * half does not hold it hostage.
9
+ *
10
+ * ## Three findings, and the reason there are only three
11
+ *
12
+ * ```
13
+ * API_ROUTE ──SERVES_API──▶ API_ENDPOINT ◀──USES_API── caller
14
+ * ```
15
+ *
16
+ * An `API_ENDPOINT` is the join. Every question here is about which of its two
17
+ * sides is present:
18
+ *
19
+ * | both sides | one side | finding |
20
+ * | --- | --- | --- |
21
+ * | caller + route | — | the contract holds at path level |
22
+ * | — | caller only | **the call reaches nothing that is served** |
23
+ * | — | route only | **nothing in this graph calls the route** |
24
+ *
25
+ * ## The trap, and it is the whole design
26
+ *
27
+ * On two real repositories: **117 callers, 24 routes, 16 joined.** The other 101
28
+ * callers are, overwhelmingly, calls to services that are not in the graph at
29
+ * all — a third-party API, a backend nobody analysed. Reporting them as "no
30
+ * route serves this" would be 101 confident wrong statements that bury the
31
+ * handful that are real.
32
+ *
33
+ * So an unmatched caller is a **finding** only when a served route exists that
34
+ * it nearly matches (see `paths.ts`, where every rule is a specific mechanical
35
+ * mistake rather than a similarity threshold). Everything else is counted and
36
+ * disclosed as *not analysable*, because the honest description of it is "the
37
+ * provider side of this call was never read", not "the provider side is wrong".
38
+ *
39
+ * Two more refusals in the same spirit:
40
+ *
41
+ * - **No routes at all in the graph → no caller findings, at all.** With nothing
42
+ * on the provider side, every caller is unmatched by construction and the
43
+ * comparison has no content. Reported as not analysable, with the reason.
44
+ * - **`ROUTE_NOT_CALLED` is stated weakly on purpose.** A route nothing calls is
45
+ * usually a route called by something outside this graph — a mobile client,
46
+ * another service, curl. It is a lead, not a defect, and the wording says so.
47
+ */
48
+ import type { IRNode, ResolutionLevel } from "@descryy/ir";
49
+ import type { AdjacencyProvider, QueryResult } from "../query/provider.ts";
50
+ import { type NearMiss } from "./paths.ts";
51
+ export type ContractFindingKind = "CALL_NOT_SERVED" | "ROUTE_NOT_CALLED";
52
+ export interface ContractFinding {
53
+ readonly kind: ContractFindingKind;
54
+ readonly endpoint: IRNode;
55
+ readonly method: string;
56
+ readonly pathTemplate: string;
57
+ /** Callers reaching this endpoint. Populated for `CALL_NOT_SERVED`. */
58
+ readonly callers: readonly IRNode[];
59
+ /** Routes serving it. Populated for `ROUTE_NOT_CALLED`. */
60
+ readonly routes: readonly IRNode[];
61
+ /** Why this is reportable rather than merely unmatched. Null for `ROUTE_NOT_CALLED`. */
62
+ readonly nearMiss: NearMiss | null;
63
+ /** The sentence a developer reads. */
64
+ readonly detail: string;
65
+ /** Lowest resolution among the edges this finding rests on. Caps its class. */
66
+ readonly resolution: ResolutionLevel;
67
+ }
68
+ export interface ContractReport {
69
+ readonly findings: readonly ContractFinding[];
70
+ readonly checked: {
71
+ readonly endpoints: number;
72
+ readonly withCallers: number;
73
+ readonly withRoutes: number;
74
+ readonly joined: number;
75
+ };
76
+ /**
77
+ * Unmatched callers with nothing on the provider side that resembles them.
78
+ *
79
+ * Not findings, and the number matters: it is the size of the region this
80
+ * analysis could not speak about.
81
+ */
82
+ readonly unmatchedWithoutNearMiss: number;
83
+ /** True when the R3 shape comparison did not run. Always true in this half. */
84
+ readonly shapesCompared: boolean;
85
+ }
86
+ export interface OrphanOptions {
87
+ /** Cap on endpoints enumerated. Truncation is reported, never silent. */
88
+ readonly limit?: number;
89
+ /**
90
+ * Cap on findings returned — rule 5's "a few high-confidence findings, never
91
+ * an exhaustive maybe-list". Overflow is disclosed, not dropped quietly.
92
+ */
93
+ readonly maxFindings?: number;
94
+ }
95
+ export declare function findContractOrphans(provider: AdjacencyProvider, options?: OrphanOptions): QueryResult<ContractReport>;
96
+ //# sourceMappingURL=orphans.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orphans.d.ts","sourceRoot":"","sources":["../../src/contracts/orphans.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAc,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAiC,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAEzE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IACnC,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;IACF;;;;;OAKG;IACH,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAYD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,aAAkB,GAC1B,WAAW,CAAC,cAAc,CAAC,CA6M7B"}
@@ -0,0 +1,241 @@
1
+ /**
2
+ * The contract engine's R2 half: who calls what, and who serves it.
3
+ *
4
+ * §13's mechanism is shape comparison at R3. This is the part that works
5
+ * **below** it, and the architecture is explicit that it should exist: at R2,
6
+ * report path-level orphans only, and say so. Splitting the engine this way
7
+ * means half the value lands on graphs no type checker has touched, and the R3
8
+ * half does not hold it hostage.
9
+ *
10
+ * ## Three findings, and the reason there are only three
11
+ *
12
+ * ```
13
+ * API_ROUTE ──SERVES_API──▶ API_ENDPOINT ◀──USES_API── caller
14
+ * ```
15
+ *
16
+ * An `API_ENDPOINT` is the join. Every question here is about which of its two
17
+ * sides is present:
18
+ *
19
+ * | both sides | one side | finding |
20
+ * | --- | --- | --- |
21
+ * | caller + route | — | the contract holds at path level |
22
+ * | — | caller only | **the call reaches nothing that is served** |
23
+ * | — | route only | **nothing in this graph calls the route** |
24
+ *
25
+ * ## The trap, and it is the whole design
26
+ *
27
+ * On two real repositories: **117 callers, 24 routes, 16 joined.** The other 101
28
+ * callers are, overwhelmingly, calls to services that are not in the graph at
29
+ * all — a third-party API, a backend nobody analysed. Reporting them as "no
30
+ * route serves this" would be 101 confident wrong statements that bury the
31
+ * handful that are real.
32
+ *
33
+ * So an unmatched caller is a **finding** only when a served route exists that
34
+ * it nearly matches (see `paths.ts`, where every rule is a specific mechanical
35
+ * mistake rather than a similarity threshold). Everything else is counted and
36
+ * disclosed as *not analysable*, because the honest description of it is "the
37
+ * provider side of this call was never read", not "the provider side is wrong".
38
+ *
39
+ * Two more refusals in the same spirit:
40
+ *
41
+ * - **No routes at all in the graph → no caller findings, at all.** With nothing
42
+ * on the provider side, every caller is unmatched by construction and the
43
+ * comparison has no content. Reported as not analysable, with the reason.
44
+ * - **`ROUTE_NOT_CALLED` is stated weakly on purpose.** A route nothing calls is
45
+ * usually a route called by something outside this graph — a mobile client,
46
+ * another service, curl. It is a lead, not a defect, and the wording says so.
47
+ */
48
+ import { nearestMiss } from "./paths.js";
49
+ const DEFAULT_MAX_FINDINGS = 25;
50
+ function keyOf(endpoint) {
51
+ const method = endpoint.attrs["method"];
52
+ const pathTemplate = endpoint.attrs["pathTemplate"];
53
+ return typeof method === "string" && typeof pathTemplate === "string"
54
+ ? { method, pathTemplate }
55
+ : null;
56
+ }
57
+ export function findContractOrphans(provider, options = {}) {
58
+ const maxFindings = options.maxFindings ?? DEFAULT_MAX_FINDINGS;
59
+ const set = provider.nodesOfType("API_ENDPOINT", ...(options.limit === undefined ? [] : [options.limit]));
60
+ const notes = [];
61
+ const sided = [];
62
+ let unkeyed = 0;
63
+ for (const endpoint of set.nodes) {
64
+ const key = keyOf(endpoint);
65
+ if (key === null) {
66
+ // An endpoint with no method or path template cannot be compared to
67
+ // anything. Counted rather than skipped: it means a producer emitted a
68
+ // join node without the two attributes the join is made of.
69
+ unkeyed += 1;
70
+ continue;
71
+ }
72
+ const callers = [];
73
+ const routes = [];
74
+ let resolution = 4;
75
+ for (const edge of provider.incoming(endpoint.id, { types: ["USES_API", "SERVES_API"] })) {
76
+ const other = provider.node(edge.from);
77
+ if (other === undefined)
78
+ continue;
79
+ if (edge.resolution < resolution)
80
+ resolution = edge.resolution;
81
+ (edge.type === "USES_API" ? callers : routes).push(other);
82
+ }
83
+ sided.push({ endpoint, key, callers, routes, resolution });
84
+ }
85
+ const withCallers = sided.filter((s) => s.callers.length > 0);
86
+ const withRoutes = sided.filter((s) => s.routes.length > 0);
87
+ const joined = sided.filter((s) => s.callers.length > 0 && s.routes.length > 0);
88
+ const servedKeys = withRoutes.map((s) => s.key);
89
+ // --- the refusals --------------------------------------------------------
90
+ if (sided.length === 0) {
91
+ notes.push("No API endpoints are in the graph, so there is no contract to check. Either no adapter " +
92
+ "read a route or a client call, or none of the configured adapters produces them. This " +
93
+ "is not a clean result.");
94
+ return report(provider, emptyReport(sided.length, 0, 0, 0), 0, false, notes);
95
+ }
96
+ const findings = [];
97
+ let unmatchedWithoutNearMiss = 0;
98
+ if (withRoutes.length === 0) {
99
+ // Every caller is unmatched by construction. Comparing against an empty
100
+ // provider side produces one finding per call site and means nothing.
101
+ unmatchedWithoutNearMiss = withCallers.length;
102
+ notes.push(`${withCallers.length} endpoint(s) are called and no route is in the graph at all, so no ` +
103
+ "comparison was possible. Every call would be reported as unserved for the same reason, " +
104
+ "which is a property of what was analysed rather than of the code. Add the service that " +
105
+ "serves these routes, or a published contract for it.");
106
+ }
107
+ else {
108
+ for (const entry of withCallers) {
109
+ if (entry.routes.length > 0)
110
+ continue;
111
+ const miss = nearestMiss(entry.key, servedKeys);
112
+ if (miss === null) {
113
+ // Nothing on the provider side resembles it. Far more likely an external
114
+ // service than a defect, and stating otherwise would be 101 confident
115
+ // wrong sentences on the repositories this was measured against.
116
+ unmatchedWithoutNearMiss += 1;
117
+ continue;
118
+ }
119
+ findings.push({
120
+ kind: "CALL_NOT_SERVED",
121
+ endpoint: entry.endpoint,
122
+ method: entry.key.method,
123
+ pathTemplate: entry.key.pathTemplate,
124
+ callers: entry.callers,
125
+ routes: [],
126
+ nearMiss: miss,
127
+ detail: `${entry.key.method} ${entry.key.pathTemplate} is called from ` +
128
+ `${describeCallers(entry.callers)} and no route serves it — but ${miss.detail}`,
129
+ resolution: entry.resolution,
130
+ });
131
+ }
132
+ }
133
+ if (withCallers.length === 0) {
134
+ // The mirror of the refusal above, and it was missing until a per-source
135
+ // ablation on `bench/contracts-real.mjs` removed the consumer side: the
136
+ // engine reported 24 ROUTE_NOT_CALLED findings and no disclosure, about a
137
+ // graph containing no callers at all.
138
+ //
139
+ // "Nothing in this graph calls this route" is literally true there — and so
140
+ // is the sentence this engine declines to say in the other direction.
141
+ // Whichever side is empty, the cause is ONE fact about what was analysed,
142
+ // not N facts about the artefacts. It is not only an ablation state either:
143
+ // a workspace where only the backend has been indexed is the normal first
144
+ // day of a deployment.
145
+ notes.push(`${withRoutes.length} route(s) are served and no call site is in the graph at all, so no ` +
146
+ "comparison was possible. Every route would be reported as uncalled for the same reason, " +
147
+ "which is a property of what was analysed rather than of the code. Add the client that " +
148
+ "calls these routes.");
149
+ }
150
+ else {
151
+ for (const entry of withRoutes) {
152
+ if (entry.callers.length > 0)
153
+ continue;
154
+ findings.push({
155
+ kind: "ROUTE_NOT_CALLED",
156
+ endpoint: entry.endpoint,
157
+ method: entry.key.method,
158
+ pathTemplate: entry.key.pathTemplate,
159
+ callers: [],
160
+ routes: entry.routes,
161
+ nearMiss: null,
162
+ detail: `${entry.key.method} ${entry.key.pathTemplate} is served by ` +
163
+ `${describeRoutes(entry.routes)} and nothing in this graph calls it. That is a lead, ` +
164
+ "not a defect: a mobile client, another service or an external consumer would not " +
165
+ "appear here.",
166
+ resolution: entry.resolution,
167
+ });
168
+ }
169
+ }
170
+ // Findings that rest on better evidence first, then the stronger kind, then by
171
+ // path so the order is identical between runs.
172
+ findings.sort((a, b) => b.resolution - a.resolution ||
173
+ (a.kind === b.kind ? 0 : a.kind === "CALL_NOT_SERVED" ? -1 : 1) ||
174
+ `${a.method} ${a.pathTemplate}`.localeCompare(`${b.method} ${b.pathTemplate}`));
175
+ const kept = findings.slice(0, maxFindings);
176
+ const truncated = set.truncated || kept.length < findings.length;
177
+ if (kept.length < findings.length) {
178
+ notes.push(`${findings.length - kept.length} further finding(s) were withheld: a report is capped at ` +
179
+ `${maxFindings} so that a short list stays worth reading. They were found, not missed — ` +
180
+ 'raise "maxFindings" to see them.');
181
+ }
182
+ if (set.truncated) {
183
+ notes.push("More endpoints exist than were enumerated, so this report covers a prefix of the graph " +
184
+ "rather than all of it. Endpoints beyond the cap were not examined and are not clean.");
185
+ }
186
+ if (unkeyed > 0) {
187
+ notes.push(`${unkeyed} endpoint(s) carry no method or path template and could not be compared to ` +
188
+ "anything. A producer emitted a join node without the two attributes the join is made of.");
189
+ }
190
+ if (unmatchedWithoutNearMiss > 0 && withRoutes.length > 0) {
191
+ notes.push(`${unmatchedWithoutNearMiss} called endpoint(s) have no route in this graph and nothing on ` +
192
+ "the provider side that resembles them. They are not reported as broken: the likeliest " +
193
+ "explanation is a service outside this workspace, and the honest statement is that their " +
194
+ "provider was never read.");
195
+ }
196
+ notes.push("Path level only. Request and response shapes were not compared — that requires R3 on both " +
197
+ "sides, because it compares shapes rather than names.");
198
+ let floor = 4;
199
+ for (const finding of kept)
200
+ if (finding.resolution < floor)
201
+ floor = finding.resolution;
202
+ if (kept.length === 0)
203
+ floor = 2;
204
+ return report(provider, {
205
+ findings: kept,
206
+ checked: {
207
+ endpoints: sided.length,
208
+ withCallers: withCallers.length,
209
+ withRoutes: withRoutes.length,
210
+ joined: joined.length,
211
+ },
212
+ unmatchedWithoutNearMiss,
213
+ shapesCompared: false,
214
+ }, floor, truncated, notes);
215
+ }
216
+ function describeCallers(callers) {
217
+ const first = callers[0];
218
+ if (first === undefined)
219
+ return "an unknown caller";
220
+ const where = first.file === null ? first.name : `${first.name} (${first.file})`;
221
+ return callers.length === 1 ? where : `${where} and ${callers.length - 1} other(s)`;
222
+ }
223
+ function describeRoutes(routes) {
224
+ const first = routes[0];
225
+ if (first === undefined)
226
+ return "an unknown route";
227
+ return routes.length === 1 ? first.name : `${first.name} and ${routes.length - 1} other(s)`;
228
+ }
229
+ function emptyReport(endpoints, withCallers, withRoutes, joined) {
230
+ return {
231
+ findings: [],
232
+ checked: { endpoints, withCallers, withRoutes, joined },
233
+ unmatchedWithoutNearMiss: 0,
234
+ shapesCompared: false,
235
+ };
236
+ }
237
+ function report(provider, data, resolutionFloor, truncated, notes) {
238
+ const stamp = { ...provider.baseStamp(), resolutionFloor };
239
+ return { data, stamp, truncated, notes };
240
+ }
241
+ //# sourceMappingURL=orphans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orphans.js","sourceRoot":"","sources":["../../src/contracts/orphans.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Comparing two endpoint paths, and knowing when not to.
3
+ *
4
+ * This file is small and is the most dangerous part of the contract engine, so
5
+ * it is separate and tested on its own.
6
+ *
7
+ * ## The problem it exists to solve
8
+ *
9
+ * A caller reaching an endpoint no route serves is *sometimes* a bug and
10
+ * *usually* not. Measured on two real repositories: 117 callers, 24 routes, 16
11
+ * joined. The other 101 are overwhelmingly calls to services that are simply not
12
+ * in this graph — a third-party API, a backend nobody has analysed. Reporting
13
+ * all 101 as "no route serves this" would be 101 confident wrong statements, and
14
+ * would bury the handful that are real.
15
+ *
16
+ * So an unmatched caller is only a **finding** when a served route exists that
17
+ * it nearly matches. Nothing similar on the provider side means the provider was
18
+ * probably never analysed, which belongs in §20.2's *not analysable* category
19
+ * and is disclosed as a count rather than enumerated as defects.
20
+ *
21
+ * ## The near-miss rules are deliberately narrow
22
+ *
23
+ * Each one describes a specific, common, *mechanical* mistake. None of them is a
24
+ * similarity score, because a threshold on a score is a knob that turns "is this
25
+ * a bug" into "how high did we set it".
26
+ *
27
+ * | rule | example | why it is a real mistake |
28
+ * | --- | --- | --- |
29
+ * | method differs, path identical | `POST /orders` vs `GET /orders` | someone changed the verb on one side |
30
+ * | exactly one **literal** segment differs, **and the two differ by a typo's worth of characters** | `/order/{param}/items` vs `/orders/{param}/items` | a typo or a rename applied on one side |
31
+ *
32
+ * ## Rule 2's similarity floor, and the measurement that set it
33
+ *
34
+ * That second clause was missing for the whole life of this file, and its
35
+ * absence was not a small thing. "Exactly one segment differs" says nothing at
36
+ * all about *how* it differs: `/api/hmac` and `/api/novu-agent` differ in
37
+ * exactly one segment, share a method and share arity. Precision came only
38
+ * incidentally, from `{param}` segments happening to sit where the paths
39
+ * diverge.
40
+ *
41
+ * `DEC-317` found this from the GraphQL side — every operation path is arity-2
42
+ * with no `{param}` and a first segment drawn from a set of size three, so the
43
+ * whole GraphQL surface of a repository collapsed into three buckets in which
44
+ * every member near-missed every other — and gated it rather than fixing it,
45
+ * because a threshold chosen carelessly costs the near-typo detections this
46
+ * rule exists for, and costs them invisibly.
47
+ *
48
+ * So it was measured, against 3,524 routes from 27 real repositories, by
49
+ * `bench/near-miss-similarity.mjs` — which pre-registers the metric, the
50
+ * candidate thresholds and the decision rule in its header, and was committed
51
+ * before it was run. What it found:
52
+ *
53
+ * - **The rule was not a precision rule.** Treating each real route as an
54
+ * unmatched caller against the rest of its own application's table, it
55
+ * reported a `ONE_SEGMENT` near miss for **1,796 of 3,524 routes — 51%.**
56
+ * Every one is a false positive by construction: both sides are real,
57
+ * deliberately distinct routes of the same service.
58
+ * - **On the genuinely reachable population it was worse.** Across every
59
+ * unmatched `USES_API` caller in the corpus, the rule emitted **18 findings,
60
+ * and all 18 were nonsense** — 15 of them naming the same `/api/novu-agent`
61
+ * route from callers like `/api/hmac` and `/api/send-email`. There were no
62
+ * true positives to lose.
63
+ *
64
+ * The floor is `1 - distance / max(len) >= 0.4`, with any distance of 1 or less
65
+ * admitted outright so that a one-character mistake in a short segment (`/v1`
66
+ * against `/v2`) is not scored away. It rejects **87.4%** of those 1,796 and
67
+ * retains **100%** of a seeded near-typo population of 49,405 — real route
68
+ * segments mutated by transposition, deletion, insertion, substitution,
69
+ * singular/plural and separator variance. Not one seeded typo is lost, in any
70
+ * class.
71
+ *
72
+ * **The distance counts an adjacent transposition as one edit, not two, and
73
+ * that is load-bearing.** With plain Levenshtein a swap costs two, so `api` →
74
+ * `aip` scores `1 - 2/3 = 0.33` and every candidate threshold silently deleted
75
+ * the entire transposition class — 2,916 real detections, all on segments of
76
+ * three characters. The first measurement pass rejected every threshold for
77
+ * exactly this reason, and the reason was a defect in the metric rather than a
78
+ * fact about the corpus.
79
+ *
80
+ * **What the floor is not.** It is a similarity test, not an oracle. `activate`
81
+ * and `deactivate` are two real routes in one API and score 0.80, so they are
82
+ * still reported. Nothing here can distinguish a two-character prefix from a
83
+ * typo, and this does not claim to — it buys precision against *unrelated*
84
+ * words, which is what the corpus says the problem was.
85
+ *
86
+ * **A trailing-slash rule was written and then deleted, and the reason is worth
87
+ * keeping.** `/orders/` and `/orders` look like the classic contract mismatch,
88
+ * but `normaliseEndpointPath` strips the trailing slash *before hashing*, so the
89
+ * two sides mint the **same `API_ENDPOINT` id** and join correctly. The rule
90
+ * could never fire. It was caught by a test that asserted it would.
91
+ *
92
+ * That unification is the right default — the two paths mean the same endpoint
93
+ * to everyone who writes them — but it does make one real class of bug
94
+ * invisible here: a framework configured for strict routing genuinely serves
95
+ * only one of them. Whether it does depends on router configuration this graph
96
+ * does not read, so the limit is stated rather than papered over with a rule
97
+ * that fires on nothing.
98
+ *
99
+ * **Deliberately not a rule: a parameter where the other side has a literal.**
100
+ * `/orders/{param}` and `/orders/recent` legitimately coexist in every routing
101
+ * framework on earth — the literal is matched first and the parameter catches
102
+ * the rest. Treating that as a near miss would report the single most common
103
+ * correct pattern in REST routing as a defect.
104
+ *
105
+ * Segment counts must be equal for the segment rule. `/orders` and
106
+ * `/orders/{param}` are two different endpoints, not a near miss, and a rule
107
+ * that ignored length would pair every collection route with its item route.
108
+ */
109
+ export interface EndpointKey {
110
+ readonly method: string;
111
+ /** Normalised: `:id`, `{id}` and `<int:id>` have all become `{param}`. */
112
+ readonly pathTemplate: string;
113
+ }
114
+ export type NearMissKind = "METHOD" | "ONE_SEGMENT";
115
+ export interface NearMiss {
116
+ readonly kind: NearMissKind;
117
+ /** The served endpoint this one nearly matches. */
118
+ readonly against: EndpointKey;
119
+ /** One sentence naming the exact difference. Goes straight into the report. */
120
+ readonly detail: string;
121
+ }
122
+ export declare function sameEndpoint(a: EndpointKey, b: EndpointKey): boolean;
123
+ /**
124
+ * The closest served endpoint to `wanted`, or null if nothing is close enough.
125
+ *
126
+ * Rules are tried in order of how certain they are, and the first hit wins —
127
+ * a method mismatch on an otherwise identical path is a better explanation than
128
+ * a one-segment difference somewhere else, and reporting both would be reporting
129
+ * one mistake twice.
130
+ */
131
+ export declare function nearestMiss(wanted: EndpointKey, served: readonly EndpointKey[]): NearMiss | null;
132
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/contracts/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2GG;AAkEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAWD,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,SAAS,WAAW,EAAE,GAC7B,QAAQ,GAAG,IAAI,CA0DjB"}