shadowjoin 0.0.0-stage → 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.
@@ -0,0 +1,87 @@
1
+ /** A single record on one side of the join. */
2
+ export interface Row {
3
+ /** Stable identifier for this row (primary key, line number — anything unique). */
4
+ id: string;
5
+ /** The value actually used as the join key today. */
6
+ key: string;
7
+ /**
8
+ * Optional ground truth. When both sides carry it, every recovered match is
9
+ * verified rather than asserted, and the report gains a precision figure.
10
+ * In the bundled example this is the ticker symbol.
11
+ */
12
+ truth?: string;
13
+ }
14
+ /** A reversible-in-spirit string transform used to build a looser blocking key. */
15
+ export interface Rule {
16
+ id: string;
17
+ label: string;
18
+ apply(value: string): string;
19
+ /** The same transform as a Postgres expression, so it can be pasted into a pipeline. */
20
+ sql(expr: string): string;
21
+ }
22
+ export type Verification = "verified" | "contradicted" | "ambiguous" | "unverifiable";
23
+ export interface RuleContribution {
24
+ rule: string;
25
+ label: string;
26
+ /** Rows recovered by BASE + this rule alone, measured against the exact-match baseline. */
27
+ recovered: number;
28
+ verified: number;
29
+ contradicted: number;
30
+ ambiguous: number;
31
+ }
32
+ /** One step of the greedy stack: the rule, and what adding it actually bought. */
33
+ export interface StackStep {
34
+ rule: string;
35
+ label: string;
36
+ /** Rows recovered by adding this rule that the preceding ones did not already cover. */
37
+ marginal: number;
38
+ /** Total rows recovered by this rule plus everything before it. */
39
+ cumulative: number;
40
+ }
41
+ export interface SamplePair {
42
+ /** Which rule (or "combined") first explained this recovery. */
43
+ rule: string;
44
+ leftId: string;
45
+ leftKey: string;
46
+ rightId: string;
47
+ rightKey: string;
48
+ /** Number of right-hand rows that collapsed onto the same normalized key. */
49
+ candidates: number;
50
+ verification: Verification;
51
+ }
52
+ export interface Report {
53
+ generatedAt: string;
54
+ leftRows: number;
55
+ rightRows: number;
56
+ /** Rows matched by the join exactly as it runs today. */
57
+ exactMatches: number;
58
+ exactMatchRate: number;
59
+ /** Rows the shadow matcher recovered on top of the exact baseline. */
60
+ recovered: number;
61
+ /** Recovered rows with no candidate under any enabled rule. */
62
+ unexplained: number;
63
+ /** exactMatches + recovered, over leftRows. */
64
+ reachableMatchRate: number;
65
+ /**
66
+ * Lower bound on the share of left rows whose match the exact join is losing.
67
+ * A FLOOR, not an estimate: the shadow matcher's own recall is unknown, so the
68
+ * true loss can only be larger.
69
+ */
70
+ lowerBoundLossPct: number;
71
+ byRule: RuleContribution[];
72
+ /**
73
+ * Rules in the order worth adopting, chosen greedily by marginal recovery.
74
+ * This is the actionable half of the report: most recoveries need a
75
+ * combination, so the individual figures in `byRule` under-state them.
76
+ */
77
+ stack: StackStep[];
78
+ verified: number;
79
+ contradicted: number;
80
+ ambiguous: number;
81
+ unverifiable: number;
82
+ /** verified / (verified + contradicted), or null when no ground truth was supplied. */
83
+ precision: number | null;
84
+ samplePairs: SamplePair[];
85
+ warnings: string[];
86
+ rulesApplied: string[];
87
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,73 @@
1
1
  {
2
2
  "name": "shadowjoin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Measure the matches your exact join is silently dropping. Runs a shadow matcher alongside your real join key and reports a verified floor on lost matches, plus the normalization rule that would recover each one.",
5
+ "keywords": [
6
+ "entity-resolution",
7
+ "record-linkage",
8
+ "data-quality",
9
+ "fuzzy-matching",
10
+ "deduplication",
11
+ "pair-completeness",
12
+ "join",
13
+ "postgres",
14
+ "csv",
15
+ "mcp",
16
+ "data-validation",
17
+ "recall"
18
+ ],
19
+ "license": "MIT",
20
+ "type": "module",
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "main": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "bin": {
27
+ "shadowjoin": "dist/cli.js",
28
+ "shadowjoin-mcp": "dist/mcp.js"
29
+ },
30
+ "exports": {
31
+ ".": "./dist/index.js",
32
+ "./mcp": "./dist/mcp.js",
33
+ "./adapters/postgres": "./dist/adapters/postgres.js"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE",
39
+ "SKILL.md"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsc",
43
+ "test": "node --test test/",
44
+ "prepublishOnly": "npm run build"
45
+ },
46
+ "peerDependencies": {
47
+ "@modelcontextprotocol/sdk": ">=1",
48
+ "pg": ">=8"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "pg": {
52
+ "optional": true
53
+ },
54
+ "@modelcontextprotocol/sdk": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "devDependencies": {
59
+ "@modelcontextprotocol/sdk": "^1.32.1",
60
+ "@types/node": "^22.0.0",
61
+ "@types/pg": "^8.23.1",
62
+ "typescript": "^5.6.0"
63
+ },
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "git+https://github.com/harsh-chandak/shadowjoin.git"
67
+ },
68
+ "homepage": "https://github.com/harsh-chandak/shadowjoin#readme",
69
+ "author": "Harsh N. Chandak",
70
+ "bugs": {
71
+ "url": "https://github.com/harsh-chandak/shadowjoin/issues"
72
+ }
73
+ }