shadowjoin 0.0.0-stage → 0.3.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.
package/dist/ui.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Terminal presentation. Everything here is silent unless stdout is a TTY, so
3
+ * piping to a file, to jq, or running in CI produces exactly the same bytes it
4
+ * always did.
5
+ */
6
+ const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
7
+ const DIM = "\x1b[2m", RESET = "\x1b[0m", BOLD = "\x1b[1m";
8
+ const BLUE = "\x1b[38;5;75m", GREY = "\x1b[38;5;244m", AMBER = "\x1b[38;5;214m";
9
+ export const isTty = () => Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
10
+ export function banner(version) {
11
+ if (!isTty())
12
+ return "";
13
+ return [
14
+ "",
15
+ ` ${BLUE}▌${RESET}${BOLD} shadowjoin ${RESET}${GREY}v${version}${RESET}`,
16
+ ` ${BLUE}▌${RESET}${DIM} the matches your join never told you it was losing${RESET}`,
17
+ "",
18
+ ].join("\n");
19
+ }
20
+ export function footer(version) {
21
+ if (!isTty())
22
+ return "";
23
+ return ` ${GREY}shadowjoin v${version} · MIT · Harsh N. Chandak · harsh-chandak.com${RESET}\n`;
24
+ }
25
+ /** A spinner that degrades to nothing when it isn't being watched by a human. */
26
+ export class Progress {
27
+ timer = null;
28
+ frame = 0;
29
+ label = "";
30
+ started = Date.now();
31
+ done = [];
32
+ start(label) {
33
+ this.label = label;
34
+ if (!isTty())
35
+ return;
36
+ if (this.timer)
37
+ return;
38
+ this.timer = setInterval(() => this.paint(), 80);
39
+ this.timer.unref?.();
40
+ this.paint();
41
+ }
42
+ /** Mark the current step finished and begin the next one. */
43
+ step(label) {
44
+ if (isTty()) {
45
+ this.clear();
46
+ process.stdout.write(` ${BLUE}✓${RESET} ${DIM}${this.label}${RESET}\n`);
47
+ }
48
+ this.done.push(this.label);
49
+ this.label = label;
50
+ if (isTty())
51
+ this.paint();
52
+ }
53
+ stop() {
54
+ if (this.timer) {
55
+ clearInterval(this.timer);
56
+ this.timer = null;
57
+ }
58
+ if (!isTty())
59
+ return;
60
+ this.clear();
61
+ const ms = Date.now() - this.started;
62
+ process.stdout.write(` ${BLUE}✓${RESET} ${DIM}${this.label}${RESET} ${GREY}${ms}ms${RESET}\n`);
63
+ }
64
+ /** Clear the spinner without claiming the step succeeded. */
65
+ abort() {
66
+ if (this.timer) {
67
+ clearInterval(this.timer);
68
+ this.timer = null;
69
+ }
70
+ if (isTty())
71
+ this.clear();
72
+ }
73
+ clear() {
74
+ process.stdout.write("\r\x1b[2K");
75
+ }
76
+ paint() {
77
+ const f = FRAMES[this.frame++ % FRAMES.length];
78
+ process.stdout.write(`\r\x1b[2K ${AMBER}${f}${RESET} ${this.label}`);
79
+ }
80
+ }
package/package.json CHANGED
@@ -1,6 +1,83 @@
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.3.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
+ "mysql",
19
+ "sqlite"
20
+ ],
21
+ "license": "MIT",
22
+ "type": "module",
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "bin": {
29
+ "shadowjoin": "dist/cli.js",
30
+ "shadowjoin-mcp": "dist/mcp.js"
31
+ },
32
+ "exports": {
33
+ ".": "./dist/index.js",
34
+ "./mcp": "./dist/mcp.js",
35
+ "./adapters/postgres": "./dist/adapters/postgres.js",
36
+ "./adapters/mysql": "./dist/adapters/mysql.js",
37
+ "./adapters/sqlite": "./dist/adapters/sqlite.js"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "README.md",
42
+ "LICENSE",
43
+ "SKILL.md"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsc",
47
+ "test": "node --test test/*.test.js",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "peerDependencies": {
51
+ "@modelcontextprotocol/sdk": ">=1",
52
+ "mysql2": ">=3",
53
+ "pg": ">=8"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "pg": {
57
+ "optional": true
58
+ },
59
+ "@modelcontextprotocol/sdk": {
60
+ "optional": true
61
+ },
62
+ "mysql2": {
63
+ "optional": true
64
+ }
65
+ },
66
+ "devDependencies": {
67
+ "@modelcontextprotocol/sdk": "^1.32.1",
68
+ "@types/node": "^22.0.0",
69
+ "@types/pg": "^8.23.1",
70
+ "mysql2": "^3.24.5",
71
+ "typescript": "^5.6.0"
72
+ },
73
+ "repository": {
74
+ "type": "git",
75
+ "url": "git+https://github.com/harsh-chandak/shadowjoin.git"
76
+ },
77
+ "homepage": "https://github.com/harsh-chandak/shadowjoin#readme",
78
+ "author": "Harsh N. Chandak",
79
+ "bugs": {
80
+ "url": "https://github.com/harsh-chandak/shadowjoin/issues"
81
+ },
82
+ "mcpName": "io.github.harsh-chandak/shadowjoin"
83
+ }