@nifrajs/cli 2.2.0 → 2.4.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 +25 -25
- package/dist/assure.d.ts +2 -2
- package/dist/assure.d.ts.map +1 -1
- package/dist/assure.js +17 -10
- package/dist/assure.js.map +1 -1
- package/dist/capabilities-tool.d.ts +7 -0
- package/dist/capabilities-tool.d.ts.map +1 -1
- package/dist/capabilities-tool.js +68 -16
- package/dist/capabilities-tool.js.map +1 -1
- package/dist/check.d.ts +61 -22
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +336 -87
- package/dist/check.js.map +1 -1
- package/dist/cli.d.ts +30 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +148 -39
- package/dist/cli.js.map +1 -1
- package/dist/diff-tool.d.ts +3 -3
- package/dist/diff-tool.js +7 -7
- package/dist/docs-search.d.ts +3 -3
- package/dist/docs-search.js +6 -6
- package/dist/doctor.d.ts +3 -3
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +12 -8
- package/dist/doctor.js.map +1 -1
- package/dist/examples.d.ts +5 -5
- package/dist/examples.js +5 -5
- package/dist/init-agents.d.ts +14 -14
- package/dist/init-agents.js +14 -14
- package/dist/internal/typescript-import.d.ts +24 -0
- package/dist/internal/typescript-import.d.ts.map +1 -0
- package/dist/internal/typescript-import.js +32 -0
- package/dist/internal/typescript-import.js.map +1 -0
- package/dist/introspect.d.ts +12 -12
- package/dist/introspect.js +24 -24
- package/dist/learn.d.ts +32 -0
- package/dist/learn.d.ts.map +1 -0
- package/dist/learn.js +127 -0
- package/dist/learn.js.map +1 -0
- package/dist/levels-tool.d.ts +8 -8
- package/dist/levels-tool.d.ts.map +1 -1
- package/dist/levels-tool.js +33 -30
- package/dist/levels-tool.js.map +1 -1
- package/dist/load.d.ts +6 -6
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +15 -7
- package/dist/load.js.map +1 -1
- package/dist/manifest-tool.d.ts +1 -1
- package/dist/manifest-tool.d.ts.map +1 -1
- package/dist/manifest-tool.js +4 -2
- package/dist/manifest-tool.js.map +1 -1
- package/dist/mcp-app-tool.d.ts +14 -0
- package/dist/mcp-app-tool.d.ts.map +1 -0
- package/dist/mcp-app-tool.js +80 -0
- package/dist/mcp-app-tool.js.map +1 -0
- package/dist/mcp-docs-tools.d.ts +1 -1
- package/dist/mcp-docs-tools.d.ts.map +1 -1
- package/dist/mcp-docs-tools.js +25 -9
- package/dist/mcp-docs-tools.js.map +1 -1
- package/dist/mcp-http.d.ts +14 -10
- package/dist/mcp-http.d.ts.map +1 -1
- package/dist/mcp-http.js +30 -16
- package/dist/mcp-http.js.map +1 -1
- package/dist/mcp-protocol.d.ts +2 -2
- package/dist/mcp-protocol.js +2 -2
- package/dist/mcp-render.d.ts +3 -3
- package/dist/mcp-render.js +6 -6
- package/dist/mcp-run.d.ts +2 -2
- package/dist/mcp-run.js +2 -2
- package/dist/mcp.d.ts +28 -14
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +263 -45
- package/dist/mcp.js.map +1 -1
- package/dist/openapi-tool.d.ts +1 -1
- package/dist/openapi-tool.js +2 -2
- package/dist/pipeline-guard.d.ts +1 -1
- package/dist/pipeline-guard.js +1 -1
- package/dist/port.d.ts +9 -9
- package/dist/port.js +27 -27
- package/dist/recipes/1.8.0.d.ts +2 -2
- package/dist/recipes/1.8.0.js +5 -5
- package/dist/recipes/index.d.ts +2 -2
- package/dist/recipes/index.js +1 -1
- package/dist/scaffold.d.ts +3 -3
- package/dist/scaffold.js +8 -8
- package/dist/sync-routes.d.ts +28 -0
- package/dist/sync-routes.d.ts.map +1 -0
- package/dist/sync-routes.js +55 -0
- package/dist/sync-routes.js.map +1 -0
- package/dist/types-search.d.ts +5 -5
- package/dist/types-search.js +7 -7
- package/dist/upgrade.d.ts +20 -9
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +117 -19
- package/dist/upgrade.js.map +1 -1
- package/dist/verification.d.ts +51 -0
- package/dist/verification.d.ts.map +1 -0
- package/dist/verification.js +80 -0
- package/dist/verification.js.map +1 -0
- package/docs/examples.json +174 -90
- package/docs/llms-full.txt +1771 -748
- package/docs/types.json +2027 -3089
- package/package.json +22 -11
- package/src/assure.ts +17 -18
- package/src/capabilities-tool.ts +82 -16
- package/src/check.ts +410 -104
- package/src/cli.ts +161 -41
- package/src/diff-tool.ts +8 -8
- package/src/docs-search.ts +6 -6
- package/src/doctor.ts +18 -10
- package/src/examples.ts +7 -7
- package/src/init-agents.ts +17 -17
- package/src/internal/typescript-import.ts +33 -0
- package/src/introspect.ts +28 -28
- package/src/learn.ts +152 -0
- package/src/levels-tool.ts +37 -31
- package/src/load.ts +21 -12
- package/src/manifest-tool.ts +4 -2
- package/src/mcp-app-tool.ts +86 -0
- package/src/mcp-docs-tools.ts +26 -9
- package/src/mcp-http.ts +31 -18
- package/src/mcp-protocol.ts +2 -2
- package/src/mcp-render.ts +6 -6
- package/src/mcp-run.ts +2 -2
- package/src/mcp.ts +319 -45
- package/src/openapi-tool.ts +2 -2
- package/src/pipeline-guard.ts +1 -1
- package/src/port.ts +31 -31
- package/src/recipes/1.8.0.ts +5 -5
- package/src/recipes/index.ts +2 -2
- package/src/scaffold.ts +8 -8
- package/src/sync-routes.ts +69 -0
- package/src/types-search.ts +8 -8
- package/src/upgrade.ts +137 -19
- package/src/verification.ts +123 -0
package/dist/introspect.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Introspect a loaded nifra project into a Markdown brief
|
|
3
|
-
* page routes (`discoverRoutes`), and a conventions summary
|
|
2
|
+
* Introspect a loaded nifra project into a Markdown brief - the API routes (`backend.routes()`), the
|
|
3
|
+
* page routes (`discoverRoutes`), and a conventions summary - for piping into an AI coding agent's
|
|
4
4
|
* prompt. Used by `nifra context` (stdout) and the `nifra mcp` server (a tool result), so an agent sees
|
|
5
5
|
* *this* project's actual surface, not just generic docs.
|
|
6
6
|
*/
|
|
@@ -10,31 +10,31 @@ import type { LoadedApp } from "./load.js";
|
|
|
10
10
|
/** Read the backend's registered routes, if it's a `server()` with a `.routes()` method. */
|
|
11
11
|
export declare function backendRoutes(backend: unknown): ReflectedRoute[];
|
|
12
12
|
/**
|
|
13
|
-
* Render a JSON Schema as a compact TypeScript-like type (`{ id: string, name?: string }`)
|
|
13
|
+
* Render a JSON Schema as a compact TypeScript-like type (`{ id: string, name?: string }`) - the
|
|
14
14
|
* shape agents already think in, at a fraction of the raw JSON Schema's tokens (~70-80% smaller).
|
|
15
15
|
* Anything the renderer doesn't model falls back to the raw JSON for that subtree, so the output
|
|
16
16
|
* is always faithful, just not always minimal.
|
|
17
17
|
*/
|
|
18
18
|
export declare function tsTypeOf(schema: unknown, depth?: number): string;
|
|
19
19
|
/**
|
|
20
|
-
* The typed-client call form for a route
|
|
20
|
+
* The typed-client call form for a route - the exact `client<typeof app>` proxy chain an agent should
|
|
21
21
|
* write, derived from the same convention `@nifrajs/client` implements (so it never has to read the
|
|
22
22
|
* client tests to learn it): a static segment is a property (`.users`), a path param/wildcard is a call
|
|
23
23
|
* that appends the value (`({ id })`), the root path is `.index`, and the HTTP verb is the terminal call.
|
|
24
|
-
* Body verbs (POST/PUT/PATCH) take the body first then call-options; other verbs take call-options first
|
|
24
|
+
* Body verbs (POST/PUT/PATCH) take the body first then call-options; other verbs take call-options first -
|
|
25
25
|
* so the `{ query }` argument lands in the right slot for each.
|
|
26
26
|
*/
|
|
27
27
|
export declare function clientCall(method: string, path: string, schema: unknown): string;
|
|
28
28
|
/** Markdown section listing the backend's API routes with their request + response field shapes. */
|
|
29
29
|
export declare function apiRoutesSection(routes: readonly ReflectedRoute[]): string;
|
|
30
|
-
/** Compact API-routes INDEX for the no-arg `nifra_context` call
|
|
30
|
+
/** Compact API-routes INDEX for the no-arg `nifra_context` call - `METHOD path` per route, WITHOUT the
|
|
31
31
|
* body/query/response shapes or the per-route `call` line. Bounds the first-call payload on a big backend:
|
|
32
32
|
* an agent gets the full route list (so it knows what's mounted) plus a pointer to fetch any route's full
|
|
33
33
|
* contract via the `path`/`kind` slice, instead of every schema up front. */
|
|
34
34
|
export declare function apiRoutesIndexSection(routes: readonly ReflectedRoute[]): string;
|
|
35
35
|
/** Markdown section listing the file-routed pages (URL pattern → source file). */
|
|
36
36
|
export declare function pageRoutesSection(manifest: Manifest | undefined): string;
|
|
37
|
-
/** Optional narrowing for {@link describeProject}
|
|
37
|
+
/** Optional narrowing for {@link describeProject} - a path prefix and/or one section. A filtered
|
|
38
38
|
* brief omits the conventions block (the agent already has it from its first full call), so
|
|
39
39
|
* follow-up calls cost a fraction of the tokens. */
|
|
40
40
|
export interface ContextFilter {
|
|
@@ -42,9 +42,9 @@ export interface ContextFilter {
|
|
|
42
42
|
readonly kind?: "api" | "pages";
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
|
-
* Build the project brief as Markdown. A no-arg call returns a tight **INDEX**
|
|
45
|
+
* Build the project brief as Markdown. A no-arg call returns a tight **INDEX** - the route list (API
|
|
46
46
|
* routes as `METHOD path`, page routes as `pattern → file`) + conventions + a pointer to fetch full
|
|
47
|
-
* contracts via the slice
|
|
47
|
+
* contracts via the slice - so learning a big project is cheap, not a full-surface schema dump. Passing a
|
|
48
48
|
* {@link ContextFilter} (`path` prefix and/or `kind`) returns the narrowed, FULL detail (per-route
|
|
49
49
|
* body/query/response shapes + the typed-client `call` form), and omits the conventions block (the agent
|
|
50
50
|
* already has it from the index call).
|
|
@@ -61,7 +61,7 @@ export interface RouteJson {
|
|
|
61
61
|
readonly response?: string;
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
* The backend's API routes as structured JSON
|
|
64
|
+
* The backend's API routes as structured JSON - what the `nifra_routes` MCP tool returns, so an agent
|
|
65
65
|
* consumes the contract programmatically (`list_routes` / `get_route_schema`) instead of parsing the
|
|
66
66
|
* Markdown brief. Optionally filtered to routes whose path starts with `pathPrefix`. Reuses the exact
|
|
67
67
|
* same `call` form and TS-shaped contracts the Markdown brief shows.
|
|
@@ -76,7 +76,7 @@ export interface RouteTableEntry {
|
|
|
76
76
|
readonly path: string;
|
|
77
77
|
/** Methods this route serves, sorted (page: `GET`, plus `POST` when it has an `action`). */
|
|
78
78
|
readonly methods: readonly string[];
|
|
79
|
-
/** Source file (page only
|
|
79
|
+
/** Source file (page only - the route module path relative to `routes/`). */
|
|
80
80
|
readonly file?: string;
|
|
81
81
|
/** True when this API route is auto-mounted under the page router's `apiPrefix` (so it's reachable
|
|
82
82
|
* through the same web app, no hand-wired `if (pathname.startsWith("/api/"))`). Pages are never
|
|
@@ -98,7 +98,7 @@ export interface RouteTableInput {
|
|
|
98
98
|
readonly apiPrefix?: string;
|
|
99
99
|
}
|
|
100
100
|
/**
|
|
101
|
-
* Build the unified, sorted route table from page routes + backend API routes. Pure
|
|
101
|
+
* Build the unified, sorted route table from page routes + backend API routes. Pure - the caller
|
|
102
102
|
* supplies page `hasAction` (resolved via a module load) and the API descriptors (`backend.routes()`).
|
|
103
103
|
* Page routes serve `GET` (+ `POST` when they export an `action`); API routes carry their declared
|
|
104
104
|
* method, and are flagged `autoMounted` when at/under `apiPrefix`. Sorted by path, then kind, then the
|
package/dist/introspect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Introspect a loaded nifra project into a Markdown brief
|
|
3
|
-
* page routes (`discoverRoutes`), and a conventions summary
|
|
2
|
+
* Introspect a loaded nifra project into a Markdown brief - the API routes (`backend.routes()`), the
|
|
3
|
+
* page routes (`discoverRoutes`), and a conventions summary - for piping into an AI coding agent's
|
|
4
4
|
* prompt. Used by `nifra context` (stdout) and the `nifra mcp` server (a tool result), so an agent sees
|
|
5
5
|
* *this* project's actual surface, not just generic docs.
|
|
6
6
|
*/
|
|
@@ -11,7 +11,7 @@ export function backendRoutes(backend) {
|
|
|
11
11
|
return [...reflectRoutes(backend)];
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Render a JSON Schema as a compact TypeScript-like type (`{ id: string, name?: string }`)
|
|
14
|
+
* Render a JSON Schema as a compact TypeScript-like type (`{ id: string, name?: string }`) - the
|
|
15
15
|
* shape agents already think in, at a fraction of the raw JSON Schema's tokens (~70-80% smaller).
|
|
16
16
|
* Anything the renderer doesn't model falls back to the raw JSON for that subtree, so the output
|
|
17
17
|
* is always faithful, just not always minimal.
|
|
@@ -58,7 +58,7 @@ export function tsTypeOf(schema, depth = 0) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
/** One route's request/response field shapes, indented under its line. Reads the declared schemas off the
|
|
61
|
-
* descriptor so an agent gets the exact input + output contract
|
|
61
|
+
* descriptor so an agent gets the exact input + output contract - not just "this route is validated". */
|
|
62
62
|
function schemaLines(schema) {
|
|
63
63
|
const s = schema;
|
|
64
64
|
if (!s)
|
|
@@ -77,11 +77,11 @@ function schemaLines(schema) {
|
|
|
77
77
|
}
|
|
78
78
|
const IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
79
79
|
/**
|
|
80
|
-
* The typed-client call form for a route
|
|
80
|
+
* The typed-client call form for a route - the exact `client<typeof app>` proxy chain an agent should
|
|
81
81
|
* write, derived from the same convention `@nifrajs/client` implements (so it never has to read the
|
|
82
82
|
* client tests to learn it): a static segment is a property (`.users`), a path param/wildcard is a call
|
|
83
83
|
* that appends the value (`({ id })`), the root path is `.index`, and the HTTP verb is the terminal call.
|
|
84
|
-
* Body verbs (POST/PUT/PATCH) take the body first then call-options; other verbs take call-options first
|
|
84
|
+
* Body verbs (POST/PUT/PATCH) take the body first then call-options; other verbs take call-options first -
|
|
85
85
|
* so the `{ query }` argument lands in the right slot for each.
|
|
86
86
|
*/
|
|
87
87
|
export function clientCall(method, path, schema) {
|
|
@@ -125,9 +125,9 @@ export function apiRoutesSection(routes) {
|
|
|
125
125
|
...schemaLines(r.schema),
|
|
126
126
|
` - call: \`${clientCall(r.method, r.path, r.schema)}\` → \`{ ok, status, data, error }\``,
|
|
127
127
|
]);
|
|
128
|
-
return `## API routes (backend.ts)\n\nEach route's \`body\`/\`query\`/\`response\` shape is its contract
|
|
128
|
+
return `## API routes (backend.ts)\n\nEach route's \`body\`/\`query\`/\`response\` shape is its contract - the typed client derives request inputs and \`res.data\` from these, so a screen built on \`client<typeof app>\` stays in sync automatically. The \`call\` line is the exact \`client<typeof app>\` form: static path segments are properties, a path param is a call (\`({ id })\`), the verb is the terminal call (body first for POST/PUT/PATCH), and every call returns the never-throwing \`{ ok, status, data, error }\` Result.\n\n${lines.join("\n")}`;
|
|
129
129
|
}
|
|
130
|
-
/** Compact API-routes INDEX for the no-arg `nifra_context` call
|
|
130
|
+
/** Compact API-routes INDEX for the no-arg `nifra_context` call - `METHOD path` per route, WITHOUT the
|
|
131
131
|
* body/query/response shapes or the per-route `call` line. Bounds the first-call payload on a big backend:
|
|
132
132
|
* an agent gets the full route list (so it knows what's mounted) plus a pointer to fetch any route's full
|
|
133
133
|
* contract via the `path`/`kind` slice, instead of every schema up front. */
|
|
@@ -138,7 +138,7 @@ export function apiRoutesIndexSection(routes) {
|
|
|
138
138
|
const lines = [...routes]
|
|
139
139
|
.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method))
|
|
140
140
|
.map((r) => `- \`${r.method} ${r.path}\``);
|
|
141
|
-
return `## API routes (backend.ts)\n\n${routes.length} route${routes.length === 1 ? "" : "s"}. Call \`nifra_context\` again with \`path\` (a route prefix) and/or \`kind: "api"\` for the body/query/response contracts + the exact \`client<typeof app>\` call form
|
|
141
|
+
return `## API routes (backend.ts)\n\n${routes.length} route${routes.length === 1 ? "" : "s"}. Call \`nifra_context\` again with \`path\` (a route prefix) and/or \`kind: "api"\` for the body/query/response contracts + the exact \`client<typeof app>\` call form - or \`nifra_routes\` for the same as structured JSON.\n\n${lines.join("\n")}`;
|
|
142
142
|
}
|
|
143
143
|
/** Markdown section listing the file-routed pages (URL pattern → source file). */
|
|
144
144
|
export function pageRoutesSection(manifest) {
|
|
@@ -154,27 +154,27 @@ const CONVENTIONS = `## Conventions (summary)
|
|
|
154
154
|
|
|
155
155
|
- **Backend:** \`server()\` from \`@nifrajs/core\`; returning a value sends JSON. Validate untrusted input with
|
|
156
156
|
a route schema (\`{ body, query, params }\` using \`t\` from \`@nifrajs/schema\` or any Standard Schema) and read
|
|
157
|
-
the typed \`c.body\` / \`c.query\` / \`c.params\`
|
|
157
|
+
the typed \`c.body\` / \`c.query\` / \`c.params\` - never hand-parse.
|
|
158
158
|
- **Cross-cutting concerns** (rate limit / \`429\`, CORS, auth, CSRF, caching, security headers, body limits)
|
|
159
159
|
→ \`@nifrajs/middleware\`, applied with \`app.use(...)\`. Don't hand-roll them; call \`nifra_docs("middleware")\`
|
|
160
160
|
for the full list + usage.
|
|
161
161
|
- **Need the exact TypeScript of a \`@nifrajs/*\` symbol?** Call \`nifra_types({ name })\` (e.g. \`RateLimitStore\`,
|
|
162
|
-
\`RouteSchema\`)
|
|
162
|
+
\`RouteSchema\`) - the authoritative, complete declaration. Never read \`@nifrajs\` \`.d.ts\` files.
|
|
163
163
|
- **Response contract (no drift):** declare \`{ response: t.object({...}) }\` on a route to lock its output
|
|
164
|
-
shape
|
|
164
|
+
shape - the handler is type-checked against it and the client sees exactly that shape. The contract above
|
|
165
165
|
is the single source of truth for both sides.
|
|
166
|
-
- **Client
|
|
166
|
+
- **Client - ALWAYS use this for API calls:** \`client<typeof app>(url)\` derives request inputs AND
|
|
167
167
|
\`res.data\` from the backend's route types, so the compiler catches any frontend/backend drift. Never
|
|
168
|
-
hand-roll \`fetch\` + ad-hoc response types for an internal API
|
|
168
|
+
hand-roll \`fetch\` + ad-hoc response types for an internal API - that's exactly how screens drift. It
|
|
169
169
|
never throws: branch on \`res.ok ? res.data : res.error\`.
|
|
170
170
|
- **Pages:** file-routed under \`routes/\`; \`loader\`/\`action\` are server-only but the module is also bundled
|
|
171
|
-
for the browser
|
|
171
|
+
for the browser - **never top-level-import server-only code** (DB, secrets, \`process.env\`) into a route
|
|
172
172
|
file; reach it via \`ctx.api\` / \`ctx.env\`.
|
|
173
173
|
- \`app.fetch(Request)\` is the universal entry. Full reference: this repo's \`AGENTS.md\`, or \`llms-full.txt\`.`;
|
|
174
174
|
/**
|
|
175
|
-
* Build the project brief as Markdown. A no-arg call returns a tight **INDEX**
|
|
175
|
+
* Build the project brief as Markdown. A no-arg call returns a tight **INDEX** - the route list (API
|
|
176
176
|
* routes as `METHOD path`, page routes as `pattern → file`) + conventions + a pointer to fetch full
|
|
177
|
-
* contracts via the slice
|
|
177
|
+
* contracts via the slice - so learning a big project is cheap, not a full-surface schema dump. Passing a
|
|
178
178
|
* {@link ContextFilter} (`path` prefix and/or `kind`) returns the narrowed, FULL detail (per-route
|
|
179
179
|
* body/query/response shapes + the typed-client `call` form), and omits the conventions block (the agent
|
|
180
180
|
* already has it from the index call).
|
|
@@ -185,7 +185,7 @@ export function describeProject(app, filter) {
|
|
|
185
185
|
manifest = discoverRoutes(app.routesDir);
|
|
186
186
|
}
|
|
187
187
|
catch {
|
|
188
|
-
// No (or unreadable) routes/
|
|
188
|
+
// No (or unreadable) routes/ - the page-routes section reports that.
|
|
189
189
|
}
|
|
190
190
|
const prefix = filter?.path;
|
|
191
191
|
let routes = backendRoutes(app.backend);
|
|
@@ -201,10 +201,10 @@ export function describeProject(app, filter) {
|
|
|
201
201
|
const isIndex = filter?.kind === undefined && (prefix === undefined || prefix === "");
|
|
202
202
|
const sections = [`# nifra project: ${app.cwd.slice(app.cwd.lastIndexOf("/") + 1)}`];
|
|
203
203
|
if (isIndex) {
|
|
204
|
-
sections.push("A full-stack nifra app
|
|
205
|
-
// Point agents at the verified surface, not recalled APIs
|
|
204
|
+
sections.push("A full-stack nifra app - Web-standard, one app on Bun, Node, Deno, and the edge. This is the route INDEX; fetch a route's full body/query/response contract on demand (see below).",
|
|
205
|
+
// Point agents at the verified surface, not recalled APIs - nifra is young, so training data is the
|
|
206
206
|
// wrong source. The MCP tools (nifra_docs / nifra_example) are checked against the installed version.
|
|
207
|
-
"> **Build against THIS surface + the verified tools
|
|
207
|
+
"> **Build against THIS surface + the verified tools - do not write nifra APIs from memory** (the\n> framework is young; recalled APIs drift). For framework code, call `nifra_example` (snippets\n> typechecked against the installed version) or `nifra_docs`; verify edits with `nifra_run` + `nifra_check`.");
|
|
208
208
|
}
|
|
209
209
|
if (filter?.kind !== "pages") {
|
|
210
210
|
// Index → compact `METHOD path` list; a slice → the full schema + call-form detail.
|
|
@@ -217,7 +217,7 @@ export function describeProject(app, filter) {
|
|
|
217
217
|
return sections.join("\n\n");
|
|
218
218
|
}
|
|
219
219
|
/**
|
|
220
|
-
* The backend's API routes as structured JSON
|
|
220
|
+
* The backend's API routes as structured JSON - what the `nifra_routes` MCP tool returns, so an agent
|
|
221
221
|
* consumes the contract programmatically (`list_routes` / `get_route_schema`) instead of parsing the
|
|
222
222
|
* Markdown brief. Optionally filtered to routes whose path starts with `pathPrefix`. Reuses the exact
|
|
223
223
|
* same `call` form and TS-shaped contracts the Markdown brief shows.
|
|
@@ -261,7 +261,7 @@ function isUnderPrefix(path, prefix) {
|
|
|
261
261
|
return path === prefix || path.startsWith(`${prefix}/`);
|
|
262
262
|
}
|
|
263
263
|
/**
|
|
264
|
-
* Build the unified, sorted route table from page routes + backend API routes. Pure
|
|
264
|
+
* Build the unified, sorted route table from page routes + backend API routes. Pure - the caller
|
|
265
265
|
* supplies page `hasAction` (resolved via a module load) and the API descriptors (`backend.routes()`).
|
|
266
266
|
* Page routes serve `GET` (+ `POST` when they export an `action`); API routes carry their declared
|
|
267
267
|
* method, and are flagged `autoMounted` when at/under `apiPrefix`. Sorted by path, then kind, then the
|
|
@@ -342,7 +342,7 @@ export async function describeRoutes(app, opts = {}) {
|
|
|
342
342
|
manifest = discoverRoutes(app.routesDir);
|
|
343
343
|
}
|
|
344
344
|
catch {
|
|
345
|
-
// No (or unreadable) routes/
|
|
345
|
+
// No (or unreadable) routes/ - fall through with no pages; the table still shows API routes.
|
|
346
346
|
}
|
|
347
347
|
const pages = [];
|
|
348
348
|
for (const route of manifest?.routes ?? []) {
|
package/dist/learn.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guided learn path - a SEQUENCE for building a nifra app end to end, as opposed to the random-access
|
|
3
|
+
* search of `nifra_docs`/`nifra_example`. It is deliberately thin on embedded code: each step points at the
|
|
4
|
+
* tool that produces the CORRECT, verified artifact (`nifra_scaffold` for a route, `nifra_example` for a
|
|
5
|
+
* verified snippet, `nifra_run`/`nifra_render` to see it work), so an agent walks the path by composing the
|
|
6
|
+
* existing tools rather than pasting code that can drift from the installed version.
|
|
7
|
+
*
|
|
8
|
+
* One source, two heads: `nifra_learn` (MCP) serves it to an agent as structured steps; `nifra learn` (CLI)
|
|
9
|
+
* prints the same path for a human. Add or reorder steps here and both update.
|
|
10
|
+
*/
|
|
11
|
+
export interface LearnStep {
|
|
12
|
+
/** Stable slug, e.g. `page-route`. */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly title: string;
|
|
15
|
+
/** What you will have once this step is done. */
|
|
16
|
+
readonly goal: string;
|
|
17
|
+
/** How to do it - prefer directing to the tool that emits the correct artifact over inline code. */
|
|
18
|
+
readonly do: string;
|
|
19
|
+
/** How to confirm it worked. */
|
|
20
|
+
readonly verify: string;
|
|
21
|
+
/** The `nifra_*` tools that carry out this step. */
|
|
22
|
+
readonly tools: readonly string[];
|
|
23
|
+
/** A `nifra_docs`/`nifra_example` query for going deeper. */
|
|
24
|
+
readonly seeAlso?: string | undefined;
|
|
25
|
+
}
|
|
26
|
+
export declare const LEARN_PATH: readonly LearnStep[];
|
|
27
|
+
/**
|
|
28
|
+
* Render the learn path. No `step` (or an out-of-range one) returns the index; a valid 1-based `step`
|
|
29
|
+
* returns that step's detail. Kept pure so `nifra_learn` and `nifra learn` render identically.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderLearnResult(step?: number): string;
|
|
32
|
+
//# sourceMappingURL=learn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learn.d.ts","sourceRoot":"","sources":["../src/learn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oGAAoG;IACpG,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EA6E1C,CAAA;AAmCD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAQvD"}
|
package/dist/learn.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guided learn path - a SEQUENCE for building a nifra app end to end, as opposed to the random-access
|
|
3
|
+
* search of `nifra_docs`/`nifra_example`. It is deliberately thin on embedded code: each step points at the
|
|
4
|
+
* tool that produces the CORRECT, verified artifact (`nifra_scaffold` for a route, `nifra_example` for a
|
|
5
|
+
* verified snippet, `nifra_run`/`nifra_render` to see it work), so an agent walks the path by composing the
|
|
6
|
+
* existing tools rather than pasting code that can drift from the installed version.
|
|
7
|
+
*
|
|
8
|
+
* One source, two heads: `nifra_learn` (MCP) serves it to an agent as structured steps; `nifra learn` (CLI)
|
|
9
|
+
* prints the same path for a human. Add or reorder steps here and both update.
|
|
10
|
+
*/
|
|
11
|
+
export const LEARN_PATH = [
|
|
12
|
+
{
|
|
13
|
+
id: "create-app",
|
|
14
|
+
title: "Create the app",
|
|
15
|
+
goal: "A running nifra dev server with one page route.",
|
|
16
|
+
do: "Scaffold a new project with `npm create nifra@latest`, pick your framework adapter (React/Solid/Vue/Svelte/Preact), then `npm run dev`.",
|
|
17
|
+
verify: "The dev server prints a localhost URL and the default page renders.",
|
|
18
|
+
tools: ["nifra_context"],
|
|
19
|
+
seeAlso: "project structure routes backend",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "page-route",
|
|
23
|
+
title: "Add a page route",
|
|
24
|
+
goal: "A second page, server-rendered, reachable by URL.",
|
|
25
|
+
do: "Ask nifra_scaffold for the path (e.g. /about) - it returns the exact `routes/` file and a contract-correct page stub. File-based: `routes/about.tsx` serves `/about`.",
|
|
26
|
+
verify: "nifra_render /about returns the rendered HTML.",
|
|
27
|
+
tools: ["nifra_scaffold", "nifra_render"],
|
|
28
|
+
seeAlso: "page routes file-based routing layouts",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "loader",
|
|
32
|
+
title: "Load data for a page",
|
|
33
|
+
goal: "A page whose data is fetched on the server before render.",
|
|
34
|
+
do: "Export a `loader` from the route and read its result in the component. Get the exact shape from nifra_example ('loader').",
|
|
35
|
+
verify: "nifra_render the page and confirm the loaded data is in the HTML.",
|
|
36
|
+
tools: ["nifra_example", "nifra_render"],
|
|
37
|
+
seeAlso: "loader server data page",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "api-route",
|
|
41
|
+
title: "Add a typed API route",
|
|
42
|
+
goal: "A backend endpoint with a typed request/response contract.",
|
|
43
|
+
do: "Add the route to `backend.ts` with an @nifrajs/schema body/query/response schema. nifra_context shows the resulting contract and the exact typed-client call form.",
|
|
44
|
+
verify: "nifra_run { method, path, body } and check the status + parsed body; nifra_check confirms the contract typechecks.",
|
|
45
|
+
tools: ["nifra_context", "nifra_run", "nifra_check"],
|
|
46
|
+
seeAlso: "api route schema validation backend",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "typed-client",
|
|
50
|
+
title: "Call the API, fully typed",
|
|
51
|
+
goal: "The page calls the API through the compile-time typed client - no hand-written client twin.",
|
|
52
|
+
do: "Use @nifrajs/client against the backend type. The call form is exactly what nifra_context prints for that route; a drifted call is a compile error, not a runtime surprise.",
|
|
53
|
+
verify: "nifra_check passes; nifra_run the page path and see the data flow end to end.",
|
|
54
|
+
tools: ["nifra_context", "nifra_check", "nifra_run"],
|
|
55
|
+
seeAlso: "typed client treaty end-to-end types",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "protect",
|
|
59
|
+
title: "Protect a route",
|
|
60
|
+
goal: "A route that requires an authenticated session.",
|
|
61
|
+
do: "Wire @nifrajs/auth (or @nifrajs/better-auth) and gate the route. nifra_example ('protected route') has the verified pattern; nifra_assure reports whether the gate is actually enforced.",
|
|
62
|
+
verify: "nifra_run the route unauthenticated (expect 401/redirect) and authenticated (expect 200); nifra_assure shows the enforcement evidence.",
|
|
63
|
+
tools: ["nifra_example", "nifra_assure", "nifra_run"],
|
|
64
|
+
seeAlso: "auth session protected route better-auth",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "background-work",
|
|
68
|
+
title: "Do work in the background",
|
|
69
|
+
goal: "A request that enqueues a job instead of blocking on slow work.",
|
|
70
|
+
do: "Define a job with @nifrajs/jobs and enqueue it from a route. nifra_example ('job') has the shape; nifra_inspect shows the enqueue landing on the dev server.",
|
|
71
|
+
verify: "nifra_run the route, then nifra_inspect the dev server to see the request completed fast and the effect fired.",
|
|
72
|
+
tools: ["nifra_example", "nifra_run", "nifra_inspect"],
|
|
73
|
+
seeAlso: "jobs queue background enqueue",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "ship",
|
|
77
|
+
title: "Build and deploy",
|
|
78
|
+
goal: "A production build served by your chosen runtime.",
|
|
79
|
+
do: "Pick an adapter (@nifrajs/node, @nifrajs/workers, @nifrajs/deno, or a static/Vercel target) and run the production build. nifra_levels shows what the app already proves before you ship.",
|
|
80
|
+
verify: "The build completes and the artifact serves the app; nifra_check + nifra_levels are green.",
|
|
81
|
+
tools: ["nifra_levels", "nifra_check"],
|
|
82
|
+
seeAlso: "deploy build adapters node workers deno",
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
/** The compact index: numbered steps with their goal, plus how to drill into one. */
|
|
86
|
+
function renderIndex() {
|
|
87
|
+
const lines = LEARN_PATH.map((step, i) => `${i + 1}. ${step.title} - ${step.goal} [${step.id}]`);
|
|
88
|
+
return [
|
|
89
|
+
`nifra learn path (${LEARN_PATH.length} steps). Call nifra_learn with step: N for the detail of step N.`,
|
|
90
|
+
"",
|
|
91
|
+
...lines,
|
|
92
|
+
].join("\n");
|
|
93
|
+
}
|
|
94
|
+
/** One step's full detail, with a pointer to the next. */
|
|
95
|
+
function renderStep(step, index) {
|
|
96
|
+
const parts = [
|
|
97
|
+
`Step ${index + 1}/${LEARN_PATH.length}: ${step.title} [${step.id}]`,
|
|
98
|
+
"",
|
|
99
|
+
`Goal: ${step.goal}`,
|
|
100
|
+
`Do: ${step.do}`,
|
|
101
|
+
`Verify: ${step.verify}`,
|
|
102
|
+
`Tools: ${step.tools.join(", ")}`,
|
|
103
|
+
];
|
|
104
|
+
if (step.seeAlso !== undefined) {
|
|
105
|
+
parts.push(`See: nifra_docs / nifra_example "${step.seeAlso}"`);
|
|
106
|
+
}
|
|
107
|
+
const next = LEARN_PATH[index + 1];
|
|
108
|
+
parts.push("", next !== undefined
|
|
109
|
+
? `Next: step ${index + 2} (${next.title})`
|
|
110
|
+
: "Done - you have shipped a nifra app.");
|
|
111
|
+
return parts.join("\n");
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Render the learn path. No `step` (or an out-of-range one) returns the index; a valid 1-based `step`
|
|
115
|
+
* returns that step's detail. Kept pure so `nifra_learn` and `nifra learn` render identically.
|
|
116
|
+
*/
|
|
117
|
+
export function renderLearnResult(step) {
|
|
118
|
+
if (step === undefined)
|
|
119
|
+
return renderIndex();
|
|
120
|
+
const index = step - 1;
|
|
121
|
+
const found = LEARN_PATH[index];
|
|
122
|
+
if (found === undefined) {
|
|
123
|
+
return `No step ${step}. The path has ${LEARN_PATH.length} steps.\n\n${renderIndex()}`;
|
|
124
|
+
}
|
|
125
|
+
return renderStep(found, index);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=learn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learn.js","sourceRoot":"","sources":["../src/learn.ts"],"names":[],"mappings":"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"}
|
package/dist/levels-tool.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `nifra levels`
|
|
2
|
+
* `nifra levels` - the verification ladder, computed from gates that already exist:
|
|
3
3
|
*
|
|
4
|
-
* L0 typed contract
|
|
5
|
-
* L1 route assurance
|
|
6
|
-
* L2 capability lockfile
|
|
7
|
-
* L3 route manifest
|
|
8
|
-
* L4 invariant-tested
|
|
4
|
+
* L0 typed contract - `nifra check` passes (compiler-enforced frontend↔backend contract)
|
|
5
|
+
* L1 route assurance - `nifra.assurance.ts` present and every route satisfies its policy
|
|
6
|
+
* L2 capability lockfile - capability/effect assurance passes and the lockfile is in sync
|
|
7
|
+
* L3 route manifest - the emitted manifest hash-verifies and matches the current app
|
|
8
|
+
* L4 invariant-tested - every route passes through an explicitly isolated executor
|
|
9
9
|
*
|
|
10
|
-
* A level counts only when every level below it holds
|
|
10
|
+
* A level counts only when every level below it holds - the ladder is cumulative, so "L3" is one
|
|
11
11
|
* honest word for a whole posture. Levels are computed, never self-declared.
|
|
12
12
|
*/
|
|
13
13
|
export interface VerificationLevelStatus {
|
|
14
14
|
readonly level: number;
|
|
15
15
|
readonly name: string;
|
|
16
16
|
readonly ok: boolean;
|
|
17
|
-
/** Why the level does not hold
|
|
17
|
+
/** Why the level does not hold - a gate failure or a missing prerequisite. Empty when ok. */
|
|
18
18
|
readonly reasons: readonly string[];
|
|
19
19
|
}
|
|
20
20
|
export interface VerificationLevelsResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"levels-tool.d.ts","sourceRoot":"","sources":["../src/levels-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"levels-tool.d.ts","sourceRoot":"","sources":["../src/levels-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;CACpD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,iGAAiG;AACjG,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC,CAgJnC;AAqBD,2FAA2F;AAC3F,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GACtF,OAAO,CAAC,OAAO,CAAC,CAkBlB"}
|
package/dist/levels-tool.js
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `nifra levels`
|
|
2
|
+
* `nifra levels` - the verification ladder, computed from gates that already exist:
|
|
3
3
|
*
|
|
4
|
-
* L0 typed contract
|
|
5
|
-
* L1 route assurance
|
|
6
|
-
* L2 capability lockfile
|
|
7
|
-
* L3 route manifest
|
|
8
|
-
* L4 invariant-tested
|
|
4
|
+
* L0 typed contract - `nifra check` passes (compiler-enforced frontend↔backend contract)
|
|
5
|
+
* L1 route assurance - `nifra.assurance.ts` present and every route satisfies its policy
|
|
6
|
+
* L2 capability lockfile - capability/effect assurance passes and the lockfile is in sync
|
|
7
|
+
* L3 route manifest - the emitted manifest hash-verifies and matches the current app
|
|
8
|
+
* L4 invariant-tested - every route passes through an explicitly isolated executor
|
|
9
9
|
*
|
|
10
|
-
* A level counts only when every level below it holds
|
|
10
|
+
* A level counts only when every level below it holds - the ladder is cumulative, so "L3" is one
|
|
11
11
|
* honest word for a whole posture. Levels are computed, never self-declared.
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
import { resolve } from "node:path";
|
|
15
|
-
import { evaluateRouteAssurance } from "@nifrajs/core/assurance";
|
|
16
15
|
import { snapshotCapabilities } from "@nifrajs/core/capabilities";
|
|
17
16
|
import { buildNifraManifest, parseNifraManifest } from "@nifrajs/core/manifest";
|
|
18
17
|
import { runAdversarialContract } from "@nifrajs/testing";
|
|
19
|
-
import {
|
|
20
|
-
import { collectCapabilityProjectReport, diffCapabilitySnapshots, parseCapabilityLockfile, } from "./capabilities-tool.js";
|
|
21
|
-
import { collectCheckResult } from "./check.js";
|
|
18
|
+
import { diffCapabilitySnapshots, parseCapabilityLockfile, } from "./capabilities-tool.js";
|
|
22
19
|
import { DEFAULT_MANIFEST_FILE } from "./manifest-tool.js";
|
|
23
20
|
/** Compute the ladder. Each level is evaluated independently, then gated on the levels below. */
|
|
24
21
|
export async function collectVerificationLevels(cwd, options = {}) {
|
|
22
|
+
// The ladder is the fullest view over the one project verification: L0 is the check view, L1/L2 read
|
|
23
|
+
// the route-assurance + capability evidence the same collector already computed (no second walk), and
|
|
24
|
+
// only the ladder-specific L3 (manifest) and L4 (invariants) are evaluated here.
|
|
25
|
+
const { collectProjectVerification } = await import("./verification.js");
|
|
26
|
+
const verification = await collectProjectVerification(cwd, {
|
|
27
|
+
...(options.config !== undefined ? { config: options.config } : {}),
|
|
28
|
+
maxDiagnostics: 5,
|
|
29
|
+
});
|
|
25
30
|
const statuses = [];
|
|
26
|
-
// L0
|
|
27
|
-
const check = await
|
|
31
|
+
// L0 - typed contract (`nifra check`).
|
|
32
|
+
const check = await verification.check();
|
|
28
33
|
statuses.push({
|
|
29
34
|
level: 0,
|
|
30
35
|
name: "typed contract",
|
|
@@ -32,12 +37,10 @@ export async function collectVerificationLevels(cwd, options = {}) {
|
|
|
32
37
|
reasons: check.ok ? [] : check.diagnostics.map((d) => `${d.rule}: ${d.message}`).slice(0, 5),
|
|
33
38
|
});
|
|
34
39
|
// L1-L4 all hang off the assurance config; without it the ladder stops at L0 by definition.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
catch (err) {
|
|
40
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
40
|
+
if (verification.config === undefined) {
|
|
41
|
+
const reason = verification.configError instanceof Error
|
|
42
|
+
? verification.configError.message
|
|
43
|
+
: String(verification.configError);
|
|
41
44
|
for (const [level, name] of [
|
|
42
45
|
[1, "route assurance"],
|
|
43
46
|
[2, "capability lockfile"],
|
|
@@ -48,23 +51,23 @@ export async function collectVerificationLevels(cwd, options = {}) {
|
|
|
48
51
|
}
|
|
49
52
|
return finalize(statuses);
|
|
50
53
|
}
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
const config = verification.config;
|
|
55
|
+
// L1 - route assurance.
|
|
56
|
+
const assurance = verification.routeAssurance;
|
|
53
57
|
statuses.push({
|
|
54
58
|
level: 1,
|
|
55
59
|
name: "route assurance",
|
|
56
60
|
ok: assurance.ok,
|
|
57
61
|
reasons: assurance.findings.map((f) => f.message).slice(0, 5),
|
|
58
62
|
});
|
|
59
|
-
// L2
|
|
63
|
+
// L2 - capability assurance + lockfile in sync.
|
|
60
64
|
const l2Reasons = [];
|
|
61
|
-
|
|
65
|
+
const capabilityReport = verification.capability?.report;
|
|
62
66
|
if (config.capabilities === undefined) {
|
|
63
67
|
l2Reasons.push("nifra.assurance.ts declares no capabilities policy");
|
|
64
68
|
}
|
|
65
69
|
else {
|
|
66
|
-
const project =
|
|
67
|
-
capabilityReport = project.report;
|
|
70
|
+
const project = verification.capability;
|
|
68
71
|
if (!project.report.ok) {
|
|
69
72
|
l2Reasons.push(...project.report.findings.map((f) => f.message).slice(0, 5));
|
|
70
73
|
}
|
|
@@ -85,11 +88,11 @@ export async function collectVerificationLevels(cwd, options = {}) {
|
|
|
85
88
|
ok: l2Reasons.length === 0,
|
|
86
89
|
reasons: l2Reasons,
|
|
87
90
|
});
|
|
88
|
-
// L3
|
|
91
|
+
// L3 - emitted manifest hash-verifies AND matches the current app.
|
|
89
92
|
const l3Reasons = [];
|
|
90
93
|
const manifestPath = resolve(cwd, config.manifest?.path ?? DEFAULT_MANIFEST_FILE);
|
|
91
94
|
if (!existsSync(manifestPath)) {
|
|
92
|
-
l3Reasons.push(`manifest missing (${manifestPath})
|
|
95
|
+
l3Reasons.push(`manifest missing (${manifestPath}) - run \`nifra manifest emit\``);
|
|
93
96
|
}
|
|
94
97
|
else {
|
|
95
98
|
try {
|
|
@@ -101,7 +104,7 @@ export async function collectVerificationLevels(cwd, options = {}) {
|
|
|
101
104
|
...(capabilityReport !== undefined ? { capabilities: capabilityReport } : {}),
|
|
102
105
|
});
|
|
103
106
|
if (current.contentHash !== recorded.contentHash) {
|
|
104
|
-
l3Reasons.push("manifest is stale
|
|
107
|
+
l3Reasons.push("manifest is stale - the app changed since it was emitted");
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
110
|
else {
|
|
@@ -118,10 +121,10 @@ export async function collectVerificationLevels(cwd, options = {}) {
|
|
|
118
121
|
ok: l3Reasons.length === 0,
|
|
119
122
|
reasons: l3Reasons,
|
|
120
123
|
});
|
|
121
|
-
// L4
|
|
124
|
+
// L4 - contract-generated invariants against an explicitly isolated executor.
|
|
122
125
|
const l4Reasons = [];
|
|
123
126
|
if (config.invariants === undefined) {
|
|
124
|
-
l4Reasons.push("no isolated invariant executor configured
|
|
127
|
+
l4Reasons.push("no isolated invariant executor configured - add `invariants.executor` in nifra.assurance.ts");
|
|
125
128
|
}
|
|
126
129
|
else {
|
|
127
130
|
const invariants = await runAdversarialContract(config.source, {
|
package/dist/levels-tool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"levels-tool.js","sourceRoot":"","sources":["../src/levels-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"levels-tool.js","sourceRoot":"","sources":["../src/levels-tool.ts"],"names":[],"mappings":"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"}
|