Conventions de données PRISM
Les itinéraires PRISM sont des composants bext — purs (props) => string (ou
Promise<string> pour les pages asynchrones). Ils rendent le côté serveur via
Le moteur d'exécution h() de @bext-stack/framework ; il n'y a pas de React, pas de DOM virtuel, pas de cycle de vie côté client. Voir PRISM pour la présentation du framework.
Pour les pages qui nécessitent une récupération de données ou une gestion de mutation côté serveur, PRISM propose les conventions loader et action de style Remix : colocalisez le gestionnaire de données avec le composant dans le même fichier de route, et PRISM l'appelle sur le serveur avant l'exécution du composant.
Anatomie d'une route de données#
// src/app/posts/[id]/page.tsx
export async function loader({ request, params }: LoaderArgs) {
const post = await db.post.findUnique({ where: { id: params.id } });
if (!post) throw new Response("Not Found", { status: 404 });
return { post };
}
export async function action({ request, params }: ActionArgs) {
const form = await request.formData();
await db.post.update({
where: { id: params.id },
data: { title: form.get("title") as string },
});
return { ok: true };
}
export default function PostPage({ data, actionData }: PageProps) {
return (
<article>
<h1>{data.post.title}</h1>
{actionData?.ok && <p>Saved.</p>}
<form method="post">
<input name="title" defaultValue={data.post.title} />
<button type="submit">Save</button>
</form>
</article>
);
}
Trois exports depuis le même module :
loader— fonctionne pourGET/HEAD. La valeur de retour est transmise commeprops.dataau composant.action— fonctionne pourPOST/PUT/DELETE/PATCH. Valeur de retour est transmis sous la formeprops.actionData. PRISM réexécuteloaderaprès une action réussie afin que la page s'affiche avec de nouvelles données, correspondant à la sémantique Remix.default— le composant bext (renvoiestringouAsyncIterable<string>).
Règles d'expédition#
| Méthode | A un chargeur | A de l'action | Qu'est-ce qui court |
|---|---|---|---|
GET / HEAD |
Oui | n'importe lequel | loader → composant |
GET / HEAD |
Non | n'importe lequel | composant uniquement |
| Non-GET | n'importe lequel | Oui | action → loader → composant |
| Non-GET | n'importe lequel | Non | composant uniquement |
La décision se produit dans Rust, avant que le pool de rendu ne soit engagé — voir PrismDataCall::for_request dans
crates/bext-core/src/framework/prism_data.rs. Si la route n'exporte aucun gestionnaire, PRISM emprunte le même chemin de code sans données qu'il a toujours ; la convention est strictement additive.
Détection des exportations#
PRISM analyse les fichiers source de route au moment de la construction pour quatre canoniques export shapes:
export async function loader(...) {}
export function loader(...) {}
export const loader = ...
export { loader }
…et les mêmes quatre pour action. Le détecteur est intentionnellement au niveau de la source - pas d'analyseur ESM, juste des correspondances de sous-chaînes - car les fichiers de route sont petits et les faux positifs sont inoffensifs (le temps d'exécution échoue lorsque l'exportation n'existe pas réellement).
Un faux négatif connu : export { something as loader }. Si vous renommez un identifiant dans une liste d'exportation, le détecteur ne le remarquera pas. Utilisez l’une des quatre formes canoniques ci-dessus.
Lancer des réponses#
L’un ou l’autre gestionnaire peut throw new Response(...) pour court-circuiter le rendu. PRISM récupère la réponse lancée et la renvoie textuellement. Utilisez-le pour les 404, les redirections et les portes d'authentification sans polluer le code des composants :
export async function loader({ request }: LoaderArgs) {
const session = await getSession(request);
if (!session.user) {
throw new Response(null, {
status: 302,
headers: { Location: "/login" },
});
}
return { user: session.user };
}
TypeScript#
PRISM exporte LoaderArgs, ActionArgs et PageProps depuis
@bext-stack/framework. Le générique PageProps déduit data et
actionData des types de retour frères loader / action, vous obtenez donc une inférence de bout en bout sans annotations explicites :
import type { LoaderArgs, PageProps } from "@bext-stack/framework";
export async function loader({ params }: LoaderArgs) {
return { post: await load(params.id) };
}
export default function Page({ data }: PageProps<typeof loader>) {
// data.post inferred
}
Le détecteur d'exportation au niveau source utilise la correspondance de sous-chaînes et non une analyse complète. Le formulaire
export { something as loader } est un faux négatif connu : utilisez l'un des quatre codes canoniques.
Formes export async function loader / export const loader = … pour assurer la détection.
Mises en garde#
- Pas encore connecté au pool de rendu. A partir de E7, la détection
et la logique de répartition sont actives dans
bext-coreavec une couverture unitaire, mais le chemin de rendu V8/V8 appelle toujours directement le composant. Le câblage du saut intermédiaireloader/actiondans le pool de rendu atterrit dans le suivi E7 - même cadence que la répartition du sous-processus SolidStart. - Le JSON renvoyé par l'exécution est sérialisé via
JSON.stringify.Date,BigIntet les instances de classe perdront leur type. Le support de Superjson est sur la feuille de route. - Aucun routage imbriqué pour l'instant. Dans Remix, les routes parents obtiennent leur
Posséder
loader. Le routeur du PRISM est plat pour l'instant ; Les chargeurs de route parent arrivent lorsque PRISM développe des mises en page imbriquées au-delà du modèle de mise en page unique actuel.
Documents associés#
- PRISM en aperçu — le moteur lui-même
- Routage — comment
src/app/correspond aux URL - SolidStart — mêmes conventions, différentes cadre