openplanter 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 (126) hide show
  1. package/README.md +210 -0
  2. package/dist/builder.d.ts +11 -0
  3. package/dist/builder.d.ts.map +1 -0
  4. package/dist/builder.js +179 -0
  5. package/dist/builder.js.map +1 -0
  6. package/dist/cli.d.ts +9 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +548 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +51 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +114 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/credentials.d.ts +52 -0
  15. package/dist/credentials.d.ts.map +1 -0
  16. package/dist/credentials.js +371 -0
  17. package/dist/credentials.js.map +1 -0
  18. package/dist/demo.d.ts +26 -0
  19. package/dist/demo.d.ts.map +1 -0
  20. package/dist/demo.js +95 -0
  21. package/dist/demo.js.map +1 -0
  22. package/dist/engine.d.ts +91 -0
  23. package/dist/engine.d.ts.map +1 -0
  24. package/dist/engine.js +1036 -0
  25. package/dist/engine.js.map +1 -0
  26. package/dist/index.d.ts +30 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +39 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/investigation-tools/aph-holdings.d.ts +61 -0
  31. package/dist/investigation-tools/aph-holdings.d.ts.map +1 -0
  32. package/dist/investigation-tools/aph-holdings.js +459 -0
  33. package/dist/investigation-tools/aph-holdings.js.map +1 -0
  34. package/dist/investigation-tools/asic-officer-lookup.d.ts +42 -0
  35. package/dist/investigation-tools/asic-officer-lookup.d.ts.map +1 -0
  36. package/dist/investigation-tools/asic-officer-lookup.js +197 -0
  37. package/dist/investigation-tools/asic-officer-lookup.js.map +1 -0
  38. package/dist/investigation-tools/asx-calendar-fetcher.d.ts +42 -0
  39. package/dist/investigation-tools/asx-calendar-fetcher.d.ts.map +1 -0
  40. package/dist/investigation-tools/asx-calendar-fetcher.js +271 -0
  41. package/dist/investigation-tools/asx-calendar-fetcher.js.map +1 -0
  42. package/dist/investigation-tools/asx-parser.d.ts +66 -0
  43. package/dist/investigation-tools/asx-parser.d.ts.map +1 -0
  44. package/dist/investigation-tools/asx-parser.js +314 -0
  45. package/dist/investigation-tools/asx-parser.js.map +1 -0
  46. package/dist/investigation-tools/bulk-asx-announcements.d.ts +53 -0
  47. package/dist/investigation-tools/bulk-asx-announcements.d.ts.map +1 -0
  48. package/dist/investigation-tools/bulk-asx-announcements.js +204 -0
  49. package/dist/investigation-tools/bulk-asx-announcements.js.map +1 -0
  50. package/dist/investigation-tools/entity-resolver.d.ts +77 -0
  51. package/dist/investigation-tools/entity-resolver.d.ts.map +1 -0
  52. package/dist/investigation-tools/entity-resolver.js +346 -0
  53. package/dist/investigation-tools/entity-resolver.js.map +1 -0
  54. package/dist/investigation-tools/hotcopper-scraper.d.ts +73 -0
  55. package/dist/investigation-tools/hotcopper-scraper.d.ts.map +1 -0
  56. package/dist/investigation-tools/hotcopper-scraper.js +318 -0
  57. package/dist/investigation-tools/hotcopper-scraper.js.map +1 -0
  58. package/dist/investigation-tools/index.d.ts +15 -0
  59. package/dist/investigation-tools/index.d.ts.map +1 -0
  60. package/dist/investigation-tools/index.js +15 -0
  61. package/dist/investigation-tools/index.js.map +1 -0
  62. package/dist/investigation-tools/insider-graph.d.ts +173 -0
  63. package/dist/investigation-tools/insider-graph.d.ts.map +1 -0
  64. package/dist/investigation-tools/insider-graph.js +732 -0
  65. package/dist/investigation-tools/insider-graph.js.map +1 -0
  66. package/dist/investigation-tools/insider-suspicion-scorer.d.ts +97 -0
  67. package/dist/investigation-tools/insider-suspicion-scorer.d.ts.map +1 -0
  68. package/dist/investigation-tools/insider-suspicion-scorer.js +327 -0
  69. package/dist/investigation-tools/insider-suspicion-scorer.js.map +1 -0
  70. package/dist/investigation-tools/multi-forum-scraper.d.ts +104 -0
  71. package/dist/investigation-tools/multi-forum-scraper.d.ts.map +1 -0
  72. package/dist/investigation-tools/multi-forum-scraper.js +415 -0
  73. package/dist/investigation-tools/multi-forum-scraper.js.map +1 -0
  74. package/dist/investigation-tools/price-fetcher.d.ts +81 -0
  75. package/dist/investigation-tools/price-fetcher.d.ts.map +1 -0
  76. package/dist/investigation-tools/price-fetcher.js +268 -0
  77. package/dist/investigation-tools/price-fetcher.js.map +1 -0
  78. package/dist/investigation-tools/shared.d.ts +39 -0
  79. package/dist/investigation-tools/shared.d.ts.map +1 -0
  80. package/dist/investigation-tools/shared.js +203 -0
  81. package/dist/investigation-tools/shared.js.map +1 -0
  82. package/dist/investigation-tools/timeline-linker.d.ts +90 -0
  83. package/dist/investigation-tools/timeline-linker.d.ts.map +1 -0
  84. package/dist/investigation-tools/timeline-linker.js +219 -0
  85. package/dist/investigation-tools/timeline-linker.js.map +1 -0
  86. package/dist/investigation-tools/volume-scanner.d.ts +70 -0
  87. package/dist/investigation-tools/volume-scanner.d.ts.map +1 -0
  88. package/dist/investigation-tools/volume-scanner.js +227 -0
  89. package/dist/investigation-tools/volume-scanner.js.map +1 -0
  90. package/dist/model.d.ts +136 -0
  91. package/dist/model.d.ts.map +1 -0
  92. package/dist/model.js +1071 -0
  93. package/dist/model.js.map +1 -0
  94. package/dist/patching.d.ts +45 -0
  95. package/dist/patching.d.ts.map +1 -0
  96. package/dist/patching.js +317 -0
  97. package/dist/patching.js.map +1 -0
  98. package/dist/prompts.d.ts +15 -0
  99. package/dist/prompts.d.ts.map +1 -0
  100. package/dist/prompts.js +351 -0
  101. package/dist/prompts.js.map +1 -0
  102. package/dist/replay-log.d.ts +54 -0
  103. package/dist/replay-log.d.ts.map +1 -0
  104. package/dist/replay-log.js +94 -0
  105. package/dist/replay-log.js.map +1 -0
  106. package/dist/runtime.d.ts +53 -0
  107. package/dist/runtime.d.ts.map +1 -0
  108. package/dist/runtime.js +259 -0
  109. package/dist/runtime.js.map +1 -0
  110. package/dist/settings.d.ts +39 -0
  111. package/dist/settings.d.ts.map +1 -0
  112. package/dist/settings.js +146 -0
  113. package/dist/settings.js.map +1 -0
  114. package/dist/tool-defs.d.ts +58 -0
  115. package/dist/tool-defs.d.ts.map +1 -0
  116. package/dist/tool-defs.js +1029 -0
  117. package/dist/tool-defs.js.map +1 -0
  118. package/dist/tools.d.ts +72 -0
  119. package/dist/tools.d.ts.map +1 -0
  120. package/dist/tools.js +1454 -0
  121. package/dist/tools.js.map +1 -0
  122. package/dist/tui.d.ts +49 -0
  123. package/dist/tui.d.ts.map +1 -0
  124. package/dist/tui.js +699 -0
  125. package/dist/tui.js.map +1 -0
  126. package/package.json +126 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * entity-resolver.ts — Fuzzy entity matching and normalization for
3
+ * Australian politicians, ASX-listed companies, directors, ABNs and tickers.
4
+ */
5
+ /** Suffixes stripped from company names (order matters – longest first). */
6
+ export declare const COMPANY_SUFFIXES: readonly string[];
7
+ export declare const PERSON_TITLES: readonly string[];
8
+ export interface EntityInput {
9
+ name?: string;
10
+ type?: string;
11
+ }
12
+ export interface NormalizeResult {
13
+ originalName: string;
14
+ normalizedName: string;
15
+ type: string;
16
+ validTicker?: boolean;
17
+ validAbn?: boolean;
18
+ formattedAbn?: string;
19
+ }
20
+ export interface MatchResult {
21
+ entity: string;
22
+ entityNormalized: string;
23
+ type: string;
24
+ bestMatch: string | null;
25
+ bestMatchNormalized: string | null;
26
+ score: number;
27
+ aboveThreshold: boolean;
28
+ }
29
+ export interface ResolveResult {
30
+ canonicalName: string;
31
+ type: string;
32
+ aliases: string[];
33
+ count: number;
34
+ }
35
+ /** Normalise an Australian company name for comparison. */
36
+ export declare function normalizeCompany(name: string | null): string;
37
+ /** Normalise a person's name, handling SURNAME-first formats and titles. */
38
+ export declare function normalizePerson(name: string | null): string;
39
+ /** Normalise an ASX ticker symbol. */
40
+ export declare function normalizeTicker(ticker: string | null): string;
41
+ /** Check that a normalised ticker is a valid 3-character ASX code. */
42
+ export declare function validateTicker(ticker: string | null): boolean;
43
+ /**
44
+ * Validate an ABN using the official ATO check-digit algorithm.
45
+ *
46
+ * 1. Subtract 1 from the first digit.
47
+ * 2. Multiply each digit by its weight.
48
+ * 3. Sum the products — if divisible by 89 the ABN is valid.
49
+ */
50
+ export declare function entityValidateAbn(abn: string | null): boolean;
51
+ /** Format an ABN as XX XXX XXX XXX. */
52
+ export declare function entityFormatAbn(abn: string | null): string;
53
+ /** Normalise and format an ABN. */
54
+ export declare function normalizeAbn(abn: string | null): string;
55
+ /** Normalise *name* according to *entityType*. */
56
+ export declare function normalizeEntity(name: string | null, entityType?: string): string;
57
+ /**
58
+ * Clean and standardise a list of entity names.
59
+ *
60
+ * Each entity dict must have at least `name` and `type`.
61
+ * Returns the list with added `normalizedName`.
62
+ */
63
+ export declare function modeNormalize(entities: EntityInput[]): NormalizeResult[];
64
+ /**
65
+ * Find best matches between *entities* and *reference*.
66
+ *
67
+ * Returns a list of matched pairs with confidence scores.
68
+ */
69
+ export declare function modeMatch(entities: EntityInput[], reference: EntityInput[], threshold?: number): MatchResult[];
70
+ /**
71
+ * Cluster entities into unique canonical entries with aliases.
72
+ *
73
+ * Uses single-linkage clustering: if entity A matches B above threshold
74
+ * and B matches C, they all land in the same cluster.
75
+ */
76
+ export declare function modeResolve(entities: EntityInput[], threshold?: number): ResolveResult[];
77
+ //# sourceMappingURL=entity-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-resolver.d.ts","sourceRoot":"","sources":["../../src/investigation-tools/entity-resolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAuB7C,CAAC;AAWF,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAgC1C,CAAC;AAgBF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAeD,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAY5D;AAID,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CA0B3D;AAID,sCAAsC;AACtC,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK7D;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAG7D;AAUD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAK7D;AAED,uCAAuC;AACvC,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAI1D;AAED,mCAAmC;AACnC,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEvD;AAaD,kDAAkD;AAClD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,UAAU,GAAE,MAAkB,GAC7B,MAAM,CAGR;AA8CD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,eAAe,EAAE,CAuBxE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,WAAW,EAAE,EACvB,SAAS,EAAE,WAAW,EAAE,EACxB,SAAS,GAAE,MAAW,GACrB,WAAW,EAAE,CAuCf;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,WAAW,EAAE,EACvB,SAAS,GAAE,MAAW,GACrB,aAAa,EAAE,CAuEjB"}
@@ -0,0 +1,346 @@
1
+ /**
2
+ * entity-resolver.ts — Fuzzy entity matching and normalization for
3
+ * Australian politicians, ASX-listed companies, directors, ABNs and tickers.
4
+ */
5
+ import { fuzzyRatio, tokenSortRatio, validateAbn, formatAbn } from "./shared.js";
6
+ // ── Constants ───────────────────────────────────────────────────
7
+ /** Suffixes stripped from company names (order matters – longest first). */
8
+ export const COMPANY_SUFFIXES = [
9
+ "Proprietary Limited",
10
+ "Pty Limited",
11
+ "Pty Ltd",
12
+ "Pty. Ltd.",
13
+ "Pty. Ltd",
14
+ "Pty Ltd.",
15
+ "Limited",
16
+ "Ltd.",
17
+ "Ltd",
18
+ "Incorporated",
19
+ "Inc.",
20
+ "Inc",
21
+ "Corporation",
22
+ "Corp.",
23
+ "Corp",
24
+ "Holdings",
25
+ "Group",
26
+ "Australia",
27
+ "Australasia",
28
+ "NL",
29
+ "N.L.",
30
+ "No Liability",
31
+ ];
32
+ const COMPANY_SUFFIX_RE = new RegExp("\\b(?:" +
33
+ COMPANY_SUFFIXES.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|") +
34
+ ")\\s*$", "i");
35
+ export const PERSON_TITLES = [
36
+ "The Hon.",
37
+ "The Hon",
38
+ "Hon.",
39
+ "Hon",
40
+ "Dr.",
41
+ "Dr",
42
+ "Mr.",
43
+ "Mr",
44
+ "Mrs.",
45
+ "Mrs",
46
+ "Ms.",
47
+ "Ms",
48
+ "Prof.",
49
+ "Prof",
50
+ "Sir",
51
+ "Dame",
52
+ "Senator",
53
+ "Sen.",
54
+ "Sen",
55
+ "MP",
56
+ "M.P.",
57
+ "AM",
58
+ "AO",
59
+ "AC",
60
+ "QC",
61
+ "SC",
62
+ "KC",
63
+ "OAM",
64
+ "OBE",
65
+ "MBE",
66
+ "CBE",
67
+ ];
68
+ const TITLE_RE = new RegExp("(?:^|\\b)(?:" +
69
+ PERSON_TITLES.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|") +
70
+ ")(?:\\b|$)", "gi");
71
+ /** ABN weights per the official ATO algorithm. */
72
+ const ABN_WEIGHTS = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
73
+ // ── Normalisation Helpers ───────────────────────────────────────
74
+ /** Normalise unicode to NFKD. */
75
+ function stripUnicode(text) {
76
+ return text.normalize("NFKD");
77
+ }
78
+ function collapseWhitespace(text) {
79
+ return text.replace(/\s+/g, " ").trim();
80
+ }
81
+ // ── Company ─────────────────────────────────────────────────────
82
+ /** Normalise an Australian company name for comparison. */
83
+ export function normalizeCompany(name) {
84
+ if (!name)
85
+ return "";
86
+ let result = stripUnicode(name);
87
+ // Remove common punctuation noise
88
+ result = result.replace(/&/g, "and");
89
+ result = result.replace(/[.,;:!?'"()\[\]\-]/g, " ");
90
+ // Strip known suffixes (may need multiple passes)
91
+ for (let i = 0; i < 3; i++) {
92
+ result = result.replace(COMPANY_SUFFIX_RE, "");
93
+ }
94
+ result = collapseWhitespace(result);
95
+ return result.trim().toUpperCase();
96
+ }
97
+ // ── Person ──────────────────────────────────────────────────────
98
+ /** Normalise a person's name, handling SURNAME-first formats and titles. */
99
+ export function normalizePerson(name) {
100
+ if (!name)
101
+ return "";
102
+ let result = stripUnicode(name);
103
+ // Strip titles & honorifics
104
+ result = result.replace(TITLE_RE, "");
105
+ // Remove punctuation except comma (used for SURNAME, First detection)
106
+ result = result.replace(/[.;:!?'"()\[\]\-]/g, " ");
107
+ // Detect "SURNAME, First [Middle]" format
108
+ if (result.includes(",")) {
109
+ const parts = result.split(",", 2).map((p) => p.trim());
110
+ if (parts.length === 2 && parts[0] && parts[1]) {
111
+ result = `${parts[1]} ${parts[0]}`;
112
+ }
113
+ }
114
+ // Remove stray commas left over
115
+ result = result.replace(/,/g, " ");
116
+ // Collapse initials: "J." -> "J"
117
+ result = result.replace(/\b([A-Za-z])\.\s*/g, "$1 ");
118
+ result = collapseWhitespace(result);
119
+ return result.trim().toUpperCase();
120
+ }
121
+ // ── ASX Ticker ──────────────────────────────────────────────────
122
+ /** Normalise an ASX ticker symbol. */
123
+ export function normalizeTicker(ticker) {
124
+ if (!ticker)
125
+ return "";
126
+ let result = ticker.trim().toUpperCase();
127
+ result = result.replace(/\.(AX|ASX)$/i, "");
128
+ return result;
129
+ }
130
+ /** Check that a normalised ticker is a valid 3-character ASX code. */
131
+ export function validateTicker(ticker) {
132
+ const t = normalizeTicker(ticker);
133
+ return /^[A-Z0-9]{3}$/.test(t);
134
+ }
135
+ // ── ABN ─────────────────────────────────────────────────────────
136
+ /** Return only the digits from an ABN string. */
137
+ function abnDigits(abn) {
138
+ if (!abn)
139
+ return "";
140
+ return abn.replace(/\D/g, "");
141
+ }
142
+ /**
143
+ * Validate an ABN using the official ATO check-digit algorithm.
144
+ *
145
+ * 1. Subtract 1 from the first digit.
146
+ * 2. Multiply each digit by its weight.
147
+ * 3. Sum the products — if divisible by 89 the ABN is valid.
148
+ */
149
+ export function entityValidateAbn(abn) {
150
+ const digitsStr = abnDigits(abn);
151
+ if (digitsStr.length !== 11)
152
+ return false;
153
+ // Delegate to shared validateAbn which expects a whitespace-only string
154
+ return validateAbn(digitsStr);
155
+ }
156
+ /** Format an ABN as XX XXX XXX XXX. */
157
+ export function entityFormatAbn(abn) {
158
+ const d = abnDigits(abn);
159
+ if (d.length !== 11)
160
+ return d; // return raw if invalid length
161
+ return formatAbn(d);
162
+ }
163
+ /** Normalise and format an ABN. */
164
+ export function normalizeAbn(abn) {
165
+ return entityFormatAbn(abn);
166
+ }
167
+ const NORMALIZERS = {
168
+ person: normalizePerson,
169
+ company: normalizeCompany,
170
+ ticker: normalizeTicker,
171
+ abn: normalizeAbn,
172
+ };
173
+ /** Normalise *name* according to *entityType*. */
174
+ export function normalizeEntity(name, entityType = "company") {
175
+ const fn = NORMALIZERS[entityType] ?? normalizeCompany;
176
+ return fn(name);
177
+ }
178
+ // ── Fuzzy-Matching Helpers ──────────────────────────────────────
179
+ /**
180
+ * Token-set ratio: compares the intersection and differences of token sets
181
+ * from two strings. Uses fuzzyRatio from shared utilities.
182
+ */
183
+ function tokenSetRatio(a, b) {
184
+ if (!a || !b)
185
+ return 0;
186
+ const tokensA = new Set(a.toLowerCase().split(/\s+/).filter(Boolean));
187
+ const tokensB = new Set(b.toLowerCase().split(/\s+/).filter(Boolean));
188
+ const intersection = new Set([...tokensA].filter((t) => tokensB.has(t)));
189
+ const diffAB = new Set([...tokensA].filter((t) => !tokensB.has(t)));
190
+ const diffBA = new Set([...tokensB].filter((t) => !tokensA.has(t)));
191
+ const sortedSect = [...intersection].sort().join(" ");
192
+ const combined1A = [sortedSect, [...diffAB].sort().join(" ")]
193
+ .filter(Boolean)
194
+ .join(" ");
195
+ const combined2B = [sortedSect, [...diffBA].sort().join(" ")]
196
+ .filter(Boolean)
197
+ .join(" ");
198
+ return Math.max(fuzzyRatio(sortedSect, combined1A), fuzzyRatio(sortedSect, combined2B), fuzzyRatio(combined1A, combined2B));
199
+ }
200
+ /** Return a 0–100 fuzzy-match score for two already-normalised strings. */
201
+ function score(nameA, nameB, entityType) {
202
+ if (!nameA || !nameB)
203
+ return 0;
204
+ if (entityType === "person")
205
+ return tokenSetRatio(nameA, nameB);
206
+ if (entityType === "ticker" || entityType === "abn") {
207
+ return nameA === nameB ? 100 : 0;
208
+ }
209
+ // Default: company
210
+ return tokenSortRatio(nameA, nameB);
211
+ }
212
+ // ── Mode Implementations ────────────────────────────────────────
213
+ /**
214
+ * Clean and standardise a list of entity names.
215
+ *
216
+ * Each entity dict must have at least `name` and `type`.
217
+ * Returns the list with added `normalizedName`.
218
+ */
219
+ export function modeNormalize(entities) {
220
+ const results = [];
221
+ for (const ent of entities) {
222
+ const name = ent.name ?? "";
223
+ const etype = ent.type ?? "company";
224
+ const norm = normalizeEntity(name, etype);
225
+ const rec = {
226
+ originalName: name,
227
+ normalizedName: norm,
228
+ type: etype,
229
+ };
230
+ if (etype === "ticker") {
231
+ rec.validTicker = validateTicker(name);
232
+ }
233
+ else if (etype === "abn") {
234
+ rec.validAbn = entityValidateAbn(name);
235
+ rec.formattedAbn = entityFormatAbn(name);
236
+ }
237
+ results.push(rec);
238
+ }
239
+ return results;
240
+ }
241
+ /**
242
+ * Find best matches between *entities* and *reference*.
243
+ *
244
+ * Returns a list of matched pairs with confidence scores.
245
+ */
246
+ export function modeMatch(entities, reference, threshold = 80) {
247
+ const results = [];
248
+ for (const ent of entities) {
249
+ const ename = ent.name ?? "";
250
+ const etype = ent.type ?? "company";
251
+ const enorm = normalizeEntity(ename, etype);
252
+ let bestScore = 0;
253
+ let bestRef = null;
254
+ let bestRefNorm = "";
255
+ for (const ref of reference) {
256
+ const rname = ref.name ?? "";
257
+ const rtype = ref.type ?? "company";
258
+ // Only compare same types
259
+ if (rtype !== etype)
260
+ continue;
261
+ const rnorm = normalizeEntity(rname, rtype);
262
+ const s = score(enorm, rnorm, etype);
263
+ if (s > bestScore) {
264
+ bestScore = s;
265
+ bestRef = ref;
266
+ bestRefNorm = rnorm;
267
+ }
268
+ }
269
+ results.push({
270
+ entity: ename,
271
+ entityNormalized: enorm,
272
+ type: etype,
273
+ bestMatch: bestRef?.name ?? null,
274
+ bestMatchNormalized: bestRef ? bestRefNorm : null,
275
+ score: bestScore,
276
+ aboveThreshold: bestScore >= threshold,
277
+ });
278
+ }
279
+ return results;
280
+ }
281
+ /**
282
+ * Cluster entities into unique canonical entries with aliases.
283
+ *
284
+ * Uses single-linkage clustering: if entity A matches B above threshold
285
+ * and B matches C, they all land in the same cluster.
286
+ */
287
+ export function modeResolve(entities, threshold = 80) {
288
+ const items = entities.map((ent) => {
289
+ const name = ent.name ?? "";
290
+ const etype = ent.type ?? "company";
291
+ const norm = normalizeEntity(name, etype);
292
+ return { name, type: etype, norm };
293
+ });
294
+ const n = items.length;
295
+ // Union-Find for clustering
296
+ const parent = Array.from({ length: n }, (_, i) => i);
297
+ function find(x) {
298
+ while (parent[x] !== x) {
299
+ parent[x] = parent[parent[x]];
300
+ x = parent[x];
301
+ }
302
+ return x;
303
+ }
304
+ function union(a, b) {
305
+ const ra = find(a);
306
+ const rb = find(b);
307
+ if (ra !== rb)
308
+ parent[rb] = ra;
309
+ }
310
+ for (let i = 0; i < n; i++) {
311
+ for (let j = i + 1; j < n; j++) {
312
+ if (items[i].type !== items[j].type)
313
+ continue;
314
+ const s = score(items[i].norm, items[j].norm, items[i].type);
315
+ if (s >= threshold) {
316
+ union(i, j);
317
+ }
318
+ }
319
+ }
320
+ const clusters = new Map();
321
+ for (let i = 0; i < n; i++) {
322
+ const root = find(i);
323
+ const members = clusters.get(root);
324
+ if (members) {
325
+ members.push(i);
326
+ }
327
+ else {
328
+ clusters.set(root, [i]);
329
+ }
330
+ }
331
+ const results = [];
332
+ for (const [root, members] of clusters) {
333
+ const canonical = items[root].norm;
334
+ const aliases = [
335
+ ...new Set(members.map((m) => items[m].name)),
336
+ ].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
337
+ results.push({
338
+ canonicalName: canonical,
339
+ type: items[root].type,
340
+ aliases,
341
+ count: members.length,
342
+ });
343
+ }
344
+ return results;
345
+ }
346
+ //# sourceMappingURL=entity-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-resolver.js","sourceRoot":"","sources":["../../src/investigation-tools/entity-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * hotcopper-scraper.ts – HotCopper Forum Scraper for ASX Ticker Sentiment & Rumor Extraction
3
+ *
4
+ * Scrapes HotCopper (https://hotcopper.com.au), Australia's largest stock
5
+ * market forum, for posts related to a given ASX ticker. Extracts post
6
+ * metadata, performs basic keyword-based sentiment analysis, and flags
7
+ * rumor-type posts that may be relevant to insider-trading investigations.
8
+ *
9
+ * DISCLAIMER:
10
+ * This tool is provided for research and educational purposes only.
11
+ * Scraping HotCopper may violate their Terms of Service. Users are
12
+ * solely responsible for ensuring compliance with all applicable laws,
13
+ * regulations, and website terms before using this tool. The authors
14
+ * accept no liability for misuse.
15
+ */
16
+ /** A single scraped HotCopper post. */
17
+ export interface HotCopperPost {
18
+ thread_title: string;
19
+ post_author: string;
20
+ post_date: string;
21
+ post_text: string;
22
+ sentiment_score: number;
23
+ ticker_mentions: string[];
24
+ url: string;
25
+ is_rumor: boolean;
26
+ }
27
+ /** A notable post within a daily sentiment summary. */
28
+ export interface NotablePost {
29
+ title: string;
30
+ sentiment: number;
31
+ url: string;
32
+ }
33
+ /** Aggregated sentiment for a single day. */
34
+ export interface DailySentiment {
35
+ date: string;
36
+ post_count: number;
37
+ avg_sentiment: number;
38
+ notable_posts: NotablePost[];
39
+ }
40
+ /** Full result from a HotCopper scrape run. */
41
+ export interface HotCopperResult {
42
+ ticker: string;
43
+ scraped_at: string;
44
+ period: string;
45
+ total_posts: number;
46
+ posts: HotCopperPost[];
47
+ daily_sentiment: DailySentiment[];
48
+ flagged_rumors: HotCopperPost[];
49
+ overall_sentiment: number;
50
+ }
51
+ /** Options for {@link scrapeHotcopper}. */
52
+ export interface HotCopperOptions {
53
+ /** ASX ticker symbol (e.g. "BHP"). */
54
+ ticker: string;
55
+ /** Look-back period in days (default: 7). */
56
+ days?: number;
57
+ /** Maximum number of discussion list pages to scrape (default: 5). */
58
+ maxPages?: number;
59
+ /** Seconds to wait between HTTP requests (default: 2.0). */
60
+ delay?: number;
61
+ /** Custom User-Agent string. */
62
+ userAgent?: string;
63
+ /** If true, also fetch individual thread pages for full post text. */
64
+ scrapeThreadBodies?: boolean;
65
+ }
66
+ /**
67
+ * Scrape HotCopper discussion pages for the given ASX ticker.
68
+ *
69
+ * @param opts - Scraper options.
70
+ * @returns Structured result with posts, daily sentiment, flagged rumors, etc.
71
+ */
72
+ export declare function scrapeHotcopper(opts: HotCopperOptions): Promise<HotCopperResult>;
73
+ //# sourceMappingURL=hotcopper-scraper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hotcopper-scraper.d.ts","sourceRoot":"","sources":["../../src/investigation-tools/hotcopper-scraper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgDH,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AA6MD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,eAAe,CAAC,CA2G1B"}