Skip to content

Instantly share code, notes, and snippets.

@Swader
Created January 28, 2026 11:24
Show Gist options
  • Select an option

  • Save Swader/12f8438d0f2af06129c6029845e209c1 to your computer and use it in GitHub Desktop.

Select an option

Save Swader/12f8438d0f2af06129c6029845e209c1 to your computer and use it in GitHub Desktop.
import ipfsOnlyHashModule from "ipfs-only-hash";
import { resolve } from "node:path";
type HashOptions = {
cidVersion: 0 | 1;
rawLeaves: boolean;
};
type ParsedArgs = {
targetPath?: string;
desiredCid?: string;
maxVariants?: number;
reportEvery: number;
useJson: boolean;
onlyJson: boolean;
useRaw: boolean;
sortKeys: boolean;
indents: string[];
newlines: Array<"\n" | "\r\n">;
lineBreaks: boolean[];
trailingNewline: boolean[];
cidVersion?: 0 | 1;
rawLeaves?: boolean;
writePath?: string;
printMatch?: boolean;
showHelp: boolean;
error?: string;
};
type JsonFormatConfig = {
indent: string;
newline: "\n" | "\r\n";
lineBreaks: boolean;
trailingNewline: boolean;
colonBefore: string;
colonAfter: string;
commaAfter: string;
sortKeys: boolean;
};
type Candidate = {
content: string;
label: string;
details: Record<string, string>;
};
const { of: ipfsOnlyHash } = ipfsOnlyHashModule as {
of: (content: Uint8Array, options?: HashOptions) => Promise<string>;
};
const DEFAULT_INDENT_SIZES = [0, 1, 2, 3, 4, 8];
const DEFAULT_REPORT_EVERY = 500;
const DEFAULT_NEWLINES: Array<"\n" | "\r\n"> = ["\n", "\r\n"];
const DEFAULT_LINE_BREAKS = [true, false];
const DEFAULT_TRAILING_NEWLINE = [true, false];
const DEFAULT_COLON_BEFORE = ["", " "];
const DEFAULT_COLON_AFTER = ["", " ", "\t"];
const DEFAULT_COMMA_AFTER = ["", " ", "\t"];
const USAGE = `Usage:
bun run ./ipfs-format-hunt.ts <file> <cid> [options]
Options:
--max <n> Stop after N variants
--report <n> Log every N attempts (default: ${DEFAULT_REPORT_EVERY})
--no-json Skip JSON formatting variants
--only-json Only JSON formatting variants (error if not JSON)
--no-raw Skip raw text variants
--sort-keys Sort object keys when formatting JSON
--indents <list> Comma list of indent sizes (e.g. 0,1,2,4,8,tab)
--newline <list> Comma list of newline styles: lf,crlf
--pretty-only Only pretty JSON (line breaks on)
--minified-only Only minified JSON (line breaks off)
--cidv1 Use CIDv1
--raw-leaves Use raw leaves (forces CIDv1)
--write <path> Write matched content to file
--no-print Skip printing matched content to stdout
--help Show this help text
`;
function parseIndentList(value: string): string[] {
const tokens = value
.split(",")
.map((token) => token.trim())
.filter(Boolean);
const indents: string[] = [];
for (const token of tokens) {
if (token.toLowerCase() === "tab") {
indents.push("\t");
continue;
}
const size = Number.parseInt(token, 10);
if (!Number.isFinite(size) || size < 0) continue;
indents.push(" ".repeat(size));
}
return indents;
}
function parseNewlineList(value: string): Array<"\n" | "\r\n"> {
const tokens = value
.split(",")
.map((token) => token.trim().toLowerCase())
.filter(Boolean);
const newlines: Array<"\n" | "\r\n"> = [];
for (const token of tokens) {
if (token === "lf") newlines.push("\n");
if (token === "crlf") newlines.push("\r\n");
}
return newlines;
}
function parseArgs(args: string[]): ParsedArgs {
let targetPath: string | undefined;
let desiredCid: string | undefined;
let maxVariants: number | undefined;
let reportEvery = DEFAULT_REPORT_EVERY;
let useJson = true;
let onlyJson = false;
let useRaw = true;
let sortKeys = false;
let indents = DEFAULT_INDENT_SIZES.map((size) => " ".repeat(size));
indents.push("\t");
let newlines = [...DEFAULT_NEWLINES];
let lineBreaks = [...DEFAULT_LINE_BREAKS];
let trailingNewline = [...DEFAULT_TRAILING_NEWLINE];
let cidVersion: 0 | 1 | undefined;
let rawLeaves: boolean | undefined;
let writePath: string | undefined;
let printMatch = true;
let showHelp = false;
let stopFlags = false;
const extra: string[] = [];
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
if (!stopFlags && (arg === "--help" || arg === "-h")) {
showHelp = true;
continue;
}
if (!stopFlags && arg === "--") {
stopFlags = true;
continue;
}
if (!stopFlags && arg === "--max") {
const value = args[i + 1];
if (!value) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Missing value for --max",
};
}
maxVariants = Number.parseInt(value, 10);
if (!Number.isFinite(maxVariants) || maxVariants <= 0) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Invalid value for --max",
};
}
i += 1;
continue;
}
if (!stopFlags && arg === "--report") {
const value = args[i + 1];
if (!value) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Missing value for --report",
};
}
reportEvery = Number.parseInt(value, 10);
if (!Number.isFinite(reportEvery) || reportEvery < 0) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Invalid value for --report",
};
}
i += 1;
continue;
}
if (!stopFlags && arg === "--no-json") {
useJson = false;
continue;
}
if (!stopFlags && arg === "--only-json") {
useJson = true;
useRaw = false;
onlyJson = true;
continue;
}
if (!stopFlags && arg === "--no-raw") {
useRaw = false;
continue;
}
if (!stopFlags && arg === "--sort-keys") {
sortKeys = true;
continue;
}
if (!stopFlags && arg === "--indents") {
const value = args[i + 1];
if (!value) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Missing value for --indents",
};
}
const parsed = parseIndentList(value);
if (parsed.length === 0) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Invalid value for --indents",
};
}
indents = parsed;
i += 1;
continue;
}
if (!stopFlags && arg === "--newline") {
const value = args[i + 1];
if (!value) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Missing value for --newline",
};
}
const parsed = parseNewlineList(value);
if (parsed.length === 0) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Invalid value for --newline",
};
}
newlines = parsed;
i += 1;
continue;
}
if (!stopFlags && arg === "--pretty-only") {
lineBreaks = [true];
continue;
}
if (!stopFlags && arg === "--minified-only") {
lineBreaks = [false];
continue;
}
if (!stopFlags && arg === "--cidv1") {
cidVersion = 1;
continue;
}
if (!stopFlags && arg === "--raw-leaves") {
rawLeaves = true;
continue;
}
if (!stopFlags && arg === "--no-print") {
printMatch = false;
continue;
}
if (!stopFlags && arg === "--write") {
const value = args[i + 1];
if (!value) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: "Missing value for --write",
};
}
writePath = value;
i += 1;
continue;
}
if (!targetPath) {
targetPath = arg;
continue;
}
if (!desiredCid) {
desiredCid = arg;
continue;
}
extra.push(arg);
}
if (extra.length > 0) {
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
showHelp,
error: `Unexpected extra arguments: ${extra.join(" ")}`,
};
}
return {
targetPath,
desiredCid,
maxVariants,
reportEvery,
useJson,
onlyJson,
useRaw,
sortKeys,
indents,
newlines,
lineBreaks,
trailingNewline,
cidVersion,
rawLeaves,
writePath,
printMatch,
showHelp,
};
}
function normalizeTargetCid(value: string): string {
const trimmed = value.trim();
const withoutScheme = trimmed.replace(/^ipfs:\/\//i, "");
const withoutPath = withoutScheme.split(/[/?#]/)[0] ?? withoutScheme;
const dotIndex = withoutPath.lastIndexOf(".");
const withoutExt = dotIndex > 0 ? withoutPath.slice(0, dotIndex) : withoutPath;
if (withoutExt.startsWith("b")) {
return withoutExt.toLowerCase();
}
return withoutExt;
}
function inferCidVersion(cid: string): 0 | 1 | undefined {
if (cid.startsWith("Qm")) return 0;
if (cid.startsWith("b")) return 1;
return undefined;
}
function buildHashOptions(
targetCid: string,
options: ParsedArgs,
): HashOptions[] {
if (options.cidVersion !== undefined || options.rawLeaves !== undefined) {
let cidVersion = options.cidVersion ?? inferCidVersion(targetCid) ?? 0;
let rawLeaves = options.rawLeaves ?? false;
if (rawLeaves && cidVersion === 0) {
cidVersion = 1;
}
return [{ cidVersion, rawLeaves }];
}
const inferred = inferCidVersion(targetCid);
if (inferred === 0) {
return [{ cidVersion: 0, rawLeaves: false }];
}
if (inferred === 1) {
return [
{ cidVersion: 1, rawLeaves: false },
{ cidVersion: 1, rawLeaves: true },
];
}
return [
{ cidVersion: 0, rawLeaves: false },
{ cidVersion: 1, rawLeaves: false },
{ cidVersion: 1, rawLeaves: true },
];
}
function normalizeLineEndings(text: string, newline: "\n" | "\r\n"): string {
const normalized = text.replace(/\r\n?/g, "\n");
if (newline === "\n") return normalized;
return normalized.replace(/\n/g, "\r\n");
}
function applyTrailingNewline(
text: string,
newline: "\n" | "\r\n",
trailing: boolean,
): string {
const stripped = text.replace(/(\r\n|\n|\r)+$/, "");
return trailing ? `${stripped}${newline}` : stripped;
}
function stringifyWhitespace(value: string): string {
if (value.length === 0) return "<empty>";
if (value === " ") return "<space>";
if (value === "\t") return "<tab>";
if (/^ +$/.test(value)) return `<${value.length} spaces>`;
return value.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
}
function formatJson(value: unknown, config: JsonFormatConfig): string {
const render = (current: unknown, depth: number): string => {
if (current === null || typeof current !== "object") {
return JSON.stringify(current);
}
if (Array.isArray(current)) {
if (current.length === 0) return "[]";
const items = current.map((item) => render(item, depth + 1));
if (!config.lineBreaks) {
return `[${items.join(`,${config.commaAfter}`)}]`;
}
const indentCurrent = config.indent.repeat(depth);
const indentChild = config.indent.repeat(depth + 1);
const joined = items
.map((item) => `${indentChild}${item}`)
.join(`,${config.commaAfter}${config.newline}`);
return `[${config.newline}${joined}${config.newline}${indentCurrent}]`;
}
const obj = current as Record<string, unknown>;
let keys = Object.keys(obj);
if (keys.length === 0) return "{}";
if (config.sortKeys) {
keys = [...keys].sort();
}
if (!config.lineBreaks) {
const items = keys.map(
(key) =>
`${JSON.stringify(key)}${config.colonBefore}:${config.colonAfter}${render(
obj[key],
depth + 1,
)}`,
);
return `{${items.join(`,${config.commaAfter}`)}}`;
}
const indentCurrent = config.indent.repeat(depth);
const indentChild = config.indent.repeat(depth + 1);
const items = keys.map(
(key) =>
`${indentChild}${JSON.stringify(key)}${config.colonBefore}:${config.colonAfter}${render(
obj[key],
depth + 1,
)}`,
);
return `{${config.newline}${items.join(
`,${config.commaAfter}${config.newline}`,
)}${config.newline}${indentCurrent}}`;
};
let output = render(value, 0);
if (config.trailingNewline) {
output += config.newline;
}
return output;
}
function describeCandidate(candidate: Candidate): string {
const details = Object.entries(candidate.details)
.map(([key, value]) => `${key}=${value}`)
.join(", ");
return `${candidate.label}${details ? ` (${details})` : ""}`;
}
async function run(): Promise<void> {
const parsed = parseArgs(process.argv.slice(2));
if (parsed.error) {
console.error(parsed.error);
console.log(USAGE);
process.exit(1);
}
if (parsed.showHelp) {
console.log(USAGE);
process.exit(0);
}
if (!parsed.targetPath || !parsed.desiredCid) {
console.log(USAGE);
process.exit(1);
}
const targetCid = normalizeTargetCid(parsed.desiredCid);
const targetPath = resolve(process.cwd(), parsed.targetPath);
const hashOptionsList = buildHashOptions(targetCid, parsed);
let originalText: string;
try {
originalText = await Bun.file(targetPath).text();
} catch (error) {
console.error(`Failed to read file: ${parsed.targetPath}`);
if (error instanceof Error) {
console.error(error.message);
}
process.exit(1);
return;
}
let parsedJson: unknown = undefined;
let jsonError: Error | undefined;
if (parsed.useJson) {
try {
parsedJson = JSON.parse(originalText);
} catch (error) {
jsonError = error instanceof Error ? error : new Error("Invalid JSON");
if (parsed.onlyJson) {
console.error("JSON parse failed; --only-json requested.");
console.error(jsonError.message);
process.exit(1);
return;
}
}
}
const encoder = new TextEncoder();
const seen = new Set<string>();
let attempts = 0;
let found: Candidate | undefined;
let foundCid: string | undefined;
let foundHashOptions: HashOptions | undefined;
let stoppedEarly = false;
const tryCandidate = async (candidate: Candidate): Promise<boolean> => {
if (seen.has(candidate.content)) {
return false;
}
seen.add(candidate.content);
attempts += 1;
const bytes = encoder.encode(candidate.content);
for (const options of hashOptionsList) {
const cid = await ipfsOnlyHash(bytes, options);
if (cid === targetCid) {
found = candidate;
foundCid = cid;
foundHashOptions = options;
return true;
}
}
if (parsed.reportEvery > 0 && attempts % parsed.reportEvery === 0) {
console.log(`Checked ${attempts} variants...`);
}
if (parsed.maxVariants && attempts >= parsed.maxVariants) {
stoppedEarly = true;
return true;
}
return false;
};
if (parsed.useRaw) {
const rawCandidate: Candidate = {
content: originalText,
label: "raw",
details: {
source: "original",
},
};
if (await tryCandidate(rawCandidate)) {
stoppedEarly = stoppedEarly || !!found;
}
for (const newline of parsed.newlines) {
for (const trailingNewline of parsed.trailingNewline) {
const normalized = applyTrailingNewline(
normalizeLineEndings(originalText, newline),
newline,
trailingNewline,
);
const candidate: Candidate = {
content: normalized,
label: "raw",
details: {
newline: newline === "\n" ? "LF" : "CRLF",
trailingNewline: trailingNewline ? "on" : "off",
},
};
if (await tryCandidate(candidate)) {
stoppedEarly = stoppedEarly || !!found;
}
if (found || stoppedEarly) break;
}
if (found || stoppedEarly) break;
}
}
if (parsed.useJson && parsedJson !== undefined && !found && !stoppedEarly) {
for (const lineBreaks of parsed.lineBreaks) {
const indents = lineBreaks ? parsed.indents : [""];
for (const indent of indents) {
for (const newline of parsed.newlines) {
for (const trailingNewline of parsed.trailingNewline) {
for (const colonBefore of DEFAULT_COLON_BEFORE) {
for (const colonAfter of DEFAULT_COLON_AFTER) {
for (const commaAfter of DEFAULT_COMMA_AFTER) {
const config: JsonFormatConfig = {
indent,
newline,
lineBreaks,
trailingNewline,
colonBefore,
colonAfter,
commaAfter,
sortKeys: parsed.sortKeys,
};
const formatted = formatJson(parsedJson, config);
const candidate: Candidate = {
content: formatted,
label: "json",
details: {
lineBreaks: lineBreaks ? "on" : "off",
indent: stringifyWhitespace(indent),
newline: newline === "\n" ? "LF" : "CRLF",
trailingNewline: trailingNewline ? "on" : "off",
colonBefore: stringifyWhitespace(colonBefore),
colonAfter: stringifyWhitespace(colonAfter),
commaAfter: stringifyWhitespace(commaAfter),
sortKeys: parsed.sortKeys ? "on" : "off",
},
};
if (await tryCandidate(candidate)) {
stoppedEarly = stoppedEarly || !!found;
}
if (found || stoppedEarly) break;
}
if (found || stoppedEarly) break;
}
if (found || stoppedEarly) break;
}
if (found || stoppedEarly) break;
}
if (found || stoppedEarly) break;
}
if (found || stoppedEarly) break;
}
if (found || stoppedEarly) break;
}
}
if (found) {
console.log(`Match found after ${attempts} variants.`);
console.log(`CID: ${foundCid}`);
console.log(`Format: ${describeCandidate(found)}`);
if (foundHashOptions) {
console.log(
`Hash options: cidVersion=${foundHashOptions.cidVersion}, rawLeaves=${foundHashOptions.rawLeaves}`,
);
}
if (parsed.printMatch ?? true) {
console.log("");
console.log("-----BEGIN MATCHED CONTENT-----");
process.stdout.write(found.content);
if (
!found.content.endsWith("\n") &&
!found.content.endsWith("\r\n")
) {
process.stdout.write("\n");
}
console.log("-----END MATCHED CONTENT-----");
}
if (parsed.writePath) {
try {
await Bun.write(parsed.writePath, found.content);
console.log(`Wrote content to ${parsed.writePath}`);
} catch (error) {
console.error(`Failed to write to ${parsed.writePath}`);
if (error instanceof Error) {
console.error(error.message);
}
}
}
return;
}
if (stoppedEarly) {
console.log(`Stopped after ${attempts} variants (limit reached).`);
process.exitCode = 1;
return;
}
console.log(`No match found after ${attempts} variants.`);
if (jsonError) {
console.log("JSON parsing failed, so only raw variants were checked.");
}
process.exitCode = 1;
}
await run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment