json-term 1.0.0 → 1.2.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/README.md CHANGED
@@ -49,8 +49,14 @@ npm install json-term
49
49
  # View local JSON file
50
50
  jsonx package.json
51
51
 
52
+ # Directly parse inline JSON string
53
+ jsonx '{"service": "auth-api", "port": 443, "active": true, "tags": ["prod", "v2"]}'
54
+
52
55
  # View piped API response
53
56
  curl -s https://api.github.com/repos/nodejs/node | jsonx
57
+
58
+ # Echo / pipe JSON string
59
+ echo '{"status": 200, "data": [1, 2, 3]}' | jsonx
54
60
  ```
55
61
 
56
62
  ### 2. Structural JSON Diffing
@@ -58,22 +64,42 @@ curl -s https://api.github.com/repos/nodejs/node | jsonx
58
64
  # Compare two JSON configuration files
59
65
  jsonx diff config.old.json config.new.json
60
66
 
61
- # Diff against piped stdin
67
+ # Diff two direct inline JSON strings
68
+ jsonx diff '{"port": 8080, "debug": true}' '{"port": 8443, "debug": true, "ssl": true}'
69
+
70
+ # Diff file against piped stdin
62
71
  curl -s https://api.com/v2/config | jsonx diff config.v1.json
63
72
 
64
73
  # Hide unchanged lines in diff
65
74
  jsonx diff a.json b.json --no-unchanged
66
75
  ```
67
76
 
68
- ### 3. Non-Interactive Pretty-Print (for scripts & CI)
77
+ ### 3. Simple Colorized JSON View (jq-style with Bold Keys)
78
+ ```bash
79
+ # Print syntax-highlighted standard JSON format from file
80
+ jsonx --raw data.json
81
+
82
+ # Print syntax-highlighted direct JSON string
83
+ jsonx --raw '{"user": "alice", "roles": ["admin", "developer"]}'
84
+
85
+ # Piped stream
86
+ curl -s https://api.github.com/users/octocat | jsonx --raw
87
+ ```
88
+
89
+ ### 4. Non-Interactive Pretty Tree Print (for scripts & CI)
69
90
  ```bash
70
91
  # Print formatted tree to stdout
71
92
  jsonx --print data.json
72
93
 
94
+ # Pretty print inline JSON string
95
+ jsonx --print '{"name": "test", "count": 42}'
96
+
73
97
  # Set initial collapse depth (e.g. depth 2)
74
98
  jsonx data.json --depth 2 --print
75
99
  ```
76
100
 
101
+
102
+
77
103
  ---
78
104
 
79
105
  ## ⌨️ Interactive TUI Keybindings
@@ -112,11 +138,15 @@ const data = {
112
138
  active: true,
113
139
  };
114
140
 
115
- // 1. Pretty Print Formatted Tree String
141
+ // 1. Syntax-Highlighted Standard JSON String
142
+ const colored = colorizeJSON(data);
143
+ console.log(colored);
144
+
145
+ // 2. Pretty Print Formatted Tree String
116
146
  const tree = buildTree(data, "myService", "$", 0, 2);
117
147
  console.log(prettyPrintTree(tree));
118
148
 
119
- // 2. Compute Structural Diff
149
+ // 3. Compute Structural Diff
120
150
  const oldConfig = { port: 8080, debug: true };
121
151
  const newConfig = { port: 8443, debug: true, ssl: true };
122
152
 
@@ -125,7 +155,7 @@ const stats = calculateDiffStats(diffTree);
125
155
  console.log(`Changes: +${stats.added} -${stats.removed} ~${stats.modified}`);
126
156
  console.log(prettyPrintDiff(diffTree));
127
157
 
128
- // 3. Launch Interactive TUI Programmatically
158
+ // 4. Launch Interactive TUI Programmatically
129
159
  const viewer = new InteractiveViewer(tree);
130
160
  await viewer.start();
131
161
  ```
@@ -136,6 +166,8 @@ await viewer.start();
136
166
 
137
167
  | Option | Shorthand | Default | Description |
138
168
  | :--- | :--- | :--- | :--- |
169
+ | `--raw` | `-r` | `false` | Print standard syntax-highlighted JSON (bold keys & typed colors) |
170
+ | `--format <type>` | `-F` | `"tree"` | Output format (`tree` or `json`) |
139
171
  | `--print` | `-p` | `false` | Non-interactive formatted print to stdout |
140
172
  | `--depth <number>` | `-d` | `2` | Initial tree expansion depth |
141
173
  | `--search <query>` | `-s` | `""` | Initial filter query for keys/values |
@@ -143,6 +175,7 @@ await viewer.start();
143
175
  | `--version` | `-v` | | Output version |
144
176
  | `--help` | `-h` | | Display help menu |
145
177
 
178
+
146
179
  ---
147
180
 
148
181
  ## 🧪 Development & Testing
package/dist/cli.js CHANGED
@@ -137,6 +137,29 @@ function prettyPrintTree(node, indent = "", isLast = true) {
137
137
  }
138
138
  return lines.join("\n");
139
139
  }
140
+ function colorizeJSON(data, space = 2) {
141
+ const jsonStr = typeof data === "string" ? data : JSON.stringify(data, null, space);
142
+ if (!jsonStr) return "";
143
+ return jsonStr.replace(
144
+ /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
145
+ (match) => {
146
+ if (/^"/.test(match)) {
147
+ if (/:$/.test(match)) {
148
+ const keyName = match.slice(0, -1);
149
+ return import_picocolors.default.bold(import_picocolors.default.cyan(keyName)) + import_picocolors.default.dim(":");
150
+ }
151
+ return import_picocolors.default.green(match);
152
+ }
153
+ if (/true|false/.test(match)) {
154
+ return import_picocolors.default.magenta(match);
155
+ }
156
+ if (/null/.test(match)) {
157
+ return import_picocolors.default.dim(match);
158
+ }
159
+ return import_picocolors.default.yellow(match);
160
+ }
161
+ );
162
+ }
140
163
 
141
164
  // src/differ.ts
142
165
  var import_picocolors2 = __toESM(require("picocolors"));
@@ -635,26 +658,41 @@ function parseJSON(raw, label = "Input") {
635
658
  throw new Error(`Invalid JSON in ${label}: ${err.message}`);
636
659
  }
637
660
  }
661
+ async function resolveJSONInput(inputArg) {
662
+ if (inputArg) {
663
+ const trimmed = inputArg.trim();
664
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
665
+ return { raw: trimmed, label: "inline JSON" };
666
+ }
667
+ if (import_fs.default.existsSync(inputArg)) {
668
+ const raw = await import_fs.default.promises.readFile(inputArg, "utf-8");
669
+ return { raw, label: inputArg };
670
+ }
671
+ try {
672
+ JSON.parse(trimmed);
673
+ return { raw: trimmed, label: "inline JSON" };
674
+ } catch {
675
+ }
676
+ throw new Error(`File not found or invalid inline JSON: ${inputArg}`);
677
+ }
678
+ const stdinData = await readStdin();
679
+ return { raw: stdinData, label: "stdin" };
680
+ }
638
681
  async function main() {
639
682
  const program = new import_commander.Command();
640
- program.name("json-term").description("High-fidelity interactive terminal JSON viewer, collapsible tree explorer, type syntax highlighter, and structural diff tool.").version("1.0.0", "-v, --version", "Output the current version").argument("[file]", "JSON file to view or compare").option("-p, --print", "Print formatted non-interactive tree to stdout", false).option("-d, --depth <number>", "Initial tree expansion depth", (val) => parseInt(val, 10), 2).option("-s, --search <query>", "Filter tree by key or value query").action(async (fileArg, options) => {
683
+ program.name("json-term").description("High-fidelity interactive terminal JSON viewer, collapsible tree explorer, type syntax highlighter, and structural diff tool.").version("1.2.0", "-v, --version", "Output the current version").argument("[input]", "JSON file path, inline JSON string, or piped stdin").option("-p, --print", "Print formatted non-interactive output to stdout", false).option("-F, --format <format>", "Output format: 'tree' or 'json'", "tree").option("-r, --raw", "Print standard syntax-highlighted JSON (shorthand for --format json --print)", false).option("-d, --depth <number>", "Initial tree expansion depth", (val) => parseInt(val, 10), 2).option("-s, --search <query>", "Filter tree by key or value query").action(async (inputArg, options) => {
641
684
  try {
642
- let raw = "";
643
- if (fileArg) {
644
- if (!import_fs.default.existsSync(fileArg)) {
645
- console.error(import_picocolors4.default.red(`Error: File not found: ${fileArg}`));
646
- process.exit(1);
647
- }
648
- raw = await import_fs.default.promises.readFile(fileArg, "utf-8");
649
- } else {
650
- raw = await readStdin();
651
- }
685
+ const { raw, label } = await resolveJSONInput(inputArg);
652
686
  if (!raw) {
653
687
  program.help();
654
688
  process.exit(1);
655
689
  }
656
- const data = parseJSON(raw, fileArg || "stdin");
657
- const tree = buildTree(data, fileArg || "root", "$", 0, options.depth);
690
+ const data = parseJSON(raw, label);
691
+ if (options.raw || options.format === "json") {
692
+ console.log(colorizeJSON(data));
693
+ return;
694
+ }
695
+ const tree = buildTree(data, label, "$", 0, options.depth);
658
696
  if (options.print || !process.stdin.isTTY) {
659
697
  console.log(prettyPrintTree(tree));
660
698
  } else {
@@ -666,31 +704,21 @@ async function main() {
666
704
  process.exit(1);
667
705
  }
668
706
  });
669
- program.command("diff <fileA> [fileB]").description("Structural JSON diff between two files or stdin").option("-p, --print", "Print formatted non-interactive diff to stdout", false).option("-d, --depth <number>", "Initial expansion depth", (val) => parseInt(val, 10), 3).option("--no-unchanged", "Hide unchanged nodes from the diff view", false).action(async (fileA, fileB, diffOptions) => {
707
+ program.command("diff <inputA> [inputB]").description("Structural JSON diff between files, inline JSON strings, or stdin").option("-p, --print", "Print formatted non-interactive diff to stdout", false).option("-d, --depth <number>", "Initial expansion depth", (val) => parseInt(val, 10), 3).option("--no-unchanged", "Hide unchanged nodes from the diff view", false).action(async (inputA, inputB, diffOptions) => {
670
708
  try {
671
- if (!import_fs.default.existsSync(fileA)) {
672
- console.error(import_picocolors4.default.red(`Error: File not found: ${fileA}`));
709
+ const itemA = await resolveJSONInput(inputA);
710
+ if (!itemA.raw) {
711
+ console.error(import_picocolors4.default.red("Error: Please provide valid first JSON input (file or string)."));
673
712
  process.exit(1);
674
713
  }
675
- const rawA = await import_fs.default.promises.readFile(fileA, "utf-8");
676
- const dataA = parseJSON(rawA, fileA);
677
- let dataB;
678
- if (fileB) {
679
- if (!import_fs.default.existsSync(fileB)) {
680
- console.error(import_picocolors4.default.red(`Error: File not found: ${fileB}`));
681
- process.exit(1);
682
- }
683
- const rawB = await import_fs.default.promises.readFile(fileB, "utf-8");
684
- dataB = parseJSON(rawB, fileB);
685
- } else {
686
- const stdinData = await readStdin();
687
- if (!stdinData) {
688
- console.error(import_picocolors4.default.red("Error: Please specify second file or pipe JSON via stdin."));
689
- process.exit(1);
690
- }
691
- dataB = parseJSON(stdinData, "stdin");
714
+ const dataA = parseJSON(itemA.raw, itemA.label);
715
+ const itemB = await resolveJSONInput(inputB);
716
+ if (!itemB.raw) {
717
+ console.error(import_picocolors4.default.red("Error: Please specify second JSON input (file, string, or piped stdin)."));
718
+ process.exit(1);
692
719
  }
693
- const diffTree = diffJSON(dataA, dataB, `${fileA} \u2194 ${fileB || "stdin"}`, "$", 0, diffOptions.depth);
720
+ const dataB = parseJSON(itemB.raw, itemB.label);
721
+ const diffTree = diffJSON(dataA, dataB, `${itemA.label} \u2194 ${itemB.label}`, "$", 0, diffOptions.depth);
694
722
  const stats = calculateDiffStats(diffTree);
695
723
  if (diffOptions.print || !process.stdin.isTTY) {
696
724
  console.log(
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/formatter.ts","../src/differ.ts","../src/interactive.ts","../src/utils/clipboard.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport pc from \"picocolors\";\nimport { buildTree, prettyPrintTree } from \"./formatter.js\";\nimport { diffJSON, prettyPrintDiff, calculateDiffStats } from \"./differ.js\";\nimport { InteractiveViewer } from \"./interactive.js\";\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let data = \"\";\n if (process.stdin.isTTY) {\n resolve(\"\");\n return;\n }\n\n process.stdin.setEncoding(\"utf-8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => {\n resolve(data.trim());\n });\n setTimeout(() => {\n if (!data) resolve(\"\");\n }, 100);\n });\n}\n\nfunction parseJSON(raw: string, label = \"Input\"): any {\n try {\n return JSON.parse(raw);\n } catch (err: any) {\n throw new Error(`Invalid JSON in ${label}: ${err.message}`);\n }\n}\n\nasync function main() {\n const program = new Command();\n\n program\n .name(\"json-term\")\n .description(\"High-fidelity interactive terminal JSON viewer, collapsible tree explorer, type syntax highlighter, and structural diff tool.\")\n .version(\"1.0.0\", \"-v, --version\", \"Output the current version\")\n .argument(\"[file]\", \"JSON file to view or compare\")\n .option(\"-p, --print\", \"Print formatted non-interactive tree to stdout\", false)\n .option(\"-d, --depth <number>\", \"Initial tree expansion depth\", (val) => parseInt(val, 10), 2)\n .option(\"-s, --search <query>\", \"Filter tree by key or value query\")\n .action(async (fileArg, options) => {\n try {\n let raw = \"\";\n if (fileArg) {\n if (!fs.existsSync(fileArg)) {\n console.error(pc.red(`Error: File not found: ${fileArg}`));\n process.exit(1);\n }\n raw = await fs.promises.readFile(fileArg, \"utf-8\");\n } else {\n raw = await readStdin();\n }\n\n if (!raw) {\n program.help();\n process.exit(1);\n }\n\n const data = parseJSON(raw, fileArg || \"stdin\");\n const tree = buildTree(data, fileArg || \"root\", \"$\", 0, options.depth);\n\n if (options.print || !process.stdin.isTTY) {\n console.log(prettyPrintTree(tree));\n } else {\n const viewer = new InteractiveViewer(tree, false);\n await viewer.start();\n }\n } catch (err: any) {\n console.error(pc.red(`Error: ${err.message || err}`));\n process.exit(1);\n }\n });\n\n // Diff subcommand\n program\n .command(\"diff <fileA> [fileB]\")\n .description(\"Structural JSON diff between two files or stdin\")\n .option(\"-p, --print\", \"Print formatted non-interactive diff to stdout\", false)\n .option(\"-d, --depth <number>\", \"Initial expansion depth\", (val) => parseInt(val, 10), 3)\n .option(\"--no-unchanged\", \"Hide unchanged nodes from the diff view\", false)\n .action(async (fileA, fileB, diffOptions) => {\n try {\n if (!fs.existsSync(fileA)) {\n console.error(pc.red(`Error: File not found: ${fileA}`));\n process.exit(1);\n }\n const rawA = await fs.promises.readFile(fileA, \"utf-8\");\n const dataA = parseJSON(rawA, fileA);\n\n let dataB: any;\n if (fileB) {\n if (!fs.existsSync(fileB)) {\n console.error(pc.red(`Error: File not found: ${fileB}`));\n process.exit(1);\n }\n const rawB = await fs.promises.readFile(fileB, \"utf-8\");\n dataB = parseJSON(rawB, fileB);\n } else {\n const stdinData = await readStdin();\n if (!stdinData) {\n console.error(pc.red(\"Error: Please specify second file or pipe JSON via stdin.\"));\n process.exit(1);\n }\n dataB = parseJSON(stdinData, \"stdin\");\n }\n\n const diffTree = diffJSON(dataA, dataB, `${fileA} ↔ ${fileB || \"stdin\"}`, \"$\", 0, diffOptions.depth);\n const stats = calculateDiffStats(diffTree);\n\n if (diffOptions.print || !process.stdin.isTTY) {\n console.log(\n pc.bold(\n `Diff Stats: ${pc.green(`+${stats.added}`)} ${pc.red(`-${stats.removed}`)} ${pc.yellow(`~${stats.modified}`)} ${pc.dim(`=${stats.unchanged}`)}\\n`\n )\n );\n console.log(prettyPrintDiff(diffTree, { showUnchanged: diffOptions.unchanged }));\n } else {\n const viewer = new InteractiveViewer(diffTree, true);\n await viewer.start();\n }\n } catch (err: any) {\n console.error(pc.red(`Error: ${err.message || err}`));\n process.exit(1);\n }\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err) => {\n console.error(pc.red(`Error: ${err.message || err}`));\n process.exit(1);\n});\n","import pc from \"picocolors\";\nimport { JSONValueType, TreeNode } from \"./types.js\";\n\n/**\n * Determine the JSON value type.\n */\nexport function getValueType(value: any): JSONValueType {\n if (value === null || value === undefined) return \"null\";\n if (Array.isArray(value)) return \"array\";\n const t = typeof value;\n if (t === \"object\") return \"object\";\n if (t === \"string\") return \"string\";\n if (t === \"number\") return \"number\";\n if (t === \"boolean\") return \"boolean\";\n return \"string\";\n}\n\n/**\n * Colorize and format a single JSON value according to its type.\n */\nexport function formatValue(value: any, truncateLen = 80): string {\n const type = getValueType(value);\n\n switch (type) {\n case \"string\": {\n const str = JSON.stringify(value);\n const truncated = str.length > truncateLen ? str.slice(0, truncateLen - 3) + '...\"' : str;\n return pc.green(truncated);\n }\n case \"number\":\n return pc.yellow(String(value));\n case \"boolean\":\n return pc.magenta(String(value));\n case \"null\":\n return pc.dim(\"null\");\n case \"array\":\n return pc.dim(`[${value.length} items]`);\n case \"object\":\n return pc.dim(`{${Object.keys(value).length} keys}`);\n default:\n return String(value);\n }\n}\n\n/**\n * Format an object key with bold coloring.\n */\nexport function formatKey(key: string): string {\n return pc.bold(pc.cyan(key));\n}\n\n/**\n * Build a flat or nested TreeNode hierarchy from any JavaScript/JSON data.\n */\nexport function buildTree(\n data: any,\n key?: string,\n path = \"$\",\n depth = 0,\n maxInitialDepth = 2,\n parentId?: string\n): TreeNode {\n const type = getValueType(data);\n const id = `${path}-${key || \"root\"}-${depth}`;\n const collapsed = depth >= maxInitialDepth;\n\n const node: TreeNode = {\n id,\n key,\n value: data,\n type,\n depth,\n path,\n collapsed,\n parentId,\n };\n\n if (type === \"object\" && data !== null) {\n const keys = Object.keys(data);\n node.itemCount = keys.length;\n node.children = keys.map((k) =>\n buildTree(\n data[k],\n k,\n path === \"$\" ? k : `${path}.${k}`,\n depth + 1,\n maxInitialDepth,\n id\n )\n );\n } else if (type === \"array\") {\n node.itemCount = data.length;\n node.children = data.map((item: any, idx: number) =>\n buildTree(\n item,\n `[${idx}]`,\n `${path}[${idx}]`,\n depth + 1,\n maxInitialDepth,\n id\n )\n );\n }\n\n return node;\n}\n\n/**\n * Pretty-print JSON tree structure to terminal string.\n */\nexport function prettyPrintTree(node: TreeNode, indent = \"\", isLast = true): string {\n const isRoot = node.depth === 0;\n const marker = node.type === \"object\" || node.type === \"array\" ? (node.collapsed ? pc.cyan(\"▶ \") : pc.cyan(\"▼ \")) : pc.dim(\"• \");\n\n let line = \"\";\n if (!isRoot) {\n const branch = isLast ? \"└── \" : \"├── \";\n line += pc.dim(indent + branch) + marker;\n if (node.key) {\n line += formatKey(node.key) + pc.dim(\": \");\n }\n } else {\n line += marker + (node.key ? formatKey(node.key) + pc.dim(\": \") : pc.bold(pc.white(\"root: \")));\n }\n\n if (node.type === \"object\") {\n line += pc.dim(`{ ${node.itemCount} keys }`);\n } else if (node.type === \"array\") {\n line += pc.dim(`[ ${node.itemCount} items ]`);\n } else {\n line += formatValue(node.value);\n }\n\n const lines = [line];\n\n if (!node.collapsed && node.children && node.children.length > 0) {\n const nextIndent = isRoot ? \"\" : indent + (isLast ? \" \" : \"│ \");\n node.children.forEach((child, index) => {\n const childIsLast = index === node.children!.length - 1;\n lines.push(prettyPrintTree(child, nextIndent, childIsLast));\n });\n }\n\n return lines.join(\"\\n\");\n}\n","import pc from \"picocolors\";\nimport { DiffNode, DiffStatus, JSONValueType } from \"./types.js\";\nimport { getValueType, formatValue, formatKey } from \"./formatter.js\";\n\nexport interface DiffStats {\n added: number;\n removed: number;\n modified: number;\n unchanged: number;\n}\n\n/**\n * Compare two JSON values structurally and return a recursive DiffNode tree.\n */\nexport function diffJSON(\n a: any,\n b: any,\n key?: string,\n path = \"$\",\n depth = 0,\n maxInitialDepth = 3\n): DiffNode {\n const typeA = getValueType(a);\n const typeB = getValueType(b);\n const id = `${path}-${key || \"diff\"}-${depth}`;\n const collapsed = depth >= maxInitialDepth;\n\n // Case 1: Value added (a is undefined, b exists)\n if (a === undefined && b !== undefined) {\n const node: DiffNode = {\n id,\n key,\n path,\n depth,\n status: \"added\",\n newValue: b,\n type: typeB,\n collapsed: false,\n };\n if (typeB === \"object\" && b !== null) {\n node.itemCount = Object.keys(b).length;\n node.children = Object.keys(b).map((k) =>\n diffJSON(undefined, b[k], k, path === \"$\" ? k : `${path}.${k}`, depth + 1, maxInitialDepth)\n );\n } else if (typeB === \"array\") {\n node.itemCount = b.length;\n node.children = b.map((item: any, idx: number) =>\n diffJSON(undefined, item, `[${idx}]`, `${path}[${idx}]`, depth + 1, maxInitialDepth)\n );\n }\n return node;\n }\n\n // Case 2: Value removed (a exists, b is undefined)\n if (a !== undefined && b === undefined) {\n const node: DiffNode = {\n id,\n key,\n path,\n depth,\n status: \"removed\",\n oldValue: a,\n type: typeA,\n collapsed: false,\n };\n if (typeA === \"object\" && a !== null) {\n node.itemCount = Object.keys(a).length;\n node.children = Object.keys(a).map((k) =>\n diffJSON(a[k], undefined, k, path === \"$\" ? k : `${path}.${k}`, depth + 1, maxInitialDepth)\n );\n } else if (typeA === \"array\") {\n node.itemCount = a.length;\n node.children = a.map((item: any, idx: number) =>\n diffJSON(item, undefined, `[${idx}]`, `${path}[${idx}]`, depth + 1, maxInitialDepth)\n );\n }\n return node;\n }\n\n // Case 3: Type changed (e.g. from string to number or object to array)\n if (typeA !== typeB) {\n return {\n id,\n key,\n path,\n depth,\n status: \"modified\",\n oldValue: a,\n newValue: b,\n type: typeB,\n collapsed: false,\n };\n }\n\n // Case 4: Both are Objects\n if (typeA === \"object\" && a !== null && b !== null) {\n const allKeys = Array.from(new Set([...Object.keys(a), ...Object.keys(b)]));\n const children: DiffNode[] = [];\n let hasModifications = false;\n\n for (const k of allKeys) {\n const child = diffJSON(\n a[k],\n b[k],\n k,\n path === \"$\" ? k : `${path}.${k}`,\n depth + 1,\n maxInitialDepth\n );\n if (child.status !== \"unchanged\") hasModifications = true;\n children.push(child);\n }\n\n return {\n id,\n key,\n path,\n depth,\n status: hasModifications ? \"modified\" : \"unchanged\",\n oldValue: a,\n newValue: b,\n type: \"object\",\n itemCount: allKeys.length,\n collapsed,\n children,\n };\n }\n\n // Case 5: Both are Arrays\n if (typeA === \"array\") {\n const maxLen = Math.max(a.length, b.length);\n const children: DiffNode[] = [];\n let hasModifications = false;\n\n for (let i = 0; i < maxLen; i++) {\n const child = diffJSON(\n a[i],\n b[i],\n `[${i}]`,\n `${path}[${i}]`,\n depth + 1,\n maxInitialDepth\n );\n if (child.status !== \"unchanged\") hasModifications = true;\n children.push(child);\n }\n\n return {\n id,\n key,\n path,\n depth,\n status: hasModifications ? \"modified\" : \"unchanged\",\n oldValue: a,\n newValue: b,\n type: \"array\",\n itemCount: maxLen,\n collapsed,\n children,\n };\n }\n\n // Case 6: Primitive values comparison\n if (a !== b) {\n return {\n id,\n key,\n path,\n depth,\n status: \"modified\",\n oldValue: a,\n newValue: b,\n type: typeB,\n collapsed: false,\n };\n }\n\n // Unchanged primitive\n return {\n id,\n key,\n path,\n depth,\n status: \"unchanged\",\n oldValue: a,\n newValue: b,\n type: typeA,\n collapsed,\n };\n}\n\n/**\n * Calculate summary metrics for the diff.\n */\nexport function calculateDiffStats(node: DiffNode): DiffStats {\n const stats: DiffStats = { added: 0, removed: 0, modified: 0, unchanged: 0 };\n\n function traverse(n: DiffNode) {\n if (n.children && n.children.length > 0) {\n n.children.forEach(traverse);\n } else {\n stats[n.status]++;\n }\n }\n\n traverse(node);\n return stats;\n}\n\n/**\n * Pretty-print structural diff to terminal string.\n */\nexport function prettyPrintDiff(\n node: DiffNode,\n options: { showUnchanged?: boolean } = {},\n indent = \"\",\n isLast = true\n): string {\n const showUnchanged = options.showUnchanged ?? true;\n\n if (!showUnchanged && node.status === \"unchanged\" && (!node.children || node.children.length === 0)) {\n return \"\";\n }\n\n const isRoot = node.depth === 0;\n let prefix = \" \";\n let statusColor = pc.dim;\n\n if (node.status === \"added\") {\n prefix = pc.green(\"+ \");\n statusColor = pc.green;\n } else if (node.status === \"removed\") {\n prefix = pc.red(\"- \");\n statusColor = pc.red;\n } else if (node.status === \"modified\") {\n prefix = pc.yellow(\"~ \");\n statusColor = pc.yellow;\n }\n\n let line = prefix;\n if (!isRoot) {\n const branch = isLast ? \"└── \" : \"├── \";\n line += pc.dim(indent + branch);\n if (node.key) {\n line += formatKey(node.key) + pc.dim(\": \");\n }\n } else {\n line += node.key ? formatKey(node.key) + pc.dim(\": \") : pc.bold(\"diff: \");\n }\n\n if (node.status === \"added\") {\n line += statusColor(formatValue(node.newValue));\n } else if (node.status === \"removed\") {\n line += statusColor(formatValue(node.oldValue));\n } else if (node.status === \"modified\") {\n if (node.type === \"object\" || node.type === \"array\") {\n line += statusColor(`{ ${node.itemCount} items modified }`);\n } else {\n line += `${pc.red(formatValue(node.oldValue))} ${pc.dim(\"->\")} ${pc.green(formatValue(node.newValue))}`;\n }\n } else {\n // Unchanged\n line += pc.dim(formatValue(node.newValue));\n }\n\n const lines = [line];\n\n if (!node.collapsed && node.children && node.children.length > 0) {\n const nextIndent = isRoot ? \"\" : indent + (isLast ? \" \" : \"│ \");\n node.children.forEach((child, index) => {\n const childIsLast = index === node.children!.length - 1;\n const childStr = prettyPrintDiff(child, options, nextIndent, childIsLast);\n if (childStr) lines.push(childStr);\n });\n }\n\n return lines.join(\"\\n\");\n}\n","import readline from \"readline\";\nimport pc from \"picocolors\";\nimport { TreeNode, DiffNode } from \"./types.js\";\nimport { formatKey, formatValue } from \"./formatter.js\";\nimport { copyToClipboard } from \"./utils/clipboard.js\";\n\ntype AnyNode = TreeNode | DiffNode;\n\ninterface FlatItem {\n node: AnyNode;\n depth: number;\n isDiff: boolean;\n}\n\n/**\n * Interactive Terminal Tree Explorer for JSON data and Diffs.\n */\nexport class InteractiveViewer {\n private root: AnyNode;\n private flatList: FlatItem[] = [];\n private selectedIndex = 0;\n private searchQuery = \"\";\n private isSearching = false;\n private searchInput = \"\";\n private notification = \"\";\n private notificationTimer: NodeJS.Timeout | null = null;\n private isDiff: boolean;\n\n constructor(root: AnyNode, isDiff = false) {\n this.root = root;\n this.isDiff = isDiff;\n this.rebuildFlatList();\n }\n\n private isDiffNode(node: AnyNode): node is DiffNode {\n return this.isDiff && \"status\" in node;\n }\n\n private rebuildFlatList(): void {\n const list: FlatItem[] = [];\n\n const traverse = (node: AnyNode, currentDepth: number) => {\n // If search query is active, filter matching nodes or their parents\n if (this.searchQuery) {\n const query = this.searchQuery.toLowerCase();\n const keyMatch = node.key && node.key.toLowerCase().includes(query);\n const pathMatch = node.path && node.path.toLowerCase().includes(query);\n const valMatch =\n \"value\" in node\n ? JSON.stringify(node.value).toLowerCase().includes(query)\n : \"newValue\" in node\n ? JSON.stringify(node.newValue).toLowerCase().includes(query)\n : false;\n\n if (!keyMatch && !pathMatch && !valMatch && (!node.children || node.children.length === 0)) {\n return;\n }\n }\n\n list.push({ node, depth: currentDepth, isDiff: this.isDiff });\n\n if (!node.collapsed && node.children && node.children.length > 0) {\n for (const child of node.children) {\n traverse(child, currentDepth + 1);\n }\n }\n };\n\n traverse(this.root, 0);\n this.flatList = list;\n\n if (this.selectedIndex >= this.flatList.length) {\n this.selectedIndex = Math.max(0, this.flatList.length - 1);\n }\n }\n\n private setNotification(msg: string): void {\n this.notification = msg;\n if (this.notificationTimer) clearTimeout(this.notificationTimer);\n this.notificationTimer = setTimeout(() => {\n this.notification = \"\";\n this.render();\n }, 2500);\n }\n\n private render(): void {\n const rows = process.stdout.rows || 24;\n const maxVisibleRows = Math.max(5, rows - 5);\n\n // Calculate window scroll offset\n let startIdx = 0;\n if (this.selectedIndex >= maxVisibleRows) {\n startIdx = this.selectedIndex - maxVisibleRows + 1;\n }\n const endIdx = Math.min(this.flatList.length, startIdx + maxVisibleRows);\n\n // Clear terminal screen\n process.stdout.write(\"\\x1b[2J\\x1b[3J\\x1b[H\\x1b[?25l\"); // hide cursor\n\n // 1. Header Bar\n const title = this.isDiff ? \" JSON Structural Diff Explorer \" : \" Interactive JSON Tree Explorer \";\n console.log(pc.bgCyan(pc.black(pc.bold(` ${title} `)) + pc.dim(` [Total items: ${this.flatList.length}]`)));\n\n // 2. Render visible items\n for (let i = startIdx; i < endIdx; i++) {\n const { node, depth } = this.flatList[i];\n const isSelected = i === this.selectedIndex;\n const indent = \" \".repeat(depth);\n\n let marker = \"• \";\n if (node.children && node.children.length > 0) {\n marker = node.collapsed ? \"▶ \" : \"▼ \";\n }\n\n let lineContent = \"\";\n\n if (this.isDiffNode(node)) {\n let diffPrefix = \" \";\n if (node.status === \"added\") diffPrefix = pc.green(\"+ \");\n else if (node.status === \"removed\") diffPrefix = pc.red(\"- \");\n else if (node.status === \"modified\") diffPrefix = pc.yellow(\"~ \");\n\n lineContent = diffPrefix + indent + marker;\n if (node.key) lineContent += formatKey(node.key) + pc.dim(\": \");\n\n if (node.status === \"added\") {\n lineContent += pc.green(formatValue(node.newValue, 50));\n } else if (node.status === \"removed\") {\n lineContent += pc.red(formatValue(node.oldValue, 50));\n } else if (node.status === \"modified\") {\n if (node.type === \"object\" || node.type === \"array\") {\n lineContent += pc.yellow(`{ ${node.itemCount} modified }`);\n } else {\n lineContent += `${pc.red(formatValue(node.oldValue, 25))} -> ${pc.green(formatValue(node.newValue, 25))}`;\n }\n } else {\n lineContent += pc.dim(formatValue(node.newValue, 50));\n }\n } else {\n const treeNode = node as TreeNode;\n lineContent = indent + marker;\n if (treeNode.key) lineContent += formatKey(treeNode.key) + pc.dim(\": \");\n\n if (treeNode.type === \"object\") {\n lineContent += pc.dim(`{ ${treeNode.itemCount} keys }`);\n } else if (treeNode.type === \"array\") {\n lineContent += pc.dim(`[ ${treeNode.itemCount} items ]`);\n } else {\n lineContent += formatValue(treeNode.value, 60);\n }\n }\n\n if (isSelected) {\n console.log(pc.bgBlue(pc.white(pc.bold(`> ${lineContent}`))));\n } else {\n console.log(` ${lineContent}`);\n }\n }\n\n // 3. Status Bar & Footer\n const selectedItem = this.flatList[this.selectedIndex];\n const currentPath = selectedItem ? selectedItem.node.path : \"$\";\n\n console.log(\"\\n\" + pc.dim(\"─\".repeat(Math.min(process.stdout.columns || 80, 80))));\n if (this.notification) {\n console.log(pc.green(pc.bold(` ${this.notification}`)));\n } else if (this.isSearching) {\n console.log(pc.yellow(pc.bold(` Search: /${this.searchInput}█`)));\n } else {\n console.log(pc.bold(pc.cyan(` Path: ${currentPath}`)));\n console.log(\n pc.dim(\" [↑/↓] Move [Enter/Space] Toggle [e] Expand All [c] Collapse All [p] Copy Path [y] Copy Val [/] Search [q] Exit\")\n );\n }\n }\n\n private expandAll(node: AnyNode): void {\n node.collapsed = false;\n if (node.children) node.children.forEach((c) => this.expandAll(c));\n }\n\n private collapseAll(node: AnyNode): void {\n if (node.depth > 0) node.collapsed = true;\n if (node.children) node.children.forEach((c) => this.collapseAll(c));\n }\n\n public async start(): Promise<void> {\n if (!process.stdin.isTTY) {\n return;\n }\n\n readline.emitKeypressEvents(process.stdin);\n process.stdin.setRawMode(true);\n process.stdin.resume();\n\n this.render();\n\n return new Promise((resolve) => {\n const onKeypress = async (str: string, key: readline.Key) => {\n if (this.isSearching) {\n if (key.name === \"return\") {\n this.searchQuery = this.searchInput;\n this.isSearching = false;\n this.rebuildFlatList();\n this.render();\n return;\n }\n if (key.name === \"escape\") {\n this.isSearching = false;\n this.searchInput = \"\";\n this.render();\n return;\n }\n if (key.name === \"backspace\") {\n this.searchInput = this.searchInput.slice(0, -1);\n this.render();\n return;\n }\n if (str && str.length === 1) {\n this.searchInput += str;\n this.render();\n return;\n }\n }\n\n // Navigation\n if (key.name === \"up\" || key.name === \"k\") {\n if (this.selectedIndex > 0) {\n this.selectedIndex--;\n this.render();\n }\n } else if (key.name === \"down\" || key.name === \"j\") {\n if (this.selectedIndex < this.flatList.length - 1) {\n this.selectedIndex++;\n this.render();\n }\n } else if (key.name === \"return\" || key.name === \"space\" || key.name === \"right\" || key.name === \"left\") {\n const item = this.flatList[this.selectedIndex];\n if (item && item.node.children && item.node.children.length > 0) {\n item.node.collapsed = !item.node.collapsed;\n this.rebuildFlatList();\n this.render();\n }\n } else if (str === \"e\" || str === \"E\") {\n this.expandAll(this.root);\n this.rebuildFlatList();\n this.render();\n } else if (str === \"c\" || str === \"C\") {\n this.collapseAll(this.root);\n this.rebuildFlatList();\n this.render();\n } else if (str === \"p\" || str === \"P\") {\n const item = this.flatList[this.selectedIndex];\n if (item) {\n await copyToClipboard(item.node.path);\n this.setNotification(`✔ Copied JSONPath: ${item.node.path}`);\n }\n } else if (str === \"y\" || str === \"Y\") {\n const item = this.flatList[this.selectedIndex];\n if (item) {\n const val = \"value\" in item.node ? item.node.value : (item.node as DiffNode).newValue;\n const textToCopy = typeof val === \"object\" ? JSON.stringify(val, null, 2) : String(val);\n await copyToClipboard(textToCopy);\n this.setNotification(`✔ Copied value to clipboard!`);\n }\n } else if (str === \"/\") {\n this.isSearching = true;\n this.searchInput = \"\";\n this.render();\n } else if (key.ctrl && key.name === \"c\" || key.name === \"escape\" || str === \"q\" || str === \"Q\") {\n // Cleanup & Exit\n process.stdin.removeListener(\"keypress\", onKeypress);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.stdout.write(\"\\x1b[?25h\\n\"); // show cursor\n resolve();\n }\n };\n\n process.stdin.on(\"keypress\", onKeypress);\n });\n }\n}\n","import { exec } from \"child_process\";\nimport os from \"os\";\n\n/**\n * Copy text to system clipboard across macOS, Linux, and Windows.\n */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n const platform = os.platform();\n\n return new Promise((resolve) => {\n let proc;\n if (platform === \"darwin\") {\n proc = exec(\"pbcopy\", (err) => resolve(!err));\n } else if (platform === \"win32\") {\n proc = exec(\"clip\", (err) => resolve(!err));\n } else {\n // Linux: Try wl-copy then xclip\n proc = exec(\"wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || xsel --clipboard --input\", (err) => {\n resolve(!err);\n });\n }\n\n if (proc.stdin) {\n proc.stdin.write(text);\n proc.stdin.end();\n } else {\n resolve(false);\n }\n });\n}\n"],"mappings":"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","names":["import_picocolors","pc","import_picocolors","pc","import_picocolors","os","pc","readline","fs","pc"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/formatter.ts","../src/differ.ts","../src/interactive.ts","../src/utils/clipboard.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport fs from \"fs\";\nimport pc from \"picocolors\";\nimport { buildTree, prettyPrintTree, colorizeJSON } from \"./formatter.js\";\nimport { diffJSON, prettyPrintDiff, calculateDiffStats } from \"./differ.js\";\nimport { InteractiveViewer } from \"./interactive.js\";\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let data = \"\";\n if (process.stdin.isTTY) {\n resolve(\"\");\n return;\n }\n\n process.stdin.setEncoding(\"utf-8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => {\n resolve(data.trim());\n });\n setTimeout(() => {\n if (!data) resolve(\"\");\n }, 100);\n });\n}\n\nfunction parseJSON(raw: string, label = \"Input\"): any {\n try {\n return JSON.parse(raw);\n } catch (err: any) {\n throw new Error(`Invalid JSON in ${label}: ${err.message}`);\n }\n}\n\nasync function resolveJSONInput(inputArg?: string): Promise<{ raw: string; label: string }> {\n if (inputArg) {\n const trimmed = inputArg.trim();\n // 1. Direct inline JSON check\n if (\n (trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\")) ||\n (trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\"))\n ) {\n return { raw: trimmed, label: \"inline JSON\" };\n }\n // 2. Existing file check\n if (fs.existsSync(inputArg)) {\n const raw = await fs.promises.readFile(inputArg, \"utf-8\");\n return { raw, label: inputArg };\n }\n // 3. Try parsing as generic JSON value (number, boolean, string)\n try {\n JSON.parse(trimmed);\n return { raw: trimmed, label: \"inline JSON\" };\n } catch {}\n\n throw new Error(`File not found or invalid inline JSON: ${inputArg}`);\n }\n\n // 4. Stdin fallback\n const stdinData = await readStdin();\n return { raw: stdinData, label: \"stdin\" };\n}\n\nasync function main() {\n const program = new Command();\n\n program\n .name(\"json-term\")\n .description(\"High-fidelity interactive terminal JSON viewer, collapsible tree explorer, type syntax highlighter, and structural diff tool.\")\n .version(\"1.2.0\", \"-v, --version\", \"Output the current version\")\n .argument(\"[input]\", \"JSON file path, inline JSON string, or piped stdin\")\n .option(\"-p, --print\", \"Print formatted non-interactive output to stdout\", false)\n .option(\"-F, --format <format>\", \"Output format: 'tree' or 'json'\", \"tree\")\n .option(\"-r, --raw\", \"Print standard syntax-highlighted JSON (shorthand for --format json --print)\", false)\n .option(\"-d, --depth <number>\", \"Initial tree expansion depth\", (val) => parseInt(val, 10), 2)\n .option(\"-s, --search <query>\", \"Filter tree by key or value query\")\n .action(async (inputArg, options) => {\n try {\n const { raw, label } = await resolveJSONInput(inputArg);\n\n if (!raw) {\n program.help();\n process.exit(1);\n }\n\n const data = parseJSON(raw, label);\n\n // If raw/json format requested, print colorized JSON directly\n if (options.raw || options.format === \"json\") {\n console.log(colorizeJSON(data));\n return;\n }\n\n const tree = buildTree(data, label, \"$\", 0, options.depth);\n\n if (options.print || !process.stdin.isTTY) {\n console.log(prettyPrintTree(tree));\n } else {\n const viewer = new InteractiveViewer(tree, false);\n await viewer.start();\n }\n } catch (err: any) {\n console.error(pc.red(`Error: ${err.message || err}`));\n process.exit(1);\n }\n });\n\n // Diff subcommand\n program\n .command(\"diff <inputA> [inputB]\")\n .description(\"Structural JSON diff between files, inline JSON strings, or stdin\")\n .option(\"-p, --print\", \"Print formatted non-interactive diff to stdout\", false)\n .option(\"-d, --depth <number>\", \"Initial expansion depth\", (val) => parseInt(val, 10), 3)\n .option(\"--no-unchanged\", \"Hide unchanged nodes from the diff view\", false)\n .action(async (inputA, inputB, diffOptions) => {\n try {\n const itemA = await resolveJSONInput(inputA);\n if (!itemA.raw) {\n console.error(pc.red(\"Error: Please provide valid first JSON input (file or string).\"));\n process.exit(1);\n }\n const dataA = parseJSON(itemA.raw, itemA.label);\n\n const itemB = await resolveJSONInput(inputB);\n if (!itemB.raw) {\n console.error(pc.red(\"Error: Please specify second JSON input (file, string, or piped stdin).\"));\n process.exit(1);\n }\n const dataB = parseJSON(itemB.raw, itemB.label);\n\n const diffTree = diffJSON(dataA, dataB, `${itemA.label} ↔ ${itemB.label}`, \"$\", 0, diffOptions.depth);\n const stats = calculateDiffStats(diffTree);\n\n if (diffOptions.print || !process.stdin.isTTY) {\n console.log(\n pc.bold(\n `Diff Stats: ${pc.green(`+${stats.added}`)} ${pc.red(`-${stats.removed}`)} ${pc.yellow(`~${stats.modified}`)} ${pc.dim(`=${stats.unchanged}`)}\\n`\n )\n );\n console.log(prettyPrintDiff(diffTree, { showUnchanged: diffOptions.unchanged }));\n } else {\n const viewer = new InteractiveViewer(diffTree, true);\n await viewer.start();\n }\n } catch (err: any) {\n console.error(pc.red(`Error: ${err.message || err}`));\n process.exit(1);\n }\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((err) => {\n console.error(pc.red(`Error: ${err.message || err}`));\n process.exit(1);\n});\n","import pc from \"picocolors\";\nimport { JSONValueType, TreeNode } from \"./types.js\";\n\n/**\n * Determine the JSON value type.\n */\nexport function getValueType(value: any): JSONValueType {\n if (value === null || value === undefined) return \"null\";\n if (Array.isArray(value)) return \"array\";\n const t = typeof value;\n if (t === \"object\") return \"object\";\n if (t === \"string\") return \"string\";\n if (t === \"number\") return \"number\";\n if (t === \"boolean\") return \"boolean\";\n return \"string\";\n}\n\n/**\n * Colorize and format a single JSON value according to its type.\n */\nexport function formatValue(value: any, truncateLen = 80): string {\n const type = getValueType(value);\n\n switch (type) {\n case \"string\": {\n const str = JSON.stringify(value);\n const truncated = str.length > truncateLen ? str.slice(0, truncateLen - 3) + '...\"' : str;\n return pc.green(truncated);\n }\n case \"number\":\n return pc.yellow(String(value));\n case \"boolean\":\n return pc.magenta(String(value));\n case \"null\":\n return pc.dim(\"null\");\n case \"array\":\n return pc.dim(`[${value.length} items]`);\n case \"object\":\n return pc.dim(`{${Object.keys(value).length} keys}`);\n default:\n return String(value);\n }\n}\n\n/**\n * Format an object key with bold coloring.\n */\nexport function formatKey(key: string): string {\n return pc.bold(pc.cyan(key));\n}\n\n/**\n * Build a flat or nested TreeNode hierarchy from any JavaScript/JSON data.\n */\nexport function buildTree(\n data: any,\n key?: string,\n path = \"$\",\n depth = 0,\n maxInitialDepth = 2,\n parentId?: string\n): TreeNode {\n const type = getValueType(data);\n const id = `${path}-${key || \"root\"}-${depth}`;\n const collapsed = depth >= maxInitialDepth;\n\n const node: TreeNode = {\n id,\n key,\n value: data,\n type,\n depth,\n path,\n collapsed,\n parentId,\n };\n\n if (type === \"object\" && data !== null) {\n const keys = Object.keys(data);\n node.itemCount = keys.length;\n node.children = keys.map((k) =>\n buildTree(\n data[k],\n k,\n path === \"$\" ? k : `${path}.${k}`,\n depth + 1,\n maxInitialDepth,\n id\n )\n );\n } else if (type === \"array\") {\n node.itemCount = data.length;\n node.children = data.map((item: any, idx: number) =>\n buildTree(\n item,\n `[${idx}]`,\n `${path}[${idx}]`,\n depth + 1,\n maxInitialDepth,\n id\n )\n );\n }\n\n return node;\n}\n\n/**\n * Pretty-print JSON tree structure to terminal string.\n */\nexport function prettyPrintTree(node: TreeNode, indent = \"\", isLast = true): string {\n const isRoot = node.depth === 0;\n const marker = node.type === \"object\" || node.type === \"array\" ? (node.collapsed ? pc.cyan(\"▶ \") : pc.cyan(\"▼ \")) : pc.dim(\"• \");\n\n let line = \"\";\n if (!isRoot) {\n const branch = isLast ? \"└── \" : \"├── \";\n line += pc.dim(indent + branch) + marker;\n if (node.key) {\n line += formatKey(node.key) + pc.dim(\": \");\n }\n } else {\n line += marker + (node.key ? formatKey(node.key) + pc.dim(\": \") : pc.bold(pc.white(\"root: \")));\n }\n\n if (node.type === \"object\") {\n line += pc.dim(`{ ${node.itemCount} keys }`);\n } else if (node.type === \"array\") {\n line += pc.dim(`[ ${node.itemCount} items ]`);\n } else {\n line += formatValue(node.value);\n }\n\n const lines = [line];\n\n if (!node.collapsed && node.children && node.children.length > 0) {\n const nextIndent = isRoot ? \"\" : indent + (isLast ? \" \" : \"│ \");\n node.children.forEach((child, index) => {\n const childIsLast = index === node.children!.length - 1;\n lines.push(prettyPrintTree(child, nextIndent, childIsLast));\n });\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Colorize and format standard JSON structure with bold keys and typed syntax highlighting.\n */\nexport function colorizeJSON(data: any, space = 2): string {\n const jsonStr = typeof data === \"string\" ? data : JSON.stringify(data, null, space);\n if (!jsonStr) return \"\";\n\n return jsonStr.replace(\n /(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g,\n (match) => {\n // Object key with trailing colon\n if (/^\"/.test(match)) {\n if (/:$/.test(match)) {\n const keyName = match.slice(0, -1);\n return pc.bold(pc.cyan(keyName)) + pc.dim(\":\");\n }\n // String value\n return pc.green(match);\n }\n // Boolean value\n if (/true|false/.test(match)) {\n return pc.magenta(match);\n }\n // Null value\n if (/null/.test(match)) {\n return pc.dim(match);\n }\n // Number value\n return pc.yellow(match);\n }\n );\n}\n\n","import pc from \"picocolors\";\nimport { DiffNode, DiffStatus, JSONValueType } from \"./types.js\";\nimport { getValueType, formatValue, formatKey } from \"./formatter.js\";\n\nexport interface DiffStats {\n added: number;\n removed: number;\n modified: number;\n unchanged: number;\n}\n\n/**\n * Compare two JSON values structurally and return a recursive DiffNode tree.\n */\nexport function diffJSON(\n a: any,\n b: any,\n key?: string,\n path = \"$\",\n depth = 0,\n maxInitialDepth = 3\n): DiffNode {\n const typeA = getValueType(a);\n const typeB = getValueType(b);\n const id = `${path}-${key || \"diff\"}-${depth}`;\n const collapsed = depth >= maxInitialDepth;\n\n // Case 1: Value added (a is undefined, b exists)\n if (a === undefined && b !== undefined) {\n const node: DiffNode = {\n id,\n key,\n path,\n depth,\n status: \"added\",\n newValue: b,\n type: typeB,\n collapsed: false,\n };\n if (typeB === \"object\" && b !== null) {\n node.itemCount = Object.keys(b).length;\n node.children = Object.keys(b).map((k) =>\n diffJSON(undefined, b[k], k, path === \"$\" ? k : `${path}.${k}`, depth + 1, maxInitialDepth)\n );\n } else if (typeB === \"array\") {\n node.itemCount = b.length;\n node.children = b.map((item: any, idx: number) =>\n diffJSON(undefined, item, `[${idx}]`, `${path}[${idx}]`, depth + 1, maxInitialDepth)\n );\n }\n return node;\n }\n\n // Case 2: Value removed (a exists, b is undefined)\n if (a !== undefined && b === undefined) {\n const node: DiffNode = {\n id,\n key,\n path,\n depth,\n status: \"removed\",\n oldValue: a,\n type: typeA,\n collapsed: false,\n };\n if (typeA === \"object\" && a !== null) {\n node.itemCount = Object.keys(a).length;\n node.children = Object.keys(a).map((k) =>\n diffJSON(a[k], undefined, k, path === \"$\" ? k : `${path}.${k}`, depth + 1, maxInitialDepth)\n );\n } else if (typeA === \"array\") {\n node.itemCount = a.length;\n node.children = a.map((item: any, idx: number) =>\n diffJSON(item, undefined, `[${idx}]`, `${path}[${idx}]`, depth + 1, maxInitialDepth)\n );\n }\n return node;\n }\n\n // Case 3: Type changed (e.g. from string to number or object to array)\n if (typeA !== typeB) {\n return {\n id,\n key,\n path,\n depth,\n status: \"modified\",\n oldValue: a,\n newValue: b,\n type: typeB,\n collapsed: false,\n };\n }\n\n // Case 4: Both are Objects\n if (typeA === \"object\" && a !== null && b !== null) {\n const allKeys = Array.from(new Set([...Object.keys(a), ...Object.keys(b)]));\n const children: DiffNode[] = [];\n let hasModifications = false;\n\n for (const k of allKeys) {\n const child = diffJSON(\n a[k],\n b[k],\n k,\n path === \"$\" ? k : `${path}.${k}`,\n depth + 1,\n maxInitialDepth\n );\n if (child.status !== \"unchanged\") hasModifications = true;\n children.push(child);\n }\n\n return {\n id,\n key,\n path,\n depth,\n status: hasModifications ? \"modified\" : \"unchanged\",\n oldValue: a,\n newValue: b,\n type: \"object\",\n itemCount: allKeys.length,\n collapsed,\n children,\n };\n }\n\n // Case 5: Both are Arrays\n if (typeA === \"array\") {\n const maxLen = Math.max(a.length, b.length);\n const children: DiffNode[] = [];\n let hasModifications = false;\n\n for (let i = 0; i < maxLen; i++) {\n const child = diffJSON(\n a[i],\n b[i],\n `[${i}]`,\n `${path}[${i}]`,\n depth + 1,\n maxInitialDepth\n );\n if (child.status !== \"unchanged\") hasModifications = true;\n children.push(child);\n }\n\n return {\n id,\n key,\n path,\n depth,\n status: hasModifications ? \"modified\" : \"unchanged\",\n oldValue: a,\n newValue: b,\n type: \"array\",\n itemCount: maxLen,\n collapsed,\n children,\n };\n }\n\n // Case 6: Primitive values comparison\n if (a !== b) {\n return {\n id,\n key,\n path,\n depth,\n status: \"modified\",\n oldValue: a,\n newValue: b,\n type: typeB,\n collapsed: false,\n };\n }\n\n // Unchanged primitive\n return {\n id,\n key,\n path,\n depth,\n status: \"unchanged\",\n oldValue: a,\n newValue: b,\n type: typeA,\n collapsed,\n };\n}\n\n/**\n * Calculate summary metrics for the diff.\n */\nexport function calculateDiffStats(node: DiffNode): DiffStats {\n const stats: DiffStats = { added: 0, removed: 0, modified: 0, unchanged: 0 };\n\n function traverse(n: DiffNode) {\n if (n.children && n.children.length > 0) {\n n.children.forEach(traverse);\n } else {\n stats[n.status]++;\n }\n }\n\n traverse(node);\n return stats;\n}\n\n/**\n * Pretty-print structural diff to terminal string.\n */\nexport function prettyPrintDiff(\n node: DiffNode,\n options: { showUnchanged?: boolean } = {},\n indent = \"\",\n isLast = true\n): string {\n const showUnchanged = options.showUnchanged ?? true;\n\n if (!showUnchanged && node.status === \"unchanged\" && (!node.children || node.children.length === 0)) {\n return \"\";\n }\n\n const isRoot = node.depth === 0;\n let prefix = \" \";\n let statusColor = pc.dim;\n\n if (node.status === \"added\") {\n prefix = pc.green(\"+ \");\n statusColor = pc.green;\n } else if (node.status === \"removed\") {\n prefix = pc.red(\"- \");\n statusColor = pc.red;\n } else if (node.status === \"modified\") {\n prefix = pc.yellow(\"~ \");\n statusColor = pc.yellow;\n }\n\n let line = prefix;\n if (!isRoot) {\n const branch = isLast ? \"└── \" : \"├── \";\n line += pc.dim(indent + branch);\n if (node.key) {\n line += formatKey(node.key) + pc.dim(\": \");\n }\n } else {\n line += node.key ? formatKey(node.key) + pc.dim(\": \") : pc.bold(\"diff: \");\n }\n\n if (node.status === \"added\") {\n line += statusColor(formatValue(node.newValue));\n } else if (node.status === \"removed\") {\n line += statusColor(formatValue(node.oldValue));\n } else if (node.status === \"modified\") {\n if (node.type === \"object\" || node.type === \"array\") {\n line += statusColor(`{ ${node.itemCount} items modified }`);\n } else {\n line += `${pc.red(formatValue(node.oldValue))} ${pc.dim(\"->\")} ${pc.green(formatValue(node.newValue))}`;\n }\n } else {\n // Unchanged\n line += pc.dim(formatValue(node.newValue));\n }\n\n const lines = [line];\n\n if (!node.collapsed && node.children && node.children.length > 0) {\n const nextIndent = isRoot ? \"\" : indent + (isLast ? \" \" : \"│ \");\n node.children.forEach((child, index) => {\n const childIsLast = index === node.children!.length - 1;\n const childStr = prettyPrintDiff(child, options, nextIndent, childIsLast);\n if (childStr) lines.push(childStr);\n });\n }\n\n return lines.join(\"\\n\");\n}\n","import readline from \"readline\";\nimport pc from \"picocolors\";\nimport { TreeNode, DiffNode } from \"./types.js\";\nimport { formatKey, formatValue } from \"./formatter.js\";\nimport { copyToClipboard } from \"./utils/clipboard.js\";\n\ntype AnyNode = TreeNode | DiffNode;\n\ninterface FlatItem {\n node: AnyNode;\n depth: number;\n isDiff: boolean;\n}\n\n/**\n * Interactive Terminal Tree Explorer for JSON data and Diffs.\n */\nexport class InteractiveViewer {\n private root: AnyNode;\n private flatList: FlatItem[] = [];\n private selectedIndex = 0;\n private searchQuery = \"\";\n private isSearching = false;\n private searchInput = \"\";\n private notification = \"\";\n private notificationTimer: NodeJS.Timeout | null = null;\n private isDiff: boolean;\n\n constructor(root: AnyNode, isDiff = false) {\n this.root = root;\n this.isDiff = isDiff;\n this.rebuildFlatList();\n }\n\n private isDiffNode(node: AnyNode): node is DiffNode {\n return this.isDiff && \"status\" in node;\n }\n\n private rebuildFlatList(): void {\n const list: FlatItem[] = [];\n\n const traverse = (node: AnyNode, currentDepth: number) => {\n // If search query is active, filter matching nodes or their parents\n if (this.searchQuery) {\n const query = this.searchQuery.toLowerCase();\n const keyMatch = node.key && node.key.toLowerCase().includes(query);\n const pathMatch = node.path && node.path.toLowerCase().includes(query);\n const valMatch =\n \"value\" in node\n ? JSON.stringify(node.value).toLowerCase().includes(query)\n : \"newValue\" in node\n ? JSON.stringify(node.newValue).toLowerCase().includes(query)\n : false;\n\n if (!keyMatch && !pathMatch && !valMatch && (!node.children || node.children.length === 0)) {\n return;\n }\n }\n\n list.push({ node, depth: currentDepth, isDiff: this.isDiff });\n\n if (!node.collapsed && node.children && node.children.length > 0) {\n for (const child of node.children) {\n traverse(child, currentDepth + 1);\n }\n }\n };\n\n traverse(this.root, 0);\n this.flatList = list;\n\n if (this.selectedIndex >= this.flatList.length) {\n this.selectedIndex = Math.max(0, this.flatList.length - 1);\n }\n }\n\n private setNotification(msg: string): void {\n this.notification = msg;\n if (this.notificationTimer) clearTimeout(this.notificationTimer);\n this.notificationTimer = setTimeout(() => {\n this.notification = \"\";\n this.render();\n }, 2500);\n }\n\n private render(): void {\n const rows = process.stdout.rows || 24;\n const maxVisibleRows = Math.max(5, rows - 5);\n\n // Calculate window scroll offset\n let startIdx = 0;\n if (this.selectedIndex >= maxVisibleRows) {\n startIdx = this.selectedIndex - maxVisibleRows + 1;\n }\n const endIdx = Math.min(this.flatList.length, startIdx + maxVisibleRows);\n\n // Clear terminal screen\n process.stdout.write(\"\\x1b[2J\\x1b[3J\\x1b[H\\x1b[?25l\"); // hide cursor\n\n // 1. Header Bar\n const title = this.isDiff ? \" JSON Structural Diff Explorer \" : \" Interactive JSON Tree Explorer \";\n console.log(pc.bgCyan(pc.black(pc.bold(` ${title} `)) + pc.dim(` [Total items: ${this.flatList.length}]`)));\n\n // 2. Render visible items\n for (let i = startIdx; i < endIdx; i++) {\n const { node, depth } = this.flatList[i];\n const isSelected = i === this.selectedIndex;\n const indent = \" \".repeat(depth);\n\n let marker = \"• \";\n if (node.children && node.children.length > 0) {\n marker = node.collapsed ? \"▶ \" : \"▼ \";\n }\n\n let lineContent = \"\";\n\n if (this.isDiffNode(node)) {\n let diffPrefix = \" \";\n if (node.status === \"added\") diffPrefix = pc.green(\"+ \");\n else if (node.status === \"removed\") diffPrefix = pc.red(\"- \");\n else if (node.status === \"modified\") diffPrefix = pc.yellow(\"~ \");\n\n lineContent = diffPrefix + indent + marker;\n if (node.key) lineContent += formatKey(node.key) + pc.dim(\": \");\n\n if (node.status === \"added\") {\n lineContent += pc.green(formatValue(node.newValue, 50));\n } else if (node.status === \"removed\") {\n lineContent += pc.red(formatValue(node.oldValue, 50));\n } else if (node.status === \"modified\") {\n if (node.type === \"object\" || node.type === \"array\") {\n lineContent += pc.yellow(`{ ${node.itemCount} modified }`);\n } else {\n lineContent += `${pc.red(formatValue(node.oldValue, 25))} -> ${pc.green(formatValue(node.newValue, 25))}`;\n }\n } else {\n lineContent += pc.dim(formatValue(node.newValue, 50));\n }\n } else {\n const treeNode = node as TreeNode;\n lineContent = indent + marker;\n if (treeNode.key) lineContent += formatKey(treeNode.key) + pc.dim(\": \");\n\n if (treeNode.type === \"object\") {\n lineContent += pc.dim(`{ ${treeNode.itemCount} keys }`);\n } else if (treeNode.type === \"array\") {\n lineContent += pc.dim(`[ ${treeNode.itemCount} items ]`);\n } else {\n lineContent += formatValue(treeNode.value, 60);\n }\n }\n\n if (isSelected) {\n console.log(pc.bgBlue(pc.white(pc.bold(`> ${lineContent}`))));\n } else {\n console.log(` ${lineContent}`);\n }\n }\n\n // 3. Status Bar & Footer\n const selectedItem = this.flatList[this.selectedIndex];\n const currentPath = selectedItem ? selectedItem.node.path : \"$\";\n\n console.log(\"\\n\" + pc.dim(\"─\".repeat(Math.min(process.stdout.columns || 80, 80))));\n if (this.notification) {\n console.log(pc.green(pc.bold(` ${this.notification}`)));\n } else if (this.isSearching) {\n console.log(pc.yellow(pc.bold(` Search: /${this.searchInput}█`)));\n } else {\n console.log(pc.bold(pc.cyan(` Path: ${currentPath}`)));\n console.log(\n pc.dim(\" [↑/↓] Move [Enter/Space] Toggle [e] Expand All [c] Collapse All [p] Copy Path [y] Copy Val [/] Search [q] Exit\")\n );\n }\n }\n\n private expandAll(node: AnyNode): void {\n node.collapsed = false;\n if (node.children) node.children.forEach((c) => this.expandAll(c));\n }\n\n private collapseAll(node: AnyNode): void {\n if (node.depth > 0) node.collapsed = true;\n if (node.children) node.children.forEach((c) => this.collapseAll(c));\n }\n\n public async start(): Promise<void> {\n if (!process.stdin.isTTY) {\n return;\n }\n\n readline.emitKeypressEvents(process.stdin);\n process.stdin.setRawMode(true);\n process.stdin.resume();\n\n this.render();\n\n return new Promise((resolve) => {\n const onKeypress = async (str: string, key: readline.Key) => {\n if (this.isSearching) {\n if (key.name === \"return\") {\n this.searchQuery = this.searchInput;\n this.isSearching = false;\n this.rebuildFlatList();\n this.render();\n return;\n }\n if (key.name === \"escape\") {\n this.isSearching = false;\n this.searchInput = \"\";\n this.render();\n return;\n }\n if (key.name === \"backspace\") {\n this.searchInput = this.searchInput.slice(0, -1);\n this.render();\n return;\n }\n if (str && str.length === 1) {\n this.searchInput += str;\n this.render();\n return;\n }\n }\n\n // Navigation\n if (key.name === \"up\" || key.name === \"k\") {\n if (this.selectedIndex > 0) {\n this.selectedIndex--;\n this.render();\n }\n } else if (key.name === \"down\" || key.name === \"j\") {\n if (this.selectedIndex < this.flatList.length - 1) {\n this.selectedIndex++;\n this.render();\n }\n } else if (key.name === \"return\" || key.name === \"space\" || key.name === \"right\" || key.name === \"left\") {\n const item = this.flatList[this.selectedIndex];\n if (item && item.node.children && item.node.children.length > 0) {\n item.node.collapsed = !item.node.collapsed;\n this.rebuildFlatList();\n this.render();\n }\n } else if (str === \"e\" || str === \"E\") {\n this.expandAll(this.root);\n this.rebuildFlatList();\n this.render();\n } else if (str === \"c\" || str === \"C\") {\n this.collapseAll(this.root);\n this.rebuildFlatList();\n this.render();\n } else if (str === \"p\" || str === \"P\") {\n const item = this.flatList[this.selectedIndex];\n if (item) {\n await copyToClipboard(item.node.path);\n this.setNotification(`✔ Copied JSONPath: ${item.node.path}`);\n }\n } else if (str === \"y\" || str === \"Y\") {\n const item = this.flatList[this.selectedIndex];\n if (item) {\n const val = \"value\" in item.node ? item.node.value : (item.node as DiffNode).newValue;\n const textToCopy = typeof val === \"object\" ? JSON.stringify(val, null, 2) : String(val);\n await copyToClipboard(textToCopy);\n this.setNotification(`✔ Copied value to clipboard!`);\n }\n } else if (str === \"/\") {\n this.isSearching = true;\n this.searchInput = \"\";\n this.render();\n } else if (key.ctrl && key.name === \"c\" || key.name === \"escape\" || str === \"q\" || str === \"Q\") {\n // Cleanup & Exit\n process.stdin.removeListener(\"keypress\", onKeypress);\n process.stdin.setRawMode(false);\n process.stdin.pause();\n process.stdout.write(\"\\x1b[?25h\\n\"); // show cursor\n resolve();\n }\n };\n\n process.stdin.on(\"keypress\", onKeypress);\n });\n }\n}\n","import { exec } from \"child_process\";\nimport os from \"os\";\n\n/**\n * Copy text to system clipboard across macOS, Linux, and Windows.\n */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n const platform = os.platform();\n\n return new Promise((resolve) => {\n let proc;\n if (platform === \"darwin\") {\n proc = exec(\"pbcopy\", (err) => resolve(!err));\n } else if (platform === \"win32\") {\n proc = exec(\"clip\", (err) => resolve(!err));\n } else {\n // Linux: Try wl-copy then xclip\n proc = exec(\"wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || xsel --clipboard --input\", (err) => {\n resolve(!err);\n });\n }\n\n if (proc.stdin) {\n proc.stdin.write(text);\n proc.stdin.end();\n } else {\n resolve(false);\n }\n });\n}\n"],"mappings":"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","names":["import_picocolors","pc","import_picocolors","pc","import_picocolors","os","pc","readline","fs","pc"]}
package/dist/cli.mjs CHANGED
@@ -114,6 +114,29 @@ function prettyPrintTree(node, indent = "", isLast = true) {
114
114
  }
115
115
  return lines.join("\n");
116
116
  }
117
+ function colorizeJSON(data, space = 2) {
118
+ const jsonStr = typeof data === "string" ? data : JSON.stringify(data, null, space);
119
+ if (!jsonStr) return "";
120
+ return jsonStr.replace(
121
+ /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
122
+ (match) => {
123
+ if (/^"/.test(match)) {
124
+ if (/:$/.test(match)) {
125
+ const keyName = match.slice(0, -1);
126
+ return pc.bold(pc.cyan(keyName)) + pc.dim(":");
127
+ }
128
+ return pc.green(match);
129
+ }
130
+ if (/true|false/.test(match)) {
131
+ return pc.magenta(match);
132
+ }
133
+ if (/null/.test(match)) {
134
+ return pc.dim(match);
135
+ }
136
+ return pc.yellow(match);
137
+ }
138
+ );
139
+ }
117
140
 
118
141
  // src/differ.ts
119
142
  import pc2 from "picocolors";
@@ -612,26 +635,41 @@ function parseJSON(raw, label = "Input") {
612
635
  throw new Error(`Invalid JSON in ${label}: ${err.message}`);
613
636
  }
614
637
  }
638
+ async function resolveJSONInput(inputArg) {
639
+ if (inputArg) {
640
+ const trimmed = inputArg.trim();
641
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
642
+ return { raw: trimmed, label: "inline JSON" };
643
+ }
644
+ if (fs.existsSync(inputArg)) {
645
+ const raw = await fs.promises.readFile(inputArg, "utf-8");
646
+ return { raw, label: inputArg };
647
+ }
648
+ try {
649
+ JSON.parse(trimmed);
650
+ return { raw: trimmed, label: "inline JSON" };
651
+ } catch {
652
+ }
653
+ throw new Error(`File not found or invalid inline JSON: ${inputArg}`);
654
+ }
655
+ const stdinData = await readStdin();
656
+ return { raw: stdinData, label: "stdin" };
657
+ }
615
658
  async function main() {
616
659
  const program = new Command();
617
- program.name("json-term").description("High-fidelity interactive terminal JSON viewer, collapsible tree explorer, type syntax highlighter, and structural diff tool.").version("1.0.0", "-v, --version", "Output the current version").argument("[file]", "JSON file to view or compare").option("-p, --print", "Print formatted non-interactive tree to stdout", false).option("-d, --depth <number>", "Initial tree expansion depth", (val) => parseInt(val, 10), 2).option("-s, --search <query>", "Filter tree by key or value query").action(async (fileArg, options) => {
660
+ program.name("json-term").description("High-fidelity interactive terminal JSON viewer, collapsible tree explorer, type syntax highlighter, and structural diff tool.").version("1.2.0", "-v, --version", "Output the current version").argument("[input]", "JSON file path, inline JSON string, or piped stdin").option("-p, --print", "Print formatted non-interactive output to stdout", false).option("-F, --format <format>", "Output format: 'tree' or 'json'", "tree").option("-r, --raw", "Print standard syntax-highlighted JSON (shorthand for --format json --print)", false).option("-d, --depth <number>", "Initial tree expansion depth", (val) => parseInt(val, 10), 2).option("-s, --search <query>", "Filter tree by key or value query").action(async (inputArg, options) => {
618
661
  try {
619
- let raw = "";
620
- if (fileArg) {
621
- if (!fs.existsSync(fileArg)) {
622
- console.error(pc4.red(`Error: File not found: ${fileArg}`));
623
- process.exit(1);
624
- }
625
- raw = await fs.promises.readFile(fileArg, "utf-8");
626
- } else {
627
- raw = await readStdin();
628
- }
662
+ const { raw, label } = await resolveJSONInput(inputArg);
629
663
  if (!raw) {
630
664
  program.help();
631
665
  process.exit(1);
632
666
  }
633
- const data = parseJSON(raw, fileArg || "stdin");
634
- const tree = buildTree(data, fileArg || "root", "$", 0, options.depth);
667
+ const data = parseJSON(raw, label);
668
+ if (options.raw || options.format === "json") {
669
+ console.log(colorizeJSON(data));
670
+ return;
671
+ }
672
+ const tree = buildTree(data, label, "$", 0, options.depth);
635
673
  if (options.print || !process.stdin.isTTY) {
636
674
  console.log(prettyPrintTree(tree));
637
675
  } else {
@@ -643,31 +681,21 @@ async function main() {
643
681
  process.exit(1);
644
682
  }
645
683
  });
646
- program.command("diff <fileA> [fileB]").description("Structural JSON diff between two files or stdin").option("-p, --print", "Print formatted non-interactive diff to stdout", false).option("-d, --depth <number>", "Initial expansion depth", (val) => parseInt(val, 10), 3).option("--no-unchanged", "Hide unchanged nodes from the diff view", false).action(async (fileA, fileB, diffOptions) => {
684
+ program.command("diff <inputA> [inputB]").description("Structural JSON diff between files, inline JSON strings, or stdin").option("-p, --print", "Print formatted non-interactive diff to stdout", false).option("-d, --depth <number>", "Initial expansion depth", (val) => parseInt(val, 10), 3).option("--no-unchanged", "Hide unchanged nodes from the diff view", false).action(async (inputA, inputB, diffOptions) => {
647
685
  try {
648
- if (!fs.existsSync(fileA)) {
649
- console.error(pc4.red(`Error: File not found: ${fileA}`));
686
+ const itemA = await resolveJSONInput(inputA);
687
+ if (!itemA.raw) {
688
+ console.error(pc4.red("Error: Please provide valid first JSON input (file or string)."));
650
689
  process.exit(1);
651
690
  }
652
- const rawA = await fs.promises.readFile(fileA, "utf-8");
653
- const dataA = parseJSON(rawA, fileA);
654
- let dataB;
655
- if (fileB) {
656
- if (!fs.existsSync(fileB)) {
657
- console.error(pc4.red(`Error: File not found: ${fileB}`));
658
- process.exit(1);
659
- }
660
- const rawB = await fs.promises.readFile(fileB, "utf-8");
661
- dataB = parseJSON(rawB, fileB);
662
- } else {
663
- const stdinData = await readStdin();
664
- if (!stdinData) {
665
- console.error(pc4.red("Error: Please specify second file or pipe JSON via stdin."));
666
- process.exit(1);
667
- }
668
- dataB = parseJSON(stdinData, "stdin");
691
+ const dataA = parseJSON(itemA.raw, itemA.label);
692
+ const itemB = await resolveJSONInput(inputB);
693
+ if (!itemB.raw) {
694
+ console.error(pc4.red("Error: Please specify second JSON input (file, string, or piped stdin)."));
695
+ process.exit(1);
669
696
  }
670
- const diffTree = diffJSON(dataA, dataB, `${fileA} \u2194 ${fileB || "stdin"}`, "$", 0, diffOptions.depth);
697
+ const dataB = parseJSON(itemB.raw, itemB.label);
698
+ const diffTree = diffJSON(dataA, dataB, `${itemA.label} \u2194 ${itemB.label}`, "$", 0, diffOptions.depth);
671
699
  const stats = calculateDiffStats(diffTree);
672
700
  if (diffOptions.print || !process.stdin.isTTY) {
673
701
  console.log(