graphify-sf 0.3.2
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 +70 -0
- package/bin/graphify-sf.js +21 -0
- package/index.js +47 -0
- package/install.js +8 -0
- package/lib/download.js +131 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# graphify-sf (npm)
|
|
2
|
+
|
|
3
|
+
Install and run [graphify-sf](https://github.com/raykuonz/graphify-sf) — the Salesforce SFDX
|
|
4
|
+
knowledge-graph tool — from npm, without needing Python.
|
|
5
|
+
|
|
6
|
+
On install, a pre-built binary matching your OS/arch is downloaded from the GitHub Release.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install graphify-sf
|
|
12
|
+
# or
|
|
13
|
+
pnpm add graphify-sf
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
> **pnpm note:** pnpm sets `ignore-scripts=true` by default, so the `postinstall` step may not
|
|
17
|
+
> run automatically. The binary is downloaded lazily on first use instead, so everything still
|
|
18
|
+
> works — you may just see a one-time download message the first time you invoke the CLI.
|
|
19
|
+
|
|
20
|
+
## CLI usage
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx graphify-sf ./force-app --out my-graph-out --no-viz
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The output directory will contain `graph.json` with the full knowledge graph.
|
|
27
|
+
|
|
28
|
+
## Programmatic usage (Node.js / sf-cockpit)
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
const { runGraphify } = require("graphify-sf");
|
|
32
|
+
|
|
33
|
+
const { graphJsonPath } = await runGraphify("./force-app", { outDir: "my-graph-out" });
|
|
34
|
+
console.log("Graph written to:", graphJsonPath);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`runGraphify` returns a Promise that resolves with `{ code, graphJsonPath }` on success and
|
|
38
|
+
rejects on non-zero exit.
|
|
39
|
+
|
|
40
|
+
Additional exports: `ensureBinary()` (downloads if missing, returns path), `binaryPath()`
|
|
41
|
+
(returns expected path without triggering a download).
|
|
42
|
+
|
|
43
|
+
## What's bundled
|
|
44
|
+
|
|
45
|
+
This package wraps the **core static-extraction path** of graphify-sf: Apex, Flow, Object, LWC,
|
|
46
|
+
Aura, Profile/PermSet, and Agentforce metadata — everything that can be parsed without an org
|
|
47
|
+
connection. Community detection and the knowledge-graph JSON export are included.
|
|
48
|
+
|
|
49
|
+
**Not bundled:** LLM-assisted extraction (`--backend`), SVG export, Neo4j export, MCP server, and
|
|
50
|
+
file-watcher mode. Those extras require a full Python install:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install "graphify-sf[all]"
|
|
54
|
+
# or
|
|
55
|
+
pipx install "graphify-sf[all]"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Supported platforms
|
|
59
|
+
|
|
60
|
+
| Platform | x64 | arm64 |
|
|
61
|
+
|-----------|-----|-------|
|
|
62
|
+
| linux | ✓ | ✓ |
|
|
63
|
+
| macOS | ✓ | ✓ |
|
|
64
|
+
| Windows | ✓ | — |
|
|
65
|
+
|
|
66
|
+
For unsupported platforms, install via PyPI: `pipx install graphify-sf`.
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT — see [LICENSE](https://github.com/raykuonz/graphify-sf/blob/main/LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { spawn } = require("child_process");
|
|
5
|
+
const { ensureBinary } = require("../lib/download");
|
|
6
|
+
|
|
7
|
+
ensureBinary()
|
|
8
|
+
.then((bin) => {
|
|
9
|
+
const child = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
|
|
10
|
+
child.on("error", (err) => {
|
|
11
|
+
process.stderr.write(`graphify-sf: failed to start binary: ${err.message}\n`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
14
|
+
child.on("close", (code) => {
|
|
15
|
+
process.exit(code == null ? 1 : code);
|
|
16
|
+
});
|
|
17
|
+
})
|
|
18
|
+
.catch((err) => {
|
|
19
|
+
process.stderr.write(`graphify-sf: ${err.message}\n`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
package/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { spawn } = require("child_process");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { ensureBinary, binaryPath } = require("./lib/download");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Run graphify-sf programmatically.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} sfdxPath - Path to the SFDX project root.
|
|
11
|
+
* @param {object} [options]
|
|
12
|
+
* @param {string} [options.outDir] - Output directory (default: "graphify-sf-out").
|
|
13
|
+
* @param {string[]} [options.extraArgs] - Additional CLI args forwarded verbatim.
|
|
14
|
+
* @param {"inherit"|"pipe"|"ignore"} [options.stdio] - stdio mode for the child process (default: "inherit").
|
|
15
|
+
* @returns {Promise<{code: number, graphJsonPath: string}>}
|
|
16
|
+
*/
|
|
17
|
+
async function runGraphify(sfdxPath, options) {
|
|
18
|
+
if (!sfdxPath) throw new Error("graphify-sf: sfdxPath is required");
|
|
19
|
+
|
|
20
|
+
const opts = options || {};
|
|
21
|
+
const outDir = opts.outDir || "graphify-sf-out";
|
|
22
|
+
const extraArgs = opts.extraArgs || [];
|
|
23
|
+
const stdioMode = opts.stdio || "inherit";
|
|
24
|
+
|
|
25
|
+
const bin = await ensureBinary();
|
|
26
|
+
const args = [sfdxPath, "--out", outDir, "--no-viz", ...extraArgs];
|
|
27
|
+
const graphJsonPath = path.join(outDir, "graph.json");
|
|
28
|
+
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const child = spawn(bin, args, { stdio: stdioMode });
|
|
31
|
+
child.on("error", reject);
|
|
32
|
+
child.on("close", (code) => {
|
|
33
|
+
if (code === 0) {
|
|
34
|
+
resolve({ code, graphJsonPath });
|
|
35
|
+
} else {
|
|
36
|
+
reject(
|
|
37
|
+
new Error(
|
|
38
|
+
`graphify-sf exited with code ${code}. ` +
|
|
39
|
+
`Command: ${bin} ${args.join(" ")}`
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = { runGraphify, ensureBinary, binaryPath };
|
package/install.js
ADDED
package/lib/download.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const https = require("https");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const PKG = require("../package.json");
|
|
8
|
+
const VERSION = PKG.version;
|
|
9
|
+
|
|
10
|
+
const SUPPORTED = {
|
|
11
|
+
linux: ["x64", "arm64"],
|
|
12
|
+
darwin: ["x64", "arm64"],
|
|
13
|
+
win32: ["x64"],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function assetName() {
|
|
17
|
+
const plat = process.platform;
|
|
18
|
+
const arch = process.arch;
|
|
19
|
+
|
|
20
|
+
const supported = SUPPORTED[plat];
|
|
21
|
+
if (!supported) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`graphify-sf: unsupported platform "${plat}". ` +
|
|
24
|
+
`Supported: ${Object.keys(SUPPORTED).join(", ")}. ` +
|
|
25
|
+
`Remediation: install via pip/pipx instead: pipx install graphify-sf`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (!supported.includes(arch)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`graphify-sf: unsupported arch "${arch}" on ${plat}. ` +
|
|
31
|
+
`Supported archs for ${plat}: ${supported.join(", ")}. ` +
|
|
32
|
+
`Remediation: install via pip/pipx instead: pipx install graphify-sf`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ext = plat === "win32" ? ".exe" : "";
|
|
37
|
+
return `graphify-sf-${plat}-${arch}${ext}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function binDir() {
|
|
41
|
+
return path.join(__dirname, "..", "bin");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function binaryPath() {
|
|
45
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
46
|
+
return path.join(binDir(), `graphify-sf-bin${ext}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function downloadUrl(asset) {
|
|
50
|
+
return `https://github.com/raykuonz/graphify-sf/releases/download/v${VERSION}/${asset}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function fetchFollow(url, redirects) {
|
|
54
|
+
if (redirects === undefined) redirects = 5;
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
https
|
|
57
|
+
.get(url, { headers: { "User-Agent": "graphify-sf-npm-installer" } }, (res) => {
|
|
58
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
59
|
+
if (redirects === 0) {
|
|
60
|
+
reject(new Error(`graphify-sf: too many redirects downloading ${url}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
resolve(fetchFollow(res.headers.location, redirects - 1));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (res.statusCode !== 200) {
|
|
67
|
+
reject(
|
|
68
|
+
new Error(
|
|
69
|
+
`graphify-sf: download failed with HTTP ${res.statusCode} from ${url}. ` +
|
|
70
|
+
`Remediation: install via pip/pipx instead: pipx install graphify-sf`
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
resolve(res);
|
|
76
|
+
})
|
|
77
|
+
.on("error", reject);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function download() {
|
|
82
|
+
const asset = assetName();
|
|
83
|
+
const url = downloadUrl(asset);
|
|
84
|
+
const dest = binaryPath();
|
|
85
|
+
|
|
86
|
+
fs.mkdirSync(binDir(), { recursive: true });
|
|
87
|
+
|
|
88
|
+
process.stdout.write(`graphify-sf: downloading ${asset} from ${url}\n`);
|
|
89
|
+
|
|
90
|
+
const res = await fetchFollow(url);
|
|
91
|
+
|
|
92
|
+
await new Promise((resolve, reject) => {
|
|
93
|
+
const tmp = dest + ".tmp";
|
|
94
|
+
const out = fs.createWriteStream(tmp);
|
|
95
|
+
res.pipe(out);
|
|
96
|
+
out.on("finish", () => {
|
|
97
|
+
out.close(() => {
|
|
98
|
+
try {
|
|
99
|
+
fs.renameSync(tmp, dest);
|
|
100
|
+
resolve();
|
|
101
|
+
} catch (e) {
|
|
102
|
+
reject(e);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
out.on("error", (e) => {
|
|
107
|
+
fs.unlink(tmp, () => {});
|
|
108
|
+
reject(e);
|
|
109
|
+
});
|
|
110
|
+
res.on("error", (e) => {
|
|
111
|
+
fs.unlink(tmp, () => {});
|
|
112
|
+
reject(e);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (process.platform !== "win32") {
|
|
117
|
+
fs.chmodSync(dest, 0o755);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
process.stdout.write(`graphify-sf: installed to ${dest}\n`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function ensureBinary() {
|
|
124
|
+
const dest = binaryPath();
|
|
125
|
+
if (!fs.existsSync(dest)) {
|
|
126
|
+
await download();
|
|
127
|
+
}
|
|
128
|
+
return dest;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = { assetName, binaryPath, binDir, download, ensureBinary };
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "graphify-sf",
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Turn any Salesforce SFDX project into a queryable knowledge graph — no Python required.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"graphify-sf": "bin/graphify-sf.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"postinstall": "node install.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/",
|
|
14
|
+
"lib/",
|
|
15
|
+
"index.js",
|
|
16
|
+
"install.js",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"os": [
|
|
23
|
+
"linux",
|
|
24
|
+
"darwin",
|
|
25
|
+
"win32"
|
|
26
|
+
],
|
|
27
|
+
"cpu": [
|
|
28
|
+
"x64",
|
|
29
|
+
"arm64"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/raykuonz/graphify-sf.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/raykuonz/graphify-sf#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/raykuonz/graphify-sf/issues"
|
|
39
|
+
}
|
|
40
|
+
}
|