@cueloop/schema 0.1.0-alpha.65 → 0.1.0-alpha.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cueloop/schema",
3
- "version": "0.1.0-alpha.65",
3
+ "version": "0.1.0-alpha.67",
4
4
  "description": "The ReviewSession primitive: types, markdown block model, quote-primary anchors, feedback serialization",
5
5
  "homepage": "https://github.com/mmurakaru/cueloop#readme",
6
6
  "bugs": {
package/src/fuzzy.test.ts CHANGED
@@ -89,4 +89,22 @@ describe("fuzzyFindBestMatch", () => {
89
89
  // Assert
90
90
  expect(match.start).toBe(0);
91
91
  });
92
+
93
+ test("a long quote fuzzed over a long document bails within its work budget", () => {
94
+ // Arrange - a stale multi-line anchor whose text is gone, over a large document; the naive
95
+ // scan is O(haystack * needle^3) and would pin the CPU for a minute
96
+ const needle = Array.from({ length: 200 }, (_unused, index) => `token-${index}`).join(" ");
97
+ const haystack = Array.from(
98
+ { length: 400 },
99
+ (_unused, index) => `line ${index} of the diff`,
100
+ ).join("\n");
101
+
102
+ // Act
103
+ const started = Date.now();
104
+ const match = fuzzyFindBestMatch(needle, haystack, 0.75);
105
+
106
+ // Assert - it returns (here: no match clears the bar) fast, never spinning
107
+ expect(match).toBeNull();
108
+ expect(Date.now() - started).toBeLessThan(2000);
109
+ });
92
110
  });
package/src/fuzzy.ts CHANGED
@@ -62,11 +62,21 @@ export function similarityRatio(left: string, right: string): number {
62
62
  return 1 - levenshteinDistance(left, right) / longestLength;
63
63
  }
64
64
 
65
+ /**
66
+ * Character comparisons this scan may spend before it gives up and returns the
67
+ * best window found so far (or null). The scan is O(haystack * needle^3) - a
68
+ * long quote fuzzed over a long document (a stale multi-block anchor whose text
69
+ * is gone) would otherwise pin the CPU for a minute; a passage that is truly gone
70
+ * is better left orphaned than fuzzed. Small everyday quotes never approach it.
71
+ */
72
+ const FUZZY_WORK_BUDGET = 150_000_000;
73
+
65
74
  /**
66
75
  * Find the haystack window most similar to the needle, or null when nothing
67
76
  * clears `minimumSimilarity`. Windows range around the needle length so the
68
77
  * match tolerates a few inserted or deleted characters, not only substitutions.
69
- * Ties keep the earliest window. Offsets index the haystack directly.
78
+ * Ties keep the earliest window. Offsets index the haystack directly. The scan
79
+ * stops once it has spent `FUZZY_WORK_BUDGET`, returning the best window so far.
70
80
  */
71
81
  export function fuzzyFindBestMatch(
72
82
  needle: string,
@@ -80,6 +90,7 @@ export function fuzzyFindBestMatch(
80
90
  const maximumWindowLength = needle.length + lengthTolerance;
81
91
 
82
92
  let bestMatch: FuzzyMatch | null = null;
93
+ let remainingWork = FUZZY_WORK_BUDGET;
83
94
 
84
95
  for (let start = 0; start < haystack.length; start++) {
85
96
  for (
@@ -87,6 +98,9 @@ export function fuzzyFindBestMatch(
87
98
  windowLength <= maximumWindowLength && start + windowLength <= haystack.length;
88
99
  windowLength++
89
100
  ) {
101
+ // each window costs a needle x window edit-distance pass; spend that from the budget
102
+ remainingWork -= needle.length * windowLength;
103
+ if (remainingWork <= 0) return bestMatch;
90
104
  const window = haystack.slice(start, start + windowLength);
91
105
  const similarity = similarityRatio(needle, window);
92
106
 
package/src/types.ts CHANGED
@@ -12,6 +12,10 @@ export const SCHEMA_VERSION = "1";
12
12
  export interface WorkspaceKey {
13
13
  repoRoot: string;
14
14
  branch: string;
15
+ /** Earliest root commit SHA; the project key that survives moving or re-cloning the repo. Absent for a standalone thread. */
16
+ rootCommit?: string;
17
+ /** Origin remote URL when present; for display and repair only, never the project key. */
18
+ remote?: string;
15
19
  }
16
20
 
17
21
  /**