@concordance-wiki/inference 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 (139) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +15 -0
  3. package/dist/combine/confidence.d.ts +16 -0
  4. package/dist/combine/confidence.d.ts.map +1 -0
  5. package/dist/combine/confidence.js +24 -0
  6. package/dist/combine/confidence.js.map +1 -0
  7. package/dist/combine/links.d.ts +15 -0
  8. package/dist/combine/links.d.ts.map +1 -0
  9. package/dist/combine/links.js +0 -0
  10. package/dist/combine/links.js.map +1 -0
  11. package/dist/display/neighbourhood.d.ts +15 -0
  12. package/dist/display/neighbourhood.d.ts.map +1 -0
  13. package/dist/display/neighbourhood.js +83 -0
  14. package/dist/display/neighbourhood.js.map +1 -0
  15. package/dist/display/options.d.ts +5 -0
  16. package/dist/display/options.d.ts.map +1 -0
  17. package/dist/display/options.js +6 -0
  18. package/dist/display/options.js.map +1 -0
  19. package/dist/display/serialize.d.ts +7 -0
  20. package/dist/display/serialize.d.ts.map +1 -0
  21. package/dist/display/serialize.js +13 -0
  22. package/dist/display/serialize.js.map +1 -0
  23. package/dist/display/types.d.ts +44 -0
  24. package/dist/display/types.d.ts.map +1 -0
  25. package/dist/display/types.js +2 -0
  26. package/dist/display/types.js.map +1 -0
  27. package/dist/duplicates/content.d.ts +29 -0
  28. package/dist/duplicates/content.d.ts.map +1 -0
  29. package/dist/duplicates/content.js +42 -0
  30. package/dist/duplicates/content.js.map +1 -0
  31. package/dist/duplicates/lsh.d.ts +8 -0
  32. package/dist/duplicates/lsh.d.ts.map +1 -0
  33. package/dist/duplicates/lsh.js +38 -0
  34. package/dist/duplicates/lsh.js.map +1 -0
  35. package/dist/duplicates/minhash.d.ts +11 -0
  36. package/dist/duplicates/minhash.d.ts.map +1 -0
  37. package/dist/duplicates/minhash.js +51 -0
  38. package/dist/duplicates/minhash.js.map +1 -0
  39. package/dist/duplicates/options.d.ts +6 -0
  40. package/dist/duplicates/options.d.ts.map +1 -0
  41. package/dist/duplicates/options.js +23 -0
  42. package/dist/duplicates/options.js.map +1 -0
  43. package/dist/duplicates/resolve.d.ts +13 -0
  44. package/dist/duplicates/resolve.d.ts.map +1 -0
  45. package/dist/duplicates/resolve.js +257 -0
  46. package/dist/duplicates/resolve.js.map +1 -0
  47. package/dist/duplicates/score.d.ts +22 -0
  48. package/dist/duplicates/score.d.ts.map +1 -0
  49. package/dist/duplicates/score.js +190 -0
  50. package/dist/duplicates/score.js.map +1 -0
  51. package/dist/duplicates/shingles.d.ts +15 -0
  52. package/dist/duplicates/shingles.d.ts.map +1 -0
  53. package/dist/duplicates/shingles.js +35 -0
  54. package/dist/duplicates/shingles.js.map +1 -0
  55. package/dist/duplicates/similarity.d.ts +23 -0
  56. package/dist/duplicates/similarity.d.ts.map +1 -0
  57. package/dist/duplicates/similarity.js +86 -0
  58. package/dist/duplicates/similarity.js.map +1 -0
  59. package/dist/duplicates/types.d.ts +97 -0
  60. package/dist/duplicates/types.d.ts.map +1 -0
  61. package/dist/duplicates/types.js +2 -0
  62. package/dist/duplicates/types.js.map +1 -0
  63. package/dist/explicit/links.d.ts +30 -0
  64. package/dist/explicit/links.d.ts.map +1 -0
  65. package/dist/explicit/links.js +173 -0
  66. package/dist/explicit/links.js.map +1 -0
  67. package/dist/explicit/types.d.ts +35 -0
  68. package/dist/explicit/types.d.ts.map +1 -0
  69. package/dist/explicit/types.js +2 -0
  70. package/dist/explicit/types.js.map +1 -0
  71. package/dist/frontmatter/links.d.ts +21 -0
  72. package/dist/frontmatter/links.d.ts.map +1 -0
  73. package/dist/frontmatter/links.js +131 -0
  74. package/dist/frontmatter/links.js.map +1 -0
  75. package/dist/frontmatter/resolve.d.ts +32 -0
  76. package/dist/frontmatter/resolve.d.ts.map +1 -0
  77. package/dist/frontmatter/resolve.js +62 -0
  78. package/dist/frontmatter/resolve.js.map +1 -0
  79. package/dist/index.d.ts +26 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +19 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/mentions/links.d.ts +43 -0
  84. package/dist/mentions/links.d.ts.map +1 -0
  85. package/dist/mentions/links.js +106 -0
  86. package/dist/mentions/links.js.map +1 -0
  87. package/dist/mentions/sections.d.ts +17 -0
  88. package/dist/mentions/sections.d.ts.map +1 -0
  89. package/dist/mentions/sections.js +26 -0
  90. package/dist/mentions/sections.js.map +1 -0
  91. package/dist/neighbourhood/accumulate.d.ts +11 -0
  92. package/dist/neighbourhood/accumulate.d.ts.map +1 -0
  93. package/dist/neighbourhood/accumulate.js +73 -0
  94. package/dist/neighbourhood/accumulate.js.map +1 -0
  95. package/dist/neighbourhood/links.d.ts +13 -0
  96. package/dist/neighbourhood/links.d.ts.map +1 -0
  97. package/dist/neighbourhood/links.js +35 -0
  98. package/dist/neighbourhood/links.js.map +1 -0
  99. package/dist/neighbourhood/options.d.ts +5 -0
  100. package/dist/neighbourhood/options.d.ts.map +1 -0
  101. package/dist/neighbourhood/options.js +6 -0
  102. package/dist/neighbourhood/options.js.map +1 -0
  103. package/dist/neighbourhood/order.d.ts +5 -0
  104. package/dist/neighbourhood/order.d.ts.map +1 -0
  105. package/dist/neighbourhood/order.js +9 -0
  106. package/dist/neighbourhood/order.js.map +1 -0
  107. package/dist/neighbourhood/serialize.d.ts +4 -0
  108. package/dist/neighbourhood/serialize.d.ts.map +1 -0
  109. package/dist/neighbourhood/serialize.js +10 -0
  110. package/dist/neighbourhood/serialize.js.map +1 -0
  111. package/dist/neighbourhood/types.d.ts +24 -0
  112. package/dist/neighbourhood/types.d.ts.map +1 -0
  113. package/dist/neighbourhood/types.js +2 -0
  114. package/dist/neighbourhood/types.js.map +1 -0
  115. package/dist/operations/index.d.ts +30 -0
  116. package/dist/operations/index.d.ts.map +1 -0
  117. package/dist/operations/index.js +118 -0
  118. package/dist/operations/index.js.map +1 -0
  119. package/dist/operations/match.d.ts +64 -0
  120. package/dist/operations/match.d.ts.map +1 -0
  121. package/dist/operations/match.js +251 -0
  122. package/dist/operations/match.js.map +1 -0
  123. package/dist/operations/merge.d.ts +14 -0
  124. package/dist/operations/merge.d.ts.map +1 -0
  125. package/dist/operations/merge.js +60 -0
  126. package/dist/operations/merge.js.map +1 -0
  127. package/dist/relations/index.d.ts +3 -0
  128. package/dist/relations/index.d.ts.map +1 -0
  129. package/dist/relations/index.js +3 -0
  130. package/dist/relations/index.js.map +1 -0
  131. package/dist/relations/labels.d.ts +14 -0
  132. package/dist/relations/labels.d.ts.map +1 -0
  133. package/dist/relations/labels.js +22 -0
  134. package/dist/relations/labels.js.map +1 -0
  135. package/dist/relations/type.d.ts +40 -0
  136. package/dist/relations/type.d.ts.map +1 -0
  137. package/dist/relations/type.js +161 -0
  138. package/dist/relations/type.js.map +1 -0
  139. package/package.json +46 -0
@@ -0,0 +1,13 @@
1
+ import type { DuplicateInput, DuplicateOptions, DuplicateResult, DuplicateStats } from "./types.js";
2
+ export declare const DUPLICATE_CHECK = "W-DUP-CANDIDATE";
3
+ /**
4
+ * Merges the resources whose score passes `mergeAbove` into groups, reports a candidate finding
5
+ * for every pair at or above `candidateAbove`, and applies the lock: `merged` pairs merge whatever
6
+ * their score, `separated` pairs neither merge nor produce a finding. A pair enters the scoring
7
+ * through a declaration, a base name, a title or its content; the commit and the folder only
8
+ * reinforce a pair found that way.
9
+ */
10
+ export declare function resolveDuplicateResources(input: DuplicateInput, options: DuplicateOptions): DuplicateResult;
11
+ /** The lines the build summary prints. */
12
+ export declare function formatDuplicateStats(stats: DuplicateStats): string[];
13
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/duplicates/resolve.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAEV,cAAc,EACd,gBAAgB,EAIhB,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAgNjD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,gBAAgB,GACxB,eAAe,CA+DjB;AAED,0CAA0C;AAC1C,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE,CAOpE"}
@@ -0,0 +1,257 @@
1
+ import { compareFindings } from "@concordance-wiki/core";
2
+ import { contentSimilarities } from "./content.js";
3
+ import { nameSimilarity, primarySignal, resolveDeclared, scorePair, } from "./score.js";
4
+ import { comparisonKey, nameProfile } from "./similarity.js";
5
+ export const DUPLICATE_CHECK = "W-DUP-CANDIDATE";
6
+ const REMEDIATION = "Declare the twin in the markdown frontmatter under source, or record the pair as merged or separated in the lock file.";
7
+ const LOCK_CRITERION = "lock file";
8
+ /** How the page names what grouped the representations. */
9
+ const CRITERIA = {
10
+ declared: "declared in frontmatter",
11
+ same_name: "same base name",
12
+ similar_name: "similar base names",
13
+ same_title: "title equal to the heading",
14
+ similar_content: "similar content",
15
+ };
16
+ function byCodeUnit(a, b) {
17
+ return Number(a > b) - Number(a < b);
18
+ }
19
+ function ordered(a, b) {
20
+ return a.id < b.id ? [a, b] : [b, a];
21
+ }
22
+ function keyOf([a, b]) {
23
+ return `${a.id}\n${b.id}`;
24
+ }
25
+ function timer(clock) {
26
+ if (clock === undefined)
27
+ return () => 0;
28
+ const started = clock.now().getTime();
29
+ return () => clock.now().getTime() - started;
30
+ }
31
+ function groupBy(resources, keyFor) {
32
+ const groups = new Map();
33
+ for (const resource of resources) {
34
+ const key = keyFor(resource);
35
+ if (key === "")
36
+ continue;
37
+ const group = groups.get(key);
38
+ if (group === undefined) {
39
+ groups.set(key, [resource]);
40
+ }
41
+ else {
42
+ group.push(resource);
43
+ }
44
+ }
45
+ return groups;
46
+ }
47
+ function crossPairs(first, second) {
48
+ const pairs = [];
49
+ for (const a of first) {
50
+ for (const b of second) {
51
+ if (a.id !== b.id)
52
+ pairs.push(ordered(a, b));
53
+ }
54
+ }
55
+ return pairs;
56
+ }
57
+ /** Every pair sharing or nearly sharing a base name: identical names within a bucket, close names across two. */
58
+ function namePairs(resources) {
59
+ const buckets = [...groupBy(resources, (resource) => comparisonKey(resource.baseName))].map(([key, members]) => ({ profile: nameProfile(key), members }));
60
+ const pairs = [];
61
+ buckets.forEach(({ profile, members }, position) => {
62
+ members.forEach((a, index) => {
63
+ for (const b of members.slice(index + 1))
64
+ pairs.push(ordered(a, b));
65
+ });
66
+ for (const other of buckets.slice(position + 1)) {
67
+ if (nameSimilarity(profile, other.profile) !== undefined) {
68
+ pairs.push(...crossPairs(members, other.members));
69
+ }
70
+ }
71
+ });
72
+ return pairs;
73
+ }
74
+ /** Every pair where the title of one is the heading of the other. */
75
+ function titlePairs(resources) {
76
+ const byHeading = groupBy(resources, (resource) => comparisonKey(resource.heading ?? ""));
77
+ const pairs = [];
78
+ for (const resource of resources) {
79
+ const key = comparisonKey(resource.title ?? "");
80
+ pairs.push(...crossPairs([resource], byHeading.get(key) ?? []));
81
+ }
82
+ return pairs;
83
+ }
84
+ /** Every pair where one declares the other in its frontmatter. */
85
+ function declaredPairs(resources) {
86
+ const byLocation = new Map(resources.map((resource) => [`${resource.source}/${resource.path}`, resource]));
87
+ const pairs = [];
88
+ for (const resource of resources) {
89
+ const target = resolveDeclared(resource);
90
+ if (target === undefined)
91
+ continue;
92
+ const twin = byLocation.get(`${target.source}/${target.path}`);
93
+ if (twin !== undefined && twin.id !== resource.id)
94
+ pairs.push(ordered(resource, twin));
95
+ }
96
+ return pairs;
97
+ }
98
+ function lockPairs(byId, listed) {
99
+ const pairs = [];
100
+ for (const [x, y] of listed ?? []) {
101
+ const a = byId.get(x);
102
+ const b = byId.get(y);
103
+ if (a !== undefined && b !== undefined)
104
+ pairs.push(ordered(a, b));
105
+ }
106
+ return pairs;
107
+ }
108
+ function representationFormat(path) {
109
+ const name = path.slice(path.lastIndexOf("/") + 1);
110
+ const dot = name.lastIndexOf(".");
111
+ if (dot === -1)
112
+ return "file";
113
+ const extension = name.slice(dot + 1).toLowerCase();
114
+ return extension === "md" ? "markdown" : extension;
115
+ }
116
+ /**
117
+ * Union-find over the merge edges, the lower identifier as root, so that the root of a component
118
+ * is its lowest identifier whatever the order of the edges. Only non-root identifiers have a parent.
119
+ */
120
+ function components(edges) {
121
+ const parent = new Map();
122
+ const rootOf = (id) => {
123
+ const next = parent.get(id);
124
+ return next === undefined ? id : rootOf(next);
125
+ };
126
+ for (const edge of edges) {
127
+ const a = rootOf(edge.a);
128
+ const b = rootOf(edge.b);
129
+ if (a < b) {
130
+ parent.set(b, a);
131
+ }
132
+ else if (b < a) {
133
+ parent.set(a, b);
134
+ }
135
+ }
136
+ return rootOf;
137
+ }
138
+ function groupsOf(resources, rootOf, edges) {
139
+ const members = groupBy(resources, (resource) => rootOf(resource.id));
140
+ const groups = [];
141
+ for (const [root, grouped] of members) {
142
+ if (grouped.length < 2)
143
+ continue;
144
+ const representations = grouped.map((resource) => ({
145
+ id: resource.id,
146
+ path: resource.path,
147
+ format: representationFormat(resource.path),
148
+ }));
149
+ // Members are in identifier order: the first note is the lowest one.
150
+ const note = representations.find((representation) => representation.format === "markdown");
151
+ const criteria = new Set(edges.filter((edge) => rootOf(edge.a) === root).map((edge) => edge.criterion));
152
+ groups.push({
153
+ id: note === undefined ? root : note.id,
154
+ representations,
155
+ criterion: [...criteria].sort(byCodeUnit).join(", "),
156
+ });
157
+ }
158
+ return groups.sort((a, b) => byCodeUnit(a.id, b.id));
159
+ }
160
+ function candidateFinding(a, b, pair) {
161
+ const breakdown = pair.signals
162
+ .map((signal) => `${signal.detail} ${signal.weight.toFixed(2)}`)
163
+ .join(", ");
164
+ return {
165
+ check: DUPLICATE_CHECK,
166
+ severity: "info",
167
+ source: a.source,
168
+ path: a.path,
169
+ entity: a.id,
170
+ message: `${a.source}/${a.path} and ${b.source}/${b.path} look like two representations of one document (score ${pair.score.toFixed(2)}: ${breakdown}); they stay separate`,
171
+ remediation: REMEDIATION,
172
+ };
173
+ }
174
+ /**
175
+ * Merges the resources whose score passes `mergeAbove` into groups, reports a candidate finding
176
+ * for every pair at or above `candidateAbove`, and applies the lock: `merged` pairs merge whatever
177
+ * their score, `separated` pairs neither merge nor produce a finding. A pair enters the scoring
178
+ * through a declaration, a base name, a title or its content; the commit and the folder only
179
+ * reinforce a pair found that way.
180
+ */
181
+ export function resolveDuplicateResources(input, options) {
182
+ const elapsed = timer(input.clock);
183
+ const resources = [...input.resources].sort((a, b) => byCodeUnit(a.id, b.id));
184
+ const byId = new Map(resources.map((resource) => [resource.id, resource]));
185
+ if (byId.size !== resources.length) {
186
+ throw new Error("every resource needs a distinct identifier");
187
+ }
188
+ const content = contentSimilarities(resources, input.normalizeText, options);
189
+ const similarities = new Map();
190
+ const candidates = new Map();
191
+ for (const { a, b, similarity } of content.pairs) {
192
+ similarities.set(keyOf([a, b]), similarity);
193
+ candidates.set(keyOf([a, b]), [a, b]);
194
+ }
195
+ for (const pair of [
196
+ ...namePairs(resources),
197
+ ...titlePairs(resources),
198
+ ...declaredPairs(resources),
199
+ ]) {
200
+ candidates.set(keyOf(pair), pair);
201
+ }
202
+ const separated = new Set(lockPairs(byId, input.lock?.separated).map(keyOf));
203
+ const edges = lockPairs(byId, input.lock?.merged).map(([a, b]) => ({
204
+ a: a.id,
205
+ b: b.id,
206
+ criterion: LOCK_CRITERION,
207
+ }));
208
+ const pairs = [];
209
+ const scored = [];
210
+ let scoredPairs = 0;
211
+ for (const [key, [a, b]] of [...candidates].sort(([x], [y]) => byCodeUnit(x, y))) {
212
+ if (separated.has(key))
213
+ continue;
214
+ scoredPairs++;
215
+ const pair = scorePair(a, b, similarities.get(key), options);
216
+ const primary = primarySignal(pair);
217
+ if (primary === undefined || pair.score < options.candidateAbove)
218
+ continue;
219
+ pairs.push(pair);
220
+ if (pair.score > options.mergeAbove) {
221
+ edges.push({ a: a.id, b: b.id, criterion: CRITERIA[primary.name] });
222
+ }
223
+ else {
224
+ scored.push([[a, b], pair]);
225
+ }
226
+ }
227
+ const rootOf = components(edges);
228
+ const groups = groupsOf(resources, rootOf, edges);
229
+ const findings = scored
230
+ .filter(([[a, b]]) => rootOf(a.id) !== rootOf(b.id))
231
+ .map(([[a, b], pair]) => candidateFinding(a, b, pair))
232
+ .sort(compareFindings);
233
+ return {
234
+ groups,
235
+ findings,
236
+ pairs,
237
+ stats: {
238
+ resources: resources.length,
239
+ candidatePairs: content.pairs.length,
240
+ scoredPairs,
241
+ exactVerifications: content.exactVerifications,
242
+ merged: groups.length,
243
+ candidates: findings.length,
244
+ timeMs: elapsed(),
245
+ },
246
+ };
247
+ }
248
+ /** The lines the build summary prints. */
249
+ export function formatDuplicateStats(stats) {
250
+ return [
251
+ `duplicate candidate pairs: ${String(stats.candidatePairs)} by content, ${String(stats.scoredPairs)} scored, of ${String(stats.resources)} resources`,
252
+ `duplicate exact verifications: ${String(stats.exactVerifications)}`,
253
+ `duplicates merged: ${String(stats.merged)}, candidates: ${String(stats.candidates)}`,
254
+ `duplicate detection time: ${String(stats.timeMs)} ms`,
255
+ ];
256
+ }
257
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/duplicates/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { ContentSimilarity } from "./content.js";
2
+ import { type NameProfile } from "./similarity.js";
3
+ import type { DuplicateOptions, DuplicatePair, DuplicateResource, DuplicateSignal } from "./types.js";
4
+ /** Jaro-Winkler similarity from which two base names count as close. */
5
+ export declare const SIMILAR_NAME = 0.9;
6
+ export declare const INCLUSION_NOTE = "similar content, very different sizes: an inclusion rather than a duplicate";
7
+ /** The source and path a frontmatter `source` names, relative to the folder of the note or `<source>:<path>`. */
8
+ export declare function resolveDeclared(resource: DuplicateResource): {
9
+ source: string;
10
+ path: string;
11
+ } | undefined;
12
+ /** The Jaro-Winkler similarity of two base names in comparison form when it relates them, 1 for the same name. */
13
+ export declare function nameSimilarity(a: NameProfile, b: NameProfile): number | undefined;
14
+ /** The signals a pair can be enumerated from; the commit and the directory only reinforce them. */
15
+ export type PrimaryName = Exclude<DuplicateSignal["name"], "same_commit" | "same_directory">;
16
+ /** The strongest primary signal of a pair, which names the grouping criterion; none for a pair that only shares a commit or a folder. */
17
+ export declare function primarySignal(pair: DuplicatePair): (DuplicateSignal & {
18
+ name: PrimaryName;
19
+ }) | undefined;
20
+ /** The additive score of a pair, capped at 1, with every signal that contributed. `a` has the lower identifier. */
21
+ export declare function scorePair(a: DuplicateResource, b: DuplicateResource, content: ContentSimilarity | undefined, options: DuplicateOptions): DuplicatePair;
22
+ //# sourceMappingURL=score.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../src/duplicates/score.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAML,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,eAAe,EAChB,MAAM,YAAY,CAAC;AAcpB,wEAAwE;AACxE,eAAO,MAAM,YAAY,MAAM,CAAC;AAIhC,eAAO,MAAM,cAAc,gFACoD,CAAC;AAUhF,iHAAiH;AACjH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,iBAAiB,GAC1B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CA0B9C;AA+BD,kHAAkH;AAClH,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CASjF;AAmFD,mGAAmG;AACnG,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,gBAAgB,CAAC,CAAC;AAM7F,yIAAyI;AACzI,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,GAClB,CAAC,eAAe,GAAG;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,GAAG,SAAS,CAEvD;AAED,mHAAmH;AACnH,wBAAgB,SAAS,CACvB,CAAC,EAAE,iBAAiB,EACpB,CAAC,EAAE,iBAAiB,EACpB,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,OAAO,EAAE,gBAAgB,GACxB,aAAa,CAaf"}
@@ -0,0 +1,190 @@
1
+ import { comparisonKey, directoryProximity, jaroWinkler, nameProfile, sharedCodeUnits, } from "./similarity.js";
2
+ const WEIGHTS = {
3
+ declared: 1,
4
+ sameNameSameFolder: 0.7,
5
+ sameNameElsewhere: 0.5,
6
+ similarNameFactor: 0.8,
7
+ sameTitle: 0.6,
8
+ contentHigh: 0.7,
9
+ contentLow: 0.4,
10
+ sameCommit: 0.3,
11
+ directory: 0.2,
12
+ };
13
+ /** Jaro-Winkler similarity from which two base names count as close. */
14
+ export const SIMILAR_NAME = 0.9;
15
+ const CONTENT_HIGH = 0.8;
16
+ const CONTENT_LOW = 0.6;
17
+ export const INCLUSION_NOTE = "similar content, very different sizes: an inclusion rather than a duplicate";
18
+ function round(value) {
19
+ return Math.round(value * 10000) / 10000;
20
+ }
21
+ function percent(value) {
22
+ return `${String(Math.round(value * 100))}%`;
23
+ }
24
+ /** The source and path a frontmatter `source` names, relative to the folder of the note or `<source>:<path>`. */
25
+ export function resolveDeclared(resource) {
26
+ const declared = resource.declaredSource;
27
+ if (declared === undefined)
28
+ return undefined;
29
+ const colon = declared.indexOf(":");
30
+ if (colon !== -1) {
31
+ return { source: declared.slice(0, colon), path: declared.slice(colon + 1) };
32
+ }
33
+ const segments = resource.folder === "" ? [] : resource.folder.split("/");
34
+ let climbed = false;
35
+ for (const segment of declared.split("/")) {
36
+ if (segment === "." || segment === "")
37
+ continue;
38
+ if (segment !== "..") {
39
+ segments.push(segment);
40
+ }
41
+ else if (segments.length > 0) {
42
+ segments.pop();
43
+ }
44
+ else {
45
+ climbed = true;
46
+ }
47
+ }
48
+ if (!climbed)
49
+ return { source: resource.source, path: segments.join("/") };
50
+ // Climbing above the root lands in a sibling source: `../<source>/<path>`.
51
+ const sibling = segments.join("/");
52
+ const slash = sibling.indexOf("/");
53
+ return slash === -1
54
+ ? undefined
55
+ : { source: sibling.slice(0, slash), path: sibling.slice(slash + 1) };
56
+ }
57
+ function declares(from, to) {
58
+ const target = resolveDeclared(from);
59
+ return target?.source === to.source && target.path === to.path;
60
+ }
61
+ /** The resource whose frontmatter declares the other, when one does. */
62
+ function declaringOf(a, b) {
63
+ if (declares(a, b))
64
+ return a;
65
+ return declares(b, a) ? b : undefined;
66
+ }
67
+ function declaredSignal(a, b) {
68
+ const declaring = declaringOf(a, b);
69
+ return declaring === undefined
70
+ ? undefined
71
+ : {
72
+ name: "declared",
73
+ weight: WEIGHTS.declared,
74
+ detail: `declared in the frontmatter of ${declaring.path}`,
75
+ };
76
+ }
77
+ /**
78
+ * Jaro-Winkler at `SIMILAR_NAME` needs Jaro at `(SIMILAR_NAME - 0.4) / 0.6`, the prefix bonus
79
+ * covering at most 0.4 of the distance to 1, and Jaro with `m` matches is at most
80
+ * `(m / |a| + m / |b| + 1) / 3`: the matches must reach `MATCH_FACTOR * |a| * |b| / (|a| + |b|)`.
81
+ */
82
+ const MATCH_FACTOR = 3 * ((SIMILAR_NAME - 0.4) / 0.6) - 1;
83
+ /** The Jaro-Winkler similarity of two base names in comparison form when it relates them, 1 for the same name. */
84
+ export function nameSimilarity(a, b) {
85
+ const lengthA = a.key.length;
86
+ const lengthB = b.key.length;
87
+ // Matches never exceed the shared code units: most pairs of distinct names stop here.
88
+ if (sharedCodeUnits(a, b) * (lengthA + lengthB) < MATCH_FACTOR * lengthA * lengthB) {
89
+ return undefined;
90
+ }
91
+ const similarity = jaroWinkler(a.key, b.key);
92
+ return similarity >= SIMILAR_NAME ? similarity : undefined;
93
+ }
94
+ function nameSignal(a, b) {
95
+ const keyA = comparisonKey(a.baseName);
96
+ const keyB = comparisonKey(b.baseName);
97
+ const similarity = nameSimilarity(nameProfile(keyA), nameProfile(keyB));
98
+ if (similarity === undefined)
99
+ return undefined;
100
+ const sameFolder = a.source === b.source && a.folder === b.folder;
101
+ const weight = sameFolder ? WEIGHTS.sameNameSameFolder : WEIGHTS.sameNameElsewhere;
102
+ const where = whereOf(a, b, sameFolder);
103
+ if (keyA === keyB) {
104
+ return { name: "same_name", weight, detail: `same base name ${where}` };
105
+ }
106
+ return {
107
+ name: "similar_name",
108
+ weight: round(weight * WEIGHTS.similarNameFactor),
109
+ detail: `similar base names ${where} (Jaro-Winkler ${similarity.toFixed(2)})`,
110
+ };
111
+ }
112
+ /** How far apart the two resources are filed, for the detail of a name signal. */
113
+ function whereOf(a, b, sameFolder) {
114
+ if (sameFolder)
115
+ return "in the same folder";
116
+ return a.source === b.source ? "in another folder" : "across sources";
117
+ }
118
+ function titleMatchesHeading(titled, headed) {
119
+ if (titled.title === undefined || headed.heading === undefined)
120
+ return false;
121
+ const title = comparisonKey(titled.title);
122
+ return title !== "" && title === comparisonKey(headed.heading);
123
+ }
124
+ function titleSignal(a, b) {
125
+ return titleMatchesHeading(a, b) || titleMatchesHeading(b, a)
126
+ ? { name: "same_title", weight: WEIGHTS.sameTitle, detail: "title equal to the heading" }
127
+ : undefined;
128
+ }
129
+ function contentDetail(content) {
130
+ const jaccard = `${content.exact ? "exact" : "estimated"} Jaccard ${content.jaccard.toFixed(2)}`;
131
+ return content.sharedLines === undefined
132
+ ? jaccard
133
+ : `${jaccard}, ${percent(content.sharedLines)} of lines in common`;
134
+ }
135
+ function contentSignal(content, options) {
136
+ if (content === undefined || content.jaccard < CONTENT_LOW)
137
+ return undefined;
138
+ const weight = content.jaccard >= CONTENT_HIGH ? WEIGHTS.contentHigh : WEIGHTS.contentLow;
139
+ if (content.sizeRatio < options.sizeRatioMin) {
140
+ return {
141
+ name: "similar_content",
142
+ weight: Math.min(weight, WEIGHTS.contentLow),
143
+ detail: `${INCLUSION_NOTE} (${contentDetail(content)}, size ratio ${content.sizeRatio.toFixed(2)})`,
144
+ };
145
+ }
146
+ return { name: "similar_content", weight, detail: `similar content (${contentDetail(content)})` };
147
+ }
148
+ function commitSignal(a, b) {
149
+ return a.commit !== undefined && a.commit === b.commit
150
+ ? { name: "same_commit", weight: WEIGHTS.sameCommit, detail: `added in commit ${a.commit}` }
151
+ : undefined;
152
+ }
153
+ function directorySignal(a, b) {
154
+ if (a.source !== b.source)
155
+ return undefined;
156
+ const proximity = directoryProximity(a.folder, b.folder);
157
+ return proximity === 0
158
+ ? undefined
159
+ : {
160
+ name: "same_directory",
161
+ weight: round(proximity * WEIGHTS.directory),
162
+ detail: `directory proximity ${proximity.toFixed(2)}`,
163
+ };
164
+ }
165
+ function compareSignals(a, b) {
166
+ return b.weight - a.weight || Number(a.name > b.name) - Number(a.name < b.name);
167
+ }
168
+ function isPrimary(signal) {
169
+ return signal.name !== "same_commit" && signal.name !== "same_directory";
170
+ }
171
+ /** The strongest primary signal of a pair, which names the grouping criterion; none for a pair that only shares a commit or a folder. */
172
+ export function primarySignal(pair) {
173
+ return pair.signals.find(isPrimary);
174
+ }
175
+ /** The additive score of a pair, capped at 1, with every signal that contributed. `a` has the lower identifier. */
176
+ export function scorePair(a, b, content, options) {
177
+ const signals = [
178
+ declaredSignal(a, b),
179
+ nameSignal(a, b),
180
+ titleSignal(a, b),
181
+ contentSignal(content, options),
182
+ commitSignal(a, b),
183
+ directorySignal(a, b),
184
+ ]
185
+ .filter((signal) => signal !== undefined)
186
+ .sort(compareSignals);
187
+ const total = signals.reduce((sum, signal) => sum + signal.weight, 0);
188
+ return { a: a.id, b: b.id, score: round(Math.min(1, total)), signals };
189
+ }
190
+ //# sourceMappingURL=score.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/duplicates/score.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /** A text after normalisation: its lines in comparison form and its size. */
2
+ export interface NormalisedText {
3
+ /** Non-empty lines, each the words of the line joined by a space. */
4
+ lines: string[];
5
+ words: string[];
6
+ characters: number;
7
+ }
8
+ export declare function normaliseText(text: string, normalizeText: (text: string) => string[]): NormalisedText;
9
+ /** The distinct runs of `size` consecutive words; empty for a text shorter than `size`. */
10
+ export declare function shingleSet(words: readonly string[], size: number): Set<string>;
11
+ /** `|a ∩ b| / |a ∪ b|`; 0 when both are empty. */
12
+ export declare function exactJaccard(a: ReadonlySet<string>, b: ReadonlySet<string>): number;
13
+ /** Distinct lines in common over the distinct lines of both texts. */
14
+ export declare function sharedLines(a: readonly string[], b: readonly string[]): number;
15
+ //# sourceMappingURL=shingles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shingles.d.ts","sourceRoot":"","sources":["../../src/duplicates/shingles.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,GACxC,cAAc,CAUhB;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAM9E;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAOnF;AAED,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAE9E"}
@@ -0,0 +1,35 @@
1
+ export function normaliseText(text, normalizeText) {
2
+ const lines = [];
3
+ const words = [];
4
+ for (const line of text.split(/\r?\n/)) {
5
+ const lineWords = normalizeText(line);
6
+ if (lineWords.length === 0)
7
+ continue;
8
+ lines.push(lineWords.join(" "));
9
+ words.push(...lineWords);
10
+ }
11
+ return { lines, words, characters: words.join(" ").length };
12
+ }
13
+ /** The distinct runs of `size` consecutive words; empty for a text shorter than `size`. */
14
+ export function shingleSet(words, size) {
15
+ const shingles = new Set();
16
+ for (let start = 0; start + size <= words.length; start++) {
17
+ shingles.add(words.slice(start, start + size).join(" "));
18
+ }
19
+ return shingles;
20
+ }
21
+ /** `|a ∩ b| / |a ∪ b|`; 0 when both are empty. */
22
+ export function exactJaccard(a, b) {
23
+ let common = 0;
24
+ for (const shingle of a) {
25
+ if (b.has(shingle))
26
+ common++;
27
+ }
28
+ const union = a.size + b.size - common;
29
+ return union === 0 ? 0 : common / union;
30
+ }
31
+ /** Distinct lines in common over the distinct lines of both texts. */
32
+ export function sharedLines(a, b) {
33
+ return exactJaccard(new Set(a), new Set(b));
34
+ }
35
+ //# sourceMappingURL=shingles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shingles.js","sourceRoot":"","sources":["../../src/duplicates/shingles.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,aAAyC;IAEzC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACrC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;AAC9D,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,UAAU,CAAC,KAAwB,EAAE,IAAY;IAC/D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,YAAY,CAAC,CAAsB,EAAE,CAAsB;IACzE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,OAAO,IAAI,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,MAAM,EAAE,CAAC;IAC/B,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC;IACvC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC;AAC1C,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,WAAW,CAAC,CAAoB,EAAE,CAAoB;IACpE,OAAO,YAAY,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,23 @@
1
+ /** The form base names and titles are compared on: lowercase, accents stripped, punctuation folded to spaces. */
2
+ export declare function comparisonKey(text: string): string;
3
+ /** Jaro similarity with the Winkler bonus for a common prefix of up to four characters. */
4
+ export declare function jaroWinkler(a: string, b: string): number;
5
+ /**
6
+ * A name with the count of its code units per class (code unit modulo 32) and the set of classes
7
+ * present, which bound what it can share with another name.
8
+ */
9
+ export interface NameProfile {
10
+ key: string;
11
+ /** One byte per class, `counts.getUint8(unitClass)`; a base name never has 256 code units of one class. */
12
+ counts: DataView;
13
+ classes: number;
14
+ }
15
+ export declare function nameProfile(key: string): NameProfile;
16
+ /**
17
+ * An upper bound of the code units two names have in common: their counts merged by class, since
18
+ * a minimum of sums is at least a sum of minimums. Walks the shared classes only.
19
+ */
20
+ export declare function sharedCodeUnits(a: NameProfile, b: NameProfile): number;
21
+ /** Depth of the common prefix over the deeper folder, 1 for the same folder; the root has depth 0. */
22
+ export declare function directoryProximity(a: string, b: string): number;
23
+ //# sourceMappingURL=similarity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similarity.d.ts","sourceRoot":"","sources":["../../src/duplicates/similarity.ts"],"names":[],"mappings":"AAGA,iHAAiH;AACjH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOlD;AA+BD,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,2GAA2G;IAC3G,MAAM,EAAE,QAAQ,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CASpD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,MAAM,CAQtE;AAED,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAM/D"}