create-nifra 3.3.0 → 4.0.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 (106) hide show
  1. package/README.md +3 -3
  2. package/dist/agent-files.d.ts +41 -26
  3. package/dist/agent-files.d.ts.map +1 -1
  4. package/dist/agent-files.js +80 -47
  5. package/dist/agent-files.js.map +1 -1
  6. package/dist/agents.d.ts +3 -3
  7. package/dist/agents.d.ts.map +1 -1
  8. package/dist/agents.js +65 -69
  9. package/dist/agents.js.map +1 -1
  10. package/dist/auth.d.ts +5 -4
  11. package/dist/auth.d.ts.map +1 -1
  12. package/dist/auth.js +27 -14
  13. package/dist/auth.js.map +1 -1
  14. package/dist/cli.d.ts +23 -25
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +141 -130
  17. package/dist/cli.js.map +1 -1
  18. package/dist/db.d.ts.map +1 -1
  19. package/dist/db.js +49 -48
  20. package/dist/db.js.map +1 -1
  21. package/dist/scaffold/frameworks.d.ts +13 -38
  22. package/dist/scaffold/frameworks.d.ts.map +1 -1
  23. package/dist/scaffold/frameworks.js +9 -32
  24. package/dist/scaffold/frameworks.js.map +1 -1
  25. package/dist/scaffold/route-types.d.ts +9 -0
  26. package/dist/scaffold/route-types.d.ts.map +1 -0
  27. package/dist/scaffold/route-types.js +61 -0
  28. package/dist/scaffold/route-types.js.map +1 -0
  29. package/dist/scaffold/site-files.d.ts +14 -8
  30. package/dist/scaffold/site-files.d.ts.map +1 -1
  31. package/dist/scaffold/site-files.js +29 -60
  32. package/dist/scaffold/site-files.js.map +1 -1
  33. package/dist/scaffold/site.d.ts +8 -5
  34. package/dist/scaffold/site.d.ts.map +1 -1
  35. package/dist/scaffold/site.js +47 -34
  36. package/dist/scaffold/site.js.map +1 -1
  37. package/dist/scaffold/targets.d.ts +39 -0
  38. package/dist/scaffold/targets.d.ts.map +1 -0
  39. package/dist/scaffold/targets.js +116 -0
  40. package/dist/scaffold/targets.js.map +1 -0
  41. package/package.json +4 -1
  42. package/src/agent-files.ts +89 -47
  43. package/template/README.md +12 -6
  44. package/template/{src → backend}/app.test.ts +2 -2
  45. package/template/{src → backend}/app.ts +8 -3
  46. package/template/{src → backend}/index.ts +2 -2
  47. package/template/{src → backend}/routes.ts +1 -1
  48. package/template/nifra.assurance.ts +16 -14
  49. package/template/package.json +7 -7
  50. package/template/tsconfig.json +1 -1
  51. package/template-batteries/README.md +10 -6
  52. package/template-batteries/{src → backend}/app.test.ts +23 -23
  53. package/template-batteries/{src → backend}/app.ts +8 -3
  54. package/template-batteries/{src → backend}/index.ts +2 -2
  55. package/template-batteries/{src → backend}/notes.ts +1 -1
  56. package/template-batteries/nifra.assurance.ts +16 -14
  57. package/template-batteries/package.json +10 -9
  58. package/template-batteries/tsconfig.json +1 -1
  59. package/template-isr/README.md +3 -0
  60. package/template-isr/{backend.ts → backend/app.ts} +2 -0
  61. package/template-isr/{server.ts → backend/dev-server.ts} +4 -4
  62. package/template-isr/{worker.ts → backend/worker.ts} +4 -2
  63. package/template-isr/build.ts +3 -1
  64. package/template-isr/gitignore +7 -1
  65. package/template-isr/nifra.assurance.ts +15 -13
  66. package/template-isr/package.json +10 -9
  67. package/template-isr/routes/index.backend.ts +14 -0
  68. package/template-isr/routes/index.tsx +5 -17
  69. package/template-isr/tsconfig.json +5 -4
  70. package/template-isr/wrangler.toml +6 -1
  71. package/template-site/README.md +24 -23
  72. package/template-site/{backend.ts → backend/app.ts} +6 -4
  73. package/template-site/gitignore +5 -10
  74. package/template-site/nifra.assurance.ts +15 -13
  75. package/template-site/nifra.config.ts +4 -4
  76. package/template-site/routes/index.backend.ts +18 -0
  77. package/template-site/routes/index.tsx +2 -15
  78. package/template-site-preact/README.md +21 -20
  79. package/template-site-preact/nifra.config.ts +4 -4
  80. package/template-site-preact/routes/_layout.tsx +3 -1
  81. package/template-site-preact/routes/index.tsx +2 -15
  82. package/template-site-solid/README.md +21 -19
  83. package/template-site-solid/nifra.config.ts +5 -4
  84. package/template-site-solid/routes/index.tsx +2 -15
  85. package/template-site-svelte/README.md +21 -19
  86. package/template-site-svelte/nifra.config.ts +3 -2
  87. package/template-site-svelte/routes/index.svelte +3 -16
  88. package/template-site-vue/README.md +21 -20
  89. package/template-site-vue/nifra.config.ts +4 -3
  90. package/template-site-vue/routes/_layout.vue +21 -25
  91. package/template-site-vue/routes/index.vue +12 -18
  92. package/dist/scaffold/site-build.d.ts +0 -60
  93. package/dist/scaffold/site-build.d.ts.map +0 -1
  94. package/dist/scaffold/site-build.js +0 -198
  95. package/dist/scaffold/site-build.js.map +0 -1
  96. package/template-site/.dockerignore +0 -14
  97. package/template-site/Dockerfile +0 -20
  98. package/template-site/_worker.ts +0 -17
  99. package/template-site/deno.json +0 -7
  100. package/template-site/server-bun.ts +0 -35
  101. package/template-site/server-deno.ts +0 -38
  102. package/template-site/server-node.ts +0 -40
  103. package/template-site/server-vercel.ts +0 -17
  104. package/template-site/wrangler.toml +0 -4
  105. /package/template-isr/{page.ts → backend/page.ts} +0 -0
  106. /package/template-site/{counter.ts → backend/counter.ts} +0 -0
@@ -1,24 +1,11 @@
1
- import type { ActionArgs, LoaderArgs, LoaderData } from "@nifrajs/client"
2
- import type { backend } from "../backend"
1
+ import type { Route } from "./+types/index"
3
2
 
4
3
  export const meta = {
5
4
  title: "nifra site",
6
5
  meta: [{ name: "description", content: "A nifra + Preact SSR site, deployable to every runtime." }],
7
6
  }
8
7
 
9
- // Loader runs on the server (in-process during SSR). The action handles the form POST; after a
10
- // client submit the loader revalidates with no full reload (progressive enhancement).
11
- export async function loader({ api }: LoaderArgs<typeof backend>) {
12
- const res = await api.count.get()
13
- return { count: res.ok ? res.data.count : 0 }
14
- }
15
-
16
- export async function action({ api }: ActionArgs<typeof backend>) {
17
- await api.count.post()
18
- return { ok: true }
19
- }
20
-
21
- export default function Home(props: { data: LoaderData<typeof loader> }) {
8
+ export default function Home(props: Route.ComponentProps) {
22
9
  return (
23
10
  <>
24
11
  <section className="hero">
@@ -7,7 +7,6 @@ is Web-standard, so only the server entry + build target differ per platform.
7
7
  ```sh
8
8
  bun install
9
9
  bun run dev # nifra dev → true-HMR dev server (Bun + nifra SSR) at http://localhost:3000
10
- bun run preview # wrangler pages dev dist → preview the built Cloudflare bundle (run `bun run build` first)
11
10
  ```
12
11
 
13
12
  The backend starter includes response security headers, explicit-origin CORS, a 30-second request
@@ -18,32 +17,35 @@ evidence in `nifra.assurance.ts`.
18
17
 
19
18
  Routes are Solid function components (JSX, compiled by `solidBunPlugin`) under `routes/` - `index.tsx`
20
19
  (landing + a live loader/action counter), `_layout.tsx` (chrome), `_404.tsx`. The frontend adapter is
21
- one line in `framework.ts`; data lives behind `backend.ts`. Each `build*` script wires
20
+ one line in `backend/framework.ts`; data lives behind `backend/app.ts`. Each `build*` script wires
22
21
  `solidBunPlugin("dom")` for the client and `("ssr")` + the `solid` export condition for the server.
23
22
 
24
- ## Deploy - pick a target
23
+ ## Deploy
25
24
 
26
- | target | build | deploy |
25
+ The app deploys to the `target` in `nifra.config.ts` (chosen with `bun create nifra --target`, switched
26
+ with `nifra target <t>`). `bun run build` runs `nifra build`, which generates that target's server
27
+ entry from `backend/` and `routes/`, so the app carries no per-runtime entry or build script.
28
+
29
+ | target | `bun run build` emits | then |
27
30
  | --- | --- | --- |
28
- | **Bun** (flagship) | `bun run build:bun` | `bun run start` - any host |
29
- | **Node** | `bun run build:node` | `docker build -t app . && docker run -p 3000:3000 app` (or `bun run start:node`) |
30
- | **Deno Deploy** | `bun run build:deno` | `deployctl deploy --prod --entrypoint=dist-deno/server-deno.js` |
31
- | **Cloudflare Pages** | `bun run build` | `bun run deploy:cf` |
32
- | **Vercel Edge** | `bun run build:vercel` | `bun run deploy:vercel` |
31
+ | **bun** (default) | `dist/server.js` + assets | `bun dist/server.js` on any host (`--docker` adds a Dockerfile) |
32
+ | **node** | `dist/server.js` + assets | `node dist/server.js` (`--docker` adds a Dockerfile) |
33
+ | **deno** | `dist/server.js` + assets | `deployctl deploy --prod --entrypoint=dist/server.js` |
34
+ | **cloudflare** | `dist/` (`_worker.js`, `_routes.json`) | `wrangler pages deploy dist` |
35
+ | **vercel** | `.vercel/output/` (Build Output API) | `vercel deploy --prebuilt` |
33
36
 
34
- nifra never enters your cloud credentials - it scaffolds the configs (`Dockerfile`, `deno.json`,
35
- `wrangler.toml`, Vercel Build Output API); you run the vendor CLI.
37
+ nifra never enters your cloud credentials: it writes the target's config (`wrangler.toml`,
38
+ `deno.json`, a `Dockerfile`) and you run the vendor CLI.
36
39
 
37
40
  ## Structure
38
41
 
39
42
  ```
40
43
  routes/ index.tsx (landing), _layout.tsx (chrome), _404.tsx
41
- framework.ts the adapter (solidAdapter) - imported by the server entries (edge-bundlable)
42
- nifra.config.ts the nifra CLI's dev/build config (adapter + clientModule + Solid plugins) - read by `nifra dev`
43
- backend.ts your contract (loaders/actions call it in-process)
44
- server-bun.ts Bun entry (Bun.serve) build-bun.ts → dist-bun/
45
- _worker.ts Cloudflare Pages entry build.ts → dist/
46
- server-node.ts Node entry (@nifrajs/node) build-node.ts → dist-node/ (Dockerfile)
47
- server-deno.ts Deno entry (@nifrajs/deno) build-deno.ts → dist-deno/ (deno.json)
48
- server-vercel.ts Vercel Edge function build-vercel.ts → .vercel/output/
44
+ index.backend.ts - the landing page's loader/action (server only)
45
+ backend/ app.ts - your contract (loaders/actions call it in-process)
46
+ framework.ts - the adapter (solidAdapter) - imported by the server entry `nifra build` generates (edge-bundlable)
47
+ frontend/ browser-only code (components, hooks) - created when you need it
48
+ shared/ code both sides import (types, formatting, validation) - may import only shared code
49
+ nifra.config.ts the nifra CLI's dev/build config (adapter + clientModule + Solid plugins) - read by `nifra dev` and `nifra build`,
50
+ including `target`, where `nifra build` deploys
49
51
  ```
@@ -1,11 +1,12 @@
1
1
  // The nifra CLI's config - read by `nifra dev|build|start`. It's imported ONLY by the CLI (which runs on
2
2
  // Bun), so it can eagerly import the Vite plugin + the Solid compile plugin - keeping them OUT of the
3
- // edge worker bundle that `framework.ts` (the render-adapter source the entries import) must stay free
4
- // of. Solid needs the `"solid"` resolve condition (routes solid-js to its source / JSX-dev build).
5
- import { solidBunPlugin } from "@nifrajs/web-solid"
3
+ // edge worker bundle that `backend/framework.ts` (the render-adapter source the generated server entry
4
+ // imports) must stay free of. Solid needs the `"solid"` resolve condition (routes solid-js to its
5
+ // source / JSX-dev build).
6
+ import { solidBunPlugin } from "@nifrajs/web-solid/plugin"
6
7
  import solid from "vite-plugin-solid"
7
8
 
8
- export { adapter } from "./framework"
9
+ export { adapter } from "./backend/framework"
9
10
  export const clientModule = "@nifrajs/web-solid/client"
10
11
  // `ssr: true` makes vite-plugin-solid emit hydratable client output, matching nifra's Bun SSR.
11
12
  export const vitePlugins = [solid({ ssr: true })]
@@ -1,24 +1,11 @@
1
- import type { ActionArgs, LoaderArgs, LoaderData } from "@nifrajs/client"
2
- import type { backend } from "../backend"
1
+ import type { Route } from "./+types/index"
3
2
 
4
3
  export const meta = {
5
4
  title: "nifra site",
6
5
  meta: [{ name: "description", content: "A nifra + Solid SSR site, deployable to every runtime." }],
7
6
  }
8
7
 
9
- // Loader runs on the server (in-process during SSR). The action handles the form POST; after a
10
- // client submit the loader revalidates with no full reload (progressive enhancement).
11
- export async function loader({ api }: LoaderArgs<typeof backend>) {
12
- const res = await api.count.get()
13
- return { count: res.ok ? res.data.count : 0 }
14
- }
15
-
16
- export async function action({ api }: ActionArgs<typeof backend>) {
17
- await api.count.post()
18
- return { ok: true }
19
- }
20
-
21
- export default function Home(props: { data: LoaderData<typeof loader> }) {
8
+ export default function Home(props: Route.ComponentProps) {
22
9
  return (
23
10
  <div>
24
11
  <section class="hero">
@@ -7,7 +7,6 @@ is Web-standard, so only the server entry + build target differ per platform.
7
7
  ```sh
8
8
  bun install
9
9
  bun run dev # nifra dev → true-HMR dev server (Bun + nifra SSR) at http://localhost:3000
10
- bun run preview # wrangler pages dev dist → preview the built Cloudflare bundle (run `bun run build` first)
11
10
  ```
12
11
 
13
12
  The backend starter includes response security headers, explicit-origin CORS, a 30-second request
@@ -20,32 +19,35 @@ Routes are `.svelte` components under `routes/` - `index.svelte` (landing + a li
20
19
  counter), `_layout.svelte` (chrome via the `children` snippet), `_404.svelte`. A route's
21
20
  `loader`/`action`/`meta` are named exports from its `<script module>` block; the page receives the
22
21
  loader output as the `data` prop (`let { data } = $props()`). The frontend adapter is one line in
23
- `framework.ts`; data lives behind `backend.ts`. Each `build*` script wires `svelteBunPlugin("dom")` for
22
+ `backend/framework.ts`; data lives behind `backend/app.ts`. Each `build*` script wires `svelteBunPlugin("dom")` for
24
23
  the client and `("ssr")` for the server.
25
24
 
26
- ## Deploy - pick a target
25
+ ## Deploy
27
26
 
28
- | target | build | deploy |
27
+ The app deploys to the `target` in `nifra.config.ts` (chosen with `bun create nifra --target`, switched
28
+ with `nifra target <t>`). `bun run build` runs `nifra build`, which generates that target's server
29
+ entry from `backend/` and `routes/`, so the app carries no per-runtime entry or build script.
30
+
31
+ | target | `bun run build` emits | then |
29
32
  | --- | --- | --- |
30
- | **Bun** (flagship) | `bun run build:bun` | `bun run start` - any host |
31
- | **Node** | `bun run build:node` | `docker build -t app . && docker run -p 3000:3000 app` (or `bun run start:node`) |
32
- | **Deno Deploy** | `bun run build:deno` | `deployctl deploy --prod --entrypoint=dist-deno/server-deno.js` |
33
- | **Cloudflare Pages** | `bun run build` | `bun run deploy:cf` |
34
- | **Vercel Edge** | `bun run build:vercel` | `bun run deploy:vercel` |
33
+ | **bun** (default) | `dist/server.js` + assets | `bun dist/server.js` on any host (`--docker` adds a Dockerfile) |
34
+ | **node** | `dist/server.js` + assets | `node dist/server.js` (`--docker` adds a Dockerfile) |
35
+ | **deno** | `dist/server.js` + assets | `deployctl deploy --prod --entrypoint=dist/server.js` |
36
+ | **cloudflare** | `dist/` (`_worker.js`, `_routes.json`) | `wrangler pages deploy dist` |
37
+ | **vercel** | `.vercel/output/` (Build Output API) | `vercel deploy --prebuilt` |
35
38
 
36
- nifra never enters your cloud credentials - it scaffolds the configs (`Dockerfile`, `deno.json`,
37
- `wrangler.toml`, Vercel Build Output API); you run the vendor CLI.
39
+ nifra never enters your cloud credentials: it writes the target's config (`wrangler.toml`,
40
+ `deno.json`, a `Dockerfile`) and you run the vendor CLI.
38
41
 
39
42
  ## Structure
40
43
 
41
44
  ```
42
45
  routes/ index.svelte (landing), _layout.svelte (chrome), _404.svelte
43
- framework.ts the adapter (svelteAdapter) - imported by the server entries (edge-bundlable)
44
- nifra.config.ts the nifra CLI's dev/build config (adapter + clientModule + Svelte plugins) - read by `nifra dev`
45
- backend.ts your contract (loaders/actions call it in-process)
46
- server-bun.ts Bun entry (Bun.serve) build-bun.ts → dist-bun/
47
- _worker.ts Cloudflare Pages entry build.ts → dist/
48
- server-node.ts Node entry (@nifrajs/node) build-node.ts → dist-node/ (Dockerfile)
49
- server-deno.ts Deno entry (@nifrajs/deno) build-deno.ts → dist-deno/ (deno.json)
50
- server-vercel.ts Vercel Edge function build-vercel.ts → .vercel/output/
46
+ index.backend.ts - the landing page's loader/action (server only)
47
+ backend/ app.ts - your contract (loaders/actions call it in-process)
48
+ framework.ts - the adapter (svelteAdapter) - imported by the server entry `nifra build` generates (edge-bundlable)
49
+ frontend/ browser-only code (components, hooks) - created when you need it
50
+ shared/ code both sides import (types, formatting, validation) - may import only shared code
51
+ nifra.config.ts the nifra CLI's dev/build config (adapter + clientModule + Svelte plugins) - read by `nifra dev` and `nifra build`,
52
+ including `target`, where `nifra build` deploys
51
53
  ```
@@ -1,10 +1,11 @@
1
1
  // The nifra CLI's config - read by `nifra dev|build|start`. It's imported ONLY by the CLI (which runs on
2
2
  // Bun), so it can eagerly import the Vite plugin + the Svelte compiler - keeping them OUT of the edge
3
- // worker bundle that `framework.ts` (the render-adapter source the entries import) must stay free of.
3
+ // worker bundle that `backend/framework.ts` (the render-adapter source the generated server entry
4
+ // imports) must stay free of.
4
5
  import { svelte } from "@sveltejs/vite-plugin-svelte"
5
6
  import { svelteBunPlugin } from "@nifrajs/web-svelte/plugin"
6
7
 
7
- export { adapter } from "./framework"
8
+ export { adapter } from "./backend/framework"
8
9
  export const clientModule = "@nifrajs/web-svelte/client"
9
10
  // `nifra dev` HMR: Vite + the official Svelte plugin compile/HMR the .svelte client modules.
10
11
  export const vitePlugins = [svelte()]
@@ -1,27 +1,14 @@
1
1
  <script module lang="ts">
2
- import type { ActionArgs, LoaderArgs } from "@nifrajs/client"
3
- import type { backend } from "../backend"
4
-
5
2
  export const meta = {
6
3
  title: "nifra site",
7
4
  meta: [{ name: "description", content: "A nifra + Svelte SSR site, deployable to every runtime." }],
8
5
  }
9
-
10
- // Loader runs on the server (in-process during SSR). The action handles the form POST; after a
11
- // client submit the loader revalidates with no full reload (progressive enhancement).
12
- export async function loader({ api }: LoaderArgs<typeof backend>) {
13
- const res = await api.count.get()
14
- return { count: res.ok ? res.data.count : 0 }
15
- }
16
-
17
- export async function action({ api }: ActionArgs<typeof backend>) {
18
- await api.count.post()
19
- return { ok: true }
20
- }
21
6
  </script>
22
7
 
23
8
  <script lang="ts">
24
- let { data } = $props()
9
+ import type { Route } from "./+types/index"
10
+
11
+ let { data }: Route.ComponentProps = $props()
25
12
  </script>
26
13
 
27
14
  <section class="hero">
@@ -7,7 +7,6 @@ is Web-standard, so only the server entry + build target differ per platform.
7
7
  ```sh
8
8
  bun install
9
9
  bun run dev # nifra dev → true-HMR dev server (Bun + nifra SSR) at http://localhost:3000
10
- bun run preview # wrangler pages dev dist → preview the built Cloudflare bundle (run `bun run build` first)
11
10
  ```
12
11
 
13
12
  The backend starter includes response security headers, explicit-origin CORS, a 30-second request
@@ -20,32 +19,34 @@ Routes are **Vue SFCs** (`.vue`) under `routes/` - `index.vue` (landing + a live
20
19
  counter), `_layout.vue` (chrome via the default slot), `_404.vue`. A route SFC's plain `<script>`
21
20
  carries nifra's `loader`/`action`/`meta`; `<script setup>` + `<template>` are the component. The
22
21
  build compiles them with `vueBunPlugin` (`@nifrajs/web-vue/plugin`). The frontend adapter is one line in
23
- `framework.ts`; data lives behind `backend.ts`.
22
+ `backend/framework.ts`; data lives behind `backend/app.ts`.
24
23
 
25
- ## Deploy - pick a target
24
+ ## Deploy
26
25
 
27
- | target | build | deploy |
26
+ The app deploys to the `target` in `nifra.config.ts` (chosen with `bun create nifra --target`, switched
27
+ with `nifra target <t>`). `bun run build` runs `nifra build`, which generates that target's server
28
+ entry from `backend/` and `routes/`, so the app carries no per-runtime entry or build script.
29
+
30
+ | target | `bun run build` emits | then |
28
31
  | --- | --- | --- |
29
- | **Bun** (flagship) | `bun run build:bun` | `bun run start` - any host |
30
- | **Node** | `bun run build:node` | `docker build -t app . && docker run -p 3000:3000 app` (or `bun run start:node`) |
31
- | **Deno Deploy** | `bun run build:deno` | `deployctl deploy --prod --entrypoint=dist-deno/server-deno.js` |
32
- | **Cloudflare Pages** | `bun run build` | `bun run deploy:cf` |
33
- | **Vercel Edge** | `bun run build:vercel` | `bun run deploy:vercel` |
32
+ | **bun** (default) | `dist/server.js` + assets | `bun dist/server.js` on any host (`--docker` adds a Dockerfile) |
33
+ | **node** | `dist/server.js` + assets | `node dist/server.js` (`--docker` adds a Dockerfile) |
34
+ | **deno** | `dist/server.js` + assets | `deployctl deploy --prod --entrypoint=dist/server.js` |
35
+ | **cloudflare** | `dist/` (`_worker.js`, `_routes.json`) | `wrangler pages deploy dist` |
36
+ | **vercel** | `.vercel/output/` (Build Output API) | `vercel deploy --prebuilt` |
34
37
 
35
- The routes, `backend.ts`, and the client bundle are shared; each `build*` script swaps `buildServer`'s
36
- target/conditions and the server entry. nifra never enters your cloud credentials - it scaffolds the
37
- configs (`Dockerfile`, `deno.json`, `wrangler.toml`, Vercel Build Output API); you run the vendor CLI.
38
+ nifra never enters your cloud credentials: it writes the target's config (`wrangler.toml`,
39
+ `deno.json`, a `Dockerfile`) and you run the vendor CLI.
38
40
 
39
41
  ## Structure
40
42
 
41
43
  ```
42
44
  routes/ index.vue (landing), _layout.vue (chrome), _404.vue (Vue SFCs)
43
- framework.ts the adapter (vueAdapter) - imported by the server entries (edge-bundlable)
44
- nifra.config.ts the nifra CLI's dev/build config (adapter + clientModule + Vue plugins) - read by `nifra dev`
45
- backend.ts your contract (loaders/actions call it in-process)
46
- server-bun.ts Bun entry (Bun.serve) build-bun.ts → dist-bun/
47
- _worker.ts Cloudflare Pages entry build.ts → dist/
48
- server-node.ts Node entry (@nifrajs/node) build-node.ts → dist-node/ (Dockerfile)
49
- server-deno.ts Deno entry (@nifrajs/deno) build-deno.ts → dist-deno/ (deno.json)
50
- server-vercel.ts Vercel Edge function build-vercel.ts → .vercel/output/
45
+ index.backend.ts - the landing page's loader/action (server only)
46
+ backend/ app.ts - your contract (loaders/actions call it in-process)
47
+ framework.ts - the adapter (vueAdapter) - imported by the server entry `nifra build` generates (edge-bundlable)
48
+ frontend/ browser-only code (components, hooks) - created when you need it
49
+ shared/ code both sides import (types, formatting, validation) - may import only shared code
50
+ nifra.config.ts the nifra CLI's dev/build config (adapter + clientModule + Vue plugins) - read by `nifra dev` and `nifra build`,
51
+ including `target`, where `nifra build` deploys
51
52
  ```
@@ -1,17 +1,18 @@
1
1
  // The nifra CLI's config - read by `nifra dev|build|start`. It's imported ONLY by the CLI (which runs on
2
2
  // Bun), so it can eagerly import the Vite plugin + the SFC compiler - keeping them OUT of the edge
3
- // worker bundle that `framework.ts` (the render-adapter source the entries import) must stay free of.
3
+ // worker bundle that `backend/framework.ts` (the render-adapter source the generated server entry
4
+ // imports) must stay free of.
4
5
  import { vueBunPlugin } from "@nifrajs/web-vue/plugin"
5
6
  import vue from "@vitejs/plugin-vue"
6
7
 
7
- export { adapter } from "./framework"
8
+ export { adapter } from "./backend/framework"
8
9
  export const clientModule = "@nifrajs/web-vue/client"
9
10
  // `nifra dev` HMR: Vite + the official Vue plugin compile/HMR the .vue client modules.
10
11
  export const vitePlugins = [vue()]
11
12
  // `nifra build` (client) + `nifra start`/`nifra dev` (Bun-side SSR) compile .vue via these.
12
13
  export const clientPlugins = [vueBunPlugin("dom")]
13
14
  export const serverPlugins = [vueBunPlugin("ssr")]
14
- // Vue feature flags the plugin doesn't inject (mirrors the build scripts' define).
15
+ // Vue feature flags the plugin doesn't inject; `nifra build` defines them in both bundles.
15
16
  export const define = {
16
17
  __VUE_OPTIONS_API__: "true",
17
18
  __VUE_PROD_DEVTOOLS__: "false",
@@ -1,33 +1,10 @@
1
1
  <!--
2
2
  Root layout (Vue SFC) - wraps every page; the compose fold passes the page through the default slot.
3
- The global stylesheet is rendered as a <style> element via <component :is="'style'"> - SFC <style>
4
- blocks are dropped (nifra has no CSS pipeline), and a dynamic component sidesteps the template
5
- compiler's special-casing of a literal <style> tag. Keeps the whole template single-file.
3
+ The <style> block is global (not scoped): @nifrajs/web-vue/plugin compiles it into the app
4
+ stylesheet, which the document links before it renders.
6
5
  -->
7
- <script setup lang="ts">
8
- const css = `
9
- :root { --bg:#0a0a0f; --panel:#14141c; --border:#26263a; --fg:#e8e8f0; --muted:#9a9ab0; --accent:#7c5cff; --accent2:#00d4ff; }
10
- * { box-sizing: border-box; } html { color-scheme: dark; }
11
- body { margin:0; background:var(--bg); color:var(--fg); font:16px/1.6 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; -webkit-font-smoothing:antialiased; }
12
- a { color:var(--accent2); text-decoration:none; } a:hover { text-decoration:underline; }
13
- .wrap { max-width:840px; margin:0 auto; padding:0 24px; }
14
- header.site { border-bottom:1px solid var(--border); } header.site .wrap { display:flex; align-items:center; justify-content:space-between; height:62px; }
15
- .logo, .logo b { font-weight:800; font-size:20px; } .logo b { background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; color:transparent; }
16
- .hero { text-align:center; padding:80px 0 56px; }
17
- .hero h1 { font-size:clamp(36px,6vw,56px); line-height:1.05; margin:0 0 16px; letter-spacing:-0.03em; }
18
- .hero h1 .grad { background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; color:transparent; }
19
- .hero p { font-size:19px; color:var(--muted); max-width:560px; margin:0 auto 28px; }
20
- .card { background:var(--panel); border:1px solid var(--border); border-radius:14px; padding:24px 28px; display:flex; align-items:center; justify-content:space-between; gap:24px; flex-wrap:wrap; }
21
- .card h3 { margin:0 0 4px; } .card p { margin:0; color:var(--muted); font-size:14px; }
22
- .count { font-size:34px; font-weight:800; font-variant-numeric:tabular-nums; min-width:44px; text-align:center; background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; color:transparent; }
23
- .btn { background:linear-gradient(90deg,var(--accent),var(--accent2)); color:#0a0a0f; font:inherit; font-weight:700; border:none; cursor:pointer; padding:11px 20px; border-radius:10px; }
24
- footer.site { border-top:1px solid var(--border); color:var(--muted); font-size:14px; } footer.site .wrap { padding:24px; }
25
- `.trim()
26
- </script>
27
-
28
6
  <template>
29
7
  <div id="app">
30
- <component :is="'style'">{{ css }}</component>
31
8
  <header class="site">
32
9
  <div class="wrap">
33
10
  <a href="/" class="logo"><b>nifra</b></a>
@@ -38,3 +15,22 @@ const css = `
38
15
  <footer class="site"><div class="wrap">Built with nifra</div></footer>
39
16
  </div>
40
17
  </template>
18
+
19
+ <style>
20
+ :root { --bg:#0a0a0f; --panel:#14141c; --border:#26263a; --fg:#e8e8f0; --muted:#9a9ab0; --accent:#7c5cff; --accent2:#00d4ff; }
21
+ * { box-sizing: border-box; } html { color-scheme: dark; }
22
+ body { margin:0; background:var(--bg); color:var(--fg); font:16px/1.6 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; -webkit-font-smoothing:antialiased; }
23
+ a { color:var(--accent2); text-decoration:none; } a:hover { text-decoration:underline; }
24
+ .wrap { max-width:840px; margin:0 auto; padding:0 24px; }
25
+ header.site { border-bottom:1px solid var(--border); } header.site .wrap { display:flex; align-items:center; justify-content:space-between; height:62px; }
26
+ .logo, .logo b { font-weight:800; font-size:20px; } .logo b { background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; color:transparent; }
27
+ .hero { text-align:center; padding:80px 0 56px; }
28
+ .hero h1 { font-size:clamp(36px,6vw,56px); line-height:1.05; margin:0 0 16px; letter-spacing:-0.03em; }
29
+ .hero h1 .grad { background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; color:transparent; }
30
+ .hero p { font-size:19px; color:var(--muted); max-width:560px; margin:0 auto 28px; }
31
+ .card { background:var(--panel); border:1px solid var(--border); border-radius:14px; padding:24px 28px; display:flex; align-items:center; justify-content:space-between; gap:24px; flex-wrap:wrap; }
32
+ .card h3 { margin:0 0 4px; } .card p { margin:0; color:var(--muted); font-size:14px; }
33
+ .count { font-size:34px; font-weight:800; font-variant-numeric:tabular-nums; min-width:44px; text-align:center; background:linear-gradient(90deg,var(--accent),var(--accent2)); -webkit-background-clip:text; background-clip:text; color:transparent; }
34
+ .btn { background:linear-gradient(90deg,var(--accent),var(--accent2)); color:#0a0a0f; font:inherit; font-weight:700; border:none; cursor:pointer; padding:11px 20px; border-radius:10px; }
35
+ footer.site { border-top:1px solid var(--border); color:var(--muted); font-size:14px; } footer.site .wrap { padding:24px; }
36
+ </style>
@@ -1,33 +1,27 @@
1
1
  <!--
2
- Home route (Vue SFC). The plain <script> carries nifra's route convention - loader/action/meta are
3
- server-only named exports the client codegen tree-shakes out. <script setup> + <template> are the
4
- component. The form POST is the SSR action path; after a client submit the loader revalidates with
2
+ Home route (Vue SFC). The plain <script> carries meta; loader and action live in index.backend.ts.
3
+ <script setup> + <template> are the component. The form POST is the SSR action path; after a client submit the loader revalidates with
5
4
  no full reload (progressive enhancement). Compiled by @nifrajs/web-vue/plugin.
6
5
  -->
7
6
  <script lang="ts">
8
- import type { ActionArgs, LoaderArgs } from "@nifrajs/client"
9
- import type { backend } from "../backend"
10
-
11
7
  export const meta = {
12
8
  title: "nifra site",
13
9
  meta: [{ name: "description", content: "A nifra + Vue SSR site, deployable to every runtime." }],
14
10
  }
15
-
16
- // Loader runs on the server (in-process during SSR). The action handles the form POST.
17
- export async function loader({ api }: LoaderArgs<typeof backend>) {
18
- const res = await api.count.get()
19
- return { count: res.ok ? res.data.count : 0 }
20
- }
21
-
22
- export async function action({ api }: ActionArgs<typeof backend>) {
23
- await api.count.post()
24
- return { ok: true }
25
- }
26
11
  </script>
27
12
 
28
13
  <script setup lang="ts">
14
+ import type { Route } from "./+types/index"
15
+
29
16
  // compose() spreads data/actionData/pending/submission as props - declare them so they aren't attrs.
30
- defineProps(["data", "actionData", "pending", "submission"])
17
+ // Listed rather than `defineProps<Route.ComponentProps>()`: the SFC compiler cannot follow an imported
18
+ // type through tsconfig `rootDirs`, but it can declare these keys and leave their types to TypeScript.
19
+ defineProps<{
20
+ data: Route.LoaderData
21
+ actionData?: Route.ActionData
22
+ pending?: Route.ComponentProps["pending"]
23
+ submission?: Route.ComponentProps["submission"]
24
+ }>()
31
25
  </script>
32
26
 
33
27
  <template>
@@ -1,60 +0,0 @@
1
- /**
2
- * The five build entries a site scaffold ships, emitted from one shape.
3
- *
4
- * These were 25 files - five deploy targets copied across five framework directories - and the
5
- * difference between any two of them is mechanical: which runtime it targets, and which compiler
6
- * plugin, resolve conditions and defines the framework needs. Nothing else. So the target owns its
7
- * own prose (what it emits, how to run it) and the framework contributes its injections, and the 25
8
- * files come out of one function.
9
- *
10
- * The emitted text is asserted byte-for-byte against the templates that used to be checked in, so
11
- * this is a refactor whose correctness is decided by a test rather than by reading.
12
- */
13
- import type { FrameworkSpec } from "./frameworks.js";
14
- /**
15
- * Where the framework's own resolve conditions sit inside a target's server list.
16
- *
17
- * The edge targets end theirs with `browser` (the worker has no Node builtins, so packages must be
18
- * asked for their browser build); Bun and Node do not. The framework's conditions go in the middle,
19
- * which is the order every checked-in template used.
20
- */
21
- export interface ServerConditionSlots {
22
- readonly before: readonly string[];
23
- readonly after: readonly string[];
24
- }
25
- export interface BuildTarget {
26
- /** File name, e.g. `build-bun.ts`. */
27
- readonly file: string;
28
- /** Leading comment block, without the trailing newline. */
29
- readonly header: string;
30
- /**
31
- * Frameworks whose header says something extra here - what their plugin does, or how to run the
32
- * output once their compiler is in the picture.
33
- *
34
- * These are the one genuinely non-mechanical part of a build entry, and they are the reason this
35
- * table exists rather than a pure `{framework, runtime}` function: the prose a framework needs to
36
- * explain itself is written, not derived. Keeping them beside the target they belong to is the
37
- * closest thing to having them in the file without having five copies of the file.
38
- */
39
- readonly headerOverrides?: Readonly<Record<string, string>>;
40
- /** Runtime conditions this target's SERVER build resolves with. */
41
- readonly serverConditions: ServerConditionSlots;
42
- /** Extra `node:fs` named imports beyond `cpSync, mkdirSync, rmSync`. */
43
- readonly extraFsImports?: readonly string[];
44
- /** Lines between the imports and the first build call, `dir` already declared. */
45
- readonly setup: string;
46
- /** Where `buildClient` writes assets, as a template expression. */
47
- readonly clientOutDir: string;
48
- /** The server entry this target boots from. */
49
- readonly serverEntry: string;
50
- /** Scratch directory for the server build. */
51
- readonly serverOutDir: string;
52
- /** `target:` line for `buildServer`, or empty for the default (workerd). */
53
- readonly serverTarget: string;
54
- /** Everything after the `buildServer` call. */
55
- readonly tail: string;
56
- }
57
- export declare const BUILD_TARGETS: readonly BuildTarget[];
58
- /** Emit one target's build entry for one framework. */
59
- export declare function renderBuildFile(target: BuildTarget, framework: FrameworkSpec): string;
60
- //# sourceMappingURL=site-build.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"site-build.d.ts","sourceRoot":"","sources":["../../src/scaffold/site-build.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAgEpD;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC3D,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,EAAE,oBAAoB,CAAA;IAC/C,wEAAwE;IACxE,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EA2H/C,CAAA;AAeD,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,GAAG,MAAM,CAsBrF"}