@nifrajs/cli 2.1.0 → 2.2.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 (47) hide show
  1. package/dist/check.d.ts +4 -1
  2. package/dist/check.d.ts.map +1 -1
  3. package/dist/check.js +71 -0
  4. package/dist/check.js.map +1 -1
  5. package/dist/cli.d.ts +9 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +165 -23
  8. package/dist/cli.js.map +1 -1
  9. package/dist/doctor.d.ts +5 -1
  10. package/dist/doctor.d.ts.map +1 -1
  11. package/dist/doctor.js +66 -6
  12. package/dist/doctor.js.map +1 -1
  13. package/dist/load.d.ts +11 -0
  14. package/dist/load.d.ts.map +1 -1
  15. package/dist/load.js +30 -0
  16. package/dist/load.js.map +1 -1
  17. package/dist/mcp-docs-tools.d.ts.map +1 -1
  18. package/dist/mcp-docs-tools.js +7 -3
  19. package/dist/mcp-docs-tools.js.map +1 -1
  20. package/dist/mcp-http.js +1 -1
  21. package/dist/mcp-render.js +9 -1
  22. package/dist/mcp-render.js.map +1 -1
  23. package/dist/mcp.d.ts.map +1 -1
  24. package/dist/mcp.js +44 -2
  25. package/dist/mcp.js.map +1 -1
  26. package/dist/pipeline-guard.d.ts +83 -0
  27. package/dist/pipeline-guard.d.ts.map +1 -0
  28. package/dist/pipeline-guard.js +136 -0
  29. package/dist/pipeline-guard.js.map +1 -0
  30. package/dist/types-search.d.ts +1 -1
  31. package/dist/types-search.d.ts.map +1 -1
  32. package/dist/types-search.js +47 -2
  33. package/dist/types-search.js.map +1 -1
  34. package/docs/examples.json +6 -0
  35. package/docs/llms-full.txt +81 -30
  36. package/docs/types.json +613 -36
  37. package/package.json +9 -9
  38. package/src/check.ts +91 -0
  39. package/src/cli.ts +173 -24
  40. package/src/doctor.ts +71 -6
  41. package/src/load.ts +44 -0
  42. package/src/mcp-docs-tools.ts +13 -3
  43. package/src/mcp-http.ts +1 -1
  44. package/src/mcp-render.ts +9 -1
  45. package/src/mcp.ts +45 -2
  46. package/src/pipeline-guard.ts +186 -0
  47. package/src/types-search.ts +50 -1
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Pipeline separation guard — one bundler owns a phase, never two.
3
+ *
4
+ * nifra supports both Vite and Bun, and the config keeps them in separate slots: `vitePlugins` for
5
+ * the Vite pipeline, `clientPlugins`/`serverPlugins` for the Bun one. Nothing enforces the split, so
6
+ * a plugin lands in the wrong slot and is silently ignored - a Vite plugin in `clientPlugins` is
7
+ * handed to `Bun.build`, which has no `transform` hook to call, so the transform simply never runs
8
+ * and the build succeeds.
9
+ *
10
+ * That failure mode is the whole reason for this file. nifra's expensive bugs are not caused by Vite
11
+ * or by Bun; they are caused by two toolchains disagreeing about one module. Dual-React is that, the
12
+ * dedupe plugins are patches for that, and a `resolve.alias` that never reaches SSR is that. Mixing
13
+ * is the defect - choice is not - so the separation has to be checked rather than documented.
14
+ *
15
+ * Detection is by hook shape, which is what each bundler actually dispatches on:
16
+ * - a Bun plugin has `setup(build)`
17
+ * - a Rollup/Vite plugin has `resolveId` / `load` / `transform` / `config` / `configResolved` /
18
+ * `transformIndexHtml` and no `setup`
19
+ *
20
+ * Deliberately conservative: a plugin that matches neither shape is left alone rather than guessed
21
+ * at. A guard that fires on correct config is a guard people turn off.
22
+ */
23
+ /** Which pipeline a slot belongs to. */
24
+ export type PipelineSlot = "vitePlugins" | "clientPlugins" | "serverPlugins";
25
+ export interface PipelineMismatch {
26
+ readonly slot: PipelineSlot;
27
+ /** The plugin's `name`, or a positional label when it has none. */
28
+ readonly plugin: string;
29
+ /** The pipeline the slot feeds. */
30
+ readonly expected: "vite" | "bun";
31
+ /** The pipeline the plugin's hooks say it belongs to. */
32
+ readonly actual: "vite" | "bun";
33
+ readonly fix: string;
34
+ }
35
+ /**
36
+ * Check one slot's resolved plugin list. Pure: takes the already-resolved array so a thunk-valued
37
+ * field is awaited by the caller, and so this stays testable without loading a project.
38
+ */
39
+ export declare function checkPipelineSlot(slot: PipelineSlot, plugins: readonly unknown[]): PipelineMismatch[];
40
+ /** Check every slot of a resolved framework config. */
41
+ export declare function checkPipelineSeparation(slots: {
42
+ readonly vitePlugins?: readonly unknown[];
43
+ readonly clientPlugins?: readonly unknown[];
44
+ readonly serverPlugins?: readonly unknown[];
45
+ }): PipelineMismatch[];
46
+ /** Which bundler a build runs on. */
47
+ export type BuildPipeline = "bun" | "vite";
48
+ export interface PipelineDecision {
49
+ readonly pipeline: BuildPipeline;
50
+ /**
51
+ * Why this pipeline, for the `nifra build` log line - so an auto-selected Vite build never looks
52
+ * like the user got the default. `undefined` when the choice was the plain default or was forced.
53
+ */
54
+ readonly reason?: string;
55
+ }
56
+ /** The plugin slots a decision reads, already resolved. */
57
+ export interface PipelineSlots {
58
+ readonly vitePlugins?: readonly unknown[];
59
+ readonly clientPlugins?: readonly unknown[];
60
+ readonly serverPlugins?: readonly unknown[];
61
+ }
62
+ /**
63
+ * Pick the bundler for `nifra build`, per app rather than per global default.
64
+ *
65
+ * The phase defaults are deliberately asymmetric - `nifra dev` is Vite (for the plugin ecosystem, and
66
+ * because Bun's DEV-server bundler cannot compile CSS Modules), `nifra build` is Bun (faster and
67
+ * Bun-native). For an app with no transforms that asymmetry is free: there is nothing to disagree about.
68
+ * For an app whose ONLY transforms are Vite plugins it is not. Those plugins run in dev and are then
69
+ * dropped by the Bun build - `buildForTarget` reads `clientPlugins`/`serverPlugins`, never `vitePlugins`
70
+ * - so the build succeeds, the output looks plausible, and the transform simply did not happen. That is
71
+ * the same failure {@link checkPipelineSeparation} exists to prevent, reached by crossing PHASES instead
72
+ * of slots, and the slot check cannot see it because the plugins are correctly placed.
73
+ *
74
+ * So the default follows the app: Vite plugins with no Bun counterpart means the app has exactly one
75
+ * pipeline that can build it, and that is the one used. An app declaring BOTH has supplied the Bun
76
+ * equivalent on purpose - nothing is dropped - so the fast Bun default stands.
77
+ *
78
+ * `forced` is `--vite` / `--bun` and always wins, except for the one combination that silently discards
79
+ * work, which throws instead. Refusing rather than warning matches the slot guard: a build that succeeds
80
+ * and omits the transform is worse than one that stops and says so.
81
+ */
82
+ export declare function chooseBuildPipeline(slots: PipelineSlots, forced?: BuildPipeline): PipelineDecision;
83
+ //# sourceMappingURL=pipeline-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-guard.d.ts","sourceRoot":"","sources":["../src/pipeline-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,eAAe,GAAG,eAAe,CAAA;AAE5E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAA;IACjC,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAA;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAmCD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,SAAS,OAAO,EAAE,GAC1B,gBAAgB,EAAE,CAqBpB;AAED,uDAAuD;AACvD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CAC5C,GAAG,gBAAgB,EAAE,CAMrB;AAED,qCAAqC;AACrC,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,CAAA;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;IAChC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CAC5C;AAUD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,aAAa,EACpB,MAAM,CAAC,EAAE,aAAa,GACrB,gBAAgB,CAsBlB"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Pipeline separation guard — one bundler owns a phase, never two.
3
+ *
4
+ * nifra supports both Vite and Bun, and the config keeps them in separate slots: `vitePlugins` for
5
+ * the Vite pipeline, `clientPlugins`/`serverPlugins` for the Bun one. Nothing enforces the split, so
6
+ * a plugin lands in the wrong slot and is silently ignored - a Vite plugin in `clientPlugins` is
7
+ * handed to `Bun.build`, which has no `transform` hook to call, so the transform simply never runs
8
+ * and the build succeeds.
9
+ *
10
+ * That failure mode is the whole reason for this file. nifra's expensive bugs are not caused by Vite
11
+ * or by Bun; they are caused by two toolchains disagreeing about one module. Dual-React is that, the
12
+ * dedupe plugins are patches for that, and a `resolve.alias` that never reaches SSR is that. Mixing
13
+ * is the defect - choice is not - so the separation has to be checked rather than documented.
14
+ *
15
+ * Detection is by hook shape, which is what each bundler actually dispatches on:
16
+ * - a Bun plugin has `setup(build)`
17
+ * - a Rollup/Vite plugin has `resolveId` / `load` / `transform` / `config` / `configResolved` /
18
+ * `transformIndexHtml` and no `setup`
19
+ *
20
+ * Deliberately conservative: a plugin that matches neither shape is left alone rather than guessed
21
+ * at. A guard that fires on correct config is a guard people turn off.
22
+ */
23
+ const ROLLUP_HOOKS = [
24
+ "resolveId",
25
+ "load",
26
+ "transform",
27
+ "config",
28
+ "configResolved",
29
+ "configureServer",
30
+ "transformIndexHtml",
31
+ "generateBundle",
32
+ "renderChunk",
33
+ ];
34
+ /** Classify a plugin by the hooks it exposes, or `undefined` when its shape says nothing. */
35
+ function classify(plugin) {
36
+ if (typeof plugin !== "object" || plugin === null)
37
+ return undefined;
38
+ const record = plugin;
39
+ // `setup` is Bun's only entry point, so its presence is decisive even alongside other keys.
40
+ if (typeof record.setup === "function")
41
+ return "bun";
42
+ if (ROLLUP_HOOKS.some((hook) => typeof record[hook] === "function"))
43
+ return "vite";
44
+ return undefined;
45
+ }
46
+ const SLOT_PIPELINE = {
47
+ vitePlugins: "vite",
48
+ clientPlugins: "bun",
49
+ serverPlugins: "bun",
50
+ };
51
+ const MOVE_TO = {
52
+ vite: "`vitePlugins`",
53
+ bun: "`clientPlugins` (client bundle) or `serverPlugins` (SSR)",
54
+ };
55
+ /**
56
+ * Check one slot's resolved plugin list. Pure: takes the already-resolved array so a thunk-valued
57
+ * field is awaited by the caller, and so this stays testable without loading a project.
58
+ */
59
+ export function checkPipelineSlot(slot, plugins) {
60
+ const expected = SLOT_PIPELINE[slot];
61
+ const out = [];
62
+ for (let i = 0; i < plugins.length; i++) {
63
+ const plugin = plugins[i];
64
+ const actual = classify(plugin);
65
+ if (actual === undefined || actual === expected)
66
+ continue;
67
+ const named = plugin.name;
68
+ const label = typeof named === "string" && named !== "" ? named : `${slot}[${i}]`;
69
+ out.push({
70
+ slot,
71
+ plugin: label,
72
+ expected,
73
+ actual,
74
+ fix: actual === "vite"
75
+ ? `"${label}" is a Vite/Rollup plugin (it defines Rollup hooks, not \`setup\`) but sits in \`${slot}\`, which feeds Bun.build. Bun never calls those hooks, so the transform silently does not run. Move it to ${MOVE_TO.vite}.`
76
+ : `"${label}" is a Bun plugin (it defines \`setup\`) but sits in \`${slot}\`, which feeds Vite. Vite never calls \`setup\`, so the transform silently does not run. Move it to ${MOVE_TO.bun}.`,
77
+ });
78
+ }
79
+ return out;
80
+ }
81
+ /** Check every slot of a resolved framework config. */
82
+ export function checkPipelineSeparation(slots) {
83
+ return [
84
+ ...checkPipelineSlot("vitePlugins", slots.vitePlugins ?? []),
85
+ ...checkPipelineSlot("clientPlugins", slots.clientPlugins ?? []),
86
+ ...checkPipelineSlot("serverPlugins", slots.serverPlugins ?? []),
87
+ ];
88
+ }
89
+ const pluginNames = (plugins) => plugins
90
+ .map((plugin, i) => {
91
+ const named = plugin.name;
92
+ return typeof named === "string" && named !== "" ? named : `vitePlugins[${i}]`;
93
+ })
94
+ .join(", ");
95
+ /**
96
+ * Pick the bundler for `nifra build`, per app rather than per global default.
97
+ *
98
+ * The phase defaults are deliberately asymmetric - `nifra dev` is Vite (for the plugin ecosystem, and
99
+ * because Bun's DEV-server bundler cannot compile CSS Modules), `nifra build` is Bun (faster and
100
+ * Bun-native). For an app with no transforms that asymmetry is free: there is nothing to disagree about.
101
+ * For an app whose ONLY transforms are Vite plugins it is not. Those plugins run in dev and are then
102
+ * dropped by the Bun build - `buildForTarget` reads `clientPlugins`/`serverPlugins`, never `vitePlugins`
103
+ * - so the build succeeds, the output looks plausible, and the transform simply did not happen. That is
104
+ * the same failure {@link checkPipelineSeparation} exists to prevent, reached by crossing PHASES instead
105
+ * of slots, and the slot check cannot see it because the plugins are correctly placed.
106
+ *
107
+ * So the default follows the app: Vite plugins with no Bun counterpart means the app has exactly one
108
+ * pipeline that can build it, and that is the one used. An app declaring BOTH has supplied the Bun
109
+ * equivalent on purpose - nothing is dropped - so the fast Bun default stands.
110
+ *
111
+ * `forced` is `--vite` / `--bun` and always wins, except for the one combination that silently discards
112
+ * work, which throws instead. Refusing rather than warning matches the slot guard: a build that succeeds
113
+ * and omits the transform is worse than one that stops and says so.
114
+ */
115
+ export function chooseBuildPipeline(slots, forced) {
116
+ const vitePlugins = slots.vitePlugins ?? [];
117
+ const bunPlugins = [...(slots.clientPlugins ?? []), ...(slots.serverPlugins ?? [])];
118
+ const viteOnly = vitePlugins.length > 0 && bunPlugins.length === 0;
119
+ if (forced === "bun" && viteOnly) {
120
+ throw new Error("[nifra] `nifra build --bun` would silently drop this app's only transforms. `vitePlugins` are " +
121
+ "read by the Vite build; the Bun build reads `clientPlugins`/`serverPlugins`, so these would " +
122
+ "never run and the output would look plausible without them.\n" +
123
+ ` Would be dropped: ${pluginNames(vitePlugins)}\n` +
124
+ " Drop the flag to build with Vite, or add the Bun equivalents to `clientPlugins`/`serverPlugins`.");
125
+ }
126
+ if (forced !== undefined)
127
+ return { pipeline: forced };
128
+ if (viteOnly) {
129
+ return {
130
+ pipeline: "vite",
131
+ reason: `auto: this app's only transforms are \`vitePlugins\` (${pluginNames(vitePlugins)}), which the Bun build cannot run`,
132
+ };
133
+ }
134
+ return { pipeline: "bun" };
135
+ }
136
+ //# sourceMappingURL=pipeline-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-guard.js","sourceRoot":"","sources":["../src/pipeline-guard.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ export declare function searchTypes(types: readonly TypeEntry[], query: string,
30
30
  * per-package index of type names. The signatures are authoritative (generated from the built `.d.ts`),
31
31
  * so this is the complete answer — there is no need to read a `.d.ts` file.
32
32
  */
33
- export declare function renderTypesResult(types: readonly TypeEntry[], name: string | undefined, query: string | undefined, limit: number): string;
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
35
35
  * `src/` (dev) and `dist/` (published) alike. */
36
36
  export declare function loadTypesCorpus(): Promise<TypeEntry[] | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"types-search.d.ts","sourceRoot":"","sources":["../src/types-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAA;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;6BAC6B;AAC7B,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAGjF;AAED,oGAAoG;AACpG,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,UAAU,EAAE,CAed;AAQD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,EAAE,MAAM,GACZ,MAAM,CA0BR;AAED;iDACiD;AACjD,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC,CAUxE"}
1
+ {"version":3,"file":"types-search.d.ts","sourceRoot":"","sources":["../src/types-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAA;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAW,SAAQ,SAAS;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;6BAC6B;AAC7B,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAGjF;AAED,oGAAoG;AACpG,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,UAAU,EAAE,CAed;AAuDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,EAAE,MAAM,EACb,IAAI,UAAQ,GACX,MAAM,CA2BR;AAED;iDACiD;AACjD,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC,CAUxE"}
@@ -40,12 +40,56 @@ function renderEntry(t) {
40
40
  const doc = t.doc ? `${t.doc}\n` : "";
41
41
  return `${head}\n\n\`\`\`ts\n${doc}${t.signature}\n\`\`\``;
42
42
  }
43
+ /**
44
+ * Longest signature rendered whole in a SEARCH result.
45
+ *
46
+ * Search is "which symbol did I mean?", not "show me everything about it" - and the corpus is wildly
47
+ * uneven: the median declaration is small, while `Server` alone is ~32,000 characters, five times the
48
+ * next largest. One broad query that happened to match it returned the entire class for near-zero
49
+ * value. Above this, a search result shows the head plus a member count and says how to get the rest;
50
+ * an exact `name` lookup is never collapsed, because there the caller asked for that symbol.
51
+ */
52
+ const SEARCH_SIGNATURE_MAX = 600;
53
+ /** First sentence of the doc block, flattened to one line - enough to pick between candidates. */
54
+ function summarize(doc) {
55
+ if (doc === undefined)
56
+ return "";
57
+ const text = doc
58
+ .replace(/\/\*\*|\*\/|^\s*\*/gm, " ")
59
+ .replace(/\s+/g, " ")
60
+ .trim();
61
+ if (text === "")
62
+ return "";
63
+ const end = text.search(/\.\s|\.$/);
64
+ const sentence = end === -1 ? text : text.slice(0, end + 1);
65
+ return sentence.length > 200 ? `${sentence.slice(0, 197)}...` : sentence;
66
+ }
67
+ /** Keep the declaration head; replace a long body with a member count. */
68
+ function collapseSignature(signature) {
69
+ const open = signature.indexOf("{");
70
+ if (open === -1)
71
+ return `${signature.slice(0, SEARCH_SIGNATURE_MAX)}…`;
72
+ const body = signature.slice(open);
73
+ // Rough but honest: count top-level-looking member lines rather than parsing TypeScript here.
74
+ const members = body.split("\n").filter((line) => /^\s{2,4}\S/.test(line)).length;
75
+ return `${signature.slice(0, open).trim()} { … ${members} members … }`;
76
+ }
77
+ /** A search hit: enough to choose, not the whole declaration. */
78
+ function renderSearchHit(t) {
79
+ const head = `## ${t.name} — ${t.kind}, \`${t.package}\``;
80
+ const summary = summarize(t.doc);
81
+ const signature = t.signature.length <= SEARCH_SIGNATURE_MAX ? t.signature : collapseSignature(t.signature);
82
+ const note = t.signature.length <= SEARCH_SIGNATURE_MAX
83
+ ? ""
84
+ : `\n_Collapsed (${t.signature.length} chars). Call \`nifra_types({ name: "${t.name}" })\` for the full declaration._`;
85
+ return `${head}\n${summary ? `\n${summary}\n` : ""}\n\`\`\`ts\n${signature}\n\`\`\`${note}`;
86
+ }
43
87
  /**
44
88
  * Render the tool result. `name` → the exact declaration(s); else `query` → the top matches; else a
45
89
  * per-package index of type names. The signatures are authoritative (generated from the built `.d.ts`),
46
90
  * so this is the complete answer — there is no need to read a `.d.ts` file.
47
91
  */
48
- export function renderTypesResult(types, name, query, limit) {
92
+ export function renderTypesResult(types, name, query, limit, full = false) {
49
93
  if (name !== undefined && name.trim() !== "") {
50
94
  const hits = lookupType(types, name);
51
95
  if (hits.length === 0) {
@@ -58,7 +102,8 @@ export function renderTypesResult(types, name, query, limit) {
58
102
  if (matches.length === 0) {
59
103
  return `No types matched ${JSON.stringify(query)}. Try a broader term, pass an exact \`name\`, or call with no args for the index.`;
60
104
  }
61
- return matches.map(renderEntry).join("\n\n---\n\n");
105
+ // Search results are collapsed by default; `full` opts back into whole declarations.
106
+ return matches.map(full ? renderEntry : renderSearchHit).join("\n\n---\n\n");
62
107
  }
63
108
  const byPkg = new Map();
64
109
  for (const t of types) {
@@ -1 +1 @@
1
- {"version":3,"file":"types-search.js","sourceRoot":"","sources":["../src/types-search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types-search.js","sourceRoot":"","sources":["../src/types-search.ts"],"names":[],"mappings":"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"}
@@ -125,6 +125,12 @@
125
125
  "slug": "deployment",
126
126
  "code": "// _worker.ts — the edge SSR entry\nimport { toFetchHandler } from \"@nifrajs/core/server\"\nimport { createWebApp } from \"@nifrajs/web\"\nimport { reactAdapter } from \"@nifrajs/web-react\"\nimport { clientEntry, manifest } from \"./server-manifest\" // generated by buildServer\n\nconst app = createWebApp({ adapter: reactAdapter, manifest, clientEntry })\nexport default toFetchHandler(app) // a Workers/Pages fetch handler\n\n// build.ts — buildClient (→ /assets) + buildServer (edge conditions → _worker.js)\nimport { buildClient, buildServer } from \"@nifrajs/web/build\""
127
127
  },
128
+ {
129
+ "name": "VITE_PROD",
130
+ "topic": "Nifra — Dev & HMR",
131
+ "slug": "dev",
132
+ "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}"
133
+ },
128
134
  {
129
135
  "name": "LAB",
130
136
  "topic": "Nifra - Failure laboratory",
@@ -780,15 +780,15 @@ cache. Pass the same `draftSecret` to `createWebApp` and
780
780
 
781
781
  ```ts
782
782
  // doc-check: skip — fragment spanning three files: your `app`, `env`, `redirect`, and route `slug`.
783
- // 1. A route you gate yourself flips draft mode on for the editor.
784
- import { enableDraft, disableDraft } from "@nifrajs/web"
783
+ // 1. Mount the preview entry point your CMS links to. It checks the token in CONSTANT TIME,
784
+ // sets the signed HttpOnly cookie, and refuses an off-site ?to= (an open redirect otherwise).
785
+ import { previewEndpoint, disableDraft } from "@nifrajs/web"
785
786
 
786
- app.get("/api/preview", async (c) => {
787
- if (c.query.token !== env.PREVIEW_TOKEN) return new Response("nope", { status: 401 })
788
- await enableDraft(c, env.DRAFT_SECRET) // signed, HttpOnly cookie
789
- return redirect(String(c.query.to ?? "/"))
790
- })
787
+ const preview = previewEndpoint({ secret: env.PREVIEW_TOKEN, draftSecret: env.DRAFT_SECRET })
788
+ app.get("/api/preview", (c) => preview(c.req)) // ?token=…&to=/posts/hello
791
789
  app.get("/api/preview/exit", (c) => (disableDraft(c), redirect("/")))
790
+ // Gating it yourself instead? Use enableDraft(c, env.DRAFT_SECRET) AFTER your own check — and
791
+ // compare the token in constant time, since === leaks it one character at a time.
792
792
 
793
793
  // 2. Loaders branch on ctx.draft to load unpublished content.
794
794
  export async function loader({ api, draft }: LoaderArgs<typeof app>) {
@@ -2817,11 +2817,13 @@ the command printed by the build.
2817
2817
 
2818
2818
  ## Nifra — Dev & HMR
2819
2819
 
2820
- > Use `nifra dev` for a Vite-backed state-preserving HMR loop, or `@nifrajs/web/dev` for a Bun-native live-reload loop. Production builds use Bun.
2820
+ > Use `nifra dev` for a Vite-backed state-preserving HMR loop, or `@nifrajs/web/dev` for a Bun-native HMR loop with no Vite dependency. Production builds use Bun.
2821
2821
 
2822
- Nifra gives you two local development loops. Use `nifra dev` for
2823
- state-preserving UI edits, or use `@nifrajs/web/dev` when you want a Bun-only
2824
- live-reload server. Both serve your real SSR app locally.
2822
+ Nifra gives you two local development loops, and the rule between them is that one
2823
+ toolchain owns a whole phase. **Both** give you React Fast Refresh with state
2824
+ preserved. Use `nifra dev` (Vite) for the plugin ecosystem, or
2825
+ `nifra dev --bun` for one bundler across dev and prod with no Vite dependency.
2826
+ Both serve your real SSR app locally, and neither mixes the two bundlers in one process.
2825
2827
 
2826
2828
  ### Two loops, same app
2827
2829
 
@@ -2832,9 +2834,9 @@ use when
2832
2834
 
2833
2835
  `@nifrajs/web/dev`
2834
2836
 
2835
- rebuild + full-page live-reload
2837
+ Bun HMR + Fast Refresh (native)
2836
2838
  none (Bun only)
2837
- default; zero extra deps
2839
+ one bundler dev+prod; no Vite dep (no CSS Modules)
2838
2840
 
2839
2841
  `@nifrajs/web/vite`
2840
2842
 
@@ -2882,50 +2884,44 @@ can edit routes, components, loaders, actions, and styles in one local loop.
2882
2884
 
2883
2885
  ### Framework coverage
2884
2886
 
2885
- All five adapters have a dev setup. Pass the framework's official Vite plugin, and for Vue,
2886
- Svelte, and Solid, preload the matching Nifra compiler plugin for server rendering.
2887
+ All five adapters have a dev setup. Pass the framework's official Vite plugin and you are
2888
+ done: under `nifra dev` the Vite pipeline compiles both halves, so the same plugin
2889
+ that transforms your components for the browser also transforms them for SSR. No separate
2890
+ server-side compiler plugin to preload, and no way for the two halves to disagree about a
2891
+ specifier.
2887
2892
 
2888
2893
  framework
2889
- client (Vite plugin)
2890
- SSR compile
2894
+ Vite plugin (client + SSR)
2891
2895
  local state on edit
2892
2896
 
2893
2897
  React
2894
2898
 
2895
2899
  `@vitejs/plugin-react`
2896
2900
 
2897
- Bun-native
2898
2901
  preserved (Fast Refresh)
2899
2902
 
2900
2903
  Preact
2901
2904
 
2902
2905
  `@preact/preset-vite`
2903
2906
 
2904
- Bun-native
2905
2907
  preserved (prefresh)
2906
2908
 
2907
2909
  Vue
2908
2910
 
2909
2911
  `@vitejs/plugin-vue`
2910
2912
 
2911
- `vueBunPlugin` preload
2912
-
2913
2913
  preserved (rerender)
2914
2914
 
2915
2915
  Solid
2916
2916
 
2917
2917
  `vite-plugin-solid` (`{ ssr: true }`)
2918
2918
 
2919
- `solidBunPlugin` preload
2920
-
2921
2919
  resets (solid-refresh)
2922
2920
 
2923
2921
  Svelte
2924
2922
 
2925
2923
  `@sveltejs/vite-plugin-svelte`
2926
2924
 
2927
- `svelteBunPlugin` preload
2928
-
2929
2925
  resets (svelte HMR)
2930
2926
 
2931
2927
  For **React, Preact, and Vue**, an edit hot-swaps with component state preserved.
@@ -2968,17 +2964,72 @@ instead.
2968
2964
 
2969
2965
  ### The zero-dep alternative
2970
2966
 
2971
- `@nifrajs/web/dev` is a self-contained server: it builds the client on boot,
2972
- serves SSR, watches the routes, and does a full-page reload on change.
2967
+ `nifra dev --bun` (library: `@nifrajs/web/dev`) is self-contained —
2968
+ no Vite anywhere. `Bun.serve`'s native HMR bundles and hot-reloads the client while
2969
+ Bun's runtime resolves SSR, and it applies **React Fast Refresh natively**: editing
2970
+ a component-only module swaps its markup with `useState` state intact, no reload. The
2971
+ boundary rule is the same as Vite's (see below). The real prize is that dev and production use
2972
+ the _same bundler_, so the dev/prod seam disappears.
2973
+
2974
+ One gap: **CSS Modules**. Bun's dev-server bundler has no
2975
+ `*.module.css` transform (its production `Bun.build` does), so the CLI
2976
+ refuses `--bun` for a CSS-Modules app rather than serving a broken client. Plain CSS
2977
+ and Tailwind work normally.
2973
2978
 
2974
2979
  ```ts
2975
2980
  // doc-check: skip — fragment: routesDir/outDir/clientModule/createApp are your app's dev config.
2976
- // dev.ts — Bun-native live reload
2981
+ // dev.ts — Bun-native HMR, no Vite in the process
2977
2982
  import { createDevServer } from "@nifrajs/web/dev"
2978
- // builds the client on boot, serves SSR, watches routes, full-page live-reload on change.
2983
+ // Bun.serve bundles + hot-reloads the client; Bun's runtime resolves SSR. An edit reloads the
2984
+ // changed module graph — with React Fast Refresh (state preserved) applied natively by Bun, no plugin.
2985
+ // CSS + the entry URL come from Bun. Plain CSS/Tailwind work; *.module.css does not (Bun's dev bundler).
2979
2986
  const server = await createDevServer({ routesDir, outDir, clientModule, createApp })
2980
2987
  ```
2981
2988
 
2989
+ ### Production is Bun — with a Vite escape hatch
2990
+
2991
+ Production builds default to **Bun** (`buildClient` /
2992
+ `nifra build`): faster, Bun-native, and the profile Nifra is tuned for. If an app
2993
+ genuinely needs a **Vite-only transform** with no Bun equivalent, you can run a
2994
+ Vite/Rollup production client build instead — but it must carry the same client-leak guards the
2995
+ Bun build enforces, or a second pipeline becomes a way for server-only code to reach the
2996
+ browser unnoticed. Add `viteLeakGuard()`: it runs the _same_ detection and
2997
+ emits the _same_ error as the Bun build (one implementation, adapted to Rollup's graph),
2998
+ so `node:` builtins and `server-only` modules fail the build either way.
2999
+
3000
+ ```ts
3001
+ // vite.config.ts — a Vite/Rollup PRODUCTION client build (the escape hatch, not the default).
3002
+ // Only reach for this when an app needs a Vite-only transform with no Bun equivalent; nifra's default
3003
+ // production bundler stays Bun (buildClient), which is faster and Bun-native.
3004
+ import { viteLeakGuard } from "@nifrajs/web/plugins/vite-leak-guard"
3005
+
3006
+ export default {
3007
+ build: {
3008
+ // The SAME two client-leak guards nifra's Bun build runs — server-only code or a node: builtin
3009
+ // reaching the browser fails the build, with the identical error message. A second production
3010
+ // pipeline must not ship without them.
3011
+ rollupOptions: { plugins: [viteLeakGuard()] },
3012
+ },
3013
+ }
3014
+ ```
3015
+
3016
+ For the full deploy, `nifra build --vite --target <t>` builds
3017
+ _both_ halves — client and SSR worker — with Vite and assembles the identical per-target
3018
+ deploy dir the Bun build produces (same `_worker.js` / `server.js`, same
3019
+ `_routes.json`, same prerender + size report). Only the bundler differs: both go
3020
+ through one orchestrator, so the deploy shape can't drift between pipelines. The leak guards run
3021
+ automatically.
3022
+
3023
+ You usually don't need the flag. `nifra build` picks the bundler from your config:
3024
+ Bun by default, but Vite when your _only_ transforms are `vitePlugins`, and it
3025
+ prints the reason. That case is the one where the phase defaults would otherwise bite — dev runs
3026
+ Vite, so your plugins run; the Bun build reads `clientPlugins`/
3027
+ `serverPlugins` and never `vitePlugins`, so it would drop them and still
3028
+ succeed. An app declaring both slots has supplied the Bun equivalent on purpose, so it keeps the
3029
+ faster Bun default. `--vite` and `--bun` force the choice; `--bun`
3030
+ is refused for a `vitePlugins`-only app rather than silently building without your
3031
+ transforms.
3032
+
2982
3033
  ### Styling (CSS)
2983
3034
 
2984
3035
  Import a stylesheet anywhere — `import "./app.css"` in a route, layout, or component.
@@ -7486,7 +7537,7 @@ done-gate, or `nifra mcp` to give the agent live project tools.
7486
7537
 
7487
7538
  ## @nifrajs/web
7488
7539
 
7489
- `Action`, `ACTION_GLOBAL`, `apiKey`, `assertRenderAdapterConformance`, `BrowserNavigate`, `buildManifest`, `CachedResponse`, `CacheStore`, `canonical`, `clientEntry`, `ClientRouter`, `ClientRouterOptions`, `createClientRouter`, `createMatcher`, `createMutation`, `createQueryClient`, `createWebApp`, `CreateWebAppOptions`, `DATA_GLOBAL`, `DATA_HEADER`, `DEFAULT_DEV_PORT`, `defer`, `Deferred`, `DehydratedState`, `disableDraft`, `DRAFT_COOKIE`, `DraftCookieControls`, `enableDraft`, `EnableDraftOptions`, `enumerateStaticRoutes`, `Fetcher`, `FetcherState`, `FetchRouteData`, `filePathToPattern`, `filePathToPatterns`, `FontDisplay`, `fontFace`, `FontFace`, `fontPreload`, `FontPreloadInput`, `FontSource`, `generateClientEntry`, `GenerateClientEntryOptions`, `generateServerManifest`, `GenerateServerManifestOptions`, `getBrowserNavigate`, `GetStaticPaths`, `hashQueryKey`, `InfiniteData`, `InfiniteQueryHandle`, `InfiniteQueryOptions`, `isDraftEnabled`, `ISR_REVALIDATE_HEADER`, `ISR_STATUS_HEADER`, `ISRApp`, `ISROptions`, `ISRPlatform`, `jsonLd`, `KVCacheStore`, `KVCacheStoreOptions`, `KVNamespaceLike`, `LayoutEntry`, `LinkDescriptor`, `Loader`, `LoaderContext`, `manifest`, `Manifest`, `MemoryCacheStore`, `MemoryCacheStoreOptions`, `mergeHeads`, `meta`, `Meta`, `MetaArgs`, `MetaInput`, `MountRouterOptions`, `MutationCallbacks`, `MutationHandle`, `MutationState`, `MutationStatus`, `NavigateOptions`, `openGraph`, `OpenGraphInput`, `PRE_HYDRATION_GUARD`, `QueryClient`, `QueryClientOptions`, `QueryHandle`, `QueryOptions`, `QueryState`, `QueryStatus`, `redirect`, `REDIRECT_HEADER`, `RedirectOptions`, `RenderAdapter`, `RenderAdapterConformanceError`, `RenderAdapterConformanceFixture`, `RenderedPage`, `renderPage`, `RenderPageOptions`, `renderPageResult`, `RenderProps`, `resolveMeta`, `revalidate`, `REVALIDATE_HEADER`, `revalidateEndpoint`, `RevalidateEndpointOptions`, `RevalidateResult`, `ROUTE_GLOBAL`, `RouteEntry`, `RouteMatch`, `RouteModule`, `RoutePattern`, `RouterState`, `routeStyles`, `ScriptDescriptor`, `serializeData`, `ServerOnly`, `setBrowserNavigate`, `StaticPath`, `StaticPaths`, `StaticRoutes`, `styles`, `Submission`, `SubmitOptions`, `withISR`
7540
+ `Action`, `ACTION_GLOBAL`, `apiKey`, `assertRenderAdapterConformance`, `BrowserNavigate`, `buildManifest`, `CachedResponse`, `CacheStore`, `canonical`, `clientEntry`, `ClientRouter`, `ClientRouterOptions`, `createClientRouter`, `createMatcher`, `createMutation`, `createQueryClient`, `createWebApp`, `CreateWebAppOptions`, `DATA_GLOBAL`, `DATA_HEADER`, `DEFAULT_DEV_PORT`, `defer`, `Deferred`, `DehydratedState`, `disableDraft`, `DRAFT_COOKIE`, `DraftCookieControls`, `enableDraft`, `EnableDraftOptions`, `enumerateStaticRoutes`, `Fetcher`, `FetcherState`, `FetchRouteData`, `filePathToPattern`, `filePathToPatterns`, `FontDisplay`, `fontFace`, `FontFace`, `fontPreload`, `FontPreloadInput`, `FontSource`, `gate`, `generateClientEntry`, `GenerateClientEntryOptions`, `generateServerManifest`, `GenerateServerManifestOptions`, `getBrowserNavigate`, `GetStaticPaths`, `gone`, `hashQueryKey`, `InfiniteData`, `InfiniteQueryHandle`, `InfiniteQueryOptions`, `isDraftEnabled`, `ISR_REVALIDATE_HEADER`, `ISR_STATUS_HEADER`, `ISRApp`, `ISROptions`, `ISRPlatform`, `jsonLd`, `KVCacheStore`, `KVCacheStoreOptions`, `KVNamespaceLike`, `LAYOUT_DATA_GLOBAL`, `LayoutEntry`, `LinkDescriptor`, `Loader`, `LoaderContext`, `manifest`, `Manifest`, `MemoryCacheStore`, `MemoryCacheStoreOptions`, `mergeHeads`, `meta`, `Meta`, `MetaArgs`, `MetaInput`, `MountRouterOptions`, `MutationCallbacks`, `MutationHandle`, `MutationState`, `MutationStatus`, `NAV_FROM_HEADER`, `NavigateOptions`, `notFound`, `openGraph`, `OpenGraphInput`, `PRE_HYDRATION_GUARD`, `previewEndpoint`, `PreviewEndpointOptions`, `PublicDirCache`, `QueryClient`, `QueryClientOptions`, `QueryHandle`, `QueryOptions`, `QueryState`, `QueryStatus`, `redirect`, `REDIRECT_HEADER`, `RedirectOptions`, `RenderAdapter`, `RenderAdapterConformanceError`, `RenderAdapterConformanceFixture`, `RenderedPage`, `renderPage`, `RenderPageOptions`, `renderPageResult`, `RenderProps`, `resolveMeta`, `resolvePublicPath`, `RETAIN_HEADER`, `revalidate`, `REVALIDATE_HEADER`, `revalidateEndpoint`, `RevalidateEndpointOptions`, `RevalidateResult`, `ROUTE_GLOBAL`, `RouteEntry`, `RouteMatch`, `RouteModule`, `RoutePattern`, `RouterState`, `routeStyles`, `ScriptDescriptor`, `serializeData`, `servePublicDir`, `ServePublicDirOptions`, `ServerOnly`, `setBrowserNavigate`, `StaticPath`, `StaticPaths`, `StaticRoutes`, `STATUS_HEADER`, `statusPage`, `StatusPageOptions`, `styles`, `Submission`, `SubmitOptions`, `withISR`
7490
7541
 
7491
7542
  ## @nifrajs/web-preact
7492
7543