@voltro/cli 0.16.0 → 0.17.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/CHANGELOG.md +29 -0
- package/dist/{apiBuild-BJtiqoOu.js → apiBuild-ChdlLqGv.js} +2 -2
- package/dist/apiBuild-D5WB119b.js +2 -0
- package/dist/bin.js +3 -3
- package/dist/{commands-8A5RU3MW.js → commands-DYQbv-DG.js} +2328 -2292
- package/dist/dbCommand-C3R5LrBZ.js +2 -0
- package/dist/{dbCommand-OPMtO0sX.js → dbCommand-DJSxnjPt.js} +2 -2
- package/dist/{dev-HdG7JdSk.js → dev-BCmoJfBm.js} +1 -1
- package/dist/{dev-DGjEvvkV.js → dev-DB7pbLob.js} +89 -89
- package/dist/{fileConventions-Cu0tM-Bl.js → fileConventions-CBNPZVOV.js} +1 -1
- package/dist/frameworkTableAssembly-DRJrAVr1.js +2 -0
- package/dist/{frameworkTableAssembly-BA2bAmYN.js → frameworkTableAssembly-sg41s14F.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{inspectMetrics-CYDSO3za.js → inspectMetrics-DWh56Qas.js} +205 -205
- package/dist/{serveCommand-C3lq3mFd.js → serveCommand-DO6qr5Ok.js} +5 -5
- package/dist/serveEntry.js +2 -2
- package/dist/{start-BtgatJN3.js → start-ksY0wMZG.js} +379 -379
- package/dist/startEntry.js +2 -2
- package/package.json +17 -17
- package/templates/AGENTS.core.md +24 -9
- package/templates/AGENTS.md +25 -10
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +2 -2
- package/templates/agent-docs/database/migrations.md +3 -3
- package/templates/agent-docs/internationalization.md +3 -3
- package/templates/agent-docs/introduction.md +2 -2
- package/templates/agent-docs/reference.md +4 -4
- package/templates/agent-docs/routing.md +40 -40
- package/templates/agent-docs/templates/appshells.md +24 -24
- package/templates/agent-docs/whats-new.md +31 -95
- package/templates/apps/api-ai/package.json +7 -7
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/changelog/src/pages/[locale]/{[slug].page.tsx → [slug]/page.tsx} +4 -4
- package/templates/apps/changelog/src/pages/[locale]/mirrors.test.tsx +2 -2
- package/templates/apps/changelog/src/pages/[locale]/{index.page.tsx → page.tsx} +2 -2
- package/templates/apps/changelog/src/pages/{[slug].page.test.tsx → [slug]/page.test.tsx} +1 -1
- package/templates/apps/changelog/src/pages/{[slug].page.tsx → [slug]/page.tsx} +2 -2
- package/templates/apps/changelog/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-admin/src/pages/(marketing)/{login.page.test.tsx → login/page.test.tsx} +2 -2
- package/templates/apps/frontend-admin/src/pages/(marketing)/{login.page.tsx → login/page.tsx} +2 -2
- package/templates/apps/frontend-admin/src/pages/(marketing)/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-admin/src/pages/admin/{[entity].page.tsx → [entity]/page.tsx} +1 -1
- package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +1 -1
- package/templates/apps/frontend-admin/src/pages/admin/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-app/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-app/src/pages/{schema-ui.page.test.tsx → schema-ui/page.test.tsx} +2 -2
- package/templates/apps/frontend-app/src/pages/{schema-ui.page.tsx → schema-ui/page.tsx} +1 -1
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-blank/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-contact/src/pages/[locale]/{index.page.tsx → page.tsx} +2 -2
- package/templates/apps/frontend-contact/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/{login.page.test.tsx → login/page.test.tsx} +2 -2
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/{login.page.tsx → login/page.tsx} +2 -2
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-dashboard/src/pages/dashboard/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-dashboard/src/pages/dashboard/{settings.page.test.tsx → settings/page.test.tsx} +2 -2
- package/templates/apps/frontend-dashboard/src/pages/dashboard/{settings.page.tsx → settings/page.tsx} +2 -2
- package/templates/apps/frontend-docs/package.json +7 -7
- package/templates/apps/frontend-docs/src/pages/[locale]/docs/{[...slug].page.tsx → [...slug]/page.tsx} +4 -4
- package/templates/apps/frontend-docs/src/pages/[locale]/mirrors.test.tsx +2 -2
- package/templates/apps/frontend-docs/src/pages/[locale]/{index.page.tsx → page.tsx} +2 -2
- package/templates/apps/frontend-docs/src/pages/docs/{[...slug].page.test.tsx → [...slug]/page.test.tsx} +3 -3
- package/templates/apps/frontend-docs/src/pages/docs/{[...slug].page.tsx → [...slug]/page.tsx} +1 -1
- package/templates/apps/frontend-docs/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-i18n/src/pages/[locale]/{about.page.tsx → about/page.tsx} +3 -3
- package/templates/apps/frontend-i18n/src/pages/[locale]/mirrors.test.tsx +2 -2
- package/templates/apps/frontend-i18n/src/pages/[locale]/{index.page.tsx → page.tsx} +2 -2
- package/templates/apps/frontend-i18n/src/pages/{about.page.test.tsx → about/page.test.tsx} +3 -3
- package/templates/apps/frontend-i18n/src/pages/{about.page.tsx → about/page.tsx} +1 -1
- package/templates/apps/frontend-i18n/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-landing/src/pages/[locale]/mirrors.test.tsx +1 -1
- package/templates/apps/frontend-landing/src/pages/[locale]/{index.page.tsx → page.tsx} +2 -2
- package/templates/apps/frontend-landing/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-spa/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr/src/pages/{feed.page.test.tsx → feed/page.test.tsx} +2 -2
- package/templates/apps/frontend-ssr/src/pages/{feed.page.tsx → feed/page.tsx} +1 -1
- package/templates/apps/frontend-ssr/src/pages/{feed-swr.page.test.tsx → feed-swr/page.test.tsx} +2 -2
- package/templates/apps/frontend-ssr/src/pages/{feed-swr.page.tsx → feed-swr/page.tsx} +1 -1
- package/templates/apps/frontend-ssr/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-ssr-api/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-static-blog/src/pages/[locale]/blog/{[slug].page.tsx → [slug]/page.tsx} +4 -4
- package/templates/apps/frontend-static-blog/src/pages/[locale]/mirrors.test.tsx +2 -2
- package/templates/apps/frontend-static-blog/src/pages/[locale]/{index.page.tsx → page.tsx} +2 -2
- package/templates/apps/frontend-static-blog/src/pages/blog/{[slug].page.test.tsx → [slug]/page.test.tsx} +3 -3
- package/templates/apps/frontend-static-blog/src/pages/blog/{[slug].page.tsx → [slug]/page.tsx} +3 -3
- package/templates/apps/frontend-static-blog/src/pages/{index.page.test.tsx → page.test.tsx} +1 -1
- package/dist/apiBuild-BOczx9-m.js +0 -2
- package/dist/dbCommand-DNQ6l-su.js +0 -2
- package/dist/frameworkTableAssembly-D1zJSgOU.js +0 -2
- /package/templates/apps/changelog/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-admin/src/pages/(marketing)/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-admin/src/pages/admin/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-app/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-blank/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-contact/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-dashboard/src/pages/(marketing)/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-dashboard/src/pages/dashboard/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-docs/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-i18n/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-landing/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-spa/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-ssr/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-ssr-api/src/pages/{index.page.tsx → page.tsx} +0 -0
- /package/templates/apps/frontend-static-blog/src/pages/{index.page.tsx → page.tsx} +0 -0
package/dist/startEntry.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t } from "./start-
|
|
1
|
+
import { Q as e } from "./inspectMetrics-DWh56Qas.js";
|
|
2
|
+
import { t } from "./start-ksY0wMZG.js";
|
|
3
3
|
export { e as loadDotEnv, t as runStartCommand };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -62,22 +62,22 @@
|
|
|
62
62
|
"@effect/platform-node": "^0.107.0",
|
|
63
63
|
"@effect/sql": "^0.51.1",
|
|
64
64
|
"@effect/workflow": "^0.18.2",
|
|
65
|
-
"@voltro/ai": "0.
|
|
66
|
-
"@voltro/cache": "0.
|
|
67
|
-
"@voltro/data-transfer": "0.
|
|
68
|
-
"@voltro/database": "0.
|
|
69
|
-
"@voltro/env": "0.
|
|
70
|
-
"@voltro/kv": "0.
|
|
71
|
-
"@voltro/logger": "0.
|
|
72
|
-
"@voltro/plugin-auth": "0.
|
|
73
|
-
"@voltro/plugin-broadcast": "0.
|
|
74
|
-
"@voltro/plugin-mail": "0.
|
|
75
|
-
"@voltro/plugin-storage": "0.
|
|
76
|
-
"@voltro/plugin-webhooks": "0.
|
|
77
|
-
"@voltro/protocol": "0.
|
|
78
|
-
"@voltro/runtime": "0.
|
|
79
|
-
"@voltro/serverless": "0.
|
|
80
|
-
"@voltro/workflow": "0.
|
|
65
|
+
"@voltro/ai": "0.17.0",
|
|
66
|
+
"@voltro/cache": "0.17.0",
|
|
67
|
+
"@voltro/data-transfer": "0.17.0",
|
|
68
|
+
"@voltro/database": "0.17.0",
|
|
69
|
+
"@voltro/env": "0.17.0",
|
|
70
|
+
"@voltro/kv": "0.17.0",
|
|
71
|
+
"@voltro/logger": "0.17.0",
|
|
72
|
+
"@voltro/plugin-auth": "0.17.0",
|
|
73
|
+
"@voltro/plugin-broadcast": "0.17.0",
|
|
74
|
+
"@voltro/plugin-mail": "0.17.0",
|
|
75
|
+
"@voltro/plugin-storage": "0.17.0",
|
|
76
|
+
"@voltro/plugin-webhooks": "0.17.0",
|
|
77
|
+
"@voltro/protocol": "0.17.0",
|
|
78
|
+
"@voltro/runtime": "0.17.0",
|
|
79
|
+
"@voltro/serverless": "0.17.0",
|
|
80
|
+
"@voltro/workflow": "0.17.0",
|
|
81
81
|
"chokidar": "^5.0.0",
|
|
82
82
|
"ioredis": "^5.11.1",
|
|
83
83
|
"tinyglobby": "^0.2.17",
|
package/templates/AGENTS.core.md
CHANGED
|
@@ -219,7 +219,7 @@ a `web/` (frontend).
|
|
|
219
219
|
│ │ └── rpcGroup.generated.ts # codegen — do NOT edit
|
|
220
220
|
│ └── web/
|
|
221
221
|
│ ├── app.config.ts # { type:'web', apis, theme, locales, … }
|
|
222
|
-
│ └── src/pages
|
|
222
|
+
│ └── src/pages/**/page.tsx # a DIRECTORY is a route segment
|
|
223
223
|
├── packages/ # shared libs
|
|
224
224
|
└── AGENTS.md / CLAUDE.md # this file
|
|
225
225
|
```
|
|
@@ -249,7 +249,7 @@ public REST routes, declared via `restRoutes` in `app.config.ts`).
|
|
|
249
249
|
| `*.agent.tsx` + `*.agent.server.tsx` | server-side LLM chat |
|
|
250
250
|
| `*.tool.tsx` | tool an agent can call |
|
|
251
251
|
| `*.entity.ts` / `*.schema.ts` / `schema.ts` | one table per file |
|
|
252
|
-
|
|
|
252
|
+
| `page.tsx` (under `src/pages/`) | the route its DIRECTORY serves + `page.test.tsx` |
|
|
253
253
|
| `*.component.tsx` | exactly ONE component (+ types) |
|
|
254
254
|
| `*.component.ui.tsx` | presentational: one component, READS only — never writes |
|
|
255
255
|
| `*.hook.ts` | exactly ONE `use*` hook (+ types) |
|
|
@@ -260,12 +260,27 @@ public REST routes, declared via `restRoutes` in `app.config.ts`).
|
|
|
260
260
|
| `*.store.ts` | exactly ONE `defineStore` — client state, never server state |
|
|
261
261
|
| `*.client.ts` | declares itself + its transitive imports browser-safe |
|
|
262
262
|
|
|
263
|
-
**
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
263
|
+
**A DIRECTORY is a route segment, and its route is `page.tsx`.**
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
src/pages/page.tsx → /
|
|
267
|
+
src/pages/pricing/page.tsx → /pricing
|
|
268
|
+
src/pages/users/[id]/page.tsx → /users/[id]
|
|
269
|
+
src/pages/docs/[...slug]/page.tsx → /docs/<anything>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
A parameter is a DIRECTORY name (`[id]/`), never a filename. Beside `page.tsx`
|
|
273
|
+
sit the other reserved names its segment owns — `layout.tsx`, `error.tsx`,
|
|
274
|
+
`loading.tsx`, `not-found.tsx` — plus its `page.test.tsx` and any co-located
|
|
275
|
+
components. **Anything in that folder that is not one of the reserved names is
|
|
276
|
+
structurally not a route**, so put a component next to the page that uses it
|
|
277
|
+
rather than in a distant `components/` directory.
|
|
278
|
+
|
|
279
|
+
A `page.tsx` outside `src/pages/` never routes. A page MUST default-export its
|
|
280
|
+
component — without it `voltro dev` refuses to boot and names the file.
|
|
281
|
+
|
|
282
|
+
Do NOT write `x.page.tsx`, `index.page.tsx` or `[id].page.tsx` — those were the
|
|
283
|
+
0.15.0 spelling and route nothing now.
|
|
269
284
|
|
|
270
285
|
**Every suffix above is a CONTRACT, not a label.** Something else depends on each
|
|
271
286
|
promise, and `voltro doctor` enforces all of them:
|
|
@@ -308,7 +323,7 @@ rules is that everything we author follows them.
|
|
|
308
323
|
|
|
309
324
|
**Write the test in the same step as the code — always, for every primitive.**
|
|
310
325
|
Not "afterwards", not in a cleanup pass: the filename is derivable
|
|
311
|
-
(`users/[id]
|
|
326
|
+
(`users/[id]/page.tsx` → `users/[id]/page.test.tsx`,
|
|
312
327
|
`Card.component.tsx` → `Card.component.test.tsx`,
|
|
313
328
|
`order.mutation.server.ts` → `order.mutation.server.test.ts`), so there is
|
|
314
329
|
nothing to decide. A change is not finished until its test exists and passes.
|
package/templates/AGENTS.md
CHANGED
|
@@ -219,7 +219,7 @@ a `web/` (frontend).
|
|
|
219
219
|
│ │ └── rpcGroup.generated.ts # codegen — do NOT edit
|
|
220
220
|
│ └── web/
|
|
221
221
|
│ ├── app.config.ts # { type:'web', apis, theme, locales, … }
|
|
222
|
-
│ └── src/pages
|
|
222
|
+
│ └── src/pages/**/page.tsx # a DIRECTORY is a route segment
|
|
223
223
|
├── packages/ # shared libs
|
|
224
224
|
└── AGENTS.md / CLAUDE.md # this file
|
|
225
225
|
```
|
|
@@ -249,7 +249,7 @@ public REST routes, declared via `restRoutes` in `app.config.ts`).
|
|
|
249
249
|
| `*.agent.tsx` + `*.agent.server.tsx` | server-side LLM chat |
|
|
250
250
|
| `*.tool.tsx` | tool an agent can call |
|
|
251
251
|
| `*.entity.ts` / `*.schema.ts` / `schema.ts` | one table per file |
|
|
252
|
-
|
|
|
252
|
+
| `page.tsx` (under `src/pages/`) | the route its DIRECTORY serves + `page.test.tsx` |
|
|
253
253
|
| `*.component.tsx` | exactly ONE component (+ types) |
|
|
254
254
|
| `*.component.ui.tsx` | presentational: one component, READS only — never writes |
|
|
255
255
|
| `*.hook.ts` | exactly ONE `use*` hook (+ types) |
|
|
@@ -260,12 +260,27 @@ public REST routes, declared via `restRoutes` in `app.config.ts`).
|
|
|
260
260
|
| `*.store.ts` | exactly ONE `defineStore` — client state, never server state |
|
|
261
261
|
| `*.client.ts` | declares itself + its transitive imports browser-safe |
|
|
262
262
|
|
|
263
|
-
**
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
263
|
+
**A DIRECTORY is a route segment, and its route is `page.tsx`.**
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
src/pages/page.tsx → /
|
|
267
|
+
src/pages/pricing/page.tsx → /pricing
|
|
268
|
+
src/pages/users/[id]/page.tsx → /users/[id]
|
|
269
|
+
src/pages/docs/[...slug]/page.tsx → /docs/<anything>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
A parameter is a DIRECTORY name (`[id]/`), never a filename. Beside `page.tsx`
|
|
273
|
+
sit the other reserved names its segment owns — `layout.tsx`, `error.tsx`,
|
|
274
|
+
`loading.tsx`, `not-found.tsx` — plus its `page.test.tsx` and any co-located
|
|
275
|
+
components. **Anything in that folder that is not one of the reserved names is
|
|
276
|
+
structurally not a route**, so put a component next to the page that uses it
|
|
277
|
+
rather than in a distant `components/` directory.
|
|
278
|
+
|
|
279
|
+
A `page.tsx` outside `src/pages/` never routes. A page MUST default-export its
|
|
280
|
+
component — without it `voltro dev` refuses to boot and names the file.
|
|
281
|
+
|
|
282
|
+
Do NOT write `x.page.tsx`, `index.page.tsx` or `[id].page.tsx` — those were the
|
|
283
|
+
0.15.0 spelling and route nothing now.
|
|
269
284
|
|
|
270
285
|
**Every suffix above is a CONTRACT, not a label.** Something else depends on each
|
|
271
286
|
promise, and `voltro doctor` enforces all of them:
|
|
@@ -308,7 +323,7 @@ rules is that everything we author follows them.
|
|
|
308
323
|
|
|
309
324
|
**Write the test in the same step as the code — always, for every primitive.**
|
|
310
325
|
Not "afterwards", not in a cleanup pass: the filename is derivable
|
|
311
|
-
(`users/[id]
|
|
326
|
+
(`users/[id]/page.tsx` → `users/[id]/page.test.tsx`,
|
|
312
327
|
`Card.component.tsx` → `Card.component.test.tsx`,
|
|
313
328
|
`order.mutation.server.ts` → `order.mutation.server.test.ts`), so there is
|
|
314
329
|
nothing to decide. A change is not finished until its test exists and passes.
|
|
@@ -526,7 +541,7 @@ each plugin's own README.
|
|
|
526
541
|
|
|
527
542
|
| Topic | Open | Summary |
|
|
528
543
|
|---|---|---|
|
|
529
|
-
| **What's new in 0.
|
|
544
|
+
| **What's new in 0.16.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
530
545
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
531
546
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
532
547
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -9,7 +9,7 @@ each plugin's own README.
|
|
|
9
9
|
|
|
10
10
|
| Topic | Open | Summary |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| **What's new in 0.
|
|
12
|
+
| **What's new in 0.16.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
13
13
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
14
14
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
15
15
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -1211,8 +1211,8 @@ A page is listed when ALL of these hold:
|
|
|
1211
1211
|
|
|
1212
1212
|
```text
|
|
1213
1213
|
• renderMode:'spa' candidates (2 pages — loader-free, under a layout, currently ssr/default):
|
|
1214
|
-
src/pages/dashboard/
|
|
1215
|
-
src/pages/admin/settings
|
|
1214
|
+
src/pages/dashboard/page.tsx (/dashboard) — default renderMode
|
|
1215
|
+
src/pages/admin/settings/page.tsx (/admin/settings) — renderMode:'ssr'
|
|
1216
1216
|
→ renderMode:'spa' skips this page's SSR compile while its layout shell still renders server-side — adopt it if the page BODY does not need SSR (internal/authenticated pages); keep 'ssr' if the page content needs SEO or server first-paint.
|
|
1217
1217
|
```
|
|
1218
1218
|
|
|
@@ -2316,7 +2316,7 @@ If multiple apps target the same database, they're showing the same `_voltro_mig
|
|
|
2316
2316
|
|
|
2317
2317
|
## Source code
|
|
2318
2318
|
|
|
2319
|
-
The shared component lives in `voltro/packages/devtools-ui/src/pages/MigrationsPage
|
|
2319
|
+
The shared component lives in `voltro/packages/devtools-ui/src/pages/MigrationsPage/page.tsx`. The local devtools wiring is in `voltro-devtools/apps/dashboard/src/pages/apps/[appId]/migrations/page.tsx`. Both repos are open to extension.
|
|
2320
2320
|
|
|
2321
2321
|
|
|
2322
2322
|
|
|
@@ -2504,8 +2504,8 @@ server enforces this regardless of which buttons the UI renders.
|
|
|
2504
2504
|
|
|
2505
2505
|
## Source code
|
|
2506
2506
|
|
|
2507
|
-
- Component: `voltro/packages/devtools-ui/src/pages/MigrationsPage
|
|
2508
|
-
- Cloud wiring: `voltro-cloud/apps/voltro-cloud/dashboard/src/pages/_/p/[orgSlug]/[projectSlug]/apps/[appSlug]/migrations
|
|
2507
|
+
- Component: `voltro/packages/devtools-ui/src/pages/MigrationsPage/page.tsx` — shared
|
|
2508
|
+
- Cloud wiring: `voltro-cloud/apps/voltro-cloud/dashboard/src/pages/_/p/[orgSlug]/[projectSlug]/apps/[appSlug]/migrations/page.tsx`
|
|
2509
2509
|
- Cloud-api proxy: `voltro-cloud/apps/voltro-cloud/api/queries/apps.inspectMigrationsStatus.query.ts` + `.query.server.ts`
|
|
2510
2510
|
- Approval schema: `voltro-cloud/apps/voltro-cloud/api/database/migrationPlans.entity.ts` + `migrationApprovals.entity.ts`
|
|
2511
2511
|
- Approval rpc: `migrations.submitPlan` + `migrations.approve` (mutations), `migrations.pendingApprovals` (query), `migrations.reviewByToken` (action)
|
|
@@ -423,7 +423,7 @@ export default function Layout({ children }) {
|
|
|
423
423
|
}
|
|
424
424
|
```
|
|
425
425
|
|
|
426
|
-
Pair this with locale-prefixed page files: `src/pages/[locale]/
|
|
426
|
+
Pair this with locale-prefixed page files: `src/pages/[locale]/page.tsx`, `src/pages/[locale]/docs/[...slug]/page.tsx`, etc. Each re-exports the default-locale query's component, which reads `useUrlLocale()` to decide which catalog data to query.
|
|
427
427
|
|
|
428
428
|
Reference implementations: `voltro-dev/apps/voltro-dev/docs/` (URL-prefix on dynamic + static content) and `voltro-dev/apps/voltro-dev/landing/` (URL-prefix on a pure static marketing site — 10 default-locale pages, 10 locale-prefixed mirrors, one combined `voltro build` run).
|
|
429
429
|
|
|
@@ -441,7 +441,7 @@ dist/de/features/foo/index.html ← de mirror
|
|
|
441
441
|
Each variant ships with the right `<I18nProvider>`-wrapped body **and** the right per-locale `<title>` / `<meta description>` / `<link rel="canonical">` / OG tags — **if** the page's `meta` is exported as a function of `({ locale })`. With a plain static `meta: PageMeta` object, the body is correctly localised but the head tags stay default-locale on every variant.
|
|
442
442
|
|
|
443
443
|
```tsx
|
|
444
|
-
// src/pages/features/foo
|
|
444
|
+
// src/pages/features/foo/page.tsx — meta as a function of locale
|
|
445
445
|
import { getCatalog } from '../lib/locale'
|
|
446
446
|
import { localeCanonicalUrl, ogTags, standardLinks } from '../lib/seo'
|
|
447
447
|
|
|
@@ -472,7 +472,7 @@ The `[locale]/…` mirror is the trigger for SSG **output** — without mirror f
|
|
|
472
472
|
12 lines per page. Re-export the canonical page's `default`, `renderMode`, `interactive`, `meta`, plus a `getStaticPaths` that enumerates non-default locales:
|
|
473
473
|
|
|
474
474
|
```tsx
|
|
475
|
-
// src/pages/[locale]/features/foo
|
|
475
|
+
// src/pages/[locale]/features/foo/page.tsx
|
|
476
476
|
import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../../lib/locale'
|
|
477
477
|
export { default } from '../../features/foo'
|
|
478
478
|
export { renderMode, interactive, meta } from '../../features/foo'
|
|
@@ -404,7 +404,7 @@ An unmarked file makes no claim, and that is fine: `*.client.ts` is for the shar
|
|
|
404
404
|
|
|
405
405
|
| Path | What it is |
|
|
406
406
|
|---|---|
|
|
407
|
-
| `src/pages
|
|
407
|
+
| `src/pages/**/page.tsx` | A page. URL is the file path; `[id]/page.tsx` -> `/:id`, `[...slug]/page.tsx` -> catch-all. |
|
|
408
408
|
| `src/pages/layout.tsx` | Outer layout — wraps every page. |
|
|
409
409
|
| `src/pages/error.tsx` | Error boundary for the whole subtree. |
|
|
410
410
|
| `src/pages/not-found.tsx` | Fallback rendered when no page matches. |
|
|
@@ -415,7 +415,7 @@ An unmarked file makes no claim, and that is fine: `*.client.ts` is for the shar
|
|
|
415
415
|
Each page can opt into a render strategy via two exports:
|
|
416
416
|
|
|
417
417
|
```tsx
|
|
418
|
-
// src/pages/blog/[slug]
|
|
418
|
+
// src/pages/blog/[slug]/page.tsx
|
|
419
419
|
export const renderMode = 'isr' as const // 'static' | 'spa' | 'ssr' | 'isr'
|
|
420
420
|
export const interactive = 'islands' as const // 'none' | 'islands' | 'full'
|
|
421
421
|
```
|
|
@@ -482,7 +482,7 @@ className={pathname.startsWith('/dashboard') ? 'active' : ''}
|
|
|
482
482
|
URL params from `[name]` segments. Typed via the generic.
|
|
483
483
|
|
|
484
484
|
```tsx
|
|
485
|
-
// src/pages/users/[id]
|
|
485
|
+
// src/pages/users/[id]/page.tsx
|
|
486
486
|
import { useParams } from '@voltro/web'
|
|
487
487
|
|
|
488
488
|
const { id } = useParams<{ id: string }>()
|
|
@@ -491,7 +491,7 @@ const { id } = useParams<{ id: string }>()
|
|
|
491
491
|
For catch-all queries:
|
|
492
492
|
|
|
493
493
|
```tsx
|
|
494
|
-
// src/pages/docs/[...slug]
|
|
494
|
+
// src/pages/docs/[...slug]/page.tsx
|
|
495
495
|
const { slug } = useParams<{ slug: string }>()
|
|
496
496
|
// /docs/intro/getting-started → slug = "intro/getting-started"
|
|
497
497
|
```
|
|
@@ -499,7 +499,7 @@ const { slug } = useParams<{ slug: string }>()
|
|
|
499
499
|
For multi-segment dynamic paths:
|
|
500
500
|
|
|
501
501
|
```tsx
|
|
502
|
-
// src/pages/orgs/[orgId]/projects/[projectId]
|
|
502
|
+
// src/pages/orgs/[orgId]/projects/[projectId]/page.tsx
|
|
503
503
|
const { orgId, projectId } = useParams<{ orgId: string; projectId: string }>()
|
|
504
504
|
```
|
|
505
505
|
|
|
@@ -604,7 +604,7 @@ Idempotent — multiple calls for the same path fire one loader. The prefetched
|
|
|
604
604
|
Page's loader output, typed.
|
|
605
605
|
|
|
606
606
|
```tsx
|
|
607
|
-
// src/pages/notes/[id]
|
|
607
|
+
// src/pages/notes/[id]/page.tsx
|
|
608
608
|
import { useLoaderData } from '@voltro/web'
|
|
609
609
|
|
|
610
610
|
interface Note {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
_Voltro's file-based router — pages, layouts, render modes, loaders, navigation, islands. The web side of the framework._
|
|
13
13
|
|
|
14
|
-
The web side of a Voltro app uses **file-based routing**: drop a
|
|
14
|
+
The web side of a Voltro app uses **file-based routing**: drop a `page.tsx` file under `src/pages/`, the CLI discovers it on every boot + save, and the file becomes a route. No router config, no manual `<Route>` declarations, no codegen step.
|
|
15
15
|
|
|
16
16
|
The suffix is what makes a file a route — the directory tree only decides *which URL*. Anything else under `src/pages/` (components, hooks, tests) is ordinary code and gets no URL, so it can live beside the page that uses it.
|
|
17
17
|
|
|
@@ -25,18 +25,18 @@ src/pages/
|
|
|
25
25
|
├── error.tsx # error boundary
|
|
26
26
|
├── loading.tsx # pending UI
|
|
27
27
|
├── not-found.tsx # 404 fallback
|
|
28
|
-
├──
|
|
29
|
-
├── about
|
|
28
|
+
├── page.tsx # /
|
|
29
|
+
├── about/page.tsx # /about
|
|
30
30
|
├── (marketing)/ # route group — no URL segment
|
|
31
31
|
│ ├── layout.tsx # marketing-scoped layout
|
|
32
|
-
│ └── pricing
|
|
32
|
+
│ └── pricing/page.tsx # /pricing
|
|
33
33
|
├── users/
|
|
34
34
|
│ ├── layout.tsx # users-scoped layout
|
|
35
35
|
│ ├── error.tsx # users-scoped error boundary
|
|
36
|
-
│ ├── [id]
|
|
37
|
-
│ └──
|
|
36
|
+
│ ├── [id]/page.tsx # /users/:id
|
|
37
|
+
│ └── page.tsx # /users
|
|
38
38
|
└── docs/
|
|
39
|
-
└── [...slug]
|
|
39
|
+
└── [...slug]/page.tsx # /docs/<anything> (catch-all)
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
That's the whole router. No `<Route>`, no `<Switch>`, no `useRoutes`.
|
|
@@ -52,7 +52,7 @@ That's the whole router. No `<Route>`, no `<Switch>`, no `useRoutes`.
|
|
|
52
52
|
|
|
53
53
|
## Architecture in one paragraph
|
|
54
54
|
|
|
55
|
-
The framework generates a `.framework/app.tsx` on every boot that imports each
|
|
55
|
+
The framework generates a `.framework/app.tsx` on every boot that imports each `page.tsx` file under `src/pages/` (excluding `node_modules`), wraps them in their layout chains, and produces a `<Router routes={…} />` element. A `mount(App, { group })` call in `.framework/main.tsx` mounts it via `react-dom/client`'s `createRoot` (or `hydrateRoot` for SSR pages). The router watches `window.location` + intercepts `<Link>` clicks for client-side nav.
|
|
56
56
|
|
|
57
57
|
You don't write any of this. The CLI regenerates it on every save in dev; the build pipeline freezes it for production.
|
|
58
58
|
|
|
@@ -125,7 +125,7 @@ A **page** is any `*.tsx` file under `src/pages/` that's not a special file (`la
|
|
|
125
125
|
## A static page
|
|
126
126
|
|
|
127
127
|
```tsx
|
|
128
|
-
// src/pages/about
|
|
128
|
+
// src/pages/about/page.tsx → /about
|
|
129
129
|
import type { ReactNode } from 'react'
|
|
130
130
|
|
|
131
131
|
export default function About(): ReactNode {
|
|
@@ -145,9 +145,9 @@ That's it. Save the file, the CLI's discovery sees it on next save, the page is
|
|
|
145
145
|
`index.tsx` maps to the directory's URL:
|
|
146
146
|
|
|
147
147
|
```text
|
|
148
|
-
src/pages/
|
|
149
|
-
src/pages/users/
|
|
150
|
-
src/pages/admin/
|
|
148
|
+
src/pages/page.tsx → /
|
|
149
|
+
src/pages/users/page.tsx → /users
|
|
150
|
+
src/pages/admin/page.tsx → /admin
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
## Dynamic segments
|
|
@@ -155,7 +155,7 @@ src/pages/admin/index.page.tsx → /admin
|
|
|
155
155
|
Brackets in the filename are dynamic. The captured value comes through `useParams<T>()`:
|
|
156
156
|
|
|
157
157
|
```tsx
|
|
158
|
-
// src/pages/users/[id]
|
|
158
|
+
// src/pages/users/[id]/page.tsx → /users/:id
|
|
159
159
|
import { useParams } from '@voltro/web'
|
|
160
160
|
|
|
161
161
|
export default function User() {
|
|
@@ -167,7 +167,7 @@ export default function User() {
|
|
|
167
167
|
Multiple dynamic segments in one path:
|
|
168
168
|
|
|
169
169
|
```text
|
|
170
|
-
src/pages/orgs/[orgId]/projects/[projectId]
|
|
170
|
+
src/pages/orgs/[orgId]/projects/[projectId]/page.tsx
|
|
171
171
|
// → /orgs/:orgId/projects/:projectId
|
|
172
172
|
|
|
173
173
|
const { orgId, projectId } = useParams<{ orgId: string; projectId: string }>()
|
|
@@ -178,7 +178,7 @@ const { orgId, projectId } = useParams<{ orgId: string; projectId: string }>()
|
|
|
178
178
|
`[...name]` captures one OR more URL segments as a single param value (joined by `/`):
|
|
179
179
|
|
|
180
180
|
```tsx
|
|
181
|
-
// src/pages/docs/[...slug]
|
|
181
|
+
// src/pages/docs/[...slug]/page.tsx → /docs/<anything>
|
|
182
182
|
const { slug } = useParams<{ slug: string }>()
|
|
183
183
|
// /docs/intro/getting-started → slug = "intro/getting-started"
|
|
184
184
|
```
|
|
@@ -186,7 +186,7 @@ const { slug } = useParams<{ slug: string }>()
|
|
|
186
186
|
**Optional** catch-all (matches the base URL too):
|
|
187
187
|
|
|
188
188
|
```tsx
|
|
189
|
-
// src/pages/docs/[[...slug]]
|
|
189
|
+
// src/pages/docs/[[...slug]]/page.tsx
|
|
190
190
|
// /docs → slug = ""
|
|
191
191
|
// /docs/foo → slug = "foo"
|
|
192
192
|
// /docs/foo/bar → slug = "foo/bar"
|
|
@@ -201,10 +201,10 @@ When multiple files could match (static, dynamic, catch-all), priority is:
|
|
|
201
201
|
3. Optional catch-all (`[[...slug]]`) wins over required catch-all (`[...slug]`) — the optional form scores as more specific, so it matches first.
|
|
202
202
|
|
|
203
203
|
```text
|
|
204
|
-
src/pages/users/
|
|
205
|
-
src/pages/users/[id]
|
|
206
|
-
src/pages/users/new
|
|
207
|
-
src/pages/[...rest]
|
|
204
|
+
src/pages/users/page.tsx # /users → wins for /users
|
|
205
|
+
src/pages/users/[id]/page.tsx # /users/:id → wins for /users/42
|
|
206
|
+
src/pages/users/new/page.tsx # /users/new → wins (static beats dynamic)
|
|
207
|
+
src/pages/[...rest]/page.tsx # everything else
|
|
208
208
|
```
|
|
209
209
|
|
|
210
210
|
## Query strings
|
|
@@ -242,14 +242,14 @@ Parse the query string explicitly via `useServerRequest()` on the server and `wi
|
|
|
242
242
|
|
|
243
243
|
## Co-locating components, hooks and tests
|
|
244
244
|
|
|
245
|
-
Only
|
|
245
|
+
Only `page.tsx` is a route. Everything else under `src/pages/` is ordinary code and may sit next to the page that uses it:
|
|
246
246
|
|
|
247
247
|
```text
|
|
248
248
|
src/pages/
|
|
249
249
|
├── users/
|
|
250
|
-
│ ├──
|
|
251
|
-
│ ├──
|
|
252
|
-
│ ├── [id]
|
|
250
|
+
│ ├── page.tsx # → /users
|
|
251
|
+
│ ├── page.test.tsx # its test
|
|
252
|
+
│ ├── [id]/page.tsx # → /users/:id
|
|
253
253
|
│ ├── UserCard.tsx # a component — no URL
|
|
254
254
|
│ └── useFilters.ts # a hook — no URL
|
|
255
255
|
```
|
|
@@ -319,11 +319,11 @@ Conventions:
|
|
|
319
319
|
```text
|
|
320
320
|
src/pages/
|
|
321
321
|
├── layout.tsx # outer (every page)
|
|
322
|
-
├── about
|
|
322
|
+
├── about/page.tsx # /about → wrapped in outer layout
|
|
323
323
|
└── dashboard/
|
|
324
324
|
├── layout.tsx # nested (only /dashboard/*)
|
|
325
|
-
├──
|
|
326
|
-
└── settings
|
|
325
|
+
├── page.tsx # /dashboard
|
|
326
|
+
└── settings/page.tsx # /dashboard/settings
|
|
327
327
|
```
|
|
328
328
|
|
|
329
329
|
For `/dashboard/settings`, the React tree is:
|
|
@@ -346,13 +346,13 @@ A directory in `(parentheses)` does NOT contribute a URL segment, but its layout
|
|
|
346
346
|
src/pages/
|
|
347
347
|
├── (marketing)/
|
|
348
348
|
│ ├── layout.tsx # marketing-scoped chrome
|
|
349
|
-
│ ├──
|
|
350
|
-
│ ├── pricing
|
|
351
|
-
│ └── about
|
|
349
|
+
│ ├── page.tsx # /
|
|
350
|
+
│ ├── pricing/page.tsx # /pricing
|
|
351
|
+
│ └── about/page.tsx # /about
|
|
352
352
|
└── (app)/
|
|
353
353
|
├── layout.tsx # authenticated app chrome
|
|
354
|
-
├── dashboard
|
|
355
|
-
└── settings
|
|
354
|
+
├── dashboard/page.tsx # /dashboard
|
|
355
|
+
└── settings/page.tsx # /settings
|
|
356
356
|
```
|
|
357
357
|
|
|
358
358
|
Marketing pages get one layout; authenticated app pages get another; the URLs stay flat.
|
|
@@ -496,7 +496,7 @@ Those four are the **complete** set. An unrecognised value is a hard error namin
|
|
|
496
496
|
## static (SSG)
|
|
497
497
|
|
|
498
498
|
```tsx
|
|
499
|
-
// src/pages/about
|
|
499
|
+
// src/pages/about/page.tsx
|
|
500
500
|
export const renderMode = 'static' as const
|
|
501
501
|
```
|
|
502
502
|
|
|
@@ -509,7 +509,7 @@ At `voltro build`:
|
|
|
509
509
|
For dynamic patterns, export `getStaticPaths` to enumerate every URL to pre-render:
|
|
510
510
|
|
|
511
511
|
```tsx
|
|
512
|
-
// src/pages/blog/[slug]
|
|
512
|
+
// src/pages/blog/[slug]/page.tsx
|
|
513
513
|
export const renderMode = 'static' as const
|
|
514
514
|
|
|
515
515
|
export const getStaticPaths = async () => [
|
|
@@ -734,7 +734,7 @@ Both are static module exports — the framework discovers them, the build pipel
|
|
|
734
734
|
## A loader
|
|
735
735
|
|
|
736
736
|
```tsx
|
|
737
|
-
// src/pages/notes/[id]
|
|
737
|
+
// src/pages/notes/[id]/page.tsx
|
|
738
738
|
import { useLoaderData } from '@voltro/web'
|
|
739
739
|
|
|
740
740
|
interface Note {
|
|
@@ -844,7 +844,7 @@ splits the loader's result into data that blocks the shell and data that
|
|
|
844
844
|
soon as the promise settles.
|
|
845
845
|
|
|
846
846
|
```tsx
|
|
847
|
-
// src/pages/dashboard
|
|
847
|
+
// src/pages/dashboard/page.tsx
|
|
848
848
|
import { Await, defer, useLoaderData } from '@voltro/web'
|
|
849
849
|
|
|
850
850
|
export const renderMode = 'ssr' as const // required — see below
|
|
@@ -964,7 +964,7 @@ Use `signal` for any fetch that could outlive the request — pass it to `fetch(
|
|
|
964
964
|
Instead of hand-rolling a `fetch(INTERNAL_API/...)`, a server-side loader can call the backend rpc directly through `ctx.query` — the same query tags the client subscribes to, resolved to their initial snapshot:
|
|
965
965
|
|
|
966
966
|
```tsx
|
|
967
|
-
// src/pages/notes/[id]
|
|
967
|
+
// src/pages/notes/[id]/page.tsx
|
|
968
968
|
import { useSubscription } from '@voltro/client'
|
|
969
969
|
import { useLoaderData, type PageMeta } from '@voltro/web'
|
|
970
970
|
|
|
@@ -1136,7 +1136,7 @@ export const meta = ({ locale }: { locale: string }): PageMeta => {
|
|
|
1136
1136
|
### Authenticated dashboard with cookie-driven loader
|
|
1137
1137
|
|
|
1138
1138
|
```tsx
|
|
1139
|
-
// src/pages/dashboard/
|
|
1139
|
+
// src/pages/dashboard/page.tsx
|
|
1140
1140
|
import { useLoaderData } from '@voltro/web'
|
|
1141
1141
|
|
|
1142
1142
|
export const renderMode = 'ssr' as const
|
|
@@ -1162,7 +1162,7 @@ export default function Dashboard() {
|
|
|
1162
1162
|
`getStaticPaths` has no framework store — it reads its own content source (a CMS client, the filesystem, an API). The loader runs server-side and fetches via `query` (the backend rpc, resolved to its first snapshot):
|
|
1163
1163
|
|
|
1164
1164
|
```tsx
|
|
1165
|
-
// src/pages/blog/[slug]
|
|
1165
|
+
// src/pages/blog/[slug]/page.tsx
|
|
1166
1166
|
import { listPostSlugs, type Post } from '../../content/posts'
|
|
1167
1167
|
|
|
1168
1168
|
export const renderMode = 'static' as const
|
|
@@ -1563,7 +1563,7 @@ export default island(LikeButton, { name: 'LikeButton', hydrate: 'visible' })
|
|
|
1563
1563
|
Use it in a page:
|
|
1564
1564
|
|
|
1565
1565
|
```tsx
|
|
1566
|
-
// src/pages/blog/[slug]
|
|
1566
|
+
// src/pages/blog/[slug]/page.tsx
|
|
1567
1567
|
import LikeButton from '../../components/LikeButton.island'
|
|
1568
1568
|
|
|
1569
1569
|
export const renderMode = 'static' as const
|