diff --git a/examples/chat/package.json b/examples/chat/package.json index 9b2aa0339c..ea7ca58e7c 100644 --- a/examples/chat/package.json +++ b/examples/chat/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@anthropic-ai/sdk": "^0.133.0", - "@solidjs/router": "2.0.0-next.40", + "@solidjs/router": "2.0.0-next.41", "@solidjs/web": "workspace:*", "highlight.js": "^11.11.1", "marked": "^12.0.2", diff --git a/examples/chat/src/components/composer.tsx b/examples/chat/src/components/composer.tsx index 4eadcd6ad2..80b32d8525 100644 --- a/examples/chat/src/components/composer.tsx +++ b/examples/chat/src/components/composer.tsx @@ -8,7 +8,7 @@ // its id is among them — by derivation, in the same update that brings the // server's own row. import { createSignal, For, latest, Show } from "solid-js"; -import { action } from "@solidjs/router"; +import { formAction } from "@solidjs/router"; import { redirect } from "@solidjs/web"; import * as db from "~/server/db"; import * as generations from "~/server/generations"; @@ -18,7 +18,7 @@ const mintId = () => Math.random().toString(36).slice(2, 12).padEnd(10, "0"); // Stores the prompt, starts the reply as a job, and returns: the reply is // written by the job, not by this request. From the front page there is no // thread yet, so this makes one and goes to it. -export const sendMessage = action(async (form: FormData) => { +export const sendMessage = formAction(async (form: FormData) => { "use server"; const threadId = String(form.get("threadId") ?? "") || null; try { diff --git a/examples/chat/src/routes/thread.tsx b/examples/chat/src/routes/thread.tsx index d39dd6c91f..0844300eba 100644 --- a/examples/chat/src/routes/thread.tsx +++ b/examples/chat/src/routes/thread.tsx @@ -15,7 +15,7 @@ import { createMemo, For, Loading, onCleanup } from "solid-js"; import { dynamicComponent, getRequestEvent, redirect } from "@solidjs/web"; import { GET, live } from "@solidjs/web/server-functions"; import type { Slot } from "@solidjs/web/frames"; -import { action, liveQuery, type RouteSectionProps } from "@solidjs/router"; +import { formAction, liveQuery, type RouteSectionProps } from "@solidjs/router"; import Composer from "~/components/composer"; import CopyCode from "~/components/copy-code"; import * as db from "~/server/db"; @@ -29,14 +29,14 @@ type CopySlot = Slot<{ code: string }>; // Stops the reply being written. A plain form posts it, so it works before // the page's JavaScript has loaded; the thread's own stream shows the result. -const stopGeneration = action(async (replyId: string) => { +const stopGeneration = formAction(async (replyId: string) => { "use server"; generations.stop(replyId); }, "stopGeneration"); // Writes the last reply again: Regenerate on a finished one, Retry on a // failed one. Like sending, it starts a job and returns. -const regenerate = action(async (replyId: string) => { +const regenerate = formAction(async (replyId: string) => { "use server"; try { const { threadId } = await db.restart(replyId); @@ -47,7 +47,7 @@ const regenerate = action(async (replyId: string) => { } }, "regenerate"); -const renameThread = action(async (id: string, form: FormData) => { +const renameThread = formAction(async (id: string, form: FormData) => { "use server"; try { await db.renameThread(id, String(form.get("title") ?? "")); @@ -58,7 +58,7 @@ const renameThread = action(async (id: string, form: FormData) => { }, "renameThread"); // Deleting leaves nothing to look at, so it answers with where to go. -const deleteThread = action(async (id: string) => { +const deleteThread = formAction(async (id: string) => { "use server"; const writing = await db.deleteThread(id); if (writing) generations.stop(writing); diff --git a/examples/hackernews-spa/package.json b/examples/hackernews-spa/package.json index 32737c0a0f..c8d1c43145 100644 --- a/examples/hackernews-spa/package.json +++ b/examples/hackernews-spa/package.json @@ -13,7 +13,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@solidjs/router": "2.0.0-next.40", + "@solidjs/router": "2.0.0-next.41", "@solidjs/web": "workspace:*", "solid-js": "workspace:*" }, diff --git a/examples/hackernews/package.json b/examples/hackernews/package.json index 80b0be415f..93d201b8ff 100644 --- a/examples/hackernews/package.json +++ b/examples/hackernews/package.json @@ -13,7 +13,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@solidjs/router": "2.0.0-next.40", + "@solidjs/router": "2.0.0-next.41", "@solidjs/web": "workspace:*", "solid-js": "workspace:*" }, diff --git a/examples/notes/package.json b/examples/notes/package.json index e37caed1aa..3a432eceaf 100644 --- a/examples/notes/package.json +++ b/examples/notes/package.json @@ -13,7 +13,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@solidjs/router": "2.0.0-next.40", + "@solidjs/router": "2.0.0-next.41", "@solidjs/web": "workspace:*", "date-fns": "^3.6.0", "marked": "^12.0.2", diff --git a/examples/notes/src/components/NoteEditor.tsx b/examples/notes/src/components/NoteEditor.tsx index cdcd716819..010c614901 100644 --- a/examples/notes/src/components/NoteEditor.tsx +++ b/examples/notes/src/components/NoteEditor.tsx @@ -16,13 +16,13 @@ // the regions that actually show. The live preview reuses the same // NotePreview the note's server component renders — the shared-module half of // the demo. -import { action } from "@solidjs/router"; +import { formAction } from "@solidjs/router"; import { createSignal, untrack } from "solid-js"; import { redirect } from "@solidjs/web"; import * as db from "~/server/db"; import NotePreview from "./NotePreview"; -const saveNote = action(async (id: number | undefined, formData: FormData) => { +const saveNote = formAction(async (id: number | undefined, formData: FormData) => { "use server"; const saved = await db.saveNote( id, @@ -32,7 +32,7 @@ const saveNote = action(async (id: number | undefined, formData: FormData) => { return redirect(`/notes/${saved}`); }); -const deleteNote = action(async (id: number) => { +const deleteNote = formAction(async (id: number) => { "use server"; await db.deleteNote(id); return redirect("/"); diff --git a/examples/server-dbmon/package.json b/examples/server-dbmon/package.json index ca15fe37b9..0a84396431 100644 --- a/examples/server-dbmon/package.json +++ b/examples/server-dbmon/package.json @@ -13,7 +13,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@solidjs/router": "2.0.0-next.40", + "@solidjs/router": "2.0.0-next.41", "@solidjs/web": "workspace:*", "solid-js": "workspace:*" }, diff --git a/examples/todos-server/package.json b/examples/todos-server/package.json index e9ec2b87ff..34e4dddf17 100644 --- a/examples/todos-server/package.json +++ b/examples/todos-server/package.json @@ -13,7 +13,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@solidjs/router": "2.0.0-next.40", + "@solidjs/router": "2.0.0-next.41", "@solidjs/web": "workspace:*", "solid-js": "workspace:*", "unstorage": "^1.17.5" diff --git a/examples/todos-server/src/actions.ts b/examples/todos-server/src/actions.ts index 93a9e519b3..6cedd8a821 100644 --- a/examples/todos-server/src/actions.ts +++ b/examples/todos-server/src/actions.ts @@ -10,7 +10,7 @@ // fresh markup in one round trip. A failed save answers `{ error }` rather // than throwing, so the failure is typed where the client reads it; the // server words it, because only the server knows the todo's title. -import { action } from "@solidjs/router"; +import { formAction } from "@solidjs/router"; import * as db from "~/server/db"; /** The `completed` a toggle form submitted: the button's value. */ @@ -21,33 +21,33 @@ async function titleOf(id: string) { return todo ? `“${todo.title}”` : "a todo"; } -export const addTodo = action(async (form: FormData) => { +export const addTodo = formAction(async (form: FormData) => { "use server"; const title = String(form.get("title") ?? "").trim(); if (!title) return { error: "A todo needs a title" }; return db.addTodo(title); }); -export const toggleTodo = action(async (id: string, form: FormData) => { +export const toggleTodo = formAction(async (id: string, form: FormData) => { "use server"; const completed = completedOf(form); if (await db.toggleTodo(id, completed)) return { error: `Couldn't ${completed ? "complete" : "reopen"} ${await titleOf(id)}` }; }); -export const removeTodo = action(async (id: string) => { +export const removeTodo = formAction(async (id: string) => { "use server"; if (await db.removeTodo(id)) return { error: `Couldn't delete ${await titleOf(id)}` }; }); -export const toggleAll = action(async (form: FormData) => { +export const toggleAll = formAction(async (form: FormData) => { "use server"; const completed = completedOf(form); if (await db.toggleAll(completed)) return { error: `Couldn't mark everything ${completed ? "complete" : "active"}` }; }); -export const clearCompleted = action(async () => { +export const clearCompleted = formAction(async () => { "use server"; if (await db.clearCompleted()) return { error: "Couldn't clear completed" }; }); diff --git a/packages/web/src/response.ts b/packages/web/src/response.ts index 118029a0a0..08fcbf7de5 100644 --- a/packages/web/src/response.ts +++ b/packages/web/src/response.ts @@ -153,9 +153,9 @@ export interface ResponseHelperInit extends ResponseInit { * The cache keys the mutation invalidated, sent as the `X-Revalidate` * header for the client's integrations to apply. Three declarations, three * scopes: omitted sends no header — the host's default (Solid Router - * revalidates everything after an action; a router without that - * convention reloads only what it owns); an empty list sends an empty - * header — nothing, explicitly; `REVALIDATE_ALL` (`"*"`) — every entry, + * revalidates everything after any mutation, form action or direct call; + * a router without that convention reloads only what it owns); an empty + * list (or `""`) sends an empty header — nothing, explicitly; `REVALIDATE_ALL` (`"*"`) — every entry, * whatever the host's default. How named keys are matched (prefixes, * namespaces) is each integration's business. */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e57ad73072..96821ef519 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,8 +137,8 @@ importers: specifier: ^0.133.0 version: 0.133.0 '@solidjs/router': - specifier: 2.0.0-next.40 - version: 2.0.0-next.40(@solidjs/web@packages+web)(solid-js@packages+solid) + specifier: 2.0.0-next.41 + version: 2.0.0-next.41(@solidjs/web@packages+web)(solid-js@packages+solid) '@solidjs/web': specifier: workspace:* version: link:../../packages/web @@ -218,8 +218,8 @@ importers: examples/hackernews: dependencies: '@solidjs/router': - specifier: 2.0.0-next.40 - version: 2.0.0-next.40(@solidjs/web@packages+web)(solid-js@packages+solid) + specifier: 2.0.0-next.41 + version: 2.0.0-next.41(@solidjs/web@packages+web)(solid-js@packages+solid) '@solidjs/web': specifier: workspace:* version: link:../../packages/web @@ -237,8 +237,8 @@ importers: examples/hackernews-spa: dependencies: '@solidjs/router': - specifier: 2.0.0-next.40 - version: 2.0.0-next.40(@solidjs/web@packages+web)(solid-js@packages+solid) + specifier: 2.0.0-next.41 + version: 2.0.0-next.41(@solidjs/web@packages+web)(solid-js@packages+solid) '@solidjs/web': specifier: workspace:* version: link:../../packages/web @@ -277,8 +277,8 @@ importers: examples/notes: dependencies: '@solidjs/router': - specifier: 2.0.0-next.40 - version: 2.0.0-next.40(@solidjs/web@packages+web)(solid-js@packages+solid) + specifier: 2.0.0-next.41 + version: 2.0.0-next.41(@solidjs/web@packages+web)(solid-js@packages+solid) '@solidjs/web': specifier: workspace:* version: link:../../packages/web @@ -329,8 +329,8 @@ importers: examples/server-dbmon: dependencies: '@solidjs/router': - specifier: 2.0.0-next.40 - version: 2.0.0-next.40(@solidjs/web@packages+web)(solid-js@packages+solid) + specifier: 2.0.0-next.41 + version: 2.0.0-next.41(@solidjs/web@packages+web)(solid-js@packages+solid) '@solidjs/web': specifier: workspace:* version: link:../../packages/web @@ -393,8 +393,8 @@ importers: examples/todos-server: dependencies: '@solidjs/router': - specifier: 2.0.0-next.40 - version: 2.0.0-next.40(@solidjs/web@packages+web)(solid-js@packages+solid) + specifier: 2.0.0-next.41 + version: 2.0.0-next.41(@solidjs/web@packages+web)(solid-js@packages+solid) '@solidjs/web': specifier: workspace:* version: link:../../packages/web @@ -2435,8 +2435,8 @@ packages: cpu: [x64] os: [win32] - '@solidjs/router@2.0.0-next.40': - resolution: {integrity: sha512-aBxaV3GHxBn2F3i3rIe3JaEztiLWtZhRC6UI8qJGenaT2GSh6jSF3LTuLvXY5KoMjBgoxzl7Uu4XQ034yII6vg==} + '@solidjs/router@2.0.0-next.41': + resolution: {integrity: sha512-wCnhq37p4dpTYuy2Zz8nlf9Si/t4cMXUiMY0r7MxzHSH2c4jUiSrA8LkSdif6swwAIT/dOP7Pbv4hM1qY5ZOuA==} peerDependencies: '@solidjs/web': workspace:* filesystem-routing: '>=0.4.0' @@ -6119,7 +6119,7 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true - '@solidjs/router@2.0.0-next.40(@solidjs/web@packages+web)(solid-js@packages+solid)': + '@solidjs/router@2.0.0-next.41(@solidjs/web@packages+web)(solid-js@packages+solid)': dependencies: '@solidjs/web': link:packages/web solid-js: link:packages/solid