Pages: loader props lose `Date` (and other non-JSON types) and their types don't say so
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- typescript
- Ambito
- full-stack
Direzione di ricerca
Start by reading pages/client.mjs and the server serialization entry points named in the issue: serializePageData, streamDeferredResponse, and the page-object responses. Trace how initial page data, deferred chunks, navigation, and action responses are serialized and parsed, then compare the behavior with the existing Serialize<T> fetch-client types. Done means the selected transport and InferProps types agree for supported values, with coverage for server rendering, hydration, and later navigations.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Pages: loader props lose Date (and other non-JSON types) and their types don't say so
What happens
Page data is sent as plain JSON, but InferProps types the props as the loader's raw return type. A loader that returns a Drizzle row with a timestamp_ms column (every Void auth table has these) gives the page a prop typed Date, which is only a Date during the server render:
| Where | createdAt is |
|---|---|
InferProps<typeof loader> |
Date |
| Server render | Date (raw loader output) |
| Browser, hydration and later navigations/actions | string (ISO, from JSON.parse) |
Code that type-checks then crashes in the browser. For example, Intl.DateTimeFormat#format(note.createdAt) renders on the server, then throws RangeError: Invalid time value during hydration.
// pages/notes.server.ts
export type Props = InferProps<typeof loader>;
export const loader = defineHandler(async () => ({
notes: await db.select().from(noteTable), // createdAt: Date
}));
// pages/notes.tsx
const fmt = new Intl.DateTimeFormat('en', { dateStyle: 'medium' });
export default function Notes({ notes }: Props) {
return notes.map((n) => <p key={n.id}>{fmt.format(n.createdAt)}</p>);
// server: renders. browser: RangeError: Invalid time value
}
The same happens with Map/Set (they become {}), undefined fields (dropped), and bigint (throws).
Today the workaround is to convert every timestamp at the loader (createdAt: row.createdAt.getTime()).
The typed fetch client avoids the type problem by wrapping responses in Serialize<T> (Date → string), as described in the type-safety guide. Page props, which travel through the same JSON, don't get that treatment. Every meta-framework Void integrates with keeps Date in page data: SvelteKit and Nuxt (devalue), React Router (turbo-stream), TanStack Start (seroval). So this only affects native Void pages.
Proposal: serialize page data with devalue
devalue.stringify / devalue.parse preserve Date, Map, Set, bigint, undefined, RegExp, URL and repeated or cyclic references. The output is still JSON text, so it fits the existing <script type="application/json"> payload and escapeScriptJson. parse never evaluates code. SvelteKit and Nuxt use it for exactly this job.
The JSON calls that would need to switch (from the built [email protected] dist):
- Server:
serializePageDataand the frontmatter script (serialize-*.mjs)- the page-object and deferred-chunk
JSON.stringifycalls instreamDeferredResponse(protocol-*.mjs) - the JSON page-object response for client navigations and actions
- Client (
pages/client.mjs):readInitialPageDataparsePageDataFromHtml- the navigation and action
response.json()reads clonePageData(could becomestructuredClone, which also keepsDate)
After the change:
- The server render and the browser see the same values.
InferPropsstays the raw return type, which is then accurate for everything devalue supports.- Loaders can return Drizzle rows directly.
Values devalue can't handle (functions, class instances) could be stripped from InferProps with a small mapped type, the way Serialize<T> already strips functions.
Alternatives
- Minimal fix, no format change: apply
Serialize<T>toInferProps, and render the server page fromJSON.parse(JSON.stringify(props)). Types and runtime would then agree, but dates stay strings. - seroval: covers more types and can also stream Promises, so it could replace the custom
defer()streaming. It's a bigger change, and some of its output modes are JavaScript that the browser evaluates.
Related
- #478:
InferPropsreads__outputdirectly, while routes wrap it inSerialize<…>. - #443: RSC for pages would change the transport for the React adapter. Page data is JSON (NDJSON for deferred props) today.
- Lingua principale
- TypeScript
- Stelle
- 580
- Fork
- 56
- Merge medio
- 11h 23m
- PR unite (30g)
- 153
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di voidzero-dev/void
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
voidzero-dev/void#627 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
voidzero-dev/void#619 ·
I maintainer di solito rispondono entro 1 giorno
-
@void/react: client environment has no optimizeDeps.entries for pages, causing a dev re-optimization reload on first loadForse già presa @0froq l’ha presa 1 giorno fa. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 66/100
voidzero-dev/void#599 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
`void/log`: add `logger.child(fields)` to bind fields to every lineForse già presa @Dazzanova l’ha presa 1 giorno fa. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 82/100
voidzero-dev/void#596 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 56/100
voidzero-dev/void#579 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di voidzero-dev/void
Issue simili
-
e2e-failure ready-to-code
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
redhat-developer/rhdh-plugin-export-overlays#4616 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
I maintainer di solito rispondono entro 1 giorno
-
ai-handle
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
nextlevelbuilder/dewee-web-v2#73 ·
I maintainer di solito rispondono entro 1 giorno
-
backend bug good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
PoCInnovation/Distribox#255 ·
I maintainer di solito rispondono entro 1 giorno