flamerjs 1.0.0 → 1.1.1
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/CHANGELOG.md +141 -0
- package/README.md +63 -32
- package/dist/cli.js +129 -64
- package/docs/ARCHITECTURE.md +175 -0
- package/package.json +19 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to flamer are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [1.0.0] - 2026-10-24
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Rust and C++ native workers under `native/core` for coordinated native
|
|
12
|
+
processing.
|
|
13
|
+
- Versioned JSONL worker protocol with TypeScript integration through
|
|
14
|
+
`--engine all|typescript|rust|cpp`.
|
|
15
|
+
- Dedicated native Rust and C++ build/test workflows.
|
|
16
|
+
- Architecture documentation covering runtime layers, protocol, and fallback
|
|
17
|
+
behavior.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Native execution failures are explicit for targeted workers and safely fall
|
|
22
|
+
back to TypeScript in the default `all` mode.
|
|
23
|
+
|
|
24
|
+
## [1.1.1] - 2026-10-24
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Align the main npm package and native worker package versions.
|
|
29
|
+
- Publish all supported Linux, macOS, and Windows x64/arm64 native packages.
|
|
30
|
+
- Include the changelog and architecture documentation in the npm tarball.
|
|
31
|
+
|
|
32
|
+
## [1.1.0] - 2026-10-24
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Optional platform-specific npm packages containing both native workers.
|
|
37
|
+
- Release automation for Linux x64, macOS x64/arm64, and Windows x64 native
|
|
38
|
+
packages.
|
|
39
|
+
- A native package builder and explicit Rust/C++ build-phase output.
|
|
40
|
+
- `scripts/install.sh` with npm installation and guarded native release-asset
|
|
41
|
+
installation paths.
|
|
42
|
+
- The default `flamer .` execution coordinates TypeScript, Rust, and C++
|
|
43
|
+
together, with pure TypeScript fallback when native workers are unavailable.
|
|
44
|
+
- Rust owns shared file metrics and Rust-specific checks; C++ owns C/C++-specific
|
|
45
|
+
checks, so the workers no longer perform the same analysis.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- CI now builds before tests that execute the bundled CLI.
|
|
50
|
+
- Node 18 builds use a compatible pinned tsup release.
|
|
51
|
+
- Conventional Commit validation accepts the `ci` type.
|
|
52
|
+
|
|
53
|
+
- Dedicated Codecov coverage reporting and CodeQL analysis workflows.
|
|
54
|
+
|
|
55
|
+
## [0.1.6] - 2026-10-24
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- Updated the README Quick Start to use the published `flamerjs` package with
|
|
60
|
+
`npx`, local installation, and global installation examples.
|
|
61
|
+
- Updated CLI examples to use the published `flamerjs` command while retaining
|
|
62
|
+
the source checkout workflow for contributors.
|
|
63
|
+
|
|
64
|
+
## [0.1.5] - 2026-10-24
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- Human-readable terminal output is now the default CLI mode, with severity
|
|
69
|
+
grouping, clean scan summaries, and TTY-aware ANSI colors.
|
|
70
|
+
- `--color` and `--no-color` flags for controlling terminal colors.
|
|
71
|
+
- High-confidence detection for explicit not-implemented errors and empty
|
|
72
|
+
error handlers.
|
|
73
|
+
- npm packaging metadata, a `flamerjs` command alias, and automated publishing
|
|
74
|
+
to npm and GitHub Packages on version tags.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- README and CI test-count checks now follow the current TypeScript test suite.
|
|
79
|
+
- CLI, contributor, and agent documentation now matches the `src/` and `dist/`
|
|
80
|
+
project layout.
|
|
81
|
+
|
|
82
|
+
### Removed
|
|
83
|
+
|
|
84
|
+
- Bun as a project development dependency and lockfile.
|
|
85
|
+
- JSON as the default interactive output mode; JSON remains available with
|
|
86
|
+
`--format json`.
|
|
87
|
+
|
|
88
|
+
## [0.1.0] - 2026-10-02
|
|
89
|
+
|
|
90
|
+
First public release.
|
|
91
|
+
|
|
92
|
+
### Added
|
|
93
|
+
|
|
94
|
+
- Single-pass tree scanner covering 25 languages across 70 file extensions:
|
|
95
|
+
JavaScript, TypeScript, Python, Rust, Go, C, C++, C#, Java, Kotlin, Scala,
|
|
96
|
+
Terraform, HCL, Docker, SQL, Ruby, PHP, Shell, PowerShell, Haskell, Elixir,
|
|
97
|
+
Lua, R, Solidity, Swift, Dart, Zig, and Jupyter notebooks.
|
|
98
|
+
- Eight analyzers, each isolated so one throwing does not stop the rest:
|
|
99
|
+
`universal`, `notebooks`, `scaffolding`, `testing`, `git`, `node`, `python`,
|
|
100
|
+
`systems`.
|
|
101
|
+
- Redacted credential detection for 14 credential shapes, with test and fixture
|
|
102
|
+
paths exempt so a repository's own fake secrets are not reported.
|
|
103
|
+
- Adaptive bloat detection with per-language baselines, so a large generated
|
|
104
|
+
file is not scored as though a human wrote every line.
|
|
105
|
+
- Per-language test coverage ratios, so a mostly-Terraform repository is not
|
|
106
|
+
graded as though it were all application code.
|
|
107
|
+
- The roast persona in `SKILL.md`, including a confirmation gate, an audit mode
|
|
108
|
+
that drops the character, and an instruction to defer deep security analysis
|
|
109
|
+
to the `poison` skill when it is installed.
|
|
110
|
+
- JSON output by default with a human-readable `--format summary`.
|
|
111
|
+
|
|
112
|
+
### Performance
|
|
113
|
+
|
|
114
|
+
Measured on a 3.0 GB monorepo of 98,161 files, 71,150 of them source:
|
|
115
|
+
**3.60s before, 2.10s after**, with byte-identical findings.
|
|
116
|
+
|
|
117
|
+
- Newline discovery uses `indexOf` rather than a per-character `charCodeAt`
|
|
118
|
+
loop. Measured 3.7x faster on a 525 MB corpus; `split` measured slower than
|
|
119
|
+
both while also allocating an array of every line.
|
|
120
|
+
- The credential scanner gates 13 of its 14 patterns behind a literal substring
|
|
121
|
+
test, so the regex engine never runs against a file that cannot contain that
|
|
122
|
+
shape of credential. The assignment pattern is intentionally left ungated
|
|
123
|
+
because its names match case insensitively and an incomplete gate would be a
|
|
124
|
+
silently dropped rule.
|
|
125
|
+
- Line numbers for a credential finding are resolved by binary search over a
|
|
126
|
+
lazily built offset table. The previous approach sliced and split the text
|
|
127
|
+
once per match, which is up to 280 times on a single file.
|
|
128
|
+
|
|
129
|
+
### Notes
|
|
130
|
+
|
|
131
|
+
- Zero runtime dependencies. Node 18 or newer, no install step.
|
|
132
|
+
- `poison` is a separate project. Flamer does not import it, and the handoff is
|
|
133
|
+
a runtime capability check in `SKILL.md` rather than a code dependency.
|
|
134
|
+
|
|
135
|
+
[Unreleased]: https://github.com/dummy3ye/flamer/compare/v1.1.1...HEAD
|
|
136
|
+
[1.1.1]: https://github.com/dummy3ye/flamer/releases/tag/v1.1.1
|
|
137
|
+
[1.1.0]: https://github.com/dummy3ye/flamer/releases/tag/v1.1.0
|
|
138
|
+
[1.0.0]: https://github.com/dummy3ye/flamer/releases/tag/v1.0.0
|
|
139
|
+
[0.1.6]: https://github.com/dummy3ye/flamer/releases/tag/v0.1.6
|
|
140
|
+
[0.1.5]: https://github.com/dummy3ye/flamer/releases/tag/v0.1.5
|
|
141
|
+
[0.1.0]: https://github.com/OWNER/flamer/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -51,18 +51,22 @@ can be used locally, in pull requests, or in CI.
|
|
|
51
51
|
- **Language-aware checks:** JavaScript/TypeScript, Python, Rust, Go, C/C++,
|
|
52
52
|
C#, Zig, SQL, notebooks, and additional language profiles.
|
|
53
53
|
- **Stable JSON output:** suitable for CI annotations and other automation.
|
|
54
|
-
- **Single-pass scanning:**
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
- **Single-pass TypeScript scanning:** the repository is walked once and
|
|
55
|
+
content is collected once before analyzers run.
|
|
56
|
+
- **Coordinated native workers:** Rust handles shared file metrics and
|
|
57
|
+
Rust-specific checks, while C++ handles C/C++ checks; TypeScript remains the
|
|
58
|
+
source of truth and fallback path.
|
|
57
59
|
|
|
58
60
|
## Requirements
|
|
59
61
|
|
|
60
62
|
- Node.js 18 or newer
|
|
61
63
|
- npm
|
|
62
64
|
|
|
63
|
-
The default TypeScript engine needs only Node.js and npm.
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
The default TypeScript engine needs only Node.js and npm. The published package
|
|
66
|
+
also declares optional, platform-specific native packages containing both
|
|
67
|
+
workers, so a compatible npm install gets full mode automatically. Building
|
|
68
|
+
workers from this repository requires Rust/Cargo and a C++17 compiler with
|
|
69
|
+
CMake.
|
|
66
70
|
|
|
67
71
|
## Quick start
|
|
68
72
|
|
|
@@ -107,24 +111,24 @@ flamerjs /path/to/repository --format summary
|
|
|
107
111
|
flamerjs [path] [options]
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
| Option
|
|
111
|
-
|
|
|
112
|
-
| `--package, --scope <name>`
|
|
113
|
-
| `--path <dir>`
|
|
114
|
-
| `--format <json\|summary>`
|
|
115
|
-
| `--out <file>`
|
|
116
|
-
| `--fail-on <SEVERITY>`
|
|
117
|
-
| `--max-files <n>`
|
|
118
|
-
| `--commit-limit <n>`
|
|
119
|
-
| `--no-git`
|
|
120
|
-
| `--no-lockfile-warning`
|
|
121
|
-
| `--ignore-dir <name>`
|
|
122
|
-
| `--quiet`
|
|
123
|
-
| `--color`
|
|
124
|
-
| `--no-color`
|
|
125
|
-
| `--engine <typescript\|
|
|
126
|
-
| `--list-languages`
|
|
127
|
-
| `--help`
|
|
114
|
+
| Option | Description |
|
|
115
|
+
| --------------------------------------- | ----------------------------------------------------------------------------- |
|
|
116
|
+
| `--package, --scope <name>` | Analyze only a named sub-project |
|
|
117
|
+
| `--path <dir>` | Set the repository root; defaults to the current directory |
|
|
118
|
+
| `--format <json\|summary>` | Select JSON or human-readable output; summary is the default |
|
|
119
|
+
| `--out <file>` | Also write the JSON report to a file |
|
|
120
|
+
| `--fail-on <SEVERITY>` | Exit with status 1 at or above `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, or `INFO` |
|
|
121
|
+
| `--max-files <n>` | Limit the number of files scanned |
|
|
122
|
+
| `--commit-limit <n>` | Limit commits inspected by Git checks |
|
|
123
|
+
| `--no-git` | Skip Git analysis |
|
|
124
|
+
| `--no-lockfile-warning` | Suppress the missing-lockfile suggestion |
|
|
125
|
+
| `--ignore-dir <name>` | Ignore an additional directory name; repeatable |
|
|
126
|
+
| `--quiet` | Suppress the human summary |
|
|
127
|
+
| `--color` | Force ANSI colors |
|
|
128
|
+
| `--no-color` | Disable ANSI colors |
|
|
129
|
+
| `--engine <all\|typescript\|rust\|cpp>` | Run all engines (default) or select one engine |
|
|
130
|
+
| `--list-languages` | List recognized language profiles |
|
|
131
|
+
| `--help` | Show command help |
|
|
128
132
|
|
|
129
133
|
Exit status `0` means the scan completed; findings may still exist. Status `1`
|
|
130
134
|
means `--fail-on` was reached, and status `2` indicates invalid usage or a
|
|
@@ -177,30 +181,57 @@ The project uses Node.js and npm as its supported development toolchain.
|
|
|
177
181
|
`package-lock.json` is the canonical dependency lockfile.
|
|
178
182
|
The current test suite contains **175 tests**.
|
|
179
183
|
|
|
180
|
-
###
|
|
184
|
+
### Native engine
|
|
181
185
|
|
|
182
|
-
Flamer
|
|
183
|
-
|
|
186
|
+
Flamer runs all three components by default: TypeScript analyzers plus the Rust
|
|
187
|
+
and C++ native workers. They do different work and run concurrently:
|
|
188
|
+
|
|
189
|
+
- TypeScript walks the repository, runs the generic analyzers, and produces the
|
|
190
|
+
report.
|
|
191
|
+
- Rust computes shared metrics for all selected files and checks Rust files for
|
|
192
|
+
`.unwrap()` usage.
|
|
193
|
+
- C++ checks only C and C++ files for native-language issues such as
|
|
194
|
+
`#include <bits/stdc++.h>`.
|
|
184
195
|
|
|
185
196
|
```bash
|
|
186
|
-
npm run native:build
|
|
187
|
-
node dist/cli.js .
|
|
197
|
+
npm run native:build:all
|
|
198
|
+
node dist/cli.js .
|
|
188
199
|
```
|
|
189
200
|
|
|
190
|
-
|
|
201
|
+
Native findings are merged into the TypeScript report only after all requested
|
|
202
|
+
native jobs succeed. If a native binary is unavailable or fails, the default
|
|
203
|
+
`all` mode falls back to a pure TypeScript report. Use `--engine typescript` for
|
|
204
|
+
a native-free scan, or `--engine rust` / `--engine cpp` to require one specific
|
|
205
|
+
worker; explicit native modes return an error when their worker fails.
|
|
191
206
|
The native protocol and worker details are documented in
|
|
192
207
|
[`native/core/README.md`](native/core/README.md).
|
|
193
208
|
|
|
194
209
|
See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the runtime layers,
|
|
195
210
|
worker protocol, engine selection, and native development workflow.
|
|
196
211
|
|
|
212
|
+
For the source installation path:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
curl -fsSL https://raw.githubusercontent.com/dummy3ye/flamer/master/scripts/install.sh | sh
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Platform-specific native release binaries are not bundled in the current npm
|
|
219
|
+
package yet. The npm package uses optional native packages when they are
|
|
220
|
+
published for the current platform; if npm skips them, TypeScript fallback is
|
|
221
|
+
automatic. The installer's `native` path is reserved for standalone releases
|
|
222
|
+
that attach those assets.
|
|
223
|
+
|
|
197
224
|
## How it works
|
|
198
225
|
|
|
199
226
|
1. Detect the repository layout, languages, and ecosystems.
|
|
200
227
|
2. Walk the tree once and collect file metadata and content-derived signals.
|
|
201
228
|
3. Run isolated analyzers against the collected records.
|
|
202
|
-
4.
|
|
203
|
-
|
|
229
|
+
4. Run the requested native jobs through the versioned JSONL protocol. Native
|
|
230
|
+
jobs receive non-overlapping responsibilities and return metrics or
|
|
231
|
+
language-specific findings.
|
|
232
|
+
5. Merge successful native findings, sort everything, and emit JSON or a
|
|
233
|
+
concise summary. In default `all` mode, any native failure returns to the
|
|
234
|
+
TypeScript-only result.
|
|
204
235
|
|
|
205
236
|
Analyzers are intentionally factual and conservative. A finding should identify
|
|
206
237
|
something actionable rather than merely express a preference.
|
package/dist/cli.js
CHANGED
|
@@ -353,7 +353,8 @@ var CATEGORIES = [
|
|
|
353
353
|
"structure",
|
|
354
354
|
"git",
|
|
355
355
|
"error",
|
|
356
|
-
"unfinished"
|
|
356
|
+
"unfinished",
|
|
357
|
+
"native"
|
|
357
358
|
];
|
|
358
359
|
function rank(severity) {
|
|
359
360
|
const r = RANK[String(severity).toUpperCase()];
|
|
@@ -2902,39 +2903,86 @@ var ANALYZERS = [
|
|
|
2902
2903
|
];
|
|
2903
2904
|
|
|
2904
2905
|
// src/native.ts
|
|
2905
|
-
import {
|
|
2906
|
+
import { spawn } from "child_process";
|
|
2906
2907
|
import { existsSync as existsSync3 } from "fs";
|
|
2907
2908
|
import { join as join6 } from "path";
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
let response;
|
|
2909
|
+
import { createRequire } from "module";
|
|
2910
|
+
var require2 = createRequire(import.meta.url);
|
|
2911
|
+
function nativePackageName() {
|
|
2912
|
+
const platform = `${process.platform}-${process.arch}`;
|
|
2913
|
+
const supported = /* @__PURE__ */ new Set([
|
|
2914
|
+
"linux-x64",
|
|
2915
|
+
"linux-arm64",
|
|
2916
|
+
"darwin-x64",
|
|
2917
|
+
"darwin-arm64",
|
|
2918
|
+
"win32-x64",
|
|
2919
|
+
"win32-arm64"
|
|
2920
|
+
]);
|
|
2921
|
+
return supported.has(platform) ? `@dummy3ye/flamer-native-${platform}` : null;
|
|
2922
|
+
}
|
|
2923
|
+
function packagedWorkerPath(worker) {
|
|
2924
|
+
const packageName = nativePackageName();
|
|
2925
|
+
if (!packageName) return null;
|
|
2926
2926
|
try {
|
|
2927
|
-
|
|
2927
|
+
return require2.resolve(`${packageName}/${worker}`);
|
|
2928
2928
|
} catch {
|
|
2929
|
-
|
|
2930
|
-
}
|
|
2931
|
-
if (response.protocol !== 1 || response.operation !== "file_metrics") {
|
|
2932
|
-
throw new Error("native worker returned an incompatible response");
|
|
2929
|
+
return null;
|
|
2933
2930
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2931
|
+
}
|
|
2932
|
+
function nativeWorkerPath(root, worker) {
|
|
2933
|
+
const configured = worker === "rust" ? process.env.FLAMER_RUST_CORE : process.env.FLAMER_CPP_CORE;
|
|
2934
|
+
if (configured) return configured;
|
|
2935
|
+
if (process.env.FLAMER_CORE && worker === "rust") return process.env.FLAMER_CORE;
|
|
2936
|
+
const packaged = packagedWorkerPath(worker);
|
|
2937
|
+
if (packaged) return packaged;
|
|
2938
|
+
return worker === "rust" ? join6(root, "native", "core", "rust", "target", "release", "flamer-core") : join6(root, "native", "core", "cpp", "build", "flamer-core-cpp");
|
|
2939
|
+
}
|
|
2940
|
+
function runNativeWorker(root, files, worker, operation = "file_metrics") {
|
|
2941
|
+
const executable = nativeWorkerPath(root, worker);
|
|
2942
|
+
if (!existsSync3(executable)) {
|
|
2943
|
+
return Promise.reject(
|
|
2944
|
+
new Error(`${worker} worker not found at ${executable}; build the native workers first`)
|
|
2945
|
+
);
|
|
2936
2946
|
}
|
|
2937
|
-
|
|
2947
|
+
const request = JSON.stringify({ protocol: 1, operation, root, files });
|
|
2948
|
+
return new Promise((resolve2, reject) => {
|
|
2949
|
+
const child = spawn(executable, { stdio: ["pipe", "pipe", "pipe"] });
|
|
2950
|
+
let stdout = "";
|
|
2951
|
+
let stderr = "";
|
|
2952
|
+
const timeout = setTimeout(() => {
|
|
2953
|
+
child.kill();
|
|
2954
|
+
reject(new Error(`${worker} worker timed out`));
|
|
2955
|
+
}, 3e4);
|
|
2956
|
+
child.stdout.on("data", (chunk) => stdout += chunk);
|
|
2957
|
+
child.stderr.on("data", (chunk) => stderr += chunk);
|
|
2958
|
+
child.on("error", (error) => {
|
|
2959
|
+
clearTimeout(timeout);
|
|
2960
|
+
reject(new Error(`${worker} worker failed: ${error.message}`));
|
|
2961
|
+
});
|
|
2962
|
+
child.on("close", (status) => {
|
|
2963
|
+
clearTimeout(timeout);
|
|
2964
|
+
if (status !== 0) {
|
|
2965
|
+
reject(new Error(`${worker} worker exited with status ${status}: ${stderr.trim()}`));
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2968
|
+
try {
|
|
2969
|
+
const response = JSON.parse(stdout);
|
|
2970
|
+
if (response.protocol !== 1 || response.operation !== operation) {
|
|
2971
|
+
throw new Error(`${worker} worker returned an incompatible response`);
|
|
2972
|
+
}
|
|
2973
|
+
if (!response.ok) {
|
|
2974
|
+
throw new Error(
|
|
2975
|
+
response.errors.map((error) => `${error.path}: ${error.message}`).join("; ")
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
resolve2(response);
|
|
2979
|
+
} catch (error) {
|
|
2980
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
2981
|
+
}
|
|
2982
|
+
});
|
|
2983
|
+
child.stdin.end(`${request}
|
|
2984
|
+
`);
|
|
2985
|
+
});
|
|
2938
2986
|
}
|
|
2939
2987
|
|
|
2940
2988
|
// src/cli.ts
|
|
@@ -2954,7 +3002,7 @@ function parseArgs(argv) {
|
|
|
2954
3002
|
listLanguages: false,
|
|
2955
3003
|
help: false,
|
|
2956
3004
|
color: null,
|
|
2957
|
-
engine: "
|
|
3005
|
+
engine: "all",
|
|
2958
3006
|
ignoreDirs: []
|
|
2959
3007
|
};
|
|
2960
3008
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -3001,8 +3049,8 @@ function parseArgs(argv) {
|
|
|
3001
3049
|
opts.color = false;
|
|
3002
3050
|
break;
|
|
3003
3051
|
case "--engine":
|
|
3004
|
-
opts.engine = argv[++i] || "
|
|
3005
|
-
if (!["typescript", "
|
|
3052
|
+
opts.engine = argv[++i] || "all";
|
|
3053
|
+
if (!["all", "typescript", "rust", "cpp"].includes(opts.engine)) {
|
|
3006
3054
|
process.stderr.write(`invalid engine: ${opts.engine}
|
|
3007
3055
|
`);
|
|
3008
3056
|
process.exit(2);
|
|
@@ -3049,8 +3097,8 @@ Options:
|
|
|
3049
3097
|
--quiet suppress the human summary
|
|
3050
3098
|
--color force ANSI colors
|
|
3051
3099
|
--no-color disable ANSI colors
|
|
3052
|
-
--engine <typescript|
|
|
3053
|
-
|
|
3100
|
+
--engine <all|typescript|rust|cpp>
|
|
3101
|
+
all runs TypeScript, Rust, and C++ (default: all)
|
|
3054
3102
|
--list-languages print the supported language table
|
|
3055
3103
|
-h, --help show this help
|
|
3056
3104
|
`);
|
|
@@ -3067,7 +3115,7 @@ function printLanguages() {
|
|
|
3067
3115
|
${allExtensions().length} file extensions recognized.
|
|
3068
3116
|
`);
|
|
3069
3117
|
}
|
|
3070
|
-
function run2(opts) {
|
|
3118
|
+
async function run2(opts) {
|
|
3071
3119
|
const started = Date.now();
|
|
3072
3120
|
const root = opts.root;
|
|
3073
3121
|
if (!existsSync4(root) || !isDir(root)) {
|
|
@@ -3209,35 +3257,52 @@ function run2(opts) {
|
|
|
3209
3257
|
}
|
|
3210
3258
|
};
|
|
3211
3259
|
if (opts.engine !== "typescript") {
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3260
|
+
const allFiles = scopedFiles.map((file) => file.rel);
|
|
3261
|
+
const cppFiles = scopedFiles.filter((file) => ["c", "cpp"].includes(file.lang)).map((file) => file.rel);
|
|
3262
|
+
const rustFiles = scopedFiles.filter((file) => file.lang === "rust").map((file) => file.rel);
|
|
3263
|
+
const jobs = opts.engine === "all" ? [
|
|
3264
|
+
["rust", "file_metrics", allFiles],
|
|
3265
|
+
["rust", "rust_analysis", rustFiles],
|
|
3266
|
+
["cpp", "cpp_analysis", cppFiles]
|
|
3267
|
+
] : opts.engine === "rust" ? [
|
|
3268
|
+
["rust", "file_metrics", allFiles],
|
|
3269
|
+
["rust", "rust_analysis", rustFiles]
|
|
3270
|
+
] : [["cpp", "cpp_analysis", cppFiles]];
|
|
3271
|
+
const nativeResults = {};
|
|
3272
|
+
const failures = [];
|
|
3273
|
+
const nativeFindings = [];
|
|
3274
|
+
await Promise.all(
|
|
3275
|
+
jobs.map(async ([worker, operation, files2]) => {
|
|
3276
|
+
if (!files2.length && operation !== "file_metrics") return;
|
|
3277
|
+
try {
|
|
3278
|
+
const native = await runNativeWorker(root, files2, worker, operation);
|
|
3279
|
+
nativeResults[`${worker}:${operation}`] = {
|
|
3280
|
+
files: native.metrics.files,
|
|
3281
|
+
bytes: native.metrics.bytes,
|
|
3282
|
+
lines: native.metrics.lines,
|
|
3283
|
+
durationMs: native.duration_ms
|
|
3284
|
+
};
|
|
3285
|
+
nativeFindings.push(...native.findings || []);
|
|
3286
|
+
} catch (error) {
|
|
3287
|
+
failures.push(error?.message || String(error));
|
|
3288
|
+
}
|
|
3289
|
+
})
|
|
3290
|
+
);
|
|
3291
|
+
if (failures.length && opts.engine !== "all") {
|
|
3292
|
+
findings.push(
|
|
3293
|
+
finding({
|
|
3294
|
+
category: "error",
|
|
3295
|
+
severity: "CRITICAL",
|
|
3296
|
+
title: "Native engine failed",
|
|
3297
|
+
detail: failures.join("; ")
|
|
3298
|
+
})
|
|
3216
3299
|
);
|
|
3217
|
-
report.
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
bytes: native.metrics.bytes,
|
|
3221
|
-
lines: native.metrics.lines,
|
|
3222
|
-
durationMs: native.duration_ms
|
|
3223
|
-
};
|
|
3224
|
-
} catch (error) {
|
|
3225
|
-
const message = error?.message || String(error);
|
|
3226
|
-
if (opts.engine === "native") {
|
|
3227
|
-
findings.push(
|
|
3228
|
-
finding({
|
|
3229
|
-
category: "error",
|
|
3230
|
-
severity: "CRITICAL",
|
|
3231
|
-
title: "Native engine failed",
|
|
3232
|
-
detail: message
|
|
3233
|
-
})
|
|
3234
|
-
);
|
|
3235
|
-
report.findings = sortFindings(findings);
|
|
3236
|
-
report.metadata.native = { engine: "rust", error: message };
|
|
3237
|
-
return { report, exitCode: 2 };
|
|
3238
|
-
}
|
|
3239
|
-
report.metadata.native = { engine: "typescript", fallback: message };
|
|
3300
|
+
report.findings = sortFindings(findings);
|
|
3301
|
+
report.metadata.native = { engine: opts.engine, errors: failures };
|
|
3302
|
+
return { report, exitCode: 2 };
|
|
3240
3303
|
}
|
|
3304
|
+
if (!failures.length) findings.push(...nativeFindings);
|
|
3305
|
+
report.metadata.native = failures.length ? { engine: "typescript", fallback: failures, workers: nativeResults } : { engine: opts.engine, workers: nativeResults };
|
|
3241
3306
|
}
|
|
3242
3307
|
let exitCode = 0;
|
|
3243
3308
|
if (opts.failOn && report.metadata.worst && rank(report.metadata.worst) <= rank(opts.failOn)) {
|
|
@@ -3362,11 +3427,11 @@ for (const stream of [process.stdout, process.stderr]) {
|
|
|
3362
3427
|
throw err;
|
|
3363
3428
|
});
|
|
3364
3429
|
}
|
|
3365
|
-
function main() {
|
|
3430
|
+
async function main() {
|
|
3366
3431
|
const opts = parseArgs(process.argv.slice(2));
|
|
3367
3432
|
if (opts.help) return printHelp();
|
|
3368
3433
|
if (opts.listLanguages) return printLanguages();
|
|
3369
|
-
const { report, exitCode } = run2(opts);
|
|
3434
|
+
const { report, exitCode } = await run2(opts);
|
|
3370
3435
|
const json = JSON.stringify(report, null, 2);
|
|
3371
3436
|
if (opts.out) {
|
|
3372
3437
|
try {
|
|
@@ -3383,4 +3448,4 @@ function main() {
|
|
|
3383
3448
|
}
|
|
3384
3449
|
process.exitCode = exitCode;
|
|
3385
3450
|
}
|
|
3386
|
-
main();
|
|
3451
|
+
void main();
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Flamer architecture
|
|
2
|
+
|
|
3
|
+
Flamer is a Node.js CLI with a coordinated native execution layer. TypeScript
|
|
4
|
+
owns the public interface, repository traversal, generic analyzers, findings,
|
|
5
|
+
fallback behavior, and report format. Rust and C++ run together as specialized
|
|
6
|
+
workers with non-overlapping responsibilities. Native findings are validated
|
|
7
|
+
and merged into the TypeScript report; they are not alternative scanners.
|
|
8
|
+
|
|
9
|
+
## Runtime layers
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
CLI (src/cli.ts)
|
|
13
|
+
├─ argument parsing and terminal/JSON output
|
|
14
|
+
├─ repository detection and scope selection
|
|
15
|
+
├─ TypeScript scanner and analyzers
|
|
16
|
+
└─ native worker adapter (src/native.ts)
|
|
17
|
+
├─ Rust worker: native/core/rust
|
|
18
|
+
│ ├─ shared file metrics
|
|
19
|
+
│ └─ Rust-specific analysis
|
|
20
|
+
└─ C++ worker: native/core/cpp
|
|
21
|
+
└─ C/C++-specific analysis
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### TypeScript layer
|
|
25
|
+
|
|
26
|
+
The TypeScript CLI is the default and source of truth. `src/scan.ts` walks and
|
|
27
|
+
reads files once, `src/detect.ts` builds project profiles, and registered
|
|
28
|
+
analyzers turn the collected context into findings. This path works with the
|
|
29
|
+
published npm package and does not require Rust, C++, Cargo, or CMake.
|
|
30
|
+
|
|
31
|
+
### Native layer
|
|
32
|
+
|
|
33
|
+
The native layer is intentionally separate from generic analyzer logic. Rust
|
|
34
|
+
receives all selected files for shared byte/newline metrics and receives Rust
|
|
35
|
+
files for Rust-specific checks. C++ receives only C and C++ files for
|
|
36
|
+
C/C++-specific checks. These jobs run concurrently in `all` mode, so no worker
|
|
37
|
+
repeats another worker's language analysis.
|
|
38
|
+
|
|
39
|
+
Rust is built from `native/core/rust/Cargo.toml` and exposes a reusable library
|
|
40
|
+
plus a thin JSONL binary. C++ is built with CMake from
|
|
41
|
+
`native/core/cpp/CMakeLists.txt` and exposes a public header, analyzer
|
|
42
|
+
implementation, and protocol executable.
|
|
43
|
+
|
|
44
|
+
The current native checks are deliberately small but real: Rust reports
|
|
45
|
+
`.unwrap()` usage and C++ reports the non-portable
|
|
46
|
+
`#include <bits/stdc++.h>` include. Additional expensive, language-specific
|
|
47
|
+
work can be added without moving report ownership out of TypeScript.
|
|
48
|
+
|
|
49
|
+
## Engine selection
|
|
50
|
+
|
|
51
|
+
The CLI accepts:
|
|
52
|
+
|
|
53
|
+
| Engine | Behavior |
|
|
54
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
55
|
+
| `all` | Default; runs TypeScript, Rust, and C++ together. Falls back to the TypeScript-only report if any native job is unavailable or fails. |
|
|
56
|
+
| `typescript` | Runs only the TypeScript implementation. |
|
|
57
|
+
| `rust` | Runs TypeScript plus Rust metrics and Rust analysis; fails if Rust is unavailable. |
|
|
58
|
+
| `cpp` | Runs TypeScript plus C++ analysis; fails if C++ is unavailable. |
|
|
59
|
+
|
|
60
|
+
The workers can be overridden with `FLAMER_RUST_CORE` and `FLAMER_CPP_CORE`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
FLAMER_RUST_CORE="$PWD/native/core/rust/target/release/flamer-core" \
|
|
64
|
+
FLAMER_CPP_CORE="$PWD/native/core/cpp/build/flamer-core-cpp" \
|
|
65
|
+
node dist/cli.js .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Worker protocol
|
|
69
|
+
|
|
70
|
+
Workers communicate over newline-delimited JSON (JSONL):
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{ "protocol": 1, "operation": "file_metrics", "root": ".", "files": ["README.md"] }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Each request produces exactly one response on stdout. The operation determines
|
|
77
|
+
the worker contract:
|
|
78
|
+
|
|
79
|
+
| Worker | Operation | Input files | Responsibility |
|
|
80
|
+
| ------ | --------------- | ------------------ | ---------------------------------------- |
|
|
81
|
+
| Rust | `file_metrics` | all selected files | byte, newline, and readable-file metrics |
|
|
82
|
+
| Rust | `rust_analysis` | Rust files | Rust-specific findings |
|
|
83
|
+
| C++ | `cpp_analysis` | C/C++ files | C/C++-specific findings |
|
|
84
|
+
|
|
85
|
+
Example response:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"protocol": 1,
|
|
90
|
+
"ok": true,
|
|
91
|
+
"operation": "file_metrics",
|
|
92
|
+
"metrics": { "files": 1, "bytes": 1234, "lines": 42 },
|
|
93
|
+
"errors": [],
|
|
94
|
+
"duration_ms": 1
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Stdout is reserved for protocol messages. Diagnostics belong on stderr.
|
|
99
|
+
Unknown protocol versions, operations, malformed responses, non-zero exits, and
|
|
100
|
+
timeouts are errors. The TypeScript adapter validates the response before using
|
|
101
|
+
it.
|
|
102
|
+
|
|
103
|
+
## Build and test
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm test
|
|
107
|
+
npm run native:build
|
|
108
|
+
npm run native:test
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The individual projects can also be built directly:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
cargo build --release --manifest-path native/core/rust/Cargo.toml
|
|
115
|
+
cmake -S native/core/cpp -B native/core/cpp/build -DCMAKE_BUILD_TYPE=Release
|
|
116
|
+
cmake --build native/core/cpp/build --config Release
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The npm native scripts validate that Cargo and CMake are installed and return
|
|
120
|
+
an actionable error when either toolchain is missing. The C++ CMake project
|
|
121
|
+
also exports `compile_commands.json` and contains a protocol smoke test.
|
|
122
|
+
|
|
123
|
+
Native builds are checked independently in the Native Rust and Native C++
|
|
124
|
+
GitHub Actions workflows. Compiled output is ignored and is not committed.
|
|
125
|
+
|
|
126
|
+
## Release and installation roadmap
|
|
127
|
+
|
|
128
|
+
The npm package remains the required installation:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm install --global flamerjs
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`flamerjs` declares platform-specific native packages under the publisher scope
|
|
135
|
+
as optional
|
|
136
|
+
dependencies. npm installs only the package matching the host OS and CPU:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
flamerjs
|
|
140
|
+
└── @dummy3ye/flamer-native-linux-x64
|
|
141
|
+
├── bin/flamer-core
|
|
142
|
+
└── bin/flamer-core-cpp
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
At runtime `src/native.ts` resolves both workers from that package. If npm
|
|
146
|
+
skips the optional package, the package is unavailable for the platform, or a
|
|
147
|
+
worker cannot start, default `all` mode falls back to TypeScript. The source
|
|
148
|
+
build command creates the same package shape:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm run native:build:all
|
|
152
|
+
npm run native:package -- linux-x64
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The release workflow must build and publish both workers for each supported
|
|
156
|
+
OS/architecture package before that platform is advertised as native-enabled.
|
|
157
|
+
The standalone installer and release archives can use the same worker
|
|
158
|
+
artifacts.
|
|
159
|
+
|
|
160
|
+
The eventual bundled release keeps the TypeScript CLI as the orchestration
|
|
161
|
+
layer and packages both Rust and C++ workers. They are not alternatives:
|
|
162
|
+
standard execution starts both, while the explicit engine flags are for
|
|
163
|
+
debugging, benchmarking, and degraded environments.
|
|
164
|
+
|
|
165
|
+
## Design constraints
|
|
166
|
+
|
|
167
|
+
- Keep npm installation functional with Node.js alone; native packages remain
|
|
168
|
+
optional.
|
|
169
|
+
- Keep findings and report semantics in TypeScript.
|
|
170
|
+
- Keep workers deterministic and communicate only through the versioned
|
|
171
|
+
protocol; stdout is protocol-only and diagnostics use stderr.
|
|
172
|
+
- Keep native execution fallback-safe until platform packaging and release
|
|
173
|
+
binaries are mature.
|
|
174
|
+
- Do not duplicate generic analyzer work in native workers.
|
|
175
|
+
- Add a benchmark before moving more work out of the TypeScript scanner.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flamerjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Language-agnostic code health auditor that walks a repository once and reports what is wrong with it",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,13 +20,28 @@
|
|
|
20
20
|
"flamerjs": "./dist/cli.js"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"dist"
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"docs/ARCHITECTURE.md"
|
|
24
28
|
],
|
|
29
|
+
"optionalDependencies": {
|
|
30
|
+
"@dummy3ye/flamer-native-darwin-arm64": "1.1.1",
|
|
31
|
+
"@dummy3ye/flamer-native-darwin-x64": "1.1.1",
|
|
32
|
+
"@dummy3ye/flamer-native-linux-arm64": "1.1.1",
|
|
33
|
+
"@dummy3ye/flamer-native-linux-x64": "1.1.1",
|
|
34
|
+
"@dummy3ye/flamer-native-win32-arm64": "1.1.1",
|
|
35
|
+
"@dummy3ye/flamer-native-win32-x64": "1.1.1"
|
|
36
|
+
},
|
|
25
37
|
"scripts": {
|
|
26
38
|
"build": "tsup",
|
|
27
39
|
"dev": "tsup --watch",
|
|
28
|
-
"native:build": "
|
|
29
|
-
"native:test": "
|
|
40
|
+
"native:build": "sh scripts/native-build.sh",
|
|
41
|
+
"native:test": "sh scripts/native-test.sh",
|
|
42
|
+
"native:build:cpp": "sh scripts/native-build-cpp.sh",
|
|
43
|
+
"native:build:all": "sh scripts/native-build.sh",
|
|
44
|
+
"native:package": "node scripts/package-native.mjs",
|
|
30
45
|
"prepublishOnly": "npm run lint && npm run build && npm test",
|
|
31
46
|
"lint": "eslint .",
|
|
32
47
|
"lint:fix": "eslint . --fix",
|