40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { parseNodePath } from "~/utils/nodePath"
|
|
import { resolvePath } from "~/utils/resolvePath"
|
|
|
|
/**
|
|
* Route loader for `/voyages/:id`. The URL is the single source of truth for
|
|
* navigation; this guard projects it into the store before the page renders:
|
|
*
|
|
* 1. ensure the link + all its entities are loaded (idempotent, deduped — a
|
|
* node change within the same voyage is an instant no-op, no refetch),
|
|
* 2. validate the `?n=` path against the freshly-built tree → 404 if it points
|
|
* at an event that doesn't exist (stale/shared deep link),
|
|
* 3. write the focused path (this guard is its only writer).
|
|
*
|
|
* On a load failure we don't 404 (the path can't be judged against missing
|
|
* data): we reset the focus and let the page surface its retryable error state.
|
|
*/
|
|
export default defineNuxtRouteMiddleware(async (to) => {
|
|
const linkId = to.params.id as string
|
|
const voyage = useVoyageStore()
|
|
|
|
await voyage.ensureVoyageLoaded(linkId)
|
|
|
|
if (voyage.error) {
|
|
voyage.setFocusedPath([])
|
|
return
|
|
}
|
|
|
|
const path = parseNodePath(to.query.n)
|
|
const resolved = resolvePath(voyage.voyageTree, path)
|
|
if (resolved.length !== path.length + 1) {
|
|
throw createError({
|
|
statusCode: 404,
|
|
statusMessage: "Not Found",
|
|
message: "Étape introuvable",
|
|
fatal: true,
|
|
})
|
|
}
|
|
|
|
voyage.setFocusedPath(path)
|
|
})
|