backlinkflow 0.1.1 → 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/README.md +5 -2
- package/dist/awesome.js +80 -0
- package/dist/awesome.js.map +7 -0
- package/dist/engine/adapters.js +39 -16
- package/dist/engine/adapters.js.map +2 -2
- package/dist/engine/fields.js +161 -16
- package/dist/engine/fields.js.map +3 -3
- package/dist/engine/submit.js +50 -3
- package/dist/engine/submit.js.map +2 -2
- package/dist/index.js +112 -6
- package/dist/index.js.map +2 -2
- package/dist/indexnow.js +122 -0
- package/dist/indexnow.js.map +7 -0
- package/dist/measure.js +189 -0
- package/dist/measure.js.map +7 -0
- package/package.json +1 -1
package/dist/measure.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
function toDomain(url) {
|
|
8
|
+
const host = url.replace(/^[a-z]+:\/\//i, "").replace(/^\/\//, "").split(/[/?#]/)[0].split("@").pop() || "";
|
|
9
|
+
return host.replace(/^www\./, "").toLowerCase();
|
|
10
|
+
}
|
|
11
|
+
function findCommonCrawlScript() {
|
|
12
|
+
const candidates = [
|
|
13
|
+
path.join(__dirname, "..", "..", "seoflow", "python", "commoncrawl_graph.py"),
|
|
14
|
+
path.join(__dirname, "..", "..", "..", "seoflow", "python", "commoncrawl_graph.py"),
|
|
15
|
+
path.join(process.cwd(), "..", "seoflow", "python", "commoncrawl_graph.py")
|
|
16
|
+
];
|
|
17
|
+
for (const p of candidates) {
|
|
18
|
+
if (fs.existsSync(p)) return p;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
async function measureViaCommonCrawl(domain) {
|
|
23
|
+
const script = findCommonCrawlScript();
|
|
24
|
+
if (!script) return { unavailable: true, note: "seoflow python/commoncrawl_graph.py not found (sibling checkout)" };
|
|
25
|
+
try {
|
|
26
|
+
const out = execFileSync("python3", [script, domain, "--json"], {
|
|
27
|
+
encoding: "utf8",
|
|
28
|
+
timeout: 18e4,
|
|
29
|
+
maxBuffer: 10 * 1024 * 1024
|
|
30
|
+
});
|
|
31
|
+
const start = out.indexOf("{");
|
|
32
|
+
const data = start === -1 ? null : JSON.parse(out.slice(start));
|
|
33
|
+
if (data.status !== "success" || !data.data) {
|
|
34
|
+
return { unavailable: true, note: data.error || "commoncrawl lookup failed" };
|
|
35
|
+
}
|
|
36
|
+
const d = data.data;
|
|
37
|
+
return {
|
|
38
|
+
source: "commoncrawl",
|
|
39
|
+
inCommonCrawl: Boolean(d.in_crawl),
|
|
40
|
+
pagerank: d.pagerank ?? null,
|
|
41
|
+
harmonicCentrality: d.harmonic_centrality ?? null,
|
|
42
|
+
note: d.note || void 0,
|
|
43
|
+
unavailable: false
|
|
44
|
+
};
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return { unavailable: true, note: `commoncrawl failed: ${err.message.slice(0, 120)}` };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function measureViaOpenLinkProfiles(domain) {
|
|
50
|
+
const url = `https://openlinkprofiles.org/api/free/${encodeURIComponent(domain)}`;
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(3e4), headers: { "User-Agent": "Mozilla/5.0 BacklinkFlow/0.4" } });
|
|
53
|
+
if (!res.ok) return { unavailable: true, note: `openlinkprofiles HTTP ${res.status}` };
|
|
54
|
+
const data = await res.json();
|
|
55
|
+
const total = data?.total_backlinks ?? data?.backlinks ?? data?.total ?? null;
|
|
56
|
+
const refs = data?.referring_domains ?? data?.domains ?? null;
|
|
57
|
+
return {
|
|
58
|
+
source: "openlinkprofiles",
|
|
59
|
+
totalBacklinks: typeof total === "number" ? total : total != null ? Number(total) : null,
|
|
60
|
+
referringDomains: typeof refs === "number" ? refs : refs != null ? Number(refs) : null,
|
|
61
|
+
note: data?.note || (total == null && refs == null ? "API returned no numeric fields" : void 0),
|
|
62
|
+
unavailable: total == null && refs == null
|
|
63
|
+
};
|
|
64
|
+
} catch (err) {
|
|
65
|
+
return { unavailable: true, note: `openlinkprofiles failed: ${err.message.slice(0, 120)}` };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function measureViaSeoflow(domain) {
|
|
69
|
+
try {
|
|
70
|
+
const mod = await import("seoflow/lib/backlinks/backlinks.js").catch(() => null);
|
|
71
|
+
const analyzer = mod?.BacklinkAnalyzer;
|
|
72
|
+
if (!analyzer) return null;
|
|
73
|
+
const res = analyzer.analyze(`https://${domain}`, { includeBing: false, includeMoz: false, includeCommonCrawl: true, limit: 50 });
|
|
74
|
+
return {
|
|
75
|
+
source: "seoflow",
|
|
76
|
+
totalBacklinks: res?.totalBacklinks ?? null,
|
|
77
|
+
referringDomains: res?.referringDomains ?? null,
|
|
78
|
+
note: res?.isMock ? "seoflow returned mock data (analyzer unavailable)" : void 0,
|
|
79
|
+
unavailable: Boolean(res?.isMock) || res == null
|
|
80
|
+
};
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const CDX_API = "https://index.commoncrawl.org";
|
|
86
|
+
const CDX_COLLECTIONS = ["CC-MAIN-2026-34", "CC-MAIN-2026-30", "CC-MAIN-2026-25", "CC-MAIN-2026-21", "CC-MAIN-2026-17", "CC-MAIN-2025-51"];
|
|
87
|
+
async function measureViaCdx(domain) {
|
|
88
|
+
for (const coll of CDX_COLLECTIONS) {
|
|
89
|
+
const own = await cdxQuery(coll, `${domain}%2F*`, `url=${encodeURIComponent(domain)}%2F*`);
|
|
90
|
+
const inbound = await cdxQuery(coll, `*%2F%2F${domain}%2F*`, `url=*%2F%2F${encodeURIComponent(domain)}%2F*`);
|
|
91
|
+
if (own.unavailable && inbound.unavailable) continue;
|
|
92
|
+
const linking = inbound.parsed.filter((r) => !(r.url || "").includes(domain));
|
|
93
|
+
return {
|
|
94
|
+
source: "commoncrawl-cdx",
|
|
95
|
+
pagesInIndex: own.parsed.length,
|
|
96
|
+
totalBacklinks: linking.length,
|
|
97
|
+
referringDomains: new Set(linking.map((r) => r.url.split("/")[2])).size,
|
|
98
|
+
note: `captures for ${domain} in ${coll} (pages: ${own.parsed.length}, linking: ${linking.length})`,
|
|
99
|
+
unavailable: false
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return { unavailable: true, note: `no captures for ${domain} in any recent Common Crawl index (too new or never crawled)` };
|
|
103
|
+
}
|
|
104
|
+
async function cdxQuery(coll, encodedUrl, urlParam) {
|
|
105
|
+
const api = `${CDX_API}/${coll}-index?${urlParam}&output=json&filter=status:200&collapse=urlkey`;
|
|
106
|
+
try {
|
|
107
|
+
const res = await fetch(api, { signal: AbortSignal.timeout(45e3), headers: { "User-Agent": "Mozilla/5.0 BacklinkFlow/0.4" } });
|
|
108
|
+
const text = await res.text();
|
|
109
|
+
if (res.status === 404 || /No index found|No Captures found/i.test(text)) return { parsed: [], unavailable: true };
|
|
110
|
+
if (!res.ok) return { parsed: [], unavailable: true };
|
|
111
|
+
const lines = text.split("\n").filter((l) => l.trim().startsWith("{"));
|
|
112
|
+
return { parsed: lines.map((l) => {
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(l);
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}).filter(Boolean), unavailable: false };
|
|
119
|
+
} catch {
|
|
120
|
+
return { parsed: [], unavailable: true };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function measureBacklinks(siteUrl) {
|
|
124
|
+
const domain = toDomain(siteUrl);
|
|
125
|
+
const base = {
|
|
126
|
+
url: siteUrl,
|
|
127
|
+
domain,
|
|
128
|
+
source: "unknown",
|
|
129
|
+
measuredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
130
|
+
unavailable: true
|
|
131
|
+
};
|
|
132
|
+
const parts = [];
|
|
133
|
+
const seoflow = await measureViaSeoflow(domain);
|
|
134
|
+
if (seoflow && !seoflow.unavailable) parts.push(seoflow);
|
|
135
|
+
const cc = await measureViaCommonCrawl(domain);
|
|
136
|
+
if (!cc.unavailable) parts.push(cc);
|
|
137
|
+
const cdx = await measureViaCdx(domain);
|
|
138
|
+
if (!cdx.unavailable) parts.push(cdx);
|
|
139
|
+
const olp = await measureViaOpenLinkProfiles(domain);
|
|
140
|
+
if (!olp.unavailable) parts.push(olp);
|
|
141
|
+
if (!parts.length) {
|
|
142
|
+
const notes = [seoflow?.note, cc.note, cdx.note, olp.note].filter(Boolean);
|
|
143
|
+
return {
|
|
144
|
+
...base,
|
|
145
|
+
source: "none",
|
|
146
|
+
note: notes[0] || "all free sources unavailable",
|
|
147
|
+
unavailable: true
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const merged = {
|
|
151
|
+
...base,
|
|
152
|
+
source: parts.map((p) => p.source).filter(Boolean).join("+"),
|
|
153
|
+
unavailable: false
|
|
154
|
+
};
|
|
155
|
+
const first = parts[0];
|
|
156
|
+
merged.totalBacklinks = first.totalBacklinks ?? null;
|
|
157
|
+
merged.referringDomains = first.referringDomains ?? null;
|
|
158
|
+
merged.pagesInIndex = cdx.pagesInIndex ?? null;
|
|
159
|
+
merged.inCommonCrawl = parts.some((p) => p.inCommonCrawl) || Boolean(cdx.pagesInIndex);
|
|
160
|
+
merged.pagerank = cc.pagerank ?? null;
|
|
161
|
+
merged.harmonicCentrality = cc.harmonicCentrality ?? null;
|
|
162
|
+
const note = [cc.note, cdx.note, olp.note, seoflow?.note].filter(Boolean).join("; ");
|
|
163
|
+
if (note) merged.note = note;
|
|
164
|
+
return merged;
|
|
165
|
+
}
|
|
166
|
+
function saveMeasurement(m) {
|
|
167
|
+
const dir = path.join(process.cwd(), ".linkflow");
|
|
168
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
169
|
+
const p = path.join(dir, "measure.json");
|
|
170
|
+
const records = fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, "utf8")) : [];
|
|
171
|
+
records.push(m);
|
|
172
|
+
fs.writeFileSync(p, JSON.stringify(records.slice(-50), null, 2));
|
|
173
|
+
return p;
|
|
174
|
+
}
|
|
175
|
+
function osHome() {
|
|
176
|
+
return os.homedir();
|
|
177
|
+
}
|
|
178
|
+
export {
|
|
179
|
+
findCommonCrawlScript,
|
|
180
|
+
measureBacklinks,
|
|
181
|
+
measureViaCdx,
|
|
182
|
+
measureViaCommonCrawl,
|
|
183
|
+
measureViaOpenLinkProfiles,
|
|
184
|
+
measureViaSeoflow,
|
|
185
|
+
osHome,
|
|
186
|
+
saveMeasurement,
|
|
187
|
+
toDomain
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=measure.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/measure.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * BacklinkFlow v0.4 \u2014 backlink measurement (free sources only).\n *\n * Strategy (mirrors seoflow's lib/backlinks/backlinks.ts, free path):\n * 1. Common Crawl web graph via seoflow's python/commoncrawl_graph.py when\n * the seoflow checkout exists next to this repo (domain-level PageRank,\n * harmonic centrality, crawl presence). No API key, public data.\n * 2. OpenLinkProfiles.org API fallback (free, no key) \u2014 returns a real\n * backlink count for the domain.\n * 3. Explicit scaffold hook: if a seoflow BacklinkAnalyzer is importable\n * from node_modules, delegate to it (returns mock data when unavailable).\n *\n * Bing Webmaster / Moz are paid or keyed \u2014 deliberately NOT wired up.\n */\nimport { execFileSync } from 'child_process';\nimport fs from 'fs';\nimport path from 'path';\nimport os from 'os';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface BacklinkMetrics {\n url: string;\n domain: string;\n source: string;\n measuredAt: string;\n totalBacklinks?: number | null;\n referringDomains?: number | null;\n /** pages of the domain captured in the latest Common Crawl crawl (CDX) */\n pagesInIndex?: number | null;\n pagerank?: number | null;\n harmonicCentrality?: number | null;\n inCommonCrawl?: boolean;\n note?: string;\n /** true when every real source failed and numbers are null */\n unavailable: boolean;\n}\n\nexport function toDomain(url: string): string {\n const host = url.replace(/^[a-z]+:\\/\\//i, '').replace(/^\\/\\//, '').split(/[/?#]/)[0].split('@').pop() || '';\n return host.replace(/^www\\./, '').toLowerCase();\n}\n\n/** Locate seoflow's commoncrawl_graph.py (sibling checkout, not modified). */\nexport function findCommonCrawlScript(): string | null {\n const candidates = [\n path.join(__dirname, '..', '..', 'seoflow', 'python', 'commoncrawl_graph.py'),\n path.join(__dirname, '..', '..', '..', 'seoflow', 'python', 'commoncrawl_graph.py'),\n path.join(process.cwd(), '..', 'seoflow', 'python', 'commoncrawl_graph.py'),\n ];\n for (const p of candidates) {\n if (fs.existsSync(p)) return p;\n }\n return null;\n}\n\n/** Run seoflow's Common Crawl script (domain-level metrics, free, no key). */\nexport async function measureViaCommonCrawl(domain: string): Promise<Partial<BacklinkMetrics>> {\n const script = findCommonCrawlScript();\n if (!script) return { unavailable: true, note: 'seoflow python/commoncrawl_graph.py not found (sibling checkout)' };\n try {\n const out = execFileSync('python3', [script, domain, '--json'], {\n encoding: 'utf8',\n timeout: 180000,\n maxBuffer: 10 * 1024 * 1024,\n });\n // script pretty-prints JSON (indent=2); urllib3 warnings land on stderr.\n // Find the first `{` and parse from there to the end.\n const start = out.indexOf('{');\n const data = start === -1 ? null : JSON.parse(out.slice(start));\n if (data.status !== 'success' || !data.data) {\n return { unavailable: true, note: data.error || 'commoncrawl lookup failed' };\n }\n const d = data.data;\n return {\n source: 'commoncrawl',\n inCommonCrawl: Boolean(d.in_crawl),\n pagerank: d.pagerank ?? null,\n harmonicCentrality: d.harmonic_centrality ?? null,\n note: d.note || undefined,\n unavailable: false,\n };\n } catch (err) {\n return { unavailable: true, note: `commoncrawl failed: ${(err as Error).message.slice(0, 120)}` };\n }\n}\n\n/** OpenLinkProfiles free API \u2014 real backlink count, no key. */\nexport async function measureViaOpenLinkProfiles(domain: string): Promise<Partial<BacklinkMetrics>> {\n const url = `https://openlinkprofiles.org/api/free/${encodeURIComponent(domain)}`;\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(30000), headers: { 'User-Agent': 'Mozilla/5.0 BacklinkFlow/0.4' } });\n if (!res.ok) return { unavailable: true, note: `openlinkprofiles HTTP ${res.status}` };\n const data: any = await res.json();\n const total = data?.total_backlinks ?? data?.backlinks ?? data?.total ?? null;\n const refs = data?.referring_domains ?? data?.domains ?? null;\n return {\n source: 'openlinkprofiles',\n totalBacklinks: typeof total === 'number' ? total : total != null ? Number(total) : null,\n referringDomains: typeof refs === 'number' ? refs : refs != null ? Number(refs) : null,\n note: data?.note || (total == null && refs == null ? 'API returned no numeric fields' : undefined),\n unavailable: total == null && refs == null,\n };\n } catch (err) {\n return { unavailable: true, note: `openlinkprofiles failed: ${(err as Error).message.slice(0, 120)}` };\n }\n}\n\n/** Scaffold: delegate to seoflow's BacklinkAnalyzer when importable. */\nexport async function measureViaSeoflow(domain: string): Promise<Partial<BacklinkMetrics> | null> {\n try {\n const mod = await import('seoflow/lib/backlinks/backlinks.js').catch(() => null);\n const analyzer = (mod as any)?.BacklinkAnalyzer;\n if (!analyzer) return null;\n const res = analyzer.analyze(`https://${domain}`, { includeBing: false, includeMoz: false, includeCommonCrawl: true, limit: 50 });\n return {\n source: 'seoflow',\n totalBacklinks: res?.totalBacklinks ?? null,\n referringDomains: res?.referringDomains ?? null,\n note: res?.isMock ? 'seoflow returned mock data (analyzer unavailable)' : undefined,\n unavailable: Boolean(res?.isMock) || res == null,\n };\n } catch {\n return null;\n }\n}\n\nconst CDX_API = 'https://index.commoncrawl.org';\n\n/** Try a handful of recent collections \u2014 newer crawls may not have the domain yet. */\nconst CDX_COLLECTIONS = ['CC-MAIN-2026-34', 'CC-MAIN-2026-30', 'CC-MAIN-2026-25', 'CC-MAIN-2026-21', 'CC-MAIN-2026-17', 'CC-MAIN-2025-51'];\n\n/**\n * IndexNow CDX backlink scan \u2014 real counts from Common Crawl's index (free, no key).\n * Query 1: pages of the domain (url=domain,star). Query 2: pages linking TO it\n * (url=star,star//domain,star, excluding the domain's own pages).\n */\nexport async function measureViaCdx(domain: string): Promise<Partial<BacklinkMetrics>> {\n for (const coll of CDX_COLLECTIONS) {\n const own = await cdxQuery(coll, `${domain}%2F*`, `url=${encodeURIComponent(domain)}%2F*`);\n const inbound = await cdxQuery(coll, `*%2F%2F${domain}%2F*`, `url=*%2F%2F${encodeURIComponent(domain)}%2F*`);\n if (own.unavailable && inbound.unavailable) continue; // try older crawl\n const linking = inbound.parsed.filter((r) => !(r.url || '').includes(domain));\n return {\n source: 'commoncrawl-cdx',\n pagesInIndex: own.parsed.length,\n totalBacklinks: linking.length,\n referringDomains: new Set(linking.map((r) => r.url.split('/')[2])).size,\n note: `captures for ${domain} in ${coll} (pages: ${own.parsed.length}, linking: ${linking.length})`,\n unavailable: false,\n };\n }\n return { unavailable: true, note: `no captures for ${domain} in any recent Common Crawl index (too new or never crawled)` };\n}\n\nasync function cdxQuery(coll: string, encodedUrl: string, urlParam: string): Promise<{ parsed: any[]; unavailable: boolean }> {\n const api = `${CDX_API}/${coll}-index?${urlParam}&output=json&filter=status:200&collapse=urlkey`;\n try {\n const res = await fetch(api, { signal: AbortSignal.timeout(45000), headers: { 'User-Agent': 'Mozilla/5.0 BacklinkFlow/0.4' } });\n const text = await res.text();\n // CDX answers 404 for \"no captures\" and \"no such collection\" alike \u2014 the\n // JSON body distinguishes them, so read it before checking the status code.\n if (res.status === 404 || /No index found|No Captures found/i.test(text)) return { parsed: [], unavailable: true };\n if (!res.ok) return { parsed: [], unavailable: true };\n const lines = text.split('\\n').filter((l) => l.trim().startsWith('{'));\n return { parsed: lines.map((l) => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean), unavailable: false };\n } catch {\n return { parsed: [], unavailable: true };\n }\n}\n\n/**\n * Measure backlinks for a site. Tries in order: seoflow analyzer scaffold,\n * Common Crawl web graph, Common Crawl CDX index, OpenLinkProfiles API.\n * Merges whatever succeeds.\n */\nexport async function measureBacklinks(siteUrl: string): Promise<BacklinkMetrics> {\n const domain = toDomain(siteUrl);\n const base: BacklinkMetrics = {\n url: siteUrl,\n domain,\n source: 'unknown',\n measuredAt: new Date().toISOString(),\n unavailable: true,\n };\n\n const parts: Partial<BacklinkMetrics>[] = [];\n\n const seoflow = await measureViaSeoflow(domain);\n if (seoflow && !seoflow.unavailable) parts.push(seoflow);\n\n const cc = await measureViaCommonCrawl(domain);\n if (!cc.unavailable) parts.push(cc);\n\n const cdx = await measureViaCdx(domain);\n if (!cdx.unavailable) parts.push(cdx);\n\n const olp = await measureViaOpenLinkProfiles(domain);\n if (!olp.unavailable) parts.push(olp);\n\n if (!parts.length) {\n const notes = [seoflow?.note, cc.note, cdx.note, olp.note].filter(Boolean);\n return {\n ...base,\n source: 'none',\n note: notes[0] || 'all free sources unavailable',\n unavailable: true,\n };\n }\n\n // Merge: prefer the highest-quality source for each field.\n const merged: BacklinkMetrics = {\n ...base,\n source: parts.map((p) => p.source).filter(Boolean).join('+'),\n unavailable: false,\n };\n const first = parts[0];\n merged.totalBacklinks = first.totalBacklinks ?? null;\n merged.referringDomains = first.referringDomains ?? null;\n merged.pagesInIndex = cdx.pagesInIndex ?? null;\n merged.inCommonCrawl = parts.some((p) => p.inCommonCrawl) || Boolean(cdx.pagesInIndex);\n merged.pagerank = cc.pagerank ?? null;\n merged.harmonicCentrality = cc.harmonicCentrality ?? null;\n const note = [cc.note, cdx.note, olp.note, seoflow?.note].filter(Boolean).join('; ');\n if (note) merged.note = note;\n return merged;\n}\n\n/** Cache the last measurement in .linkflow/measure.json for before/after diffing. */\nexport function saveMeasurement(m: BacklinkMetrics): string {\n const dir = path.join(process.cwd(), '.linkflow');\n fs.mkdirSync(dir, { recursive: true });\n const p = path.join(dir, 'measure.json');\n const records: BacklinkMetrics[] = fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : [];\n records.push(m);\n fs.writeFileSync(p, JSON.stringify(records.slice(-50), null, 2));\n return p;\n}\n\nexport function osHome(): string {\n return os.homedir();\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backlinkflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "LinkFlow — zero-cost backlink & directory submission engine: vetted directory database, AI-tailored submission payloads, tracker, and proof-of-submission reports.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|