Created
September 13, 2026 14:30
-
-
Save scripting/06b7dc714456e2baf283ab17c3a68af1 to your computer and use it in GitHub Desktop.
A script Claude wrote to find all the parts that changed in my Frontier.root odb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //compareRoots.js -- what in a snapshot of DW's root differs from the shipping root (or another snapshot): node compareRoots.js <his .root> <shipping .db or .root> | |
| const odb = require ("/Users/davewiner/Claude/trigger/frontierodb.js"); | |
| const sqlite3 = require ("/Users/davewiner/Claude/trigger/node_modules/better-sqlite3"); | |
| function val (v) { return ((v && v.value !== undefined && v.type) ? v.value : v); } | |
| function typeOfNode (v) { return ((v && v.type) ? v.type : (v && typeof v === "object" && !Array.isArray (v) && !(v instanceof Date) ? "table" : typeof v)); } | |
| function textOfLines (lines) { return (lines.map (function (l) { return ("\t".repeat (l.level) + (l.flComment ? "«" : "") + l.text); }).join ("\n")); } | |
| function flatten (t, path, out) { //path -> {type, text}; a node from the root reader is {type, value} | |
| Object.keys (t).forEach (function (name) { | |
| const node = t [name], p = path ? path + "." + name : name; | |
| if ((node === undefined) || (node === null)) { out [p] = {type: "novalue", text: ""}; return; } | |
| const type = node.type || "unknown", v = node.value; | |
| if (type === "table") { out [p] = {type: "table"}; flatten ((v && typeof v === "object") ? v : {}, p, out); return; } | |
| const lines = (v && v.lines) || node.lines; | |
| if (lines) { out [p] = {type, text: textOfLines (lines)}; return; } | |
| out [p] = {type, text: (v instanceof Date) ? v.toISOString () : ((v && typeof v === "object") ? JSON.stringify (v) : String (v))}; | |
| }); | |
| } | |
| function fromRoot (path) { const out = {}; flatten (odb.readRootFile (path), "", out); return (out); } | |
| function fromDb (path) { | |
| const db = new sqlite3 (path, {readonly: true}); const out = {}; | |
| const rows = db.prepare ("select id, parentid, name, type, value from odb").all (); const byParent = {}; | |
| rows.forEach (function (r) { (byParent [r.parentid] = byParent [r.parentid] || []).push (r); }); | |
| function walk (pid, path) { (byParent [pid] || []).forEach (function (r) { const p = path ? path + "." + r.name : r.name; | |
| if (r.type === "table") { out [p] = {type: "table"}; walk (r.id, p); return; } | |
| if ((r.type === "script" || r.type === "outline" || r.type === "menubar" || r.type === "wptext") && r.value && r.value.charAt (0) === "[") { try { out [p] = {type: r.type, text: textOfLines (JSON.parse (r.value))}; return; } catch (e) {} } | |
| out [p] = {type: r.type, text: String (r.value)}; }); } | |
| walk (0, ""); return (out); | |
| } | |
| const A = fromRoot (process.argv [2]); | |
| const B = process.argv [3].endsWith (".db") ? fromDb (process.argv [3]) : fromRoot (process.argv [3]); | |
| const lowerB = {}; Object.keys (B).forEach (function (p) { lowerB [p.toLowerCase ()] = p; }); | |
| const lowerA = {}; Object.keys (A).forEach (function (p) { lowerA [p.toLowerCase ()] = p; }); | |
| const added = [], changed = [], missing = []; | |
| Object.keys (A).forEach (function (p) { const a = A [p]; if (a.type === "table") return; const bp = lowerB [p.toLowerCase ()]; if (bp === undefined) { added.push (p + " (" + a.type + ")"); return; } const b = B [bp]; if (b.type === "table") { changed.push (p + " (" + a.type + ", was a table)"); return; } if ((a.text || "").replace (/\r\n|\r/g, "\n").trim () !== (b.text || "").replace (/\r\n|\r/g, "\n").trim ()) changed.push (p + " (" + a.type + ")"); }); | |
| Object.keys (B).forEach (function (p) { if (B [p].type !== "table" && lowerA [p.toLowerCase ()] === undefined) missing.push (p + " (" + B [p].type + ")"); }); | |
| function group (list) { const g = {}; list.forEach (function (p) { const top = p.split (".").slice (0, 3).join ("."); (g [top] = g [top] || []).push (p); }); return (g); } | |
| console.log ("ADDED " + added.length + " / CHANGED " + changed.length + " / MISSING " + missing.length); | |
| [["ADDED", added], ["CHANGED", changed], ["MISSING (in the second, not in the first)", missing]].forEach (function (pair) { console.log ("\n## " + pair [0]); const g = group (pair [1]); Object.keys (g).sort ().forEach (function (top) { console.log ("- " + top + " (" + g [top].length + ")"); g [top].slice (0, 400).forEach (function (p) { console.log (" " + p); }); }); }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment