@nifrajs/cli 3.3.0 → 3.5.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 (118) hide show
  1. package/dist/check-diagnostics.d.ts +3 -15
  2. package/dist/check-diagnostics.d.ts.map +1 -1
  3. package/dist/check-diagnostics.js +149 -713
  4. package/dist/check-diagnostics.js.map +1 -1
  5. package/dist/check-scan.d.ts +9 -0
  6. package/dist/check-scan.d.ts.map +1 -1
  7. package/dist/check-scan.js +32 -0
  8. package/dist/check-scan.js.map +1 -1
  9. package/dist/check.d.ts +1 -0
  10. package/dist/check.d.ts.map +1 -1
  11. package/dist/check.js +63 -8
  12. package/dist/check.js.map +1 -1
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +46 -7
  15. package/dist/cli.js.map +1 -1
  16. package/dist/command-catalog.d.ts +4 -1
  17. package/dist/command-catalog.d.ts.map +1 -1
  18. package/dist/command-catalog.js +28 -7
  19. package/dist/command-catalog.js.map +1 -1
  20. package/dist/contracts.d.ts +3 -2
  21. package/dist/contracts.d.ts.map +1 -1
  22. package/dist/contracts.js +4 -3
  23. package/dist/contracts.js.map +1 -1
  24. package/dist/diagnostics.d.ts +86 -0
  25. package/dist/diagnostics.d.ts.map +1 -1
  26. package/dist/diagnostics.js +85 -0
  27. package/dist/diagnostics.js.map +1 -1
  28. package/dist/introspect.d.ts +1 -3
  29. package/dist/introspect.d.ts.map +1 -1
  30. package/dist/introspect.js +1 -47
  31. package/dist/introspect.js.map +1 -1
  32. package/dist/load.d.ts +5 -0
  33. package/dist/load.d.ts.map +1 -1
  34. package/dist/load.js.map +1 -1
  35. package/dist/mcp-context.d.ts.map +1 -1
  36. package/dist/mcp-context.js +4 -5
  37. package/dist/mcp-context.js.map +1 -1
  38. package/dist/mcp-exec.d.ts +1 -0
  39. package/dist/mcp-exec.d.ts.map +1 -1
  40. package/dist/mcp-exec.js +133 -25
  41. package/dist/mcp-exec.js.map +1 -1
  42. package/dist/mcp-http.d.ts.map +1 -1
  43. package/dist/mcp-http.js +4 -1
  44. package/dist/mcp-http.js.map +1 -1
  45. package/dist/mcp-io.d.ts +12 -0
  46. package/dist/mcp-io.d.ts.map +1 -1
  47. package/dist/mcp-io.js +68 -0
  48. package/dist/mcp-io.js.map +1 -1
  49. package/dist/mcp-path.d.ts +10 -0
  50. package/dist/mcp-path.d.ts.map +1 -0
  51. package/dist/mcp-path.js +51 -0
  52. package/dist/mcp-path.js.map +1 -0
  53. package/dist/mcp-reflect.d.ts.map +1 -1
  54. package/dist/mcp-reflect.js +2 -2
  55. package/dist/mcp-reflect.js.map +1 -1
  56. package/dist/mcp-render.d.ts.map +1 -1
  57. package/dist/mcp-render.js +62 -43
  58. package/dist/mcp-render.js.map +1 -1
  59. package/dist/mcp-root.d.ts.map +1 -1
  60. package/dist/mcp-root.js +14 -2
  61. package/dist/mcp-root.js.map +1 -1
  62. package/dist/mcp-run.d.ts.map +1 -1
  63. package/dist/mcp-run.js +82 -51
  64. package/dist/mcp-run.js.map +1 -1
  65. package/dist/mcp.d.ts +2 -2
  66. package/dist/mcp.d.ts.map +1 -1
  67. package/dist/mcp.js +84 -29
  68. package/dist/mcp.js.map +1 -1
  69. package/dist/project-facts.d.ts +23 -4
  70. package/dist/project-facts.d.ts.map +1 -1
  71. package/dist/project-facts.js +12 -2
  72. package/dist/project-facts.js.map +1 -1
  73. package/dist/review.d.ts +21 -0
  74. package/dist/review.d.ts.map +1 -0
  75. package/dist/review.js +819 -0
  76. package/dist/review.js.map +1 -0
  77. package/dist/rules/legacy.d.ts +3 -1
  78. package/dist/rules/legacy.d.ts.map +1 -1
  79. package/dist/rules/legacy.js +796 -34
  80. package/dist/rules/legacy.js.map +1 -1
  81. package/dist/scaffold.d.ts.map +1 -1
  82. package/dist/scaffold.js +23 -4
  83. package/dist/scaffold.js.map +1 -1
  84. package/dist/verification-plan.d.ts.map +1 -1
  85. package/dist/verification-plan.js +4 -0
  86. package/dist/verification-plan.js.map +1 -1
  87. package/dist/work-graph.d.ts +3 -0
  88. package/dist/work-graph.d.ts.map +1 -1
  89. package/dist/work-graph.js +4 -3
  90. package/dist/work-graph.js.map +1 -1
  91. package/docs/llms-full.txt +369 -21
  92. package/docs/types.json +1121 -80
  93. package/package.json +10 -9
  94. package/src/check-diagnostics.ts +181 -796
  95. package/src/check-scan.ts +36 -0
  96. package/src/check.ts +65 -12
  97. package/src/cli.ts +53 -7
  98. package/src/command-catalog.ts +33 -7
  99. package/src/contracts.ts +12 -3
  100. package/src/diagnostics.ts +179 -0
  101. package/src/introspect.ts +3 -55
  102. package/src/load.ts +5 -0
  103. package/src/mcp-context.ts +4 -6
  104. package/src/mcp-exec.ts +142 -30
  105. package/src/mcp-http.ts +4 -1
  106. package/src/mcp-io.ts +73 -0
  107. package/src/mcp-path.ts +50 -0
  108. package/src/mcp-reflect.ts +3 -2
  109. package/src/mcp-render.ts +70 -40
  110. package/src/mcp-root.ts +13 -2
  111. package/src/mcp-run.ts +90 -50
  112. package/src/mcp.ts +87 -27
  113. package/src/project-facts.ts +37 -8
  114. package/src/review.ts +983 -0
  115. package/src/rules/legacy.ts +915 -35
  116. package/src/scaffold.ts +29 -4
  117. package/src/verification-plan.ts +14 -0
  118. package/src/work-graph.ts +13 -3
package/src/scaffold.ts CHANGED
@@ -45,11 +45,35 @@ function segmentToFile(seg: string): string {
45
45
  return seg
46
46
  }
47
47
 
48
+ /** Reject syntax that could be interpreted by the host filesystem rather than the route mapper. */
49
+ function assertSafeRouteSegment(segment: string, urlPath: string): void {
50
+ if (segment === "." || segment === ".." || segment.includes("\0") || segment.includes("\\")) {
51
+ throw new Error(
52
+ `invalid route segment in ${JSON.stringify(urlPath)}: ${JSON.stringify(segment)}`,
53
+ )
54
+ }
55
+ const fileSegment = segmentToFile(segment)
56
+ if (
57
+ fileSegment === "." ||
58
+ fileSegment === ".." ||
59
+ fileSegment.includes("\0") ||
60
+ fileSegment.includes("/") ||
61
+ fileSegment.includes("\\") ||
62
+ fileSegment.includes(":") ||
63
+ /^[A-Za-z]:/.test(fileSegment)
64
+ ) {
65
+ throw new Error(
66
+ `invalid route segment in ${JSON.stringify(urlPath)}: ${JSON.stringify(segment)}`,
67
+ )
68
+ }
69
+ }
70
+
48
71
  /** Map a URL path to its `routes/` file path (relative to `routes/`, without extension prefix dir).
49
72
  * `/` → `index`; `/users/:id` → `users/[id]`; `/blog/*slug` → `blog/[...slug]`. */
50
73
  export function routePathToFile(urlPath: string, ext: string): string {
51
74
  const segments = urlPath.split("/").filter((s) => s.length > 0)
52
75
  if (segments.length === 0) return `routes/index.${ext}`
76
+ for (const segment of segments) assertSafeRouteSegment(segment, urlPath)
53
77
  const last = segments.length - 1
54
78
  // Catch-all must be the final segment (mirrors @nifrajs/web); flag it rather than emit an invalid file.
55
79
  for (let i = 0; i < last; i++) {
@@ -268,11 +292,12 @@ export function scaffoldRoute(
268
292
  }
269
293
  }
270
294
 
271
- function resolveInsideCwd(cwd: string, relativeFile: string): string {
295
+ function resolveInsideRoutes(cwd: string, relativeFile: string): string {
272
296
  const root = resolve(cwd)
297
+ const routesRoot = resolve(root, "routes")
273
298
  const target = resolve(root, relativeFile)
274
- if (target !== root && !target.startsWith(`${root}${sep}`)) {
275
- throw new Error(`refusing to write outside project root: ${relativeFile}`)
299
+ if (target === routesRoot || !target.startsWith(`${routesRoot}${sep}`)) {
300
+ throw new Error(`refusing to write outside routes directory: ${relativeFile}`)
276
301
  }
277
302
  return target
278
303
  }
@@ -325,7 +350,7 @@ export async function writeScaffoldRoute(
325
350
  reason: "no verified ready-to-write stub for this framework; use nifra_example for the body",
326
351
  }
327
352
  }
328
- const target = resolveInsideCwd(cwd, result.file)
353
+ const target = resolveInsideRoutes(cwd, result.file)
329
354
  await assertNoSymlinkedAncestors(cwd, target)
330
355
  await mkdir(dirname(target), { recursive: true })
331
356
  await assertNoSymlinkedAncestors(cwd, target)
@@ -102,6 +102,18 @@ const GATES = Object.freeze([
102
102
  "Run `bun run check:core-performance` and investigate the measured performance regression.",
103
103
  { workflowRequired: false },
104
104
  ),
105
+ gate(
106
+ "middleware-performance",
107
+ [["run", "check:middleware-overhead"]],
108
+ "Run `bun run check:middleware-overhead` and investigate the middleware-chain regression.",
109
+ { workflowRequired: false },
110
+ ),
111
+ gate(
112
+ "edge-startup",
113
+ [["run", "check:edge-startup"]],
114
+ "Run `bun run check:edge-startup` and investigate edge/Workers import-time growth.",
115
+ { workflowRequired: false },
116
+ ),
105
117
  gate(
106
118
  "publish",
107
119
  [["run", "check:publish"]],
@@ -187,6 +199,8 @@ const RELEASE_PLAN = Object.freeze([
187
199
  "plugin-manifest",
188
200
  "size",
189
201
  "core-performance",
202
+ "middleware-performance",
203
+ "edge-startup",
190
204
  "publish",
191
205
  "consumer",
192
206
  "cold-start",
package/src/work-graph.ts CHANGED
@@ -9,7 +9,12 @@
9
9
  import { existsSync } from "node:fs"
10
10
  import { readFile, stat } from "node:fs/promises"
11
11
  import { basename, resolve } from "node:path"
12
- import { type ReflectedRoute, reflectRoutes } from "@nifrajs/core/reflection"
12
+ import {
13
+ type ProjectEvidenceSnapshot,
14
+ reflectedRoutesFromEvidence,
15
+ snapshotProjectEvidence,
16
+ } from "@nifrajs/core/evidence"
17
+ import type { ReflectedRoute } from "@nifrajs/core/reflection"
13
18
  import { Glob } from "bun"
14
19
  import { digestRoute } from "./contracts.ts"
15
20
 
@@ -52,6 +57,8 @@ export interface WorkGraphSourceFile {
52
57
 
53
58
  export interface WorkGraphBuildInput {
54
59
  readonly source: unknown
60
+ /** Reuse a canonical route-evidence pass when the caller already has one. */
61
+ readonly evidence?: ProjectEvidenceSnapshot
55
62
  readonly files?: readonly WorkGraphSourceFile[]
56
63
  readonly freshness?: BuildFreshness
57
64
  }
@@ -224,7 +231,8 @@ export async function buildProjectWorkGraph(
224
231
  ): Promise<ProjectWorkGraphResult> {
225
232
  const freshness = input.freshness ?? { ok: true }
226
233
  if (!freshness.ok) throw new StaleBuildError(freshness)
227
- const graph = await buildWorkGraph(input)
234
+ const projectEvidence = input.evidence ?? snapshotProjectEvidence(input.source)
235
+ const graph = await buildWorkGraph({ ...input, evidence: projectEvidence })
228
236
  const impact = queryImpact(graph, options.changedFiles ?? [])
229
237
  const plan = planProofs(graph, impact, options.minLevel ?? 1)
230
238
  const evidence = createEvidenceBundle(graph, impact, plan)
@@ -232,7 +240,9 @@ export async function buildProjectWorkGraph(
232
240
  }
233
241
 
234
242
  export async function buildWorkGraph(input: WorkGraphBuildInput): Promise<WorkGraph> {
235
- const routes = reflectRoutes(input.source)
243
+ const routes = reflectedRoutesFromEvidence(
244
+ input.evidence ?? snapshotProjectEvidence(input.source),
245
+ )
236
246
  const files = input.files ?? []
237
247
  const nodes = new Map<string, WorkGraphNode>()
238
248
  const edges: WorkGraphEdge[] = []