@nifrajs/cli 2.8.1 → 2.9.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/dist/cli.js CHANGED
@@ -134,7 +134,7 @@ routes/ from the current directory. Run from your project root.
134
134
  Port: \`dev\` and \`start\` share the default ${DEFAULT_DEV_PORT}. Override with \`--port <n>\` (alias \`-p\`) or the
135
135
  \`PORT\` env var (\`--port\` wins over \`PORT\`, which wins over the default).`;
136
136
  // Kept in lockstep with packages/cli/package.json by check:publish's version-consistency gate.
137
- const CLI_VERSION = "2.8.1";
137
+ const CLI_VERSION = "2.9.0";
138
138
  // A render adapter + nifra server are opaque to the CLI (it just forwards them); cast at the seam.
139
139
  const asAdapter = (v) => v;
140
140
  const asBunPlugins = (v) => v;
package/dist/mcp-http.js CHANGED
@@ -23,7 +23,7 @@ import { loadTypesCorpus } from "./types-search.js";
23
23
  export { examplesAppTool, examplesWidget } from "./mcp-app-tool.js";
24
24
  export { docsTools } from "./mcp-docs-tools.js";
25
25
  // Kept in lockstep with packages/cli/package.json by check:publish's version-consistency gate.
26
- const VERSION = "2.8.1";
26
+ const VERSION = "2.9.0";
27
27
  const SERVER_INFO = { name: "nifra-docs", version: VERSION };
28
28
  // Derive the GET/health tool list from the tools actually served, so the line can never drift from them.
29
29
  const docsHealth = (tools) => `nifra docs MCP - POST JSON-RPC 2.0 here (methods: initialize, tools/list, tools/call). Tools: ${tools
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAA;AAWnF,sGAAsG;AACtG,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAGrF;AAWD;mFACmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAUpE;AAiCD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,mBAAoB,SAAQ,cAAc;IACzD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;oFACoF;AACpF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,cAAc,CAenF;AAWD;;oEAEoE;AACpE,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,mBAAmB,CAAC,CAoB9B;AAED,oGAAoG;AACpG,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAS5E"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAA;AAWnF,sGAAsG;AACtG,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAGrF;AAWD;mFACmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAUpE;AAiCD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,mBAAoB,SAAQ,cAAc;IACzD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;oFACoF;AACpF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,cAAc,CAenF;AA0CD;;oEAEoE;AACpE,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,mBAAmB,CAAC,CAsB9B;AAED,oGAAoG;AACpG,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAS5E"}
package/dist/scaffold.js CHANGED
@@ -8,7 +8,7 @@
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
  */
11
- import { mkdir, writeFile } from "node:fs/promises";
11
+ import { lstat, mkdir, realpath, writeFile } from "node:fs/promises";
12
12
  import { dirname, resolve, sep } from "node:path";
13
13
  const EXT = {
14
14
  react: "tsx",
@@ -100,6 +100,37 @@ function resolveInsideCwd(cwd, relativeFile) {
100
100
  }
101
101
  return target;
102
102
  }
103
+ /** Lexical containment is not enough when an existing route directory is a symlink. Check every
104
+ * existing ancestor and its real path before scaffolding so an agent cannot redirect a write outside
105
+ * the selected project. The target itself is created with `wx`, so an existing target symlink is also
106
+ * never followed/overwritten. */
107
+ async function assertNoSymlinkedAncestors(root, target) {
108
+ const rootPath = resolve(root);
109
+ const realRoot = await realpath(rootPath);
110
+ let current = dirname(target);
111
+ while (current !== rootPath) {
112
+ if (!current.startsWith(`${rootPath}${sep}`)) {
113
+ throw new Error(`refusing to write outside project root: ${target}`);
114
+ }
115
+ try {
116
+ if ((await lstat(current)).isSymbolicLink()) {
117
+ throw new Error(`refusing to write through symlinked directory: ${current}`);
118
+ }
119
+ const actual = await realpath(current);
120
+ if (actual !== realRoot && !actual.startsWith(`${realRoot}${sep}`)) {
121
+ throw new Error(`refusing to write outside project root through: ${current}`);
122
+ }
123
+ }
124
+ catch (err) {
125
+ if (err && typeof err === "object" && err.code === "ENOENT") {
126
+ current = dirname(current);
127
+ continue;
128
+ }
129
+ throw err;
130
+ }
131
+ current = dirname(current);
132
+ }
133
+ }
103
134
  /** Write a scaffolded route stub when the framework has a verified ready-to-write body. The write is
104
135
  * intentionally conservative: it refuses non-JSX stubs (where we only return contract guidance) and
105
136
  * uses `wx`, so an agent cannot overwrite user work by accident. */
@@ -113,7 +144,9 @@ export async function writeScaffoldRoute(cwd, urlPath, framework) {
113
144
  };
114
145
  }
115
146
  const target = resolveInsideCwd(cwd, result.file);
147
+ await assertNoSymlinkedAncestors(cwd, target);
116
148
  await mkdir(dirname(target), { recursive: true });
149
+ await assertNoSymlinkedAncestors(cwd, target);
117
150
  try {
118
151
  await writeFile(target, result.content, { flag: "wx" });
119
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
@@ -105,7 +105,7 @@
105
105
  "name": "DUAL_API",
106
106
  "topic": "Nifra - Framework contract",
107
107
  "slug": "contract",
108
- "code": "// TWO ways to handle a request - pick by who is calling.\nimport type { LoaderContext } from \"@nifrajs/web\"\n\n// (1) A page's own data + mutations → route exports. A PUBLIC POST is a route ACTION\n// (routes/contact.tsx) - the browser POSTs the route's own URL, nifra runs `action`:\nexport async function action(ctx: LoaderContext) {\n const form = await ctx.request.formData() // the browser POSTed this route\n return { ok: true, body: String(form.get(\"body\")) } // → props.actionData on re-render\n}\n\n// (2) ctx.api is the IN-PROCESS backend client (the inProcessClient(server) the app was given).\n// It is LOADERS-ONLY: the SSR backend registers GET handlers, so a .post() to ctx.api 405s.\nexport async function loader(ctx: LoaderContext) {\n // ✅ a GET in-process - no HTTP hop. (A .post() to ctx.api would 405; write an `action` instead.)\n return { id: ctx.params.id }\n}\n\n// Rule of thumb: data INTO a page → loader + ctx.api (GET). A mutation FROM the browser\n// (a form/fetch POST) → an `action` export on the route. Don't `inProcessClient(api).post()`."
108
+ "code": "// TWO ways to handle a request - pick by who is calling.\nimport type { LoaderContext } from \"@nifrajs/web\"\n\n// (1) A page's own data + mutations → route exports. A PUBLIC POST is a route ACTION\n// (routes/contact.tsx) - the browser POSTs the route's own URL, Nifra runs `action`:\nexport async function action(ctx: LoaderContext) {\n const form = await ctx.request.formData() // the browser POSTed this route\n return { ok: true, body: String(form.get(\"body\")) } // → props.actionData on re-render\n}\n\n// (2) ctx.api is the IN-PROCESS backend client (the inProcessClient(server) the app was given).\n// It is LOADERS-ONLY: the SSR backend registers GET handlers, so a .post() to ctx.api 405s.\nexport async function loader(ctx: LoaderContext) {\n // ✅ a GET in-process - no HTTP hop. (A .post() to ctx.api would 405; write an `action` instead.)\n return { id: ctx.params.id }\n}\n\n// Rule of thumb: data INTO a page → loader + ctx.api (GET). A mutation FROM the browser\n// (a form/fetch POST) → an `action` export on the route. Don't `inProcessClient(api).post()`."
109
109
  },
110
110
  {
111
111
  "name": "INLINE_SCRIPT",
@@ -183,7 +183,7 @@
183
183
  "name": "VITE_PROD",
184
184
  "topic": "Nifra - Dev & HMR",
185
185
  "slug": "dev",
186
- "code": "// vite.config.ts - a Vite/Rollup PRODUCTION client build (the escape hatch, not the default).\n// Only reach for this when an app needs a Vite-only transform with no Bun equivalent; nifra's default\n// production bundler stays Bun (buildClient), which is faster and Bun-native.\nimport { viteLeakGuard } from \"@nifrajs/web/plugins/vite-leak-guard\"\n\nexport default {\n build: {\n // The SAME two client-leak guards nifra's Bun build runs - server-only code or a node: builtin\n // reaching the browser fails the build, with the identical error message. A second production\n // pipeline must not ship without them.\n rollupOptions: { plugins: [viteLeakGuard()] },\n },\n}"
186
+ "code": "// vite.config.ts - a Vite/Rollup PRODUCTION client build (the escape hatch, not the default).\n// Only reach for this when an app needs a Vite-only transform with no Bun equivalent; Nifra's default\n// production bundler stays Bun (buildClient), which is faster and Bun-native.\nimport { viteLeakGuard } from \"@nifrajs/web/plugins/vite-leak-guard\"\n\nexport default {\n build: {\n // The SAME two client-leak guards Nifra's Bun build runs - server-only code or a node: builtin\n // reaching the browser fails the build, with the identical error message. A second production\n // pipeline must not ship without them.\n rollupOptions: { plugins: [viteLeakGuard()] },\n },\n}"
187
187
  },
188
188
  {
189
189
  "name": "LAB",
@@ -287,6 +287,12 @@
287
287
  "slug": "plugins",
288
288
  "code": "import { server } from \"@nifrajs/core/server\"\nimport { compression, cacheControl } from \"@nifrajs/middleware\"\n\nconst app = server()\n .use(compression()) // gzip compressible responses (Accept-Encoding)\n .use(cacheControl(\"public, max-age=60\")) // Cache-Control on GET/HEAD 2xx (won't clobber)\n // …or a per-path policy - return undefined to leave a response untouched:\n .use(cacheControl((req) =>\n new URL(req.url).pathname.startsWith(\"/assets/\")\n ? \"public, max-age=31536000, immutable\"\n : undefined))"
289
289
  },
290
+ {
291
+ "name": "PORTABLE_HOOK",
292
+ "topic": "Nifra - Plugins & middleware",
293
+ "slug": "plugins",
294
+ "code": "import type { Middleware } from \"@nifrajs/core/server\"\n\n// ONE implementation, fast on every runtime: on Bun/Deno it mutates the\n// response's own Headers in place; on Node it writes the outcome record on the\n// direct socket writer - no Web Request/Response is ever built for it.\nexport function serverName(value: string): Middleware {\n return {\n onResponseHeaders(headers, req, status) {\n if (status < 500) headers.set(\"server\", value)\n },\n }\n}"
295
+ },
290
296
  {
291
297
  "name": "RELIABILITY",
292
298
  "topic": "Nifra - Plugins & middleware",
@@ -297,7 +303,7 @@
297
303
  "name": "SHAPING",
298
304
  "topic": "Nifra - Plugins & middleware",
299
305
  "slug": "plugins",
300
- "code": "import { server } from \"@nifrajs/core/server\"\nimport { methodOverride, trimTrailingSlash, language, timing, poweredBy, combine } from \"@nifrajs/middleware\"\n\nconst app = server()\n .use(methodOverride()) // POST + X-HTTP-Method-Override → PUT/PATCH/DELETE (a real pre-routing request rewrite)\n .use(trimTrailingSlash()) // canonicalize URLs: 308 redirect (or rewrite), same-origin only, conservative methods\n .use(language({ supported: [\"en\", \"fr\"], defaultLanguage: \"en\" })) // Accept-Language → c.language + Content-Language\n .use(timing()) // Server-Timing header + typed c.timing marks/measures\n// poweredBy() is opt-in (nifra emits no X-Powered-By by default). combine(a, b, c) bundles several into one plugin."
306
+ "code": "import { server } from \"@nifrajs/core/server\"\nimport { methodOverride, trimTrailingSlash, language, timing, poweredBy, combine } from \"@nifrajs/middleware\"\n\nconst app = server()\n .use(methodOverride()) // POST + X-HTTP-Method-Override → PUT/PATCH/DELETE (a real pre-routing request rewrite)\n .use(trimTrailingSlash()) // canonicalize URLs: 308 redirect (or rewrite), same-origin only, conservative methods\n .use(language({ supported: [\"en\", \"fr\"], defaultLanguage: \"en\" })) // Accept-Language → c.language + Content-Language\n .use(timing()) // Server-Timing header + typed c.timing marks/measures\n// poweredBy() is opt-in (Nifra emits no X-Powered-By by default). combine(a, b, c) bundles several into one plugin."
301
307
  },
302
308
  {
303
309
  "name": "BUILD",