@nifrajs/cli 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +25 -25
  2. package/dist/assure.d.ts +2 -2
  3. package/dist/assure.d.ts.map +1 -1
  4. package/dist/assure.js +17 -10
  5. package/dist/assure.js.map +1 -1
  6. package/dist/capabilities-tool.d.ts +7 -0
  7. package/dist/capabilities-tool.d.ts.map +1 -1
  8. package/dist/capabilities-tool.js +68 -16
  9. package/dist/capabilities-tool.js.map +1 -1
  10. package/dist/check.d.ts +61 -22
  11. package/dist/check.d.ts.map +1 -1
  12. package/dist/check.js +332 -86
  13. package/dist/check.js.map +1 -1
  14. package/dist/cli.d.ts +30 -2
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +132 -36
  17. package/dist/cli.js.map +1 -1
  18. package/dist/diff-tool.d.ts +3 -3
  19. package/dist/diff-tool.js +7 -7
  20. package/dist/docs-search.d.ts +3 -3
  21. package/dist/docs-search.js +6 -6
  22. package/dist/doctor.d.ts +3 -3
  23. package/dist/doctor.js +8 -8
  24. package/dist/examples.d.ts +5 -5
  25. package/dist/examples.js +5 -5
  26. package/dist/init-agents.d.ts +14 -14
  27. package/dist/init-agents.js +14 -14
  28. package/dist/internal/typescript-import.d.ts +24 -0
  29. package/dist/internal/typescript-import.d.ts.map +1 -0
  30. package/dist/internal/typescript-import.js +32 -0
  31. package/dist/internal/typescript-import.js.map +1 -0
  32. package/dist/introspect.d.ts +12 -12
  33. package/dist/introspect.js +24 -24
  34. package/dist/levels-tool.d.ts +8 -8
  35. package/dist/levels-tool.d.ts.map +1 -1
  36. package/dist/levels-tool.js +33 -30
  37. package/dist/levels-tool.js.map +1 -1
  38. package/dist/load.d.ts +6 -6
  39. package/dist/load.d.ts.map +1 -1
  40. package/dist/load.js +15 -7
  41. package/dist/load.js.map +1 -1
  42. package/dist/manifest-tool.d.ts +1 -1
  43. package/dist/manifest-tool.d.ts.map +1 -1
  44. package/dist/manifest-tool.js +4 -2
  45. package/dist/manifest-tool.js.map +1 -1
  46. package/dist/mcp-docs-tools.d.ts +1 -1
  47. package/dist/mcp-docs-tools.js +9 -9
  48. package/dist/mcp-http.d.ts +5 -5
  49. package/dist/mcp-http.js +7 -7
  50. package/dist/mcp-protocol.d.ts +2 -2
  51. package/dist/mcp-protocol.js +2 -2
  52. package/dist/mcp-render.d.ts +3 -3
  53. package/dist/mcp-render.js +6 -6
  54. package/dist/mcp-run.d.ts +2 -2
  55. package/dist/mcp-run.js +2 -2
  56. package/dist/mcp.d.ts +14 -14
  57. package/dist/mcp.d.ts.map +1 -1
  58. package/dist/mcp.js +75 -45
  59. package/dist/mcp.js.map +1 -1
  60. package/dist/openapi-tool.d.ts +1 -1
  61. package/dist/openapi-tool.js +2 -2
  62. package/dist/pipeline-guard.d.ts +1 -1
  63. package/dist/pipeline-guard.js +1 -1
  64. package/dist/port.d.ts +9 -9
  65. package/dist/port.js +27 -27
  66. package/dist/recipes/1.8.0.d.ts +2 -2
  67. package/dist/recipes/1.8.0.js +5 -5
  68. package/dist/recipes/index.d.ts +2 -2
  69. package/dist/recipes/index.js +1 -1
  70. package/dist/scaffold.d.ts +3 -3
  71. package/dist/scaffold.js +8 -8
  72. package/dist/sync-routes.d.ts +28 -0
  73. package/dist/sync-routes.d.ts.map +1 -0
  74. package/dist/sync-routes.js +55 -0
  75. package/dist/sync-routes.js.map +1 -0
  76. package/dist/types-search.d.ts +5 -5
  77. package/dist/types-search.js +7 -7
  78. package/dist/upgrade.d.ts +7 -7
  79. package/dist/upgrade.js +12 -12
  80. package/dist/verification.d.ts +51 -0
  81. package/dist/verification.d.ts.map +1 -0
  82. package/dist/verification.js +80 -0
  83. package/dist/verification.js.map +1 -0
  84. package/docs/examples.json +174 -90
  85. package/docs/llms-full.txt +1696 -747
  86. package/docs/types.json +1892 -3105
  87. package/package.json +22 -11
  88. package/src/assure.ts +17 -18
  89. package/src/capabilities-tool.ts +82 -16
  90. package/src/check.ts +405 -103
  91. package/src/cli.ts +141 -38
  92. package/src/diff-tool.ts +8 -8
  93. package/src/docs-search.ts +6 -6
  94. package/src/doctor.ts +10 -10
  95. package/src/examples.ts +7 -7
  96. package/src/init-agents.ts +17 -17
  97. package/src/internal/typescript-import.ts +33 -0
  98. package/src/introspect.ts +28 -28
  99. package/src/levels-tool.ts +37 -31
  100. package/src/load.ts +21 -12
  101. package/src/manifest-tool.ts +4 -2
  102. package/src/mcp-docs-tools.ts +9 -9
  103. package/src/mcp-http.ts +7 -7
  104. package/src/mcp-protocol.ts +2 -2
  105. package/src/mcp-render.ts +6 -6
  106. package/src/mcp-run.ts +2 -2
  107. package/src/mcp.ts +82 -45
  108. package/src/openapi-tool.ts +2 -2
  109. package/src/pipeline-guard.ts +1 -1
  110. package/src/port.ts +31 -31
  111. package/src/recipes/1.8.0.ts +5 -5
  112. package/src/recipes/index.ts +2 -2
  113. package/src/scaffold.ts +8 -8
  114. package/src/sync-routes.ts +69 -0
  115. package/src/types-search.ts +8 -8
  116. package/src/upgrade.ts +12 -12
  117. package/src/verification.ts +123 -0
package/dist/scaffold.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `nifra_scaffold` — turn a URL path into the correct `routes/` file (the convention an agent most often
2
+ * `nifra_scaffold` - turn a URL path into the correct `routes/` file (the convention an agent most often
3
3
  * gets wrong) + a minimal, contract-correct stub. The mapping is the inverse of @nifrajs/web's
4
4
  * `filePathToPatterns`: `:id`/`[id]` → `[id]`, `*rest`/`[...rest]` → `[...rest]`, `[[lang]]` optional,
5
5
  * `/` → `index`. The framework (→ file extension) comes from the project's `clientModule`.
6
6
  *
7
- * Page stubs are emitted only for the JSX family (react/preact/solid — one shared, verified shape);
7
+ * Page stubs are emitted only for the JSX family (react/preact/solid - one shared, verified shape);
8
8
  * for vue/svelte/vanilla we return the correct PATH + the route-module contract and point at
9
9
  * `nifra_example` for the body, rather than hand-writing an SFC we can't typecheck here.
10
10
  */
@@ -49,17 +49,17 @@ export function routePathToFile(urlPath, ext) {
49
49
  return `routes/${segments.map(segmentToFile).join("/")}.${ext}`;
50
50
  }
51
51
  const ROUTE_CONTRACT = `A route module may export:
52
- - \`export default function Page(props: { data: LoaderData<typeof loader> }) { … }\` — the page component.
53
- - \`export async function loader({ params, request, api }: LoaderArgs<typeof backend>) { … }\` — server-only; data for SSR. Reach the backend via \`api\` (typed) / DB via the backend, NEVER a top-level server-only import.
54
- - \`export async function action({ request, api }: ActionArgs<typeof backend>) { … }\` — server-only; handles the form POST.
55
- - \`export const meta = { title, meta:[…] }\` — head tags.
52
+ - \`export default function Page(props: { data: LoaderData<typeof loader> }) { … }\` - the page component.
53
+ - \`export async function loader({ params, request, api }: LoaderArgs<typeof backend>) { … }\` - server-only; data for SSR. Reach the backend via \`api\` (typed) / DB via the backend, NEVER a top-level server-only import.
54
+ - \`export async function action({ request, api }: ActionArgs<typeof backend>) { … }\` - server-only; handles the form POST.
55
+ - \`export const meta = { title, meta:[…] }\` - head tags.
56
56
  Path params are typed on \`params\`.`;
57
57
  function jsxStub(file, params) {
58
58
  const paramsNote = params.length > 0 ? `params.${params.join(", params.")}` : "no path params on this route";
59
59
  const loaderLine = params.length > 0
60
60
  ? `// export async function loader({ params, api }: LoaderArgs<typeof backend>) { return { /* fetch by ${params[0]} */ } }`
61
61
  : `// export async function loader({ api }: LoaderArgs<typeof backend>) { return {} }`;
62
- return `// ${file} — server-only loader/action allowed; never top-level-import server-only code (DB/secrets).
62
+ return `// ${file} - server-only loader/action allowed; never top-level-import server-only code (DB/secrets).
63
63
  // Available here: ${paramsNote}. Fetch data in a loader via the typed \`api\`; see nifra_example("loader").
64
64
  ${loaderLine}
65
65
  export default function Page() {
@@ -125,7 +125,7 @@ export async function writeScaffoldRoute(cwd, urlPath, framework) {
125
125
  }
126
126
  return { ...result, written: true };
127
127
  }
128
- /** Render the tool result as markdown — the file path, the stub (if any), and the contract note. */
128
+ /** Render the tool result as markdown - the file path, the stub (if any), and the contract note. */
129
129
  export function renderScaffold(urlPath, framework) {
130
130
  let r;
131
131
  try {
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `nifra sync-routes` - regenerate the route-search types (`nifra-routes.d.ts`) from the current `routes/`
3
+ * tree, so `navigate({ to, search })` is typed against each static route's `searchSchema`. It runs
4
+ * `@nifrajs/web`'s `generateRouteSearchTypes` over the discovered routes and writes the `.d.ts` at the
5
+ * project root; include it in your tsconfig's `include`. Because it is regenerated from the route files, a
6
+ * stale navigate search shape becomes a `tsc` error - run it after adding/renaming a route or changing a
7
+ * `searchSchema` (a pure file write, no build). A dynamic route (`:param`/catch-all) is omitted, since its
8
+ * pattern is not a concrete `to`; those keep the loose object form.
9
+ */
10
+ /** The committed route-types file `sync-routes` writes, so `nifra check` can recognise + drift-check it. */
11
+ export declare const ROUTE_TYPES_FILE = "nifra-routes.d.ts";
12
+ export interface SyncRoutesResult {
13
+ /** The written `.d.ts` (relative to the project root). */
14
+ readonly file: string;
15
+ /** True when the file's content changed and it was rewritten. */
16
+ readonly changed: boolean;
17
+ /** How many static routes got a typed entry. */
18
+ readonly typedRoutes: number;
19
+ }
20
+ /**
21
+ * Generate the route-search types `.d.ts` from `<cwd>/routes` (the nifra convention). Returns `null` when
22
+ * there is no `routes/` directory (an API-only project - nothing to type). Never loads `framework.ts`: it
23
+ * needs only the route tree, so it works before a build and on a broken framework config.
24
+ */
25
+ export declare function syncRouteTypes(cwd: string): Promise<SyncRoutesResult | null>;
26
+ /** CLI entry: write/refresh the route-types file and print a one-line summary + the tsconfig reminder. */
27
+ export declare function runSyncRoutes(cwd: string): Promise<boolean>;
28
+ //# sourceMappingURL=sync-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-routes.d.ts","sourceRoot":"","sources":["../src/sync-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,4GAA4G;AAC5G,eAAO,MAAM,gBAAgB,sBAAsB,CAAA;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAkBlF;AAED,0GAA0G;AAC1G,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBjE"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `nifra sync-routes` - regenerate the route-search types (`nifra-routes.d.ts`) from the current `routes/`
3
+ * tree, so `navigate({ to, search })` is typed against each static route's `searchSchema`. It runs
4
+ * `@nifrajs/web`'s `generateRouteSearchTypes` over the discovered routes and writes the `.d.ts` at the
5
+ * project root; include it in your tsconfig's `include`. Because it is regenerated from the route files, a
6
+ * stale navigate search shape becomes a `tsc` error - run it after adding/renaming a route or changing a
7
+ * `searchSchema` (a pure file write, no build). A dynamic route (`:param`/catch-all) is omitted, since its
8
+ * pattern is not a concrete `to`; those keep the loose object form.
9
+ */
10
+ import { existsSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ /** The committed route-types file `sync-routes` writes, so `nifra check` can recognise + drift-check it. */
13
+ export const ROUTE_TYPES_FILE = "nifra-routes.d.ts";
14
+ /**
15
+ * Generate the route-search types `.d.ts` from `<cwd>/routes` (the nifra convention). Returns `null` when
16
+ * there is no `routes/` directory (an API-only project - nothing to type). Never loads `framework.ts`: it
17
+ * needs only the route tree, so it works before a build and on a broken framework config.
18
+ */
19
+ export async function syncRouteTypes(cwd) {
20
+ const routesDir = join(cwd, "routes");
21
+ if (!existsSync(routesDir))
22
+ return null;
23
+ const { discoverRoutes } = await import("@nifrajs/web/fs");
24
+ const { generateRouteSearchTypes } = await import("@nifrajs/web");
25
+ const manifest = discoverRoutes(routesDir);
26
+ const code = generateRouteSearchTypes(manifest, {
27
+ // A route file is relative to routesDir (`reports.tsx`, `users/index.tsx`); the `.d.ts` sits at the
28
+ // project root, so the `typeof import(...)` specifier is `./routes/<file без extension>`.
29
+ resolve: (file) => `./routes/${file.replace(/\.[^./]+$/, "")}`,
30
+ });
31
+ const outFile = join(cwd, ROUTE_TYPES_FILE);
32
+ const existing = await Bun.file(outFile)
33
+ .text()
34
+ .catch(() => "");
35
+ const changed = existing !== code;
36
+ if (changed)
37
+ await Bun.write(outFile, code);
38
+ return { file: ROUTE_TYPES_FILE, changed, typedRoutes: (code.match(/: SearchOf</g) ?? []).length };
39
+ }
40
+ /** CLI entry: write/refresh the route-types file and print a one-line summary + the tsconfig reminder. */
41
+ export async function runSyncRoutes(cwd) {
42
+ const result = await syncRouteTypes(cwd);
43
+ if (result === null) {
44
+ console.log("nifra sync-routes: no routes/ directory found. Run from your project root.");
45
+ return true;
46
+ }
47
+ const n = result.typedRoutes;
48
+ const routes = `${n} typed route${n === 1 ? "" : "s"}`;
49
+ console.log(result.changed
50
+ ? `✓ wrote ${result.file} (${routes})`
51
+ : `• ${result.file} already in sync (${routes})`);
52
+ console.log(" Include it in your tsconfig's `include` so `navigate({ to, search })` is typed per route.");
53
+ return true;
54
+ }
55
+ //# sourceMappingURL=sync-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-routes.js","sourceRoot":"","sources":["../src/sync-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,4GAA4G;AAC5G,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CAAA;AAWnD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IACrC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAA;IACvC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;IAC1D,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAA;IACjE,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,CAAA;IAC1C,MAAM,IAAI,GAAG,wBAAwB,CAAC,QAAQ,EAAE;QAC9C,oGAAoG;QACpG,0FAA0F;QAC1F,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE;KAC/D,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAA;IAC3C,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;SACrC,IAAI,EAAE;SACN,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IAClB,MAAM,OAAO,GAAG,QAAQ,KAAK,IAAI,CAAA;IACjC,IAAI,OAAO;QAAE,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC3C,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;AACpG,CAAC;AAED,0GAA0G;AAC1G,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,4EAA4E,CAAC,CAAA;QACzF,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,CAAA;IAC5B,MAAM,MAAM,GAAG,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;IACtD,OAAO,CAAC,GAAG,CACT,MAAM,CAAC,OAAO;QACZ,CAAC,CAAC,WAAW,MAAM,CAAC,IAAI,KAAK,MAAM,GAAG;QACtC,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,qBAAqB,MAAM,GAAG,CACnD,CAAA;IACD,OAAO,CAAC,GAAG,CACT,6FAA6F,CAC9F,CAAA;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
- * `nifra_types` — the EXACT TypeScript of every exported `@nifrajs/*` symbol. The corpus is `types.json`
2
+ * `nifra_types` - the EXACT TypeScript of every exported `@nifrajs/*` symbol. The corpus is `types.json`
3
3
  * (written into this package by `bun run gen:llms`): each entry's `signature` is the literal declaration
4
- * parsed from the package's BUILT `.d.ts` with the TS compiler — a clean signature, never prose, never
4
+ * parsed from the package's BUILT `.d.ts` with the TS compiler - a clean signature, never prose, never
5
5
  * truncated. So an agent that needs the precise shape of a type (`RateLimitStore`, `RouteSchema`, a
6
6
  * function signature) gets the authoritative source here and never has to read a `.d.ts` file.
7
7
  *
@@ -12,7 +12,7 @@ export interface TypeEntry {
12
12
  readonly name: string;
13
13
  readonly kind: "interface" | "type" | "class" | "function" | "enum" | "const";
14
14
  readonly package: string;
15
- /** The literal declaration text from the `.d.ts` — a clean signature, no implementation. */
15
+ /** The literal declaration text from the `.d.ts` - a clean signature, no implementation. */
16
16
  readonly signature: string;
17
17
  /** The declaration's JSDoc block, if any. */
18
18
  readonly doc?: string;
@@ -20,7 +20,7 @@ export interface TypeEntry {
20
20
  export interface ScoredType extends TypeEntry {
21
21
  readonly score: number;
22
22
  }
23
- /** Exact (case-insensitive) name lookup — returns every declaration with that name (a name can exist in
23
+ /** Exact (case-insensitive) name lookup - returns every declaration with that name (a name can exist in
24
24
  * more than one package). */
25
25
  export declare function lookupType(types: readonly TypeEntry[], name: string): TypeEntry[];
26
26
  /** Keyword-score types: a term in the name is worth far more than one in the signature/doc body. */
@@ -28,7 +28,7 @@ export declare function searchTypes(types: readonly TypeEntry[], query: string,
28
28
  /**
29
29
  * Render the tool result. `name` → the exact declaration(s); else `query` → the top matches; else a
30
30
  * per-package index of type names. The signatures are authoritative (generated from the built `.d.ts`),
31
- * so this is the complete answer — there is no need to read a `.d.ts` file.
31
+ * so this is the complete answer - there is no need to read a `.d.ts` file.
32
32
  */
33
33
  export declare function renderTypesResult(types: readonly TypeEntry[], name: string | undefined, query: string | undefined, limit: number, full?: boolean): string;
34
34
  /** Load the bundled type corpus (`docs/types.json`), resolved relative to this module so it works from
@@ -1,7 +1,7 @@
1
1
  /**
2
- * `nifra_types` — the EXACT TypeScript of every exported `@nifrajs/*` symbol. The corpus is `types.json`
2
+ * `nifra_types` - the EXACT TypeScript of every exported `@nifrajs/*` symbol. The corpus is `types.json`
3
3
  * (written into this package by `bun run gen:llms`): each entry's `signature` is the literal declaration
4
- * parsed from the package's BUILT `.d.ts` with the TS compiler — a clean signature, never prose, never
4
+ * parsed from the package's BUILT `.d.ts` with the TS compiler - a clean signature, never prose, never
5
5
  * truncated. So an agent that needs the precise shape of a type (`RateLimitStore`, `RouteSchema`, a
6
6
  * function signature) gets the authoritative source here and never has to read a `.d.ts` file.
7
7
  *
@@ -9,7 +9,7 @@
9
9
  * signature/doc body; no args → a per-package index of the available type names.
10
10
  */
11
11
  import { countBodyHits, queryTermGroups, tokenize, tokenSetHas } from "./search-terms.js";
12
- /** Exact (case-insensitive) name lookup — returns every declaration with that name (a name can exist in
12
+ /** Exact (case-insensitive) name lookup - returns every declaration with that name (a name can exist in
13
13
  * more than one package). */
14
14
  export function lookupType(types, name) {
15
15
  const wanted = name.trim().toLowerCase();
@@ -36,7 +36,7 @@ export function searchTypes(types, query, limit) {
36
36
  return scored.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)).slice(0, limit);
37
37
  }
38
38
  function renderEntry(t) {
39
- const head = `## ${t.name} — ${t.kind}, \`${t.package}\``;
39
+ const head = `## ${t.name} - ${t.kind}, \`${t.package}\``;
40
40
  const doc = t.doc ? `${t.doc}\n` : "";
41
41
  return `${head}\n\n\`\`\`ts\n${doc}${t.signature}\n\`\`\``;
42
42
  }
@@ -76,7 +76,7 @@ function collapseSignature(signature) {
76
76
  }
77
77
  /** A search hit: enough to choose, not the whole declaration. */
78
78
  function renderSearchHit(t) {
79
- const head = `## ${t.name} — ${t.kind}, \`${t.package}\``;
79
+ const head = `## ${t.name} - ${t.kind}, \`${t.package}\``;
80
80
  const summary = summarize(t.doc);
81
81
  const signature = t.signature.length <= SEARCH_SIGNATURE_MAX ? t.signature : collapseSignature(t.signature);
82
82
  const note = t.signature.length <= SEARCH_SIGNATURE_MAX
@@ -87,7 +87,7 @@ function renderSearchHit(t) {
87
87
  /**
88
88
  * Render the tool result. `name` → the exact declaration(s); else `query` → the top matches; else a
89
89
  * per-package index of type names. The signatures are authoritative (generated from the built `.d.ts`),
90
- * so this is the complete answer — there is no need to read a `.d.ts` file.
90
+ * so this is the complete answer - there is no need to read a `.d.ts` file.
91
91
  */
92
92
  export function renderTypesResult(types, name, query, limit, full = false) {
93
93
  if (name !== undefined && name.trim() !== "") {
@@ -115,7 +115,7 @@ export function renderTypesResult(types, name, query, limit, full = false) {
115
115
  .sort(([a], [b]) => a.localeCompare(b))
116
116
  .map(([pkg, names]) => `- **${pkg}**: ${names.sort().join(", ")}`)
117
117
  .join("\n");
118
- return `# nifra types — index (${types.length} exported symbols; pass \`name\` for the exact declaration, or \`query\` to search)\n\nSignatures are generated from the built \`.d.ts\` — authoritative + complete. Use this instead of reading any \`.d.ts\` file.\n\n${index}`;
118
+ return `# nifra types - index (${types.length} exported symbols; pass \`name\` for the exact declaration, or \`query\` to search)\n\nSignatures are generated from the built \`.d.ts\` - authoritative + complete. Use this instead of reading any \`.d.ts\` file.\n\n${index}`;
119
119
  }
120
120
  /** Load the bundled type corpus (`docs/types.json`), resolved relative to this module so it works from
121
121
  * `src/` (dev) and `dist/` (published) alike. */
package/dist/upgrade.d.ts CHANGED
@@ -1,20 +1,20 @@
1
1
  /**
2
- * `nifra upgrade <version>` — an executable, per-release upgrade runner. Framework and shared-package
2
+ * `nifra upgrade <version>` - an executable, per-release upgrade runner. Framework and shared-package
3
3
  * bumps otherwise spread the same mechanical edits across every consuming app by hand; a recipe turns
4
4
  * them into `detect → transform → verify`:
5
5
  *
6
- * 1. **pin sweep** — set every matching dependency to the target version across the workspace's
6
+ * 1. **pin sweep** - set every matching dependency to the target version across the workspace's
7
7
  * package.json files, preserving the caret/tilde/exact style and skipping `workspace:`/`link:` specs.
8
- * 2. **dependency moves** — replace removed packages with their supported successor without
8
+ * 2. **dependency moves** - replace removed packages with their supported successor without
9
9
  * reserializing package.json or leaving duplicate dependency keys.
10
- * 3. **import moves** — rewrite exact import specifiers to their updated module paths.
11
- * 4. **verify** — reuse the existing `nifra check` gate; no new verification surface.
10
+ * 3. **import moves** - rewrite exact import specifiers to their updated module paths.
11
+ * 4. **verify** - reuse the existing `nifra check` gate; no new verification surface.
12
12
  *
13
13
  * Dry-run by default (prints the plan, writes nothing); `--write` applies. Fail-closed on an unknown
14
14
  * target version or a missing package.json, and deterministic (same repo + target → same edits).
15
15
  *
16
16
  * Deliberately NOT a codemod engine: transforms are string/specifier-level only. Structural (AST)
17
- * transforms are a future addition — a recipe that needs one is the signal to add the engine, not before.
17
+ * transforms are a future addition - a recipe that needs one is the signal to add the engine, not before.
18
18
  */
19
19
  import { type UpgradeRecipe } from "./recipes/index.js";
20
20
  export interface UpgradeOptions {
@@ -64,7 +64,7 @@ export interface UpgradePlan {
64
64
  */
65
65
  export declare function rewriteVersionSpec(spec: string, toVersion: string): string | null;
66
66
  /**
67
- * Apply pin rules to one package.json's TEXT (format-preserving — targeted string replaces, never a
67
+ * Apply pin rules to one package.json's TEXT (format-preserving - targeted string replaces, never a
68
68
  * JSON re-serialize that would reorder keys or drop comments-as-formatting). Returns the new text and
69
69
  * the changes made.
70
70
  */
package/dist/upgrade.js CHANGED
@@ -1,20 +1,20 @@
1
1
  /**
2
- * `nifra upgrade <version>` — an executable, per-release upgrade runner. Framework and shared-package
2
+ * `nifra upgrade <version>` - an executable, per-release upgrade runner. Framework and shared-package
3
3
  * bumps otherwise spread the same mechanical edits across every consuming app by hand; a recipe turns
4
4
  * them into `detect → transform → verify`:
5
5
  *
6
- * 1. **pin sweep** — set every matching dependency to the target version across the workspace's
6
+ * 1. **pin sweep** - set every matching dependency to the target version across the workspace's
7
7
  * package.json files, preserving the caret/tilde/exact style and skipping `workspace:`/`link:` specs.
8
- * 2. **dependency moves** — replace removed packages with their supported successor without
8
+ * 2. **dependency moves** - replace removed packages with their supported successor without
9
9
  * reserializing package.json or leaving duplicate dependency keys.
10
- * 3. **import moves** — rewrite exact import specifiers to their updated module paths.
11
- * 4. **verify** — reuse the existing `nifra check` gate; no new verification surface.
10
+ * 3. **import moves** - rewrite exact import specifiers to their updated module paths.
11
+ * 4. **verify** - reuse the existing `nifra check` gate; no new verification surface.
12
12
  *
13
13
  * Dry-run by default (prints the plan, writes nothing); `--write` applies. Fail-closed on an unknown
14
14
  * target version or a missing package.json, and deterministic (same repo + target → same edits).
15
15
  *
16
16
  * Deliberately NOT a codemod engine: transforms are string/specifier-level only. Structural (AST)
17
- * transforms are a future addition — a recipe that needs one is the signal to add the engine, not before.
17
+ * transforms are a future addition - a recipe that needs one is the signal to add the engine, not before.
18
18
  */
19
19
  import { readFileSync, writeFileSync } from "node:fs";
20
20
  import { join, relative } from "node:path";
@@ -26,8 +26,8 @@ const DEP_FIELDS = [
26
26
  "peerDependencies",
27
27
  "optionalDependencies",
28
28
  ];
29
- // Only a bare semver spec is rewritten. Anything else — workspace:*, link:/file:, npm: aliases, git
30
- // urls, "*", "latest", or a multi-part range — is intentionally left untouched (skipped, not guessed).
29
+ // Only a bare semver spec is rewritten. Anything else - workspace:*, link:/file:, npm: aliases, git
30
+ // urls, "*", "latest", or a multi-part range - is intentionally left untouched (skipped, not guessed).
31
31
  const SEMVER_SPEC = /^([\^~]|>=|<=|>|<|=)?\s*(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
32
32
  /**
33
33
  * Rewrite a dependency version spec to `toVersion`, preserving the range operator (`^`, `~`, …).
@@ -43,7 +43,7 @@ export function rewriteVersionSpec(spec, toVersion) {
43
43
  }
44
44
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
45
45
  /**
46
- * Apply pin rules to one package.json's TEXT (format-preserving — targeted string replaces, never a
46
+ * Apply pin rules to one package.json's TEXT (format-preserving - targeted string replaces, never a
47
47
  * JSON re-serialize that would reorder keys or drop comments-as-formatting). Returns the new text and
48
48
  * the changes made.
49
49
  */
@@ -268,7 +268,7 @@ function renderPlan(plan, write) {
268
268
  if (plan.pins.length === 0 &&
269
269
  plan.dependencyMoves.length === 0 &&
270
270
  plan.importMoves.length === 0) {
271
- lines.push("Already up to date — no changes.");
271
+ lines.push("Already up to date - no changes.");
272
272
  return lines.join("\n");
273
273
  }
274
274
  if (plan.dependencyMoves.length > 0) {
@@ -325,7 +325,7 @@ export async function runUpgrade(cwd, options) {
325
325
  readFileSync(join(cwd, "package.json"), "utf8");
326
326
  }
327
327
  catch {
328
- console.error(`[nifra] no package.json in ${relative(process.cwd(), cwd) || "."} — run from a project root`);
328
+ console.error(`[nifra] no package.json in ${relative(process.cwd(), cwd) || "."} - run from a project root`);
329
329
  return false;
330
330
  }
331
331
  const write = options.write === true;
@@ -342,7 +342,7 @@ export async function runUpgrade(cwd, options) {
342
342
  const ok = await runCheck(cwd, { json: false });
343
343
  if (!ok) {
344
344
  if (!options.json)
345
- console.error("\n[nifra] upgrade applied but `nifra check` failed — review above.");
345
+ console.error("\n[nifra] upgrade applied but `nifra check` failed - review above.");
346
346
  return false;
347
347
  }
348
348
  }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * One project verification, four commands. `nifra check`, `nifra assure`, `nifra levels`, and
3
+ * `nifra capabilities` are not separate audits - they are renderings of the same reflected project: the
4
+ * typed-contract scan, the route-assurance evaluation, and the static capability provenance. Historically
5
+ * each command re-derived the slice it needed, so a caller had to know which command ran which policy (and
6
+ * `levels` paid for the capability walk twice). `collectProjectVerification` runs each underlying policy
7
+ * exactly once and hands back a single value; the commands become thin formatters over subsets of it -
8
+ * `check`/`assure`/`levels` read their view directly, and `capabilities snapshot`/`check` read the same
9
+ * capability report before writing or diffing the lockfile.
10
+ *
11
+ * The typed-contract check is the one heavy pass (source walk + optional `tsc`), and `nifra assure`
12
+ * never needs it, so it is exposed as a lazily-invoked `check()` rather than computed up front.
13
+ */
14
+ import { type AssuranceConfig, type AssuranceReport } from "@nifrajs/core/assurance";
15
+ import { type CapabilityProjectReport } from "./capabilities-tool.js";
16
+ import { type CheckResult } from "./check.js";
17
+ export interface CollectProjectVerificationOptions {
18
+ /** Assurance config path, default `nifra.assurance.ts`. Honored by the assurance + levels views. */
19
+ readonly config?: string;
20
+ /** Skip the `tsc` pass in the check view (the agent inner-loop mode). */
21
+ readonly lintsOnly?: boolean;
22
+ /** Cancels the check view's typecheck. */
23
+ readonly signal?: AbortSignal;
24
+ /** Cap the check view's diagnostics (the `levels` L0 and MCP paths set this). */
25
+ readonly maxDiagnostics?: number;
26
+ }
27
+ export interface ProjectVerification {
28
+ readonly cwd: string;
29
+ /** Whether `nifra.assurance.ts` (or the requested `config` path) exists. */
30
+ readonly assuranceConfigPresent: boolean;
31
+ /** The loaded assurance config, when it loaded cleanly. */
32
+ readonly config?: AssuranceConfig;
33
+ /** The error from loading/evaluating the config, if any; the assurance views surface or throw it. */
34
+ readonly configError?: unknown;
35
+ /** `evaluateRouteAssurance` over the config's source + policy. Present whenever {@link config} is. */
36
+ readonly routeAssurance?: AssuranceReport;
37
+ /** Static capability provenance, when the config declares a capabilities policy. */
38
+ readonly capability?: CapabilityProjectReport;
39
+ /**
40
+ * The typed-contract check (scanners + typecheck + the assurance-fed capability/manifest diagnostics).
41
+ * Lazy and memoized: `nifra assure` never calls it, so that command pays nothing for the source walk.
42
+ */
43
+ check(): Promise<CheckResult>;
44
+ }
45
+ /**
46
+ * Run each verification policy once and return the unified value. The config is loaded (and reflected)
47
+ * a single time; the resulting route-assurance + capability evidence is both returned for the assurance
48
+ * views and fed into the check view, so nothing is computed twice.
49
+ */
50
+ export declare function collectProjectVerification(cwd: string, options?: CollectProjectVerificationOptions): Promise<ProjectVerification>;
51
+ //# sourceMappingURL=verification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../src/verification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAA8B,KAAK,WAAW,EAAsB,MAAM,YAAY,CAAA;AAE7F,MAAM,WAAW,iCAAiC;IAChD,oGAAoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,yEAAyE;IACzE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAA;IACxC,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAA;IACjC,qGAAqG;IACrG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,sGAAsG;IACtG,QAAQ,CAAC,cAAc,CAAC,EAAE,eAAe,CAAA;IACzC,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,uBAAuB,CAAA;IAC7C;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;CAC9B;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,mBAAmB,CAAC,CAyD9B"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * One project verification, four commands. `nifra check`, `nifra assure`, `nifra levels`, and
3
+ * `nifra capabilities` are not separate audits - they are renderings of the same reflected project: the
4
+ * typed-contract scan, the route-assurance evaluation, and the static capability provenance. Historically
5
+ * each command re-derived the slice it needed, so a caller had to know which command ran which policy (and
6
+ * `levels` paid for the capability walk twice). `collectProjectVerification` runs each underlying policy
7
+ * exactly once and hands back a single value; the commands become thin formatters over subsets of it -
8
+ * `check`/`assure`/`levels` read their view directly, and `capabilities snapshot`/`check` read the same
9
+ * capability report before writing or diffing the lockfile.
10
+ *
11
+ * The typed-contract check is the one heavy pass (source walk + optional `tsc`), and `nifra assure`
12
+ * never needs it, so it is exposed as a lazily-invoked `check()` rather than computed up front.
13
+ */
14
+ import { existsSync } from "node:fs";
15
+ import { resolve } from "node:path";
16
+ import { evaluateRouteAssurance, } from "@nifrajs/core/assurance";
17
+ import { collectCapabilityProjectReport, } from "./capabilities-tool.js";
18
+ import { collectCheckResult } from "./check.js";
19
+ /**
20
+ * Run each verification policy once and return the unified value. The config is loaded (and reflected)
21
+ * a single time; the resulting route-assurance + capability evidence is both returned for the assurance
22
+ * views and fed into the check view, so nothing is computed twice.
23
+ */
24
+ export async function collectProjectVerification(cwd, options = {}) {
25
+ const { loadAssuranceConfig, DEFAULT_ASSURANCE_CONFIG } = await import("./assure.js");
26
+ const configPath = resolve(cwd, options.config ?? DEFAULT_ASSURANCE_CONFIG);
27
+ const assuranceConfigPresent = existsSync(configPath);
28
+ let config;
29
+ let configError;
30
+ let routeAssurance;
31
+ let capability;
32
+ // One try mirrors `check`'s single catch: any failure to load or reflect the config becomes the
33
+ // `configError` each view then renders in its own idiom (a diagnostic, a throw, a failed rung).
34
+ try {
35
+ config = await loadAssuranceConfig(cwd, options.config);
36
+ routeAssurance = evaluateRouteAssurance(config.source, config.policy, {
37
+ ...(config.capabilities !== undefined
38
+ ? { definitions: config.capabilities.definitions }
39
+ : {}),
40
+ });
41
+ if (config.capabilities !== undefined) {
42
+ capability = await collectCapabilityProjectReport(cwd, config.source, config.capabilities);
43
+ }
44
+ }
45
+ catch (error) {
46
+ configError = error;
47
+ config = undefined;
48
+ routeAssurance = undefined;
49
+ capability = undefined;
50
+ }
51
+ // The check view's assurance inputs. Fed to `check()` only when no custom config path was requested,
52
+ // so `nifra check` (which always reads the default `nifra.assurance.ts`) and `levels` L0 keep reading
53
+ // exactly the config they read before; a `--config` override stays a route-assurance/levels concern.
54
+ const checkContext = {
55
+ present: assuranceConfigPresent,
56
+ ...(config !== undefined ? { config } : {}),
57
+ ...(configError !== undefined ? { error: configError } : {}),
58
+ ...(routeAssurance !== undefined ? { routeAssurance } : {}),
59
+ ...(capability !== undefined ? { capability } : {}),
60
+ };
61
+ let checkResult;
62
+ return Object.freeze({
63
+ cwd,
64
+ assuranceConfigPresent,
65
+ ...(config !== undefined ? { config } : {}),
66
+ ...(configError !== undefined ? { configError } : {}),
67
+ ...(routeAssurance !== undefined ? { routeAssurance } : {}),
68
+ ...(capability !== undefined ? { capability } : {}),
69
+ check() {
70
+ checkResult ??= collectCheckResult(cwd, {
71
+ lintsOnly: options.lintsOnly ?? false,
72
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
73
+ ...(options.maxDiagnostics !== undefined ? { maxDiagnostics: options.maxDiagnostics } : {}),
74
+ ...(options.config === undefined ? { assurance: checkContext } : {}),
75
+ });
76
+ return checkResult;
77
+ },
78
+ });
79
+ }
80
+ //# sourceMappingURL=verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.js","sourceRoot":"","sources":["../src/verification.ts"],"names":[],"mappings":"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"}