@nifrajs/cli 2.10.0 → 2.12.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 +7 -1
- package/dist/assure-hydration.d.ts +14 -0
- package/dist/assure-hydration.d.ts.map +1 -0
- package/dist/assure-hydration.js +483 -0
- package/dist/assure-hydration.js.map +1 -0
- package/dist/assure.d.ts +35 -0
- package/dist/assure.d.ts.map +1 -1
- package/dist/assure.js +222 -1
- package/dist/assure.js.map +1 -1
- package/dist/capabilities-tool.d.ts +13 -0
- package/dist/capabilities-tool.d.ts.map +1 -1
- package/dist/capabilities-tool.js +64 -1
- package/dist/capabilities-tool.js.map +1 -1
- package/dist/check.d.ts +133 -10
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +741 -148
- package/dist/check.js.map +1 -1
- package/dist/cli.d.ts +21 -14
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +288 -73
- package/dist/cli.js.map +1 -1
- package/dist/contract-proof.d.ts +49 -0
- package/dist/contract-proof.d.ts.map +1 -0
- package/dist/contract-proof.js +128 -0
- package/dist/contract-proof.js.map +1 -0
- package/dist/contracts.d.ts +22 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +134 -0
- package/dist/contracts.js.map +1 -0
- package/dist/dev-bun-config.d.ts +17 -6
- package/dist/dev-bun-config.d.ts.map +1 -1
- package/dist/dev-bun-config.js +51 -14
- package/dist/dev-bun-config.js.map +1 -1
- package/dist/diagnostics.d.ts +23 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +9 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/diff-tool.d.ts +2 -0
- package/dist/diff-tool.d.ts.map +1 -1
- package/dist/diff-tool.js +1 -1
- package/dist/diff-tool.js.map +1 -1
- package/dist/docs-search.d.ts +8 -3
- package/dist/docs-search.d.ts.map +1 -1
- package/dist/docs-search.js +36 -15
- package/dist/docs-search.js.map +1 -1
- package/dist/doctor.d.ts +38 -2
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +189 -7
- package/dist/doctor.js.map +1 -1
- package/dist/examples.d.ts +1 -2
- package/dist/examples.d.ts.map +1 -1
- package/dist/examples.js +37 -16
- package/dist/examples.js.map +1 -1
- package/dist/fix-recipes.d.ts +12 -0
- package/dist/fix-recipes.d.ts.map +1 -0
- package/dist/fix-recipes.js +106 -0
- package/dist/fix-recipes.js.map +1 -0
- package/dist/init-agents.d.ts.map +1 -1
- package/dist/init-agents.js +60 -4
- package/dist/init-agents.js.map +1 -1
- package/dist/internal/source-facts.d.ts +15 -0
- package/dist/internal/source-facts.d.ts.map +1 -0
- package/dist/internal/source-facts.js +82 -0
- package/dist/internal/source-facts.js.map +1 -0
- package/dist/internal/typescript-import.d.ts +9 -0
- package/dist/internal/typescript-import.d.ts.map +1 -1
- package/dist/internal/typescript-import.js +38 -0
- package/dist/internal/typescript-import.js.map +1 -1
- package/dist/introspect.d.ts +6 -2
- package/dist/introspect.d.ts.map +1 -1
- package/dist/introspect.js +60 -5
- package/dist/introspect.js.map +1 -1
- package/dist/load.d.ts +10 -0
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +1 -0
- package/dist/load.js.map +1 -1
- package/dist/manifest-tool.d.ts.map +1 -1
- package/dist/manifest-tool.js +14 -9
- package/dist/manifest-tool.js.map +1 -1
- package/dist/mcp-docs-tools.d.ts.map +1 -1
- package/dist/mcp-docs-tools.js +3 -0
- package/dist/mcp-docs-tools.js.map +1 -1
- package/dist/mcp-http.js +1 -1
- package/dist/mcp-root.d.ts +83 -0
- package/dist/mcp-root.d.ts.map +1 -0
- package/dist/mcp-root.js +221 -0
- package/dist/mcp-root.js.map +1 -0
- package/dist/mcp-run.d.ts.map +1 -1
- package/dist/mcp-run.js +18 -4
- package/dist/mcp-run.js.map +1 -1
- package/dist/mcp.d.ts +27 -4
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +360 -27
- package/dist/mcp.js.map +1 -1
- package/dist/pipeline-guard.d.ts +25 -10
- package/dist/pipeline-guard.d.ts.map +1 -1
- package/dist/pipeline-guard.js +35 -15
- package/dist/pipeline-guard.js.map +1 -1
- package/dist/pipeline-report.d.ts +34 -0
- package/dist/pipeline-report.d.ts.map +1 -0
- package/dist/pipeline-report.js +282 -0
- package/dist/pipeline-report.js.map +1 -0
- package/dist/project-path.d.ts +3 -0
- package/dist/project-path.d.ts.map +1 -0
- package/dist/project-path.js +36 -0
- package/dist/project-path.js.map +1 -0
- package/dist/replay.d.ts +12 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +36 -0
- package/dist/replay.js.map +1 -0
- package/dist/rules/codes.d.ts +42 -0
- package/dist/rules/codes.d.ts.map +1 -0
- package/dist/rules/codes.js +52 -0
- package/dist/rules/codes.js.map +1 -0
- package/dist/rules/comment-markers.d.ts +10 -0
- package/dist/rules/comment-markers.d.ts.map +1 -0
- package/dist/rules/comment-markers.js +24 -0
- package/dist/rules/comment-markers.js.map +1 -0
- package/dist/rules/index.d.ts +31 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +133 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/legacy.d.ts +7 -0
- package/dist/rules/legacy.d.ts.map +1 -0
- package/dist/rules/legacy.js +85 -0
- package/dist/rules/legacy.js.map +1 -0
- package/dist/rules/routes.d.ts +12 -0
- package/dist/rules/routes.d.ts.map +1 -0
- package/dist/rules/routes.js +142 -0
- package/dist/rules/routes.js.map +1 -0
- package/dist/rules/security.d.ts +10 -0
- package/dist/rules/security.d.ts.map +1 -0
- package/dist/rules/security.js +471 -0
- package/dist/rules/security.js.map +1 -0
- package/dist/search-terms.d.ts +2 -0
- package/dist/search-terms.d.ts.map +1 -1
- package/dist/search-terms.js +5 -1
- package/dist/search-terms.js.map +1 -1
- package/dist/types-search.d.ts +1 -2
- package/dist/types-search.d.ts.map +1 -1
- package/dist/types-search.js +33 -16
- package/dist/types-search.js.map +1 -1
- package/dist/verification.d.ts +9 -0
- package/dist/verification.d.ts.map +1 -1
- package/dist/verification.js +17 -1
- package/dist/verification.js.map +1 -1
- package/dist/work-graph.d.ts +123 -0
- package/dist/work-graph.d.ts.map +1 -0
- package/dist/work-graph.js +465 -0
- package/dist/work-graph.js.map +1 -0
- package/docs/examples.json +32 -2
- package/docs/llms-full.txt +1105 -27
- package/docs/types.json +1772 -137
- package/package.json +34 -9
- package/src/assure-hydration.ts +606 -0
- package/src/assure.ts +316 -2
- package/src/capabilities-tool.ts +80 -1
- package/src/check.ts +923 -169
- package/src/cli.ts +297 -76
- package/src/contract-proof.ts +194 -0
- package/src/contracts.ts +157 -0
- package/src/dev-bun-config.ts +57 -14
- package/src/diagnostics.ts +32 -0
- package/src/diff-tool.ts +1 -1
- package/src/docs-search.ts +45 -15
- package/src/doctor.ts +286 -8
- package/src/examples.ts +43 -15
- package/src/fix-recipes.ts +126 -0
- package/src/init-agents.ts +61 -4
- package/src/internal/source-facts.ts +133 -0
- package/src/internal/typescript-import.ts +29 -0
- package/src/introspect.ts +81 -6
- package/src/load.ts +11 -0
- package/src/manifest-tool.ts +16 -10
- package/src/mcp-docs-tools.ts +3 -0
- package/src/mcp-http.ts +1 -1
- package/src/mcp-root.ts +271 -0
- package/src/mcp-run.ts +19 -4
- package/src/mcp.ts +440 -34
- package/src/pipeline-guard.ts +44 -14
- package/src/pipeline-report.ts +341 -0
- package/src/project-path.ts +36 -0
- package/src/replay.ts +44 -0
- package/src/rules/codes.ts +57 -0
- package/src/rules/comment-markers.ts +26 -0
- package/src/rules/index.ts +175 -0
- package/src/rules/legacy.ts +93 -0
- package/src/rules/routes.ts +157 -0
- package/src/rules/security.ts +505 -0
- package/src/search-terms.ts +9 -4
- package/src/types-search.ts +39 -15
- package/src/verification.ts +18 -1
- package/src/work-graph.ts +613 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two-pipeline rule, reported STATICALLY - which bundler this app runs on, and the config hazards
|
|
3
|
+
* that only exist because nifra supports two.
|
|
4
|
+
*
|
|
5
|
+
* `loadApp` already refuses a plugin in the wrong slot ({@link ./load.ts}), and `nifra dev`/`nifra build`
|
|
6
|
+
* print the chosen bundler. Both of those need the app's config to be IMPORTED, which is code execution -
|
|
7
|
+
* something `nifra check` deliberately never does, and something an agent inspecting a repo it has not
|
|
8
|
+
* installed cannot do at all. So this reads the config as text instead.
|
|
9
|
+
*
|
|
10
|
+
* Text, not types, so the rules are chosen for the ones that survive being read rather than run:
|
|
11
|
+
*
|
|
12
|
+
* - Which pipeline the app is on. `chooseBuildPipeline` decides from whether the plugin slots are
|
|
13
|
+
* empty, and "does this file declare a non-empty `vitePlugins`" is a question the source answers.
|
|
14
|
+
* - The adapter-entry hazard. `nifra build` imports the adapter from `framework.ts`, or from
|
|
15
|
+
* `nifra.config.ts` when there is no `framework.ts`, and bundles everything that file imports into
|
|
16
|
+
* the production server. A Vite plugin or an SFC compiler reached that way builds cleanly and then
|
|
17
|
+
* dies at startup on a missing native binding. Nothing runtime-side catches it: by then the build
|
|
18
|
+
* has already succeeded.
|
|
19
|
+
* - A plugin in the wrong slot, for the subset whose import specifier names its pipeline. The load
|
|
20
|
+
* guard classifies by hook shape and is authoritative; this catches the same thing earlier, before
|
|
21
|
+
* anything is installed or started.
|
|
22
|
+
* - `conditions` on the Bun pipeline, which reach SSR and cannot reach the dev client bundle.
|
|
23
|
+
*
|
|
24
|
+
* Everything here degrades to silence rather than guessing. A config that computes its plugins, or
|
|
25
|
+
* re-exports them from elsewhere, yields `certain: false` and no findings - a static reader that
|
|
26
|
+
* invents a pipeline is worse than one that says it could not tell.
|
|
27
|
+
*/
|
|
28
|
+
import { existsSync } from "node:fs";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import { codePositionMask, stripComments } from "./check.js";
|
|
31
|
+
/** A plugin slot, and the pipeline it feeds. */
|
|
32
|
+
const SLOT_PIPELINE = {
|
|
33
|
+
vitePlugins: "vite",
|
|
34
|
+
clientPlugins: "bun",
|
|
35
|
+
serverPlugins: "bun",
|
|
36
|
+
};
|
|
37
|
+
const SLOTS = Object.keys(SLOT_PIPELINE);
|
|
38
|
+
/**
|
|
39
|
+
* Specifiers that carry a dev toolchain: Vite itself, a Vite/Rollup plugin, or a framework's SFC
|
|
40
|
+
* compiler plugin (`@nifrajs/web-vue/plugin` and friends, which import the compiler). A relative
|
|
41
|
+
* `./plugin` is NOT matched - a local file of that name is as likely to be the app's own tiny plugin as
|
|
42
|
+
* a compiler, and this rule fails a gate.
|
|
43
|
+
*/
|
|
44
|
+
const DEV_TOOLCHAIN = /^vite$|^vite\/|^@vitejs\/|vite-plugin|^rollup$|^@rollup\/|^esbuild$|^(?:@[^/]+\/)?[^./][^/]*\/(?:.*\/)?plugin(?:\.ts)?$/;
|
|
45
|
+
/** Specifiers that name the Vite pipeline outright. */
|
|
46
|
+
const VITE_SPECIFIER = /^vite$|^vite\/|^@vitejs\/|vite-plugin/;
|
|
47
|
+
/** Specifiers whose package subpath is nifra's Bun-plugin convention (`@nifrajs/web-vue/plugin`). */
|
|
48
|
+
const BUN_SPECIFIER = /^(?:@[^/]+\/)?[^./][^/]*\/(?:.*\/)?plugin(?:\.ts)?$/;
|
|
49
|
+
const lineAt = (src, index) => {
|
|
50
|
+
let line = 1;
|
|
51
|
+
for (let i = 0; i < index && i < src.length; i++)
|
|
52
|
+
if (src[i] === "\n")
|
|
53
|
+
line++;
|
|
54
|
+
return line;
|
|
55
|
+
};
|
|
56
|
+
/** Local binding names introduced by one import clause (`a`, `{ b as c }`, `* as ns`). */
|
|
57
|
+
function localNames(clause) {
|
|
58
|
+
const names = [];
|
|
59
|
+
const braced = /\{([^}]*)\}/.exec(clause);
|
|
60
|
+
if (braced !== null) {
|
|
61
|
+
for (const part of (braced[1] ?? "").split(",")) {
|
|
62
|
+
const named = /([A-Za-z_$][\w$]*)\s*$/.exec(part.trim());
|
|
63
|
+
if (named?.[1] !== undefined)
|
|
64
|
+
names.push(named[1]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const head = clause.replace(/\{[^}]*\}/, " ").replace(/,/g, " ");
|
|
68
|
+
const star = /\*\s*as\s+([A-Za-z_$][\w$]*)/.exec(head);
|
|
69
|
+
if (star?.[1] !== undefined)
|
|
70
|
+
names.push(star[1]);
|
|
71
|
+
else {
|
|
72
|
+
const def = /^\s*([A-Za-z_$][\w$]*)\s*$/.exec(head);
|
|
73
|
+
if (def?.[1] !== undefined)
|
|
74
|
+
names.push(def[1]);
|
|
75
|
+
}
|
|
76
|
+
return names;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Every value-importing binding in a module, mapped to the specifier it came from. `import type` is
|
|
80
|
+
* skipped: a type has no runtime cost, so it is neither a bundled toolchain nor a plugin in a slot.
|
|
81
|
+
*/
|
|
82
|
+
function importedBindings(stripped) {
|
|
83
|
+
const out = new Map();
|
|
84
|
+
const re = /(?<![.\w$])import\s+(?!type[\s{])([^'";]+?)\s+from\s*['"]([^'"]+)['"]/g;
|
|
85
|
+
for (let m = re.exec(stripped); m !== null; m = re.exec(stripped)) {
|
|
86
|
+
const specifier = m[2];
|
|
87
|
+
if (specifier === undefined)
|
|
88
|
+
continue;
|
|
89
|
+
for (const name of localNames(m[1] ?? ""))
|
|
90
|
+
out.set(name, { specifier, index: m.index });
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
/** Every specifier a module imports for its side effects or bindings, with the position to report. */
|
|
95
|
+
function importedSpecifiers(stripped) {
|
|
96
|
+
const out = [];
|
|
97
|
+
const re = /(?<![.\w$])import\s+(?:(?!type[\s{])[^'";]*?\s+from\s*)?['"]([^'"]+)['"]/g;
|
|
98
|
+
for (let m = re.exec(stripped); m !== null; m = re.exec(stripped)) {
|
|
99
|
+
if (m[1] !== undefined)
|
|
100
|
+
out.push({ specifier: m[1], index: m.index });
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/** Index just past the `]` matching the `[` at `open`, or `undefined` when it never closes. */
|
|
105
|
+
function closeBracket(masked, open) {
|
|
106
|
+
let depth = 0;
|
|
107
|
+
for (let i = open; i < masked.length; i++) {
|
|
108
|
+
const c = masked[i];
|
|
109
|
+
if (c === "[")
|
|
110
|
+
depth++;
|
|
111
|
+
else if (c === "]") {
|
|
112
|
+
depth--;
|
|
113
|
+
if (depth === 0)
|
|
114
|
+
return i;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
const ABSENT = { present: false, certain: true };
|
|
120
|
+
/**
|
|
121
|
+
* Read one exported binding as a plugin slot.
|
|
122
|
+
*
|
|
123
|
+
* Only an array LITERAL is read precisely; a thunk (`() => import(…)`), a bare identifier or a
|
|
124
|
+
* re-export is recorded as present-but-uncertain. That asymmetry is deliberate: `[]` and `[vue()]`
|
|
125
|
+
* decide the pipeline, and anything else is a promise the source cannot keep.
|
|
126
|
+
*/
|
|
127
|
+
function readSlot(masked, stripped, name) {
|
|
128
|
+
const declaration = new RegExp(`(?<![.\\w$])export\\s+(?:const|let|var)\\s+${name}\\b[^=\\n]*=\\s*`);
|
|
129
|
+
const m = declaration.exec(masked);
|
|
130
|
+
if (m !== null) {
|
|
131
|
+
const start = m.index + m[0].length;
|
|
132
|
+
if (masked[start] === "[") {
|
|
133
|
+
const end = closeBracket(masked, start);
|
|
134
|
+
if (end !== undefined) {
|
|
135
|
+
// Both views are offset-preserving, and each answers a different question: the mask has string
|
|
136
|
+
// CONTENTS blanked, so identifiers found in it are real code; the stripped source keeps them, so
|
|
137
|
+
// it is the one that can tell `["solid"]` from `[]`.
|
|
138
|
+
const code = masked.slice(start + 1, end);
|
|
139
|
+
const text = stripped.slice(start + 1, end);
|
|
140
|
+
const items = [...code.matchAll(/(?<![.\w$])([A-Za-z_$][\w$]*)/g)].map((i) => i[1] ?? "");
|
|
141
|
+
return { present: text.trim() !== "", certain: true, index: m.index, items };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { present: true, certain: false, index: m.index };
|
|
145
|
+
}
|
|
146
|
+
// `export { vitePlugins } from "./plugins.ts"` - present, but its contents live in another file.
|
|
147
|
+
const reexport = new RegExp(`(?<![.\\w$])export\\s*\\{[^}]*\\b${name}\\b[^}]*\\}`);
|
|
148
|
+
const r = reexport.exec(stripped);
|
|
149
|
+
if (r !== null)
|
|
150
|
+
return { present: true, certain: false, index: r.index };
|
|
151
|
+
// `export * from …` could carry the slot; say so rather than reporting an absence that isn't one.
|
|
152
|
+
if (/(?<![.\w$])export\s*\*\s*from/.test(stripped))
|
|
153
|
+
return { present: false, certain: false };
|
|
154
|
+
return ABSENT;
|
|
155
|
+
}
|
|
156
|
+
/** Which pipeline a plugin reference belongs to, or `undefined` when its name and origin say nothing. */
|
|
157
|
+
function classifyRef(name, specifier) {
|
|
158
|
+
// nifra's own Bun plugins are named for what they are, and the name survives an alias-free import.
|
|
159
|
+
if (/BunPlugin$/.test(name))
|
|
160
|
+
return "bun";
|
|
161
|
+
if (specifier === undefined)
|
|
162
|
+
return undefined;
|
|
163
|
+
if (VITE_SPECIFIER.test(specifier))
|
|
164
|
+
return "vite";
|
|
165
|
+
if (BUN_SPECIFIER.test(specifier))
|
|
166
|
+
return "bun";
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
/** Read one file as text, or `undefined` when it is missing or unreadable. */
|
|
170
|
+
async function readSource(path) {
|
|
171
|
+
try {
|
|
172
|
+
return await Bun.file(path).text();
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Report the pipeline and its config hazards for the app at `cwd`, without executing anything.
|
|
180
|
+
* Total: a missing or unreadable config yields `ran: false`, never a throw.
|
|
181
|
+
*/
|
|
182
|
+
export async function collectPipelineReport(cwd) {
|
|
183
|
+
const configFile = existsSync(join(cwd, "nifra.config.ts")) ? "nifra.config.ts" : "framework.ts";
|
|
184
|
+
const source = await readSource(join(cwd, configFile));
|
|
185
|
+
if (source === undefined) {
|
|
186
|
+
return {
|
|
187
|
+
ran: false,
|
|
188
|
+
pipeline: "unknown",
|
|
189
|
+
reason: "no nifra.config.ts or framework.ts here",
|
|
190
|
+
certain: false,
|
|
191
|
+
findings: [],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const masked = codePositionMask(source);
|
|
195
|
+
const stripped = stripComments(source);
|
|
196
|
+
const bindings = importedBindings(stripped);
|
|
197
|
+
const slots = Object.fromEntries(SLOTS.map((slot) => [slot, readSlot(masked, stripped, slot)]));
|
|
198
|
+
const findings = [];
|
|
199
|
+
// A plugin whose specifier names the other pipeline. The load-time guard classifies by hook shape and
|
|
200
|
+
// remains the authority; this is the half of it that can run on a repo nobody has installed yet.
|
|
201
|
+
for (const slot of SLOTS) {
|
|
202
|
+
const state = slots[slot];
|
|
203
|
+
const expected = SLOT_PIPELINE[slot];
|
|
204
|
+
for (const item of state.items ?? []) {
|
|
205
|
+
const binding = bindings.get(item);
|
|
206
|
+
const actual = classifyRef(item, binding?.specifier);
|
|
207
|
+
if (actual === undefined || actual === expected)
|
|
208
|
+
continue;
|
|
209
|
+
const from = binding !== undefined ? ` (imported from "${binding.specifier}")` : "";
|
|
210
|
+
findings.push({
|
|
211
|
+
rule: "plugin-slot",
|
|
212
|
+
severity: "error",
|
|
213
|
+
file: configFile,
|
|
214
|
+
line: lineAt(source, state.index ?? 0),
|
|
215
|
+
message: `\`${item}\`${from} is a ${actual === "vite" ? "Vite" : "Bun"} plugin but sits in \`${slot}\`, which feeds ` +
|
|
216
|
+
`${expected === "vite" ? "Vite" : "Bun.build"}. The wrong bundler never calls its hooks, so the transform silently does not run and the build still succeeds.`,
|
|
217
|
+
fix: actual === "vite"
|
|
218
|
+
? `move \`${item}\` to \`vitePlugins\``
|
|
219
|
+
: `move \`${item}\` to \`clientPlugins\` (client bundle) or \`serverPlugins\` (SSR)`,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Which bundler this app gets, by the same rule `chooseBuildPipeline` applies to the loaded config.
|
|
224
|
+
const viteOnly = slots.vitePlugins.present && !slots.clientPlugins.present && !slots.serverPlugins.present;
|
|
225
|
+
const certain = SLOTS.every((slot) => slots[slot].certain);
|
|
226
|
+
const pipeline = viteOnly ? "vite" : "bun";
|
|
227
|
+
const reason = viteOnly
|
|
228
|
+
? "auto: this app's only transforms are `vitePlugins`, which the Bun build cannot run"
|
|
229
|
+
: slots.clientPlugins.present || slots.serverPlugins.present
|
|
230
|
+
? "default: this app declares Bun plugins, so both phases stay on Bun"
|
|
231
|
+
: "default: no transforms to place, so both phases stay on Bun";
|
|
232
|
+
// The adapter entry is bundled into every `nifra build` server entry. Whatever it imports goes with it.
|
|
233
|
+
const adapterEntry = existsSync(join(cwd, "framework.ts")) ? "framework.ts" : configFile;
|
|
234
|
+
const entrySource = adapterEntry === configFile ? source : await readSource(join(cwd, adapterEntry));
|
|
235
|
+
if (entrySource !== undefined) {
|
|
236
|
+
const seen = new Set();
|
|
237
|
+
for (const { specifier, index } of importedSpecifiers(stripComments(entrySource))) {
|
|
238
|
+
if (!DEV_TOOLCHAIN.test(specifier) || seen.has(specifier))
|
|
239
|
+
continue;
|
|
240
|
+
seen.add(specifier);
|
|
241
|
+
findings.push({
|
|
242
|
+
rule: "adapter-entry",
|
|
243
|
+
severity: "error",
|
|
244
|
+
file: adapterEntry,
|
|
245
|
+
line: lineAt(entrySource, index),
|
|
246
|
+
message: `${adapterEntry} imports "${specifier}", and \`nifra build\` imports the adapter from ${adapterEntry} - ` +
|
|
247
|
+
"so the dev toolchain is bundled into the production server entry. The build succeeds and the server then fails at startup on a dependency of the bundler (a missing native binding, typically), which reads like a broken install rather than a config split.",
|
|
248
|
+
fix: adapterEntry === "framework.ts"
|
|
249
|
+
? 'move "' +
|
|
250
|
+
specifier +
|
|
251
|
+
'" to `nifra.config.ts`, which only the CLI imports, and keep `framework.ts` to the adapter'
|
|
252
|
+
: 'add a `framework.ts` exporting just the adapter, re-export it from `nifra.config.ts` (`export { adapter } from "./framework"`), and leave the toolchain imports in the config',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// `conditions` reach SSR on the Bun pipeline and cannot reach the client bundle Bun's dev server
|
|
257
|
+
// serves - it takes no resolve conditions from bunfig or anywhere else. `nifra dev` says so at
|
|
258
|
+
// startup; a project whose CI only runs `nifra check` would otherwise never hear it.
|
|
259
|
+
const conditions = readSlot(masked, stripped, "conditions");
|
|
260
|
+
if (conditions.present && pipeline === "bun") {
|
|
261
|
+
findings.push({
|
|
262
|
+
rule: "bun-client-conditions",
|
|
263
|
+
severity: "warning",
|
|
264
|
+
file: configFile,
|
|
265
|
+
line: lineAt(source, conditions.index ?? 0),
|
|
266
|
+
message: "`conditions` are honoured by SSR and by `nifra build`, but Bun's dev-server bundler accepts no resolve conditions - so under `nifra dev` a package with an `exports` map can resolve to one file in the browser and another everywhere else.",
|
|
267
|
+
fix: "run `nifra dev --vite` when the client bundle must resolve exactly as production does",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
ran: true,
|
|
272
|
+
pipeline: certain ? pipeline : "unknown",
|
|
273
|
+
reason: certain
|
|
274
|
+
? reason
|
|
275
|
+
: "the config states its plugins indirectly (a thunk, an identifier or a re-export), so the bundler can only be read by loading it - `nifra dev` and `nifra build` print the real one",
|
|
276
|
+
certain,
|
|
277
|
+
configFile,
|
|
278
|
+
adapterEntry,
|
|
279
|
+
findings,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=pipeline-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-report.js","sourceRoot":"","sources":["../src/pipeline-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-path.d.ts","sourceRoot":"","sources":["../src/project-path.ts"],"names":[],"mappings":"AAQA,qGAAqG;AACrG,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuB7B"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
function escapesRoot(root, candidate) {
|
|
4
|
+
const rel = relative(root, candidate);
|
|
5
|
+
return rel === "" || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel);
|
|
6
|
+
}
|
|
7
|
+
/** Resolve a diagnostic path only when both its lexical and filesystem paths stay in the project. */
|
|
8
|
+
export async function resolveInsideProject(root, candidate) {
|
|
9
|
+
try {
|
|
10
|
+
if (isAbsolute(candidate))
|
|
11
|
+
return undefined;
|
|
12
|
+
const resolvedRoot = resolve(root);
|
|
13
|
+
const resolved = resolve(resolvedRoot, candidate);
|
|
14
|
+
if (escapesRoot(resolvedRoot, resolved))
|
|
15
|
+
return undefined;
|
|
16
|
+
const realRoot = await realpath(resolvedRoot);
|
|
17
|
+
let realCandidate;
|
|
18
|
+
try {
|
|
19
|
+
realCandidate = await realpath(resolved);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (!(error && typeof error === "object" && error.code === "ENOENT")) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const realParent = await realpath(dirname(resolved));
|
|
26
|
+
realCandidate = resolve(realParent, basename(resolved));
|
|
27
|
+
}
|
|
28
|
+
if (escapesRoot(realRoot, realCandidate))
|
|
29
|
+
return undefined;
|
|
30
|
+
return resolved;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=project-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-path.js","sourceRoot":"","sources":["../src/project-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAEjF,SAAS,WAAW,CAAC,IAAY,EAAE,SAAiB;IAClD,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IACrC,OAAO,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAA;AACpF,CAAC;AAED,qGAAqG;AACrG,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAY,EACZ,SAAiB;IAEjB,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAA;QAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAClC,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;QACjD,IAAI,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAA;QAEzD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAA;QAC7C,IAAI,aAAqB,CAAA;QACzB,IAAI,CAAC;YACH,aAAa,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAK,KAA2B,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;gBAC5F,OAAO,SAAS,CAAA;YAClB,CAAC;YACD,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;YACpD,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAA;QACzD,CAAC;QACD,IAAI,WAAW,CAAC,QAAQ,EAAE,aAAa,CAAC;YAAE,OAAO,SAAS,CAAA;QAC1D,OAAO,QAAQ,CAAA;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
|
package/dist/replay.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type CompatibleReplayFile } from "@nifrajs/core/replay";
|
|
2
|
+
export interface ReplayResult {
|
|
3
|
+
readonly ok: boolean;
|
|
4
|
+
readonly replay: CompatibleReplayFile;
|
|
5
|
+
readonly compatible?: boolean;
|
|
6
|
+
readonly error?: string;
|
|
7
|
+
readonly diagnostics?: readonly unknown[];
|
|
8
|
+
readonly replays?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
/** Validate and dispatch one token-only replay while keeping the file inside the project root. */
|
|
11
|
+
export declare function runReplay(cwd: string, file: string): Promise<ReplayResult>;
|
|
12
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,oBAAoB,EAA6B,MAAM,sBAAsB,CAAA;AAI3F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED,kGAAkG;AAClG,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA4BhF"}
|
package/dist/replay.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { resolve, sep } from "node:path";
|
|
2
|
+
import { parseCompatibleReplayFile } from "@nifrajs/core/replay";
|
|
3
|
+
import { runHydrationAssurance } from "./assure-hydration.js";
|
|
4
|
+
import { checkContractsLock } from "./contracts.js";
|
|
5
|
+
/** Validate and dispatch one token-only replay while keeping the file inside the project root. */
|
|
6
|
+
export async function runReplay(cwd, file) {
|
|
7
|
+
const path = resolve(cwd, file);
|
|
8
|
+
if (path !== cwd && !path.startsWith(cwd + sep)) {
|
|
9
|
+
throw new Error("replay file must stay inside the project root");
|
|
10
|
+
}
|
|
11
|
+
const replay = parseCompatibleReplayFile(JSON.parse(await Bun.file(path).text()));
|
|
12
|
+
if ("gate" in replay && replay.gate === "hydration") {
|
|
13
|
+
const hydration = await runHydrationAssurance(cwd, {
|
|
14
|
+
seed: replay.seed,
|
|
15
|
+
...(replay.case.startsWith("/") ? { routes: [replay.case] } : {}),
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
ok: hydration.diagnostics.length === 0,
|
|
19
|
+
replay,
|
|
20
|
+
diagnostics: hydration.diagnostics,
|
|
21
|
+
...(hydration.replays === undefined ? {} : { replays: hydration.replays }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if ("gate" in replay && replay.gate === "contracts") {
|
|
25
|
+
const contracts = await checkContractsLock(cwd);
|
|
26
|
+
return {
|
|
27
|
+
ok: contracts.present && contracts.diagnostics.length === 0,
|
|
28
|
+
replay,
|
|
29
|
+
diagnostics: contracts.diagnostics,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (!("gate" in replay))
|
|
33
|
+
return { ok: true, replay, compatible: true };
|
|
34
|
+
return { ok: false, replay, error: `no replay dispatcher for gate ${replay.gate}` };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAA6B,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAC3F,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAWnD,kGAAkG;AAClG,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,IAAY;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAC/B,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAClE,CAAC;IACD,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IACjF,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE;YACjD,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClE,CAAC,CAAA;QACF,OAAO;YACL,EAAE,EAAE,SAAS,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;YACtC,MAAM;YACN,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,GAAG,CAAC,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;SAC3E,CAAA;IACH,CAAC;IACD,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAA;QAC/C,OAAO;YACL,EAAE,EAAE,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;YAC3D,MAAM;YACN,WAAW,EAAE,SAAS,CAAC,WAAW;SACnC,CAAA;IACH,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;IACtE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,iCAAiC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAA;AACrF,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Built-in diagnostic code registry. Codes are append-only and reserved by the NF- prefix. */
|
|
2
|
+
export declare const RULE_CODES: Readonly<{
|
|
3
|
+
readonly "NF-C001": "TypeScript contract check";
|
|
4
|
+
readonly "NF-C002": "Typed client check";
|
|
5
|
+
readonly "NF-C003": "Untyped client check";
|
|
6
|
+
readonly "NF-C004": "Server-only import check";
|
|
7
|
+
readonly "NF-C005": "Removed import check";
|
|
8
|
+
readonly "NF-C006": "Interpolated SQL check";
|
|
9
|
+
readonly "NF-C007": "Raw response route advisory";
|
|
10
|
+
readonly "NF-C008": "Undeclared dependency check";
|
|
11
|
+
readonly "NF-C009": "Duplicate install check";
|
|
12
|
+
readonly "NF-C010": "Stale workspace build check";
|
|
13
|
+
readonly "NF-C011": "Build pipeline check";
|
|
14
|
+
readonly "NF-C012": "Server manifest drift check";
|
|
15
|
+
readonly "NF-C013": "Trust manifest drift check";
|
|
16
|
+
readonly "NF-C014": "Capability assurance check";
|
|
17
|
+
readonly "NF-C015": "Assurance configuration check";
|
|
18
|
+
readonly "NF-C016": "Check configuration check";
|
|
19
|
+
readonly "NF-C017": "Rule pack validation";
|
|
20
|
+
readonly "NF-C018": "Reserved client segment check";
|
|
21
|
+
readonly "NF-C019": "Duplicate route registration check";
|
|
22
|
+
readonly "NF-D001": "Doctor check";
|
|
23
|
+
readonly "NF-A001": "Assurance bundle check";
|
|
24
|
+
readonly "NF-H001": "Hydration mismatch";
|
|
25
|
+
readonly "NF-H002": "Duplicate framework runtime";
|
|
26
|
+
readonly "NF-H003": "Stale client build";
|
|
27
|
+
readonly "NF-H004": "Hydration interaction";
|
|
28
|
+
readonly "NF-K001": "Contract snapshot drift";
|
|
29
|
+
readonly "NF-S001": "Fail-open gate";
|
|
30
|
+
readonly "NF-S002": "Non-constant-time secret comparison";
|
|
31
|
+
readonly "NF-S003": "Sensitive value in log call";
|
|
32
|
+
readonly "NF-S004": "CORS origin predicate ignores the origin";
|
|
33
|
+
readonly "NF-S005": "External redirect opt-out";
|
|
34
|
+
readonly "NF-S006": "Security escape hatch enabled";
|
|
35
|
+
readonly "NF-S007": "Secure cookie without a __Host-/__Secure- prefix";
|
|
36
|
+
}>;
|
|
37
|
+
/** Codes that no longer fire but remain reserved forever. */
|
|
38
|
+
export declare const RETIRED_CODES: ReadonlySet<string>;
|
|
39
|
+
export type BuiltInCode = keyof typeof RULE_CODES;
|
|
40
|
+
export declare function isBuiltInCode(code: string): code is BuiltInCode;
|
|
41
|
+
export declare function assertUniqueRuleCodes(): void;
|
|
42
|
+
//# sourceMappingURL=codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../../src/rules/codes.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAE/F,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCZ,CAAA;AAEX,6DAA6D;AAC7D,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAAa,CAAA;AAE3D,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,UAAU,CAAA;AAEjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,WAAW,CAE/D;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAO5C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Built-in diagnostic code registry. Codes are append-only and reserved by the NF- prefix. */
|
|
2
|
+
export const RULE_CODES = Object.freeze({
|
|
3
|
+
"NF-C001": "TypeScript contract check",
|
|
4
|
+
"NF-C002": "Typed client check",
|
|
5
|
+
"NF-C003": "Untyped client check",
|
|
6
|
+
"NF-C004": "Server-only import check",
|
|
7
|
+
"NF-C005": "Removed import check",
|
|
8
|
+
"NF-C006": "Interpolated SQL check",
|
|
9
|
+
"NF-C007": "Raw response route advisory",
|
|
10
|
+
"NF-C008": "Undeclared dependency check",
|
|
11
|
+
"NF-C009": "Duplicate install check",
|
|
12
|
+
"NF-C010": "Stale workspace build check",
|
|
13
|
+
"NF-C011": "Build pipeline check",
|
|
14
|
+
"NF-C012": "Server manifest drift check",
|
|
15
|
+
"NF-C013": "Trust manifest drift check",
|
|
16
|
+
"NF-C014": "Capability assurance check",
|
|
17
|
+
"NF-C015": "Assurance configuration check",
|
|
18
|
+
"NF-C016": "Check configuration check",
|
|
19
|
+
"NF-C017": "Rule pack validation",
|
|
20
|
+
"NF-C018": "Reserved client segment check",
|
|
21
|
+
"NF-C019": "Duplicate route registration check",
|
|
22
|
+
"NF-D001": "Doctor check",
|
|
23
|
+
"NF-A001": "Assurance bundle check",
|
|
24
|
+
"NF-H001": "Hydration mismatch",
|
|
25
|
+
"NF-H002": "Duplicate framework runtime",
|
|
26
|
+
"NF-H003": "Stale client build",
|
|
27
|
+
"NF-H004": "Hydration interaction",
|
|
28
|
+
"NF-K001": "Contract snapshot drift",
|
|
29
|
+
"NF-S001": "Fail-open gate",
|
|
30
|
+
"NF-S002": "Non-constant-time secret comparison",
|
|
31
|
+
"NF-S003": "Sensitive value in log call",
|
|
32
|
+
"NF-S004": "CORS origin predicate ignores the origin",
|
|
33
|
+
"NF-S005": "External redirect opt-out",
|
|
34
|
+
"NF-S006": "Security escape hatch enabled",
|
|
35
|
+
"NF-S007": "Secure cookie without a __Host-/__Secure- prefix",
|
|
36
|
+
});
|
|
37
|
+
/** Codes that no longer fire but remain reserved forever. */
|
|
38
|
+
export const RETIRED_CODES = new Set();
|
|
39
|
+
export function isBuiltInCode(code) {
|
|
40
|
+
return code in RULE_CODES || RETIRED_CODES.has(code);
|
|
41
|
+
}
|
|
42
|
+
export function assertUniqueRuleCodes() {
|
|
43
|
+
const codes = Object.keys(RULE_CODES);
|
|
44
|
+
if (new Set(codes).size !== codes.length)
|
|
45
|
+
throw new Error("diagnostic code registry contains duplicates");
|
|
46
|
+
for (const code of codes) {
|
|
47
|
+
if (!/^NF-[A-Z]\d{3}$/.test(code))
|
|
48
|
+
throw new Error(`invalid built-in diagnostic code: ${code}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
assertUniqueRuleCodes();
|
|
52
|
+
//# sourceMappingURL=codes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codes.js","sourceRoot":"","sources":["../../src/rules/codes.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAE/F,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACtC,SAAS,EAAE,2BAA2B;IACtC,SAAS,EAAE,oBAAoB;IAC/B,SAAS,EAAE,sBAAsB;IACjC,SAAS,EAAE,0BAA0B;IACrC,SAAS,EAAE,sBAAsB;IACjC,SAAS,EAAE,wBAAwB;IACnC,SAAS,EAAE,6BAA6B;IACxC,SAAS,EAAE,6BAA6B;IACxC,SAAS,EAAE,yBAAyB;IACpC,SAAS,EAAE,6BAA6B;IACxC,SAAS,EAAE,sBAAsB;IACjC,SAAS,EAAE,6BAA6B;IACxC,SAAS,EAAE,4BAA4B;IACvC,SAAS,EAAE,4BAA4B;IACvC,SAAS,EAAE,+BAA+B;IAC1C,SAAS,EAAE,2BAA2B;IACtC,SAAS,EAAE,sBAAsB;IACjC,SAAS,EAAE,+BAA+B;IAC1C,SAAS,EAAE,oCAAoC;IAC/C,SAAS,EAAE,cAAc;IACzB,SAAS,EAAE,wBAAwB;IACnC,SAAS,EAAE,oBAAoB;IAC/B,SAAS,EAAE,6BAA6B;IACxC,SAAS,EAAE,oBAAoB;IAC/B,SAAS,EAAE,uBAAuB;IAClC,SAAS,EAAE,yBAAyB;IACpC,SAAS,EAAE,gBAAgB;IAC3B,SAAS,EAAE,qCAAqC;IAChD,SAAS,EAAE,6BAA6B;IACxC,SAAS,EAAE,0CAA0C;IACrD,SAAS,EAAE,2BAA2B;IACtC,SAAS,EAAE,+BAA+B;IAC1C,SAAS,EAAE,kDAAkD;CACrD,CAAC,CAAA;AAEX,6DAA6D;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAwB,IAAI,GAAG,EAAE,CAAA;AAI3D,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,IAAI,UAAU,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACrC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM;QACtC,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;IACjE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAA;IACjG,CAAC;AACH,CAAC;AAED,qBAAqB,EAAE,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker recognition for in-source escape hatches (`@nifra-gate-reviewed`, `nifra-expect …`).
|
|
3
|
+
*
|
|
4
|
+
* A marker counts when it appears on the flagged line itself (trailing comment) or ANYWHERE in the
|
|
5
|
+
* contiguous comment block directly above it - which is where a human naturally writes the
|
|
6
|
+
* multi-line justification the hatch asks for. A blank line or a code line ends the block, so a
|
|
7
|
+
* marker in an unrelated comment further up never leaks onto a finding below it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function commentBlockHasMarker(lines: readonly string[], line: number, marker: string): boolean;
|
|
10
|
+
//# sourceMappingURL=comment-markers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-markers.d.ts","sourceRoot":"","sources":["../../src/rules/comment-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAQT"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker recognition for in-source escape hatches (`@nifra-gate-reviewed`, `nifra-expect …`).
|
|
3
|
+
*
|
|
4
|
+
* A marker counts when it appears on the flagged line itself (trailing comment) or ANYWHERE in the
|
|
5
|
+
* contiguous comment block directly above it - which is where a human naturally writes the
|
|
6
|
+
* multi-line justification the hatch asks for. A blank line or a code line ends the block, so a
|
|
7
|
+
* marker in an unrelated comment further up never leaks onto a finding below it.
|
|
8
|
+
*/
|
|
9
|
+
// Lines that continue a comment block when walking upward: line comments, block-comment
|
|
10
|
+
// open/body/close lines, and the JSX comment form.
|
|
11
|
+
const COMMENT_LINE = /^\s*(?:\/\/|\/\*|\*\/|\*|\{\/\*)/;
|
|
12
|
+
export function commentBlockHasMarker(lines, line, marker) {
|
|
13
|
+
if ((lines[line - 1] ?? "").includes(marker))
|
|
14
|
+
return true;
|
|
15
|
+
for (let i = line - 2; i >= 0; i--) {
|
|
16
|
+
const text = lines[i] ?? "";
|
|
17
|
+
if (!COMMENT_LINE.test(text))
|
|
18
|
+
return false;
|
|
19
|
+
if (text.includes(marker))
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=comment-markers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-markers.js","sourceRoot":"","sources":["../../src/rules/comment-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wFAAwF;AACxF,mDAAmD;AACnD,MAAM,YAAY,GAAG,kCAAkC,CAAA;AAEvD,MAAM,UAAU,qBAAqB,CACnC,KAAwB,EACxB,IAAY,EACZ,MAAc;IAEd,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAA;IACzD,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAC1C,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAA;IACxC,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Diagnostic } from "../diagnostics.js";
|
|
2
|
+
export interface SourceFile {
|
|
3
|
+
readonly file: string;
|
|
4
|
+
readonly content: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SourceIndex {
|
|
7
|
+
readonly files: readonly string[];
|
|
8
|
+
read(file: string): string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface ProjectFacts {
|
|
11
|
+
readonly [key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
export interface RuleContext {
|
|
14
|
+
readonly root: string;
|
|
15
|
+
readonly sources: SourceIndex;
|
|
16
|
+
readonly project: ProjectFacts;
|
|
17
|
+
}
|
|
18
|
+
export interface CheckRule {
|
|
19
|
+
readonly code: string;
|
|
20
|
+
readonly title: string;
|
|
21
|
+
scan(ctx: RuleContext): Promise<readonly Diagnostic[]>;
|
|
22
|
+
}
|
|
23
|
+
export interface RulePack {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly rules: readonly CheckRule[];
|
|
26
|
+
}
|
|
27
|
+
export declare function validateRulePacks(packs: readonly RulePack[] | undefined): readonly RulePack[];
|
|
28
|
+
export declare function parseRulePacks(value: unknown): readonly RulePack[];
|
|
29
|
+
export declare function runRuleRegistry(ctx: RuleContext, builtIns: readonly CheckRule[], packs?: readonly RulePack[]): Promise<Diagnostic[]>;
|
|
30
|
+
export declare function sourceIndex(files: readonly SourceFile[]): SourceIndex;
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAGnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAC/B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAA;CACvD;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAA;CACrC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,GAAG,SAAS,QAAQ,EAAE,CA6B7F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,QAAQ,EAAE,CAoFlE;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,SAAS,SAAS,EAAE,EAC9B,KAAK,GAAE,SAAS,QAAQ,EAAO,GAC9B,OAAO,CAAC,UAAU,EAAE,CAAC,CAWvB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,WAAW,CAMrE"}
|