@descryy/core 0.6.3 → 0.8.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 (82) hide show
  1. package/dist/contracts/index.d.ts +3 -1
  2. package/dist/contracts/index.d.ts.map +1 -1
  3. package/dist/contracts/index.js +2 -1
  4. package/dist/contracts/index.js.map +1 -1
  5. package/dist/contracts/paths.d.ts +7 -0
  6. package/dist/contracts/paths.d.ts.map +1 -1
  7. package/dist/contracts/paths.js +7 -4
  8. package/dist/contracts/paths.js.map +1 -1
  9. package/dist/contracts/shapes.d.ts +12 -1
  10. package/dist/contracts/shapes.d.ts.map +1 -1
  11. package/dist/contracts/shapes.js +68 -0
  12. package/dist/contracts/shapes.js.map +1 -1
  13. package/dist/contracts/workspace-join.d.ts +104 -0
  14. package/dist/contracts/workspace-join.d.ts.map +1 -0
  15. package/dist/contracts/workspace-join.js +208 -0
  16. package/dist/contracts/workspace-join.js.map +1 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +4 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/query/index.d.ts +7 -3
  22. package/dist/query/index.d.ts.map +1 -1
  23. package/dist/query/index.js +4 -2
  24. package/dist/query/index.js.map +1 -1
  25. package/dist/query/model-table.d.ts +7 -0
  26. package/dist/query/model-table.d.ts.map +1 -1
  27. package/dist/query/model-table.js +7 -4
  28. package/dist/query/model-table.js.map +1 -1
  29. package/dist/query/persistence-reach.d.ts +150 -0
  30. package/dist/query/persistence-reach.d.ts.map +1 -0
  31. package/dist/query/persistence-reach.js +393 -0
  32. package/dist/query/persistence-reach.js.map +1 -0
  33. package/dist/query/queries.d.ts +5 -0
  34. package/dist/query/queries.d.ts.map +1 -1
  35. package/dist/query/queries.js +58 -0
  36. package/dist/query/queries.js.map +1 -1
  37. package/dist/query/user-chain.d.ts +89 -0
  38. package/dist/query/user-chain.d.ts.map +1 -0
  39. package/dist/query/user-chain.js +203 -0
  40. package/dist/query/user-chain.js.map +1 -0
  41. package/dist/sources/git/history.d.ts +12 -0
  42. package/dist/sources/git/history.d.ts.map +1 -1
  43. package/dist/sources/git/history.js +27 -0
  44. package/dist/sources/git/history.js.map +1 -1
  45. package/dist/sources/git/index.d.ts +1 -1
  46. package/dist/sources/git/index.d.ts.map +1 -1
  47. package/dist/sources/git/index.js +1 -1
  48. package/dist/sources/git/index.js.map +1 -1
  49. package/dist/store/incremental-cache.d.ts +120 -0
  50. package/dist/store/incremental-cache.d.ts.map +1 -0
  51. package/dist/store/incremental-cache.js +161 -0
  52. package/dist/store/incremental-cache.js.map +1 -0
  53. package/dist/store/index.d.ts +2 -0
  54. package/dist/store/index.d.ts.map +1 -1
  55. package/dist/store/index.js +1 -0
  56. package/dist/store/index.js.map +1 -1
  57. package/dist/store/reader.d.ts.map +1 -1
  58. package/dist/store/reader.js +6 -1
  59. package/dist/store/reader.js.map +1 -1
  60. package/dist/store/schema.d.ts +4 -4
  61. package/dist/store/schema.d.ts.map +1 -1
  62. package/dist/store/schema.js +46 -1
  63. package/dist/store/schema.js.map +1 -1
  64. package/dist/store/writer.d.ts.map +1 -1
  65. package/dist/store/writer.js +4 -3
  66. package/dist/store/writer.js.map +1 -1
  67. package/dist/tiers/certify.d.ts +174 -0
  68. package/dist/tiers/certify.d.ts.map +1 -1
  69. package/dist/tiers/certify.js +309 -5
  70. package/dist/tiers/certify.js.map +1 -1
  71. package/dist/tiers/index.d.ts +1 -1
  72. package/dist/tiers/index.d.ts.map +1 -1
  73. package/dist/tiers/index.js.map +1 -1
  74. package/dist/workspace/graph-layout.d.ts +85 -0
  75. package/dist/workspace/graph-layout.d.ts.map +1 -0
  76. package/dist/workspace/graph-layout.js +90 -0
  77. package/dist/workspace/graph-layout.js.map +1 -0
  78. package/dist/workspace/index.d.ts +3 -0
  79. package/dist/workspace/index.d.ts.map +1 -0
  80. package/dist/workspace/index.js +2 -0
  81. package/dist/workspace/index.js.map +1 -0
  82. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- export { nearestMiss, sameEndpoint, templateMatchesPath } from "./paths.ts";
1
+ export { nearestMiss, sameEndpoint, templateMatchesPath, editDistance, nearlyTheSameWord } from "./paths.ts";
2
2
  export type { EndpointKey, NearMiss, NearMissKind } from "./paths.ts";
3
3
  export { readShape, compareShapes } from "./shapes.ts";
4
4
  export type { Shape, ShapeField, ShapeComparison, ShapeDifference, ShapeDifferenceKind, } from "./shapes.ts";
@@ -8,4 +8,6 @@ export { checkObservedRoutes, declaredRoutesFrom } from "./observed-routes.ts";
8
8
  export type { DeclaredRoute, ObservedRouteEvidence, ObservedRouteFinding, ObservedRouteFindingKind, ObservedRouteReport, } from "./observed-routes.ts";
9
9
  export { checkContracts } from "./engine.ts";
10
10
  export type { ContractCheck, ContractOptions, ShapeCheck, ShapeDirection, ShapeMismatch, } from "./engine.ts";
11
+ export { originMatchesRepo, repoOfEndpoint, resolveWorkspaceApiJoins } from "./workspace-join.ts";
12
+ export type { AmbiguousApiJoin, ResolvedApiJoin, WorkspaceApiJoinOptions, WorkspaceApiJoinResult, } from "./workspace-join.ts";
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC5E,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,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,aAAa,EACb,eAAe,EACf,UAAU,EACV,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7G,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,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,aAAa,EACb,eAAe,EACf,UAAU,EACV,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAClG,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC"}
@@ -1,6 +1,7 @@
1
- export { nearestMiss, sameEndpoint, templateMatchesPath } from "./paths.js";
1
+ export { nearestMiss, sameEndpoint, templateMatchesPath, editDistance, nearlyTheSameWord } from "./paths.js";
2
2
  export { readShape, compareShapes } from "./shapes.js";
3
3
  export { findContractOrphans } from "./orphans.js";
4
4
  export { checkObservedRoutes, declaredRoutesFrom } from "./observed-routes.js";
5
5
  export { checkContracts } from "./engine.js";
6
+ export { originMatchesRepo, repoOfEndpoint, resolveWorkspaceApiJoins } from "./workspace-join.js";
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAG5E,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AASnD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAS/E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG7G,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AASnD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAS/E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -3,6 +3,13 @@
3
3
  * differs" alone was 51% false positives on 3,524 routes). Floor `1 - distance/max(len) >= 0.4` (transposition
4
4
  * = 1 edit not 2) rejects 87.4% of those, keeps 100% of a 49,405-case seeded typo set. Not an oracle.
5
5
  */
6
+ /** Damerau-Levenshtein distance. Adjacent transposition costs 1 edit, not 2 — else `api`/`aip` scores 0.33.
7
+ * Exported for `shapes.ts`'s `FIELD_RENAMED` detection, which needs the same name-similarity
8
+ * measure this module already validated against a seeded typo set — not a second, unvalidated one. */
9
+ export declare function editDistance(a: string, b: string): number;
10
+ /** Close enough to be a mistyping, not just a different word? Distance-1 always admitted, so `/v1`/`/v2` isn't scored away.
11
+ * Exported alongside `editDistance` — see that function's comment. */
12
+ export declare function nearlyTheSameWord(a: string, b: string): boolean;
6
13
  export interface EndpointKey {
7
14
  readonly method: string;
8
15
  /** Normalised: `:id`, `{id}` and `<int:id>` have all become `{param}`. */
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/contracts/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA2CH,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,uGAAuG;AACvG,wBAAgB,WAAW,CACzB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,SAAS,WAAW,EAAE,GAC7B,QAAQ,GAAG,IAAI,CA0DjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAKnF"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/contracts/paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH;;uGAEuG;AACvG,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBzD;AAED;uEACuE;AACvE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAI/D;AAED,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,uGAAuG;AACvG,wBAAgB,WAAW,CACzB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,SAAS,WAAW,EAAE,GAC7B,QAAQ,GAAG,IAAI,CA0DjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAKnF"}
@@ -7,8 +7,10 @@
7
7
  const PARAM = "{param}";
8
8
  /** How alike rule 2's two differing segments must be. Measured, not chosen — see module header. */
9
9
  const SEGMENT_SIMILARITY_FLOOR = 0.4;
10
- /** Damerau-Levenshtein distance. Adjacent transposition costs 1 edit, not 2 — else `api`/`aip` scores 0.33. */
11
- function editDistance(a, b) {
10
+ /** Damerau-Levenshtein distance. Adjacent transposition costs 1 edit, not 2 — else `api`/`aip` scores 0.33.
11
+ * Exported for `shapes.ts`'s `FIELD_RENAMED` detection, which needs the same name-similarity
12
+ * measure this module already validated against a seeded typo set — not a second, unvalidated one. */
13
+ export function editDistance(a, b) {
12
14
  if (a === b)
13
15
  return 0;
14
16
  if (a.length === 0)
@@ -34,8 +36,9 @@ function editDistance(a, b) {
34
36
  }
35
37
  return rows[a.length][b.length];
36
38
  }
37
- /** Close enough to be a mistyping, not just a different word? Distance-1 always admitted, so `/v1`/`/v2` isn't scored away. */
38
- function nearlyTheSameWord(a, b) {
39
+ /** Close enough to be a mistyping, not just a different word? Distance-1 always admitted, so `/v1`/`/v2` isn't scored away.
40
+ * Exported alongside `editDistance` — see that function's comment. */
41
+ export function nearlyTheSameWord(a, b) {
39
42
  const distance = editDistance(a, b);
40
43
  if (distance <= 1)
41
44
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/contracts/paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/contracts/paths.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@ export interface Shape {
23
23
  * facts from a type checker/schema) is preferred, `fields` is the name-level fallback.
24
24
  * Anything else is not a shape and is not guessed at. */
25
25
  export declare function readShape(node: IRNode): Shape | null;
26
- export type ShapeDifferenceKind = "FIELD_MISSING" | "TYPE_DIFFERS" | "NULLABILITY_DIFFERS" | "REQUIREDNESS_DIFFERS" | "LIST_DIFFERS";
26
+ export type ShapeDifferenceKind = "FIELD_MISSING" | "TYPE_DIFFERS" | "NULLABILITY_DIFFERS" | "REQUIREDNESS_DIFFERS" | "LIST_DIFFERS" | "FIELD_RENAMED";
27
27
  export interface ShapeDifference {
28
28
  readonly kind: ShapeDifferenceKind;
29
29
  readonly field: string;
@@ -32,6 +32,17 @@ export interface ShapeDifference {
32
32
  /** What the providing side declares. */
33
33
  readonly provided: string;
34
34
  readonly detail: string;
35
+ /** Set only on `FIELD_RENAMED` — the unmatched "want" name, the unmatched "have" name it
36
+ * was paired with, and `nearlyTheSameWord`'s similarity score for that pair (1 -
37
+ * distance/max(len)). Rule 2 (precision over recall): a wrong rename claim corrupts a
38
+ * consumer's understanding worse than two separate FIELD_MISSING/extra-field findings
39
+ * would, so this is reported as a disclosed *lead* — carrying the score that produced it —
40
+ * never silently substituted as fact for the pair it replaces. */
41
+ readonly rename?: {
42
+ readonly from: string;
43
+ readonly to: string;
44
+ readonly similarity: number;
45
+ };
35
46
  }
36
47
  export interface ShapeComparison {
37
48
  readonly differences: readonly ShapeDifference[];
@@ -1 +1 @@
1
- {"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../../src/contracts/shapes.ts"],"names":[],"mappings":"AAAA;;oGAEoG;AAEpG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC;;2GAEuG;IACvG,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,KAAK;IACpB,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;0DAE0D;AAC1D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAUpD;AAoCD,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,cAAc,GACd,qBAAqB,GACrB,sBAAsB,GACtB,cAAc,CAAC;AAEnB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;IACjD;gGAC4F;IAC5F,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED;qDACqD;AACrD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,GAAG,eAAe,CAuF/E"}
1
+ {"version":3,"file":"shapes.d.ts","sourceRoot":"","sources":["../../src/contracts/shapes.ts"],"names":[],"mappings":"AAAA;;oGAEoG;AAEpG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC;;2GAEuG;IACvG,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,KAAK;IACpB,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;0DAE0D;AAC1D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAUpD;AAoCD,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,cAAc,GACd,qBAAqB,GACrB,sBAAsB,GACtB,cAAc,GACd,eAAe,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;uEAKmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;KAC7B,CAAC;CACH;AAUD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;IACjD;gGAC4F;IAC5F,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED;qDACqD;AACrD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,GAAG,eAAe,CA6J/E"}
@@ -1,6 +1,7 @@
1
1
  /** R3 half of §13: comparing two declared shapes, pure over `attrs`, no language named (IR
2
2
  * boundary). Asymmetric by design — a field only in `provided` is fine; one only in `expected`
3
3
  * is broken. `comparedDetail` false means "no detail to compare," never "types agree" (rule 3). */
4
+ import { editDistance, nearlyTheSameWord } from "./paths.js";
4
5
  /** Reads a shape from `attrs`, or null if none declared. Two forms: `fieldDetail` (per-field
5
6
  * facts from a type checker/schema) is preferred, `fields` is the name-level fallback.
6
7
  * Anything else is not a shape and is not guessed at. */
@@ -47,13 +48,80 @@ function readEntries(raw) {
47
48
  }
48
49
  return { fields, detailed };
49
50
  }
51
+ /** How close two unmatched fields' array positions must be to count as "close in array-index
52
+ * position" per the rename rule below, when type alone does not already make the pair unique.
53
+ * 0 means the same index in each side's field list — the tightest defensible reading, since
54
+ * `ShapeField` carries no explicit order/position field (only array index implies one), and a
55
+ * looser tolerance risks pairing two fields that only happen to sit a few slots apart in an
56
+ * otherwise-unrelated schema. */
57
+ const RENAME_POSITION_TOLERANCE = 0;
50
58
  /** What the consumer expects that the provider doesn't deliver. `expected`: what the consumer
51
59
  * reads. `provided`: what the producer declares. */
52
60
  export function compareShapes(expected, provided) {
53
61
  const byName = new Map(provided.fields.map((f) => [f.name, f]));
62
+ const wantByName = new Map(expected.fields.map((f) => [f.name, f]));
54
63
  const comparedDetail = expected.detailed && provided.detailed;
55
64
  const differences = [];
65
+ // Rename detection runs once, up front, over every field unmatched by exact name on
66
+ // *both* sides — before the per-`want`-field loop below, so a rename is reported as one
67
+ // finding rather than as an independent FIELD_MISSING (the want side) plus a silently
68
+ // dropped extra field (the have side). Gated on `comparedDetail`: a name-only shape has no
69
+ // type/nullability to match a candidate on, and name-similarity alone is far weaker
70
+ // evidence — matching the same gate every other structural check below already uses.
71
+ const renameByWant = new Map();
72
+ if (comparedDetail) {
73
+ const unmatchedWant = expected.fields.filter((w) => !byName.has(w.name));
74
+ const unmatchedHave = provided.fields.filter((h) => !wantByName.has(h.name));
75
+ for (const want of unmatchedWant) {
76
+ // No declared type on the "want" side gives nothing to match a candidate against —
77
+ // name similarity alone is not enough (rule 2).
78
+ if (want.type === undefined)
79
+ continue;
80
+ const sameTypeHave = unmatchedHave.filter((h) => h.type === want.type &&
81
+ (want.nullable === undefined || h.nullable === undefined || h.nullable === want.nullable));
82
+ if (sameTypeHave.length === 0)
83
+ continue;
84
+ const sameTypeWant = unmatchedWant.filter((w) => w.type === want.type);
85
+ let candidate;
86
+ if (sameTypeHave.length === 1 && sameTypeWant.length === 1) {
87
+ // The only unmatched field of this type on each side — unambiguous.
88
+ candidate = sameTypeHave[0];
89
+ }
90
+ else {
91
+ // More than one same-type candidate: fall back to array position. Only a field
92
+ // sitting at the *same* index (see `RENAME_POSITION_TOLERANCE`) counts, and only
93
+ // when exactly one same-type candidate on each side sits there — otherwise which
94
+ // field renamed to which is a guess, not a lead, and none is reported (rule 2).
95
+ const wantIndex = expected.fields.indexOf(want);
96
+ const positional = sameTypeHave.filter((h) => Math.abs(provided.fields.indexOf(h) - wantIndex) <= RENAME_POSITION_TOLERANCE);
97
+ const positionalWant = sameTypeWant.filter((w) => Math.abs(expected.fields.indexOf(w) - wantIndex) <= RENAME_POSITION_TOLERANCE);
98
+ if (positional.length === 1 && positionalWant.length === 1)
99
+ candidate = positional[0];
100
+ }
101
+ if (candidate === undefined)
102
+ continue;
103
+ if (!nearlyTheSameWord(want.name, candidate.name))
104
+ continue;
105
+ const distance = editDistance(want.name, candidate.name);
106
+ const similarity = 1 - distance / Math.max(want.name.length, candidate.name.length);
107
+ renameByWant.set(want.name, { have: candidate, similarity });
108
+ }
109
+ }
56
110
  for (const want of expected.fields) {
111
+ const renamed = renameByWant.get(want.name);
112
+ if (renamed !== undefined) {
113
+ differences.push({
114
+ kind: "FIELD_RENAMED",
115
+ field: want.name,
116
+ expected: want.name,
117
+ provided: renamed.have.name,
118
+ detail: `"${want.name}" is not declared by ${provided.source.name}, which instead declares ` +
119
+ `"${renamed.have.name}" of the same type — likely a rename, not confirmed ` +
120
+ `(name similarity ${renamed.similarity.toFixed(2)}).`,
121
+ rename: { from: want.name, to: renamed.have.name, similarity: renamed.similarity },
122
+ });
123
+ continue;
124
+ }
57
125
  const have = byName.get(want.name);
58
126
  if (have === undefined) {
59
127
  differences.push({
@@ -1 +1 @@
1
- {"version":3,"file":"shapes.js","sourceRoot":"","sources":["../../src/contracts/shapes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shapes.js","sourceRoot":"","sources":["../../src/contracts/shapes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Frontend-to-backend `API_ENDPOINT` join, as a separate resolution step —
3
+ * Gap 10 of `documents/plans/site-claims-closure-production-plan.md`, built
4
+ * on the ruling in
5
+ * `documents/decisions-inbox/DEC-NEXT-endpoint-identity-repo-scoping.md`.
6
+ *
7
+ * `packages/ir/src/identity.ts`'s `NODE_SCOPE` now folds `repo` into every
8
+ * `API_ENDPOINT` id unconditionally (flipped from `"workspace"` to
9
+ * `"repo"`), so a frontend's caller-side endpoint (minted by
10
+ * `adapter-typescript`'s `extractApiUses`, using the frontend's OWN repo —
11
+ * this repo cannot change that, it lives in `descry-adapters`) and a real
12
+ * backend's `API_ENDPOINT` node (`SERVES_API`-reachable from an
13
+ * `API_ROUTE`, minted with the backend's own repo) no longer share an id
14
+ * just because method+path agree. That is the defect fixed, not a
15
+ * regression — two backends in one workspace serving the identical path
16
+ * used to collide onto one node with no way to tell that apart from a real
17
+ * shared contract.
18
+ *
19
+ * What used to be free — identity equality did the join, silently, whether
20
+ * or not it was correct — now needs an actual resolution pass, run once per
21
+ * workspace build (`packages/mcp/src/workspace-index.ts`), before the
22
+ * merged graph is queried by `contracts/orphans.ts` or anything else:
23
+ *
24
+ * - Exactly one backend in the workspace serves a caller's method+path →
25
+ * resolved directly: every `USES_API` edge that targeted the caller's own
26
+ * placeholder endpoint is rewritten to target the real served node
27
+ * instead. From there down the graph looks exactly as it did before this
28
+ * change (one `API_ENDPOINT` node, both `USES_API` and `SERVES_API`
29
+ * incoming) — the single-backend case is unchanged by design, and
30
+ * `contracts/orphans.ts` needs no changes to keep working.
31
+ * - More than one backend serves the same method+path → resolved via a
32
+ * caller-side hint (`USES_API.attrs.origin`, written by
33
+ * `extractApiUses`) matched against each candidate's own repo name. This
34
+ * is a best-effort proxy, not a real service-discovery signal — no
35
+ * adapter records a backend's own listening origin/base URL anywhere in
36
+ * the graph today (checked: `API_ROUTE`/`API_ENDPOINT` nodes carry only
37
+ * `method`/`pathTemplate`). Disclosed as exactly that, not silently
38
+ * trusted: `originMatchesRepo`'s default only fires on an exact,
39
+ * normalised hostname match, never a substring — a substring match would
40
+ * trade a disclosed ambiguity for a silently wrong single pick, which
41
+ * rule 2 forbids.
42
+ * - No hint resolves, or more than one candidate still matches → never
43
+ * silently picked. Reported as `AmbiguousApiJoin`, "N services expose
44
+ * this path" — the same precision-first pattern `nearestMiss()` already
45
+ * uses for a near-miss caller (`paths.ts`).
46
+ *
47
+ * Caller-only endpoints matching zero served endpoints are left untouched —
48
+ * `contracts/orphans.ts`'s own near-miss/unmatched handling already covers
49
+ * that case on the merged graph, unchanged by this pass.
50
+ *
51
+ * Never deletes a node: an unresolved or ambiguous placeholder stays in the
52
+ * graph exactly as the adapter minted it (rule 7 — a fallback is disclosed,
53
+ * never silently dropped).
54
+ */
55
+ import type { IRBatch, IREdge, IRNode } from "@descryy/ir";
56
+ import type { EndpointKey } from "./paths.ts";
57
+ export interface ResolvedApiJoin {
58
+ readonly callerEndpointId: string;
59
+ readonly servedEndpointId: string;
60
+ readonly key: EndpointKey;
61
+ readonly via: "single-candidate" | "caller-hint";
62
+ /** How many `USES_API` edges were rewritten from the placeholder onto the real node. */
63
+ readonly rewrittenEdges: number;
64
+ }
65
+ export interface AmbiguousApiJoin {
66
+ readonly callerEndpointId: string;
67
+ readonly key: EndpointKey;
68
+ readonly candidateServedEndpointIds: readonly string[];
69
+ /** The repo each candidate was minted by, `"unknown"` when `repoOf` had no entry for it. */
70
+ readonly candidateRepos: readonly string[];
71
+ readonly detail: string;
72
+ }
73
+ export interface WorkspaceApiJoinResult {
74
+ /** The input edges, with every resolved `USES_API` edge's `to` rewritten
75
+ * onto the real served endpoint. Every other edge is returned unchanged. */
76
+ readonly edges: readonly IREdge[];
77
+ readonly resolved: readonly ResolvedApiJoin[];
78
+ readonly ambiguous: readonly AmbiguousApiJoin[];
79
+ /** Caller-only endpoints whose key matched zero served endpoints. */
80
+ readonly unmatched: number;
81
+ /** Caller-only endpoints missing `method`/`pathTemplate` attrs entirely. */
82
+ readonly unkeyed: number;
83
+ }
84
+ /** Best-effort hostname-vs-repo match. See this module's header for why it
85
+ * is a proxy, not a real signal, and why it only fires on an exact,
86
+ * normalised match rather than a substring one. Exported so a caller with a
87
+ * stronger real signal (e.g. once a backend's own base URL is captured
88
+ * somewhere in the graph) can inject its own matcher instead. */
89
+ export declare function originMatchesRepo(origin: string, repo: string): boolean;
90
+ /** Which repo minted each `API_ENDPOINT` node — read directly off each
91
+ * batch's own (pre-merge) nodes. An adapter's own emitted `id` already
92
+ * equals the merged graph's id for anything that survived merging
93
+ * unmodified (id-preserving merge, DEC-012), so this needs no access to the
94
+ * built graph itself. */
95
+ export declare function repoOfEndpoint(batches: readonly IRBatch[]): ReadonlyMap<string, string>;
96
+ export interface WorkspaceApiJoinOptions {
97
+ /** Overrides the default `originMatchesRepo` hint matcher. */
98
+ readonly matchHint?: (origin: string, repo: string) => boolean;
99
+ }
100
+ /** Resolve every caller-only `API_ENDPOINT` placeholder in a merged graph
101
+ * against the real served endpoints in the same workspace. Pure: no store,
102
+ * no IO, same posture as `buildGraph` itself. */
103
+ export declare function resolveWorkspaceApiJoins(nodes: readonly IRNode[], edges: readonly IREdge[], repoOf: ReadonlyMap<string, string>, options?: WorkspaceApiJoinOptions): WorkspaceApiJoinResult;
104
+ //# sourceMappingURL=workspace-join.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-join.d.ts","sourceRoot":"","sources":["../../src/contracts/workspace-join.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAc9C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,aAAa,CAAC;IACjD,wFAAwF;IACxF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,0BAA0B,EAAE,SAAS,MAAM,EAAE,CAAC;IACvD,4FAA4F;IAC5F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC;iFAC6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAChD,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;kEAIkE;AAClE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASvE;AAED;;;;0BAI0B;AAC1B,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAQvF;AAED,MAAM,WAAW,uBAAuB;IACtC,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAChE;AAED;;kDAEkD;AAClD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CA8GxB"}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Frontend-to-backend `API_ENDPOINT` join, as a separate resolution step —
3
+ * Gap 10 of `documents/plans/site-claims-closure-production-plan.md`, built
4
+ * on the ruling in
5
+ * `documents/decisions-inbox/DEC-NEXT-endpoint-identity-repo-scoping.md`.
6
+ *
7
+ * `packages/ir/src/identity.ts`'s `NODE_SCOPE` now folds `repo` into every
8
+ * `API_ENDPOINT` id unconditionally (flipped from `"workspace"` to
9
+ * `"repo"`), so a frontend's caller-side endpoint (minted by
10
+ * `adapter-typescript`'s `extractApiUses`, using the frontend's OWN repo —
11
+ * this repo cannot change that, it lives in `descry-adapters`) and a real
12
+ * backend's `API_ENDPOINT` node (`SERVES_API`-reachable from an
13
+ * `API_ROUTE`, minted with the backend's own repo) no longer share an id
14
+ * just because method+path agree. That is the defect fixed, not a
15
+ * regression — two backends in one workspace serving the identical path
16
+ * used to collide onto one node with no way to tell that apart from a real
17
+ * shared contract.
18
+ *
19
+ * What used to be free — identity equality did the join, silently, whether
20
+ * or not it was correct — now needs an actual resolution pass, run once per
21
+ * workspace build (`packages/mcp/src/workspace-index.ts`), before the
22
+ * merged graph is queried by `contracts/orphans.ts` or anything else:
23
+ *
24
+ * - Exactly one backend in the workspace serves a caller's method+path →
25
+ * resolved directly: every `USES_API` edge that targeted the caller's own
26
+ * placeholder endpoint is rewritten to target the real served node
27
+ * instead. From there down the graph looks exactly as it did before this
28
+ * change (one `API_ENDPOINT` node, both `USES_API` and `SERVES_API`
29
+ * incoming) — the single-backend case is unchanged by design, and
30
+ * `contracts/orphans.ts` needs no changes to keep working.
31
+ * - More than one backend serves the same method+path → resolved via a
32
+ * caller-side hint (`USES_API.attrs.origin`, written by
33
+ * `extractApiUses`) matched against each candidate's own repo name. This
34
+ * is a best-effort proxy, not a real service-discovery signal — no
35
+ * adapter records a backend's own listening origin/base URL anywhere in
36
+ * the graph today (checked: `API_ROUTE`/`API_ENDPOINT` nodes carry only
37
+ * `method`/`pathTemplate`). Disclosed as exactly that, not silently
38
+ * trusted: `originMatchesRepo`'s default only fires on an exact,
39
+ * normalised hostname match, never a substring — a substring match would
40
+ * trade a disclosed ambiguity for a silently wrong single pick, which
41
+ * rule 2 forbids.
42
+ * - No hint resolves, or more than one candidate still matches → never
43
+ * silently picked. Reported as `AmbiguousApiJoin`, "N services expose
44
+ * this path" — the same precision-first pattern `nearestMiss()` already
45
+ * uses for a near-miss caller (`paths.ts`).
46
+ *
47
+ * Caller-only endpoints matching zero served endpoints are left untouched —
48
+ * `contracts/orphans.ts`'s own near-miss/unmatched handling already covers
49
+ * that case on the merged graph, unchanged by this pass.
50
+ *
51
+ * Never deletes a node: an unresolved or ambiguous placeholder stays in the
52
+ * graph exactly as the adapter minted it (rule 7 — a fallback is disclosed,
53
+ * never silently dropped).
54
+ */
55
+ function keyOf(endpoint) {
56
+ const method = endpoint.attrs["method"];
57
+ const pathTemplate = endpoint.attrs["pathTemplate"];
58
+ return typeof method === "string" && typeof pathTemplate === "string"
59
+ ? { method, pathTemplate }
60
+ : null;
61
+ }
62
+ function keyString(key) {
63
+ return `${key.method} ${key.pathTemplate}`;
64
+ }
65
+ /** Best-effort hostname-vs-repo match. See this module's header for why it
66
+ * is a proxy, not a real signal, and why it only fires on an exact,
67
+ * normalised match rather than a substring one. Exported so a caller with a
68
+ * stronger real signal (e.g. once a backend's own base URL is captured
69
+ * somewhere in the graph) can inject its own matcher instead. */
70
+ export function originMatchesRepo(origin, repo) {
71
+ const withoutScheme = origin.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "");
72
+ const host = withoutScheme.split(/[/:]/, 1)[0] ?? "";
73
+ const firstLabel = host.split(".")[0] ?? "";
74
+ const repoLastSegment = repo.split("/").pop() ?? repo;
75
+ const normalise = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
76
+ const normalisedHost = normalise(firstLabel);
77
+ const normalisedRepo = normalise(repoLastSegment);
78
+ return normalisedHost !== "" && normalisedHost === normalisedRepo;
79
+ }
80
+ /** Which repo minted each `API_ENDPOINT` node — read directly off each
81
+ * batch's own (pre-merge) nodes. An adapter's own emitted `id` already
82
+ * equals the merged graph's id for anything that survived merging
83
+ * unmodified (id-preserving merge, DEC-012), so this needs no access to the
84
+ * built graph itself. */
85
+ export function repoOfEndpoint(batches) {
86
+ const map = new Map();
87
+ for (const batch of batches) {
88
+ for (const node of batch.nodes) {
89
+ if (node.type === "API_ENDPOINT")
90
+ map.set(node.id, batch.repo);
91
+ }
92
+ }
93
+ return map;
94
+ }
95
+ /** Resolve every caller-only `API_ENDPOINT` placeholder in a merged graph
96
+ * against the real served endpoints in the same workspace. Pure: no store,
97
+ * no IO, same posture as `buildGraph` itself. */
98
+ export function resolveWorkspaceApiJoins(nodes, edges, repoOf, options = {}) {
99
+ const matchHint = options.matchHint ?? originMatchesRepo;
100
+ const servedIds = new Set();
101
+ const calledIds = new Set();
102
+ for (const edge of edges) {
103
+ if (edge.type === "SERVES_API")
104
+ servedIds.add(edge.to);
105
+ else if (edge.type === "USES_API")
106
+ calledIds.add(edge.to);
107
+ }
108
+ const endpointsById = new Map();
109
+ for (const node of nodes)
110
+ if (node.type === "API_ENDPOINT")
111
+ endpointsById.set(node.id, node);
112
+ const servedByKey = new Map();
113
+ for (const id of servedIds) {
114
+ const node = endpointsById.get(id);
115
+ if (node === undefined)
116
+ continue;
117
+ const key = keyOf(node);
118
+ if (key === null)
119
+ continue;
120
+ const list = servedByKey.get(keyString(key));
121
+ if (list === undefined)
122
+ servedByKey.set(keyString(key), [node]);
123
+ else
124
+ list.push(node);
125
+ }
126
+ const callerOnlyIds = [...calledIds].filter((id) => !servedIds.has(id));
127
+ const resolved = [];
128
+ const ambiguous = [];
129
+ let unmatched = 0;
130
+ let unkeyed = 0;
131
+ const rewrite = new Map();
132
+ for (const callerId of callerOnlyIds) {
133
+ const callerNode = endpointsById.get(callerId);
134
+ if (callerNode === undefined)
135
+ continue;
136
+ const key = keyOf(callerNode);
137
+ if (key === null) {
138
+ unkeyed += 1;
139
+ continue;
140
+ }
141
+ const candidates = servedByKey.get(keyString(key)) ?? [];
142
+ if (candidates.length === 0) {
143
+ unmatched += 1;
144
+ continue;
145
+ }
146
+ if (candidates.length === 1) {
147
+ rewrite.set(callerId, candidates[0].id);
148
+ resolved.push({
149
+ callerEndpointId: callerId,
150
+ servedEndpointId: candidates[0].id,
151
+ key,
152
+ via: "single-candidate",
153
+ rewrittenEdges: 0,
154
+ });
155
+ continue;
156
+ }
157
+ // Multiple backends serve this exact method+path. Try the caller-side
158
+ // hint on every USES_API edge that targets this placeholder.
159
+ const callerEdges = edges.filter((e) => e.type === "USES_API" && e.to === callerId);
160
+ const hintMatches = new Set();
161
+ for (const candidate of candidates) {
162
+ const repo = repoOf.get(candidate.id);
163
+ if (repo === undefined)
164
+ continue;
165
+ const matchesAnyEdge = callerEdges.some((e) => {
166
+ const origin = e.attrs?.["origin"];
167
+ return typeof origin === "string" && origin !== "" && matchHint(origin, repo);
168
+ });
169
+ if (matchesAnyEdge)
170
+ hintMatches.add(candidate);
171
+ }
172
+ if (hintMatches.size === 1) {
173
+ const [only] = hintMatches;
174
+ rewrite.set(callerId, only.id);
175
+ resolved.push({
176
+ callerEndpointId: callerId,
177
+ servedEndpointId: only.id,
178
+ key,
179
+ via: "caller-hint",
180
+ rewrittenEdges: 0,
181
+ });
182
+ continue;
183
+ }
184
+ ambiguous.push({
185
+ callerEndpointId: callerId,
186
+ key,
187
+ candidateServedEndpointIds: candidates.map((c) => c.id),
188
+ candidateRepos: candidates.map((c) => repoOf.get(c.id) ?? "unknown"),
189
+ detail: `${candidates.length} services in this workspace expose ${key.method} ${key.pathTemplate}, and ` +
190
+ (hintMatches.size === 0
191
+ ? "no caller-reported origin matched any of them"
192
+ : `${hintMatches.size} matched, not exactly one`) +
193
+ " — reported as ambiguous rather than silently picking one.",
194
+ });
195
+ }
196
+ const rewrittenEdges = edges.map((edge) => {
197
+ if (edge.type !== "USES_API")
198
+ return edge;
199
+ const target = rewrite.get(edge.to);
200
+ return target === undefined ? edge : { ...edge, to: target };
201
+ });
202
+ const finalResolved = resolved.map((r) => ({
203
+ ...r,
204
+ rewrittenEdges: edges.filter((e) => e.type === "USES_API" && e.to === r.callerEndpointId).length,
205
+ }));
206
+ return { edges: rewrittenEdges, resolved: finalResolved, ambiguous, unmatched, unkeyed };
207
+ }
208
+ //# sourceMappingURL=workspace-join.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-join.js","sourceRoot":"","sources":["../../src/contracts/workspace-join.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from "./sources/git/index.ts";
7
7
  export * from "./sources/migrations/index.ts";
8
8
  export * from "./sources/workspace/index.ts";
9
9
  export * from "./sources/confirmed/index.ts";
10
+ export * from "./workspace/index.ts";
10
11
  export * from "./scoping/index.ts";
11
12
  export * from "./contracts/index.ts";
12
13
  export * from "./capabilities/index.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAI7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -8,6 +8,10 @@ export * from "./sources/git/index.js";
8
8
  export * from "./sources/migrations/index.js";
9
9
  export * from "./sources/workspace/index.js";
10
10
  export * from "./sources/confirmed/index.js";
11
+ // Gap 1 (site-claims-closure-production-plan.md): the shared-graph layout for
12
+ // a workspace's own store, distinct from `./sources/workspace/` above, which
13
+ // is the declared-pairing reader (`descry.json`) and shares no names with it.
14
+ export * from "./workspace/index.js";
11
15
  export * from "./scoping/index.js";
12
16
  export * from "./contracts/index.js";
13
17
  export * from "./capabilities/index.js";