@warlock.js/web 5.1.0 → 5.2.1
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 +182 -0
- package/README.md +126 -12
- package/esm/build/contribution.d.mts +10 -13
- package/esm/build/contribution.mjs +45 -57
- package/esm/build/contribution.mjs.map +1 -1
- package/esm/build/discover-pages.mjs +104 -44
- package/esm/build/discover-pages.mjs.map +1 -1
- package/esm/build/generate-client-registry.mjs +46 -12
- package/esm/build/generate-client-registry.mjs.map +1 -1
- package/esm/build/generate-pages-barrel.mjs +54 -12
- package/esm/build/generate-pages-barrel.mjs.map +1 -1
- package/esm/build/page-default-export.mjs +48 -0
- package/esm/build/page-default-export.mjs.map +1 -0
- package/esm/build/page-routes-manifest.mjs +21 -0
- package/esm/build/page-routes-manifest.mjs.map +1 -0
- package/esm/build/public-files.mjs +45 -0
- package/esm/build/public-files.mjs.map +1 -0
- package/esm/build/read-route-exports.mjs.map +1 -1
- package/esm/client/build-hydrated-tree.mjs +39 -8
- package/esm/client/build-hydrated-tree.mjs.map +1 -1
- package/esm/client/navigation/fetch-page-data.mjs.map +1 -1
- package/esm/client/navigation/prefetch.mjs.map +1 -1
- package/esm/client/runtime/index.d.mts +2 -1
- package/esm/client/runtime/index.mjs +2 -1
- package/esm/client/runtime/manifest.mjs +2 -1
- package/esm/client/runtime/manifest.mjs.map +1 -1
- package/esm/client/runtime/matcher.d.mts +12 -0
- package/esm/client/runtime/matcher.mjs +12 -0
- package/esm/client/runtime/matcher.mjs.map +1 -1
- package/esm/client/runtime/types.d.mts +14 -1
- package/esm/components/document-context.d.mts +39 -1
- package/esm/components/document-context.mjs.map +1 -1
- package/esm/components/link.mjs.map +1 -1
- package/esm/components/scripts.mjs +3 -1
- package/esm/components/scripts.mjs.map +1 -1
- package/esm/context.d.mts +5 -101
- package/esm/hydration-payload.d.mts +1 -1
- package/esm/hydration-payload.mjs +36 -7
- package/esm/hydration-payload.mjs.map +1 -1
- package/esm/index.d.mts +3 -2
- package/esm/loaders.d.mts +6 -35
- package/esm/props.d.mts +15 -7
- package/esm/route.d.mts +1 -16
- package/esm/routing/filesystem-route.mjs +45 -0
- package/esm/routing/filesystem-route.mjs.map +1 -0
- package/esm/routing/layout-policy.mjs.map +1 -1
- package/esm/routing/query-string.mjs.map +1 -1
- package/esm/routing/route-table.mjs.map +1 -1
- package/esm/runtime/register-modules.d.mts +19 -0
- package/esm/runtime/register-modules.mjs +22 -0
- package/esm/runtime/register-modules.mjs.map +1 -0
- package/esm/server/build-hydration-payload.mjs +1 -0
- package/esm/server/build-hydration-payload.mjs.map +1 -1
- package/esm/server/create-page-module-loader.mjs +1 -0
- package/esm/server/create-page-module-loader.mjs.map +1 -1
- package/esm/server/create-page-route-handler.d.mts +30 -5
- package/esm/server/create-page-route-handler.mjs +127 -42
- package/esm/server/create-page-route-handler.mjs.map +1 -1
- package/esm/server/dev-server.mjs +1 -12
- package/esm/server/dev-server.mjs.map +1 -1
- package/esm/server/error-page.d.mts +14 -0
- package/esm/server/error-page.mjs +39 -0
- package/esm/server/error-page.mjs.map +1 -0
- package/esm/server/execute-page-request.d.mts +6 -3
- package/esm/server/execute-page-request.mjs +78 -79
- package/esm/server/execute-page-request.mjs.map +1 -1
- package/esm/server/execute-page-request.types.d.mts +32 -91
- package/esm/server/hydration-client-url.mjs +1 -1
- package/esm/server/index.d.mts +5 -5
- package/esm/server/index.mjs +3 -4
- package/esm/server/install-page-routes-from-manifest.d.mts +16 -6
- package/esm/server/install-page-routes-from-manifest.mjs +70 -9
- package/esm/server/install-page-routes-from-manifest.mjs.map +1 -1
- package/esm/server/install-page-routes.d.mts +31 -14
- package/esm/server/install-page-routes.mjs +90 -38
- package/esm/server/install-page-routes.mjs.map +1 -1
- package/esm/server/install-production-page-routes.mjs +2 -35
- package/esm/server/install-production-page-routes.mjs.map +1 -1
- package/esm/server/match-page-route.mjs +1 -1
- package/esm/server/match-page-route.mjs.map +1 -1
- package/esm/server/not-found-page.mjs +1 -1
- package/esm/server/not-found-page.mjs.map +1 -1
- package/esm/server/page-context.d.mts +2 -8
- package/esm/server/page-context.mjs +2 -15
- package/esm/server/page-context.mjs.map +1 -1
- package/esm/server/page-file-change.mjs +77 -0
- package/esm/server/page-file-change.mjs.map +1 -0
- package/esm/server/page-manifest.d.mts +24 -9
- package/esm/server/page-manifest.mjs.map +1 -1
- package/esm/server/page-render-bundle.mjs +38 -0
- package/esm/server/page-render-bundle.mjs.map +1 -0
- package/esm/server/page-route-reload.mjs +54 -0
- package/esm/server/page-route-reload.mjs.map +1 -0
- package/esm/server/register-production-public-files.mjs +40 -0
- package/esm/server/register-production-public-files.mjs.map +1 -0
- package/esm/server/render-page.d.mts +10 -13
- package/esm/server/render-page.mjs +153 -27
- package/esm/server/render-page.mjs.map +1 -1
- package/esm/server/resolve-page-metadata.mjs +1 -1
- package/esm/server/resolve-page-metadata.mjs.map +1 -1
- package/esm/server/settle-page-response.d.mts +58 -1
- package/esm/server/settle-page-response.mjs +110 -45
- package/esm/server/settle-page-response.mjs.map +1 -1
- package/esm/server/stylesheet-urls.d.mts +40 -29
- package/esm/server/stylesheet-urls.mjs +136 -51
- package/esm/server/stylesheet-urls.mjs.map +1 -1
- package/esm/server/unregistered-pages.mjs +42 -0
- package/esm/server/unregistered-pages.mjs.map +1 -0
- package/esm/server/web-connector-factory.d.mts +1 -2
- package/esm/server/web-connector-factory.mjs +6 -5
- package/esm/server/web-connector-factory.mjs.map +1 -1
- package/esm/server/web-connector.mjs +137 -17
- package/esm/server/web-connector.mjs.map +1 -1
- package/esm/shared.d.mts +17 -2
- package/esm/shared.mjs +2 -6
- package/esm/shared.mjs.map +1 -1
- package/esm/vite/build-client.mjs +1 -1
- package/esm/vite/build-client.mjs.map +1 -1
- package/esm/vite/gate-a-resolve.mjs +66 -12
- package/esm/vite/gate-a-resolve.mjs.map +1 -1
- package/esm/vite/gate-b-secrets.mjs +34 -16
- package/esm/vite/gate-b-secrets.mjs.map +1 -1
- package/esm/vite/gate-c-verify.mjs +5 -5
- package/esm/vite/gate-c-verify.mjs.map +1 -1
- package/esm/vite/hydration-entries.mjs +1 -1
- package/esm/vite/hydration-entries.mjs.map +1 -1
- package/esm/vite/index.d.mts +8 -5
- package/esm/vite/index.mjs +123 -10
- package/esm/vite/index.mjs.map +1 -1
- package/esm/vite/page-registry-plugin.d.mts +21 -2
- package/esm/vite/page-registry-plugin.mjs +47 -13
- package/esm/vite/page-registry-plugin.mjs.map +1 -1
- package/esm/vite/projection.d.mts +1 -1
- package/esm/vite/projection.mjs +37 -14
- package/esm/vite/projection.mjs.map +1 -1
- package/llms-full.txt +220 -56
- package/llms.txt +5 -3
- package/package.json +3 -3
- package/skills/add-web-to-an-app/SKILL.md +3 -3
- package/skills/create-a-page/SKILL.md +117 -10
- package/skills/load-page-data/SKILL.md +92 -12
- package/skills/navigate-on-the-client/SKILL.md +1 -1
- package/skills/serve-styles/SKILL.md +13 -12
- package/skills/use-layouts/SKILL.md +14 -8
- package/skills/write-the-root/SKILL.md +3 -1
- package/esm/server/buffered-response.d.mts +0 -58
- package/esm/server/buffered-response.mjs +0 -115
- package/esm/server/buffered-response.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.mjs","names":[],"sources":["../../../../../../../web/src/server/dev-server.ts"],"sourcesContent":["/**\n * Two small pieces of the dev/SSR page plumbing that `./web-connector.ts` wires\n * but deliberately does not own: the buffered-cookie commit, and the DEV-ONLY\n * error transport that stops a refused module from reaching the browser as a\n * bare 404 (see {@link devErrorTransportPlugin}).\n *\n * ---\n *\n * Commits one buffered cookie (`BufferedCookie`, from `./buffered-response.ts`)\n * onto the live Warlock `Response` — the single place in the SSR page pipeline\n * where a cookie the loader buffered turns into a real `Set-Cookie` header.\n *\n * It delegates to core's `Response.cookie()` rather than talking to fastify.\n * That method already owns everything this file used to reimplement: it takes\n * core's own `CookieOptions`, JSON-stringifies the value unless `{ raw: true }`,\n * strips the core-only `raw` flag, and layers the framework's secure-cookie\n * defaults and the `http.cookies.options` config under the per-call options.\n * Going through it means the SSR page path and every ordinary controller emit\n * cookies by the exact same code, so the two cannot drift apart.\n *\n * The helper is passed INTO `installPageRoutes`/`createPageRouteHandler` as an\n * option rather than imported by them, which is what keeps this module out of a\n * cycle with the page-route installer.\n */\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { stripVTControlCharacters } from \"node:util\";\nimport type { Connect, Plugin } from \"vite\";\nimport type { Response } from \"@warlock.js/core\";\nimport type { BufferedCookie } from \"./buffered-response\";\n\n/**\n * Replay a buffered cookie onto the response that will actually be sent.\n *\n * `BufferedCookie.options` is a loosely-typed bag (`Record<string, unknown>`)\n * because the buffer is written by loader code before any response exists; the\n * cast hands it to `Response.cookie()`, which is where the option shape is\n * defined and enforced for every other caller in the framework.\n */\nexport function applyBufferedCookie(response: Response, cookie: BufferedCookie): void {\n response.cookie(cookie.name, cookie.value as never, (cookie.options ?? {}) as never);\n}\n\n/**\n * Where a captured transform/resolve failure rides from Vite's connect stack to\n * the Fastify hook that mounted it.\n *\n * A `Symbol.for` key on the raw `IncomingMessage` rather than a `WeakMap`\n * because the two halves live in different modules and are wired at different\n * times; the request object is the only thing they provably share, and the\n * symbol cannot collide with a Vite/Fastify/user property.\n *\n * Exported so a test can stage a captured failure without booting a vite\n * server, and so the two halves cannot drift onto two different keys.\n */\nexport const DEV_TRANSFORM_ERROR_BODY = Symbol.for(\"warlock.web.devTransformErrorBody\");\n\ntype DevTransformErrorCarrier = { [DEV_TRANSFORM_ERROR_BODY]?: string };\n\n/**\n * The status a refused module now answers with.\n *\n * NOT the 404 this replaces. That 404 was never a decision about the module —\n * it is what an unmatched URL gets once vite has declined it, and which of the\n * two framework answers you see depends only on whether the app declares a\n * catch-all page: with one (v5/app does — `path: \"*\"`) the request lands in the\n * page pipeline, matches no route, and `./render-page.ts:604` returns\n * `{ html: \"\", status: 404 }` for `./create-page-route-handler.ts:147` to write\n * as an empty `text/html` body; without one it is `core/src/router/router.ts:879`.\n * Either way \"the module does not exist\" is precisely the wrong thing to tell a\n * developer whose module exists and was refused. 500 is the status VITE ITSELF writes for\n * this exact condition when it is not in middleware mode\n * (`node_modules/vite/dist/node/chunks/config.js:9528`), so this adopts that\n * convention rather than inventing a third one.\n */\nexport const DEV_TRANSFORM_ERROR_STATUS = 500;\n\n/**\n * `buildErrorMessage` as vite exports it. Declared structurally so this module\n * needs no value import of vite — vite is an optional, dev-only peer and a\n * production install does not carry it.\n */\nexport type BuildErrorMessage = (\n error: Error,\n args?: string[],\n includeStack?: boolean,\n) => string;\n\n/**\n * The dev error transport was constructed while the process is hosting a\n * PRODUCTION build. Refused by name at construction rather than degraded,\n * because everything this transport does — file paths, source frames, plugin\n * names — is exactly what a production response must never carry.\n */\nexport class DevErrorTransportInProductionError extends Error {\n public constructor() {\n super(\n \"The dev error transport was constructed with `Application.runtimeStrategy === \" +\n '\"production\"`. It exists only to put a Vite transform failure in front of a ' +\n \"developer and its response body carries absolute file paths and source frames, \" +\n \"so it must never be mounted on a production-hosted server.\",\n );\n this.name = \"DevErrorTransportInProductionError\";\n }\n}\n\n/**\n * Render a refused module's failure as the plain-text body the browser gets.\n *\n * Formatting is DELEGATED to vite's own exported `buildErrorMessage`, not\n * reimplemented: it is the same function vite uses to print the failure to the\n * terminal, so the text a developer reads in the network panel and the text\n * they read in the terminal cannot drift. Two things are added around it —\n * `error.name`, which vite's terminal path replaces with a fixed\n * \"Internal server error:\" prefix and which is the single most useful token for\n * a named gate refusal (`ProjectionAmbiguityError`), and the `cause` chain,\n * which vite does not walk.\n *\n * `stripVTControlCharacters` is not optional: `buildErrorMessage` colours its\n * output with picocolors, which is ON whenever the dev server owns a TTY, and\n * raw ANSI escapes in an HTTP body are noise. Vite strips them the same way for\n * the overlay payload (`config.js:9490-9497`).\n *\n * The stack is deliberately omitted (`includeStack: false`). A gate refusal's\n * stack points into the gate, not into the developer's code; the fields that\n * locate the problem — plugin, file, line, source frame — are what\n * `buildErrorMessage` puts there without it.\n */\nexport function formatDevTransformError(\n error: unknown,\n buildErrorMessage: BuildErrorMessage,\n): string {\n const failure =\n error instanceof Error ? error : new Error(typeof error === \"string\" ? error : String(error));\n\n const lines = [\n stripVTControlCharacters(\n buildErrorMessage(failure, [`${failure.name}: ${failure.message}`], false),\n ),\n ];\n\n // Walk the `cause` chain. A gate may wrap a parser failure, and the wrapped\n // message is usually the one naming the actual syntax that was refused.\n let cause = (failure as { cause?: unknown }).cause;\n\n while (cause instanceof Error) {\n lines.push(` Caused by: ${cause.name}: ${stripVTControlCharacters(cause.message)}`);\n cause = (cause as { cause?: unknown }).cause;\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\n/**\n * DEV-ONLY. Capture the transform/resolve failure that vite is about to throw\n * away, so the request that caused it can answer with it.\n *\n * WHY THIS IS A PLUGIN AND NOT A `middlewares.use(...)` CALL — this is the\n * whole defect, and it is an ordering fact, not a style choice:\n *\n * - Vite mounts its own error handler LAST, built as\n * `errorMiddleware(server, !!middlewareMode)`\n * (`node_modules/vite/dist/node/chunks/config.js:25705`).\n * - In middleware mode that `allowNext` flag is `true`, and the handler then\n * logs the error to the TERMINAL and calls `next()` — with no error\n * (`config.js:9525-9527`).\n * - connect only routes an error to a 4-arity handler while an error is in\n * flight (`config.js:10611-10626`), so `next()` clears it: every layer after\n * that point, INCLUDING the `done` callback `./web-connector.ts` hands the\n * stack, is called as if the request had simply gone unhandled. The\n * framework then answers the only way it can for a URL it does not know — a\n * 404, empty (see {@link DEV_TRANSFORM_ERROR_STATUS} for which of the two\n * produces it).\n * - Anything registered with `vite.middlewares.use(...)` after `createServer()`\n * resolves lands AFTER that handler and is therefore unreachable. A\n * `configureServer` POST hook does not: vite runs post hooks at\n * `config.js:25700`, five lines BEFORE it mounts its error handler.\n *\n * So this sits between the failure and vite's logger. It captures, then calls\n * `next(error)` and lets vite's own handler run exactly as before — the\n * terminal message and the `hot.send({ type: \"error\" })` overlay push\n * (`config.js:9511-9521`) are unchanged. This transport ADDS a reader; it\n * replaces nothing.\n *\n * @param isProductionRuntime the connector's own hosting-mode signal\n * (`./web-connector.ts:122`) — passed in rather than re-derived so there\n * is one definition of \"this process is Vite-hosted\", not two.\n */\nexport function devErrorTransportPlugin(options: {\n isProductionRuntime: () => boolean;\n buildErrorMessage: BuildErrorMessage;\n}): Plugin {\n const { isProductionRuntime, buildErrorMessage } = options;\n\n if (isProductionRuntime()) {\n throw new DevErrorTransportInProductionError();\n }\n\n const capture: Connect.ErrorHandleFunction = (error, request, _response, next) => {\n // Re-asserted per request, not just at construction: `runtimeStrategy` is\n // process state and a transport that leaks source frames is not something\n // to hold open on a boot-time reading alone. In production this layer is a\n // pass-through and vite's handler behaves exactly as it does today.\n if (isProductionRuntime()) return next(error);\n\n (request as DevTransformErrorCarrier)[DEV_TRANSFORM_ERROR_BODY] = formatDevTransformError(\n error,\n buildErrorMessage,\n );\n\n next(error);\n };\n\n return {\n name: \"warlock:dev-error-transport\",\n // Belt to the `isProductionRuntime` braces: this plugin has no business in\n // a `vite build` graph either.\n apply: \"serve\",\n configureServer(server) {\n // RETURNING a function is what makes this a POST hook — the ordering the\n // note above depends on. Mounting inline here would land the layer BEFORE\n // vite's transform middleware, where no error has been thrown yet.\n return () => {\n server.middlewares.use(capture);\n };\n },\n };\n}\n\n/**\n * Answer the request with the failure {@link devErrorTransportPlugin} captured,\n * if there was one. Returns `false` when there was not, which is the normal\n * case and means \"carry on down the framework's own path\".\n *\n * Called from the Fastify `onRequest` hook that mounts vite\n * (`./web-connector.ts:321`), in the `done` callback — i.e. at the one moment\n * where connect has finished, vite has declined to answer, and the framework is\n * about to 404. Writing to the raw `ServerResponse` rather than through Fastify\n * is what the mount already does for every response vite serves, so this stays\n * on the same side of the seam.\n */\nexport function sendCapturedDevError(request: IncomingMessage, response: ServerResponse): boolean {\n const body = (request as DevTransformErrorCarrier)[DEV_TRANSFORM_ERROR_BODY];\n\n if (typeof body !== \"string\") return false;\n\n // A middleware further down may have answered already (vite serves plenty of\n // requests itself). Never write twice; the captured body is then just dropped.\n if (response.headersSent || response.writableEnded) return false;\n\n response.statusCode = DEV_TRANSFORM_ERROR_STATUS;\n response.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n // A refusal is a fact about the CURRENT source. Caching it would survive the\n // edit that fixes it.\n response.setHeader(\"cache-control\", \"no-store\");\n response.end(body);\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;AAsCA,SAAgB,oBAAoB,UAAoB,QAA8B;CACpF,SAAS,OAAO,OAAO,MAAM,OAAO,OAAiB,OAAO,WAAW,CAAC,CAAW;AACrF;;;;;;;;;;;;;AAcA,MAAa,2BAA2B,OAAO,IAAI,mCAAmC;;;;;;;;;;;;;;;;;AAoBtF,MAAa,6BAA6B;;;;;;;AAmB1C,IAAa,qCAAb,cAAwD,MAAM;CAC5D,AAAO,cAAc;EACnB,MACE,uSAIF;EACA,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,wBACd,OACA,mBACQ;CACR,MAAM,UACJ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK,CAAC;CAE9F,MAAM,QAAQ,CACZ,yBACE,kBAAkB,SAAS,CAAC,GAAG,QAAQ,KAAK,IAAI,QAAQ,SAAS,GAAG,KAAK,CAC3E,CACF;CAIA,IAAI,QAAS,QAAgC;CAE7C,OAAO,iBAAiB,OAAO;EAC7B,MAAM,KAAK,gBAAgB,MAAM,KAAK,IAAI,yBAAyB,MAAM,OAAO,GAAG;EACnF,QAAS,MAA8B;CACzC;CAEA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,wBAAwB,SAG7B;CACT,MAAM,EAAE,qBAAqB,sBAAsB;CAEnD,IAAI,oBAAoB,GACtB,MAAM,IAAI,mCAAmC;CAG/C,MAAM,WAAwC,OAAO,SAAS,WAAW,SAAS;EAKhF,IAAI,oBAAoB,GAAG,OAAO,KAAK,KAAK;EAE5C,AAAC,QAAqC,4BAA4B,wBAChE,OACA,iBACF;EAEA,KAAK,KAAK;CACZ;CAEA,OAAO;EACL,MAAM;EAGN,OAAO;EACP,gBAAgB,QAAQ;GAItB,aAAa;IACX,OAAO,YAAY,IAAI,OAAO;GAChC;EACF;CACF;AACF;;;;;;;;;;;;;AAcA,SAAgB,qBAAqB,SAA0B,UAAmC;CAChG,MAAM,OAAQ,QAAqC;CAEnD,IAAI,OAAO,SAAS,UAAU,OAAO;CAIrC,IAAI,SAAS,eAAe,SAAS,eAAe,OAAO;CAE3D,SAAS;CACT,SAAS,UAAU,gBAAgB,2BAA2B;CAG9D,SAAS,UAAU,iBAAiB,UAAU;CAC9C,SAAS,IAAI,IAAI;CAEjB,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"dev-server.mjs","names":[],"sources":["../../../../../../../web/src/server/dev-server.ts"],"sourcesContent":["/** Development-only transport for Vite transform failures. */\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { stripVTControlCharacters } from \"node:util\";\nimport type { Connect, Plugin } from \"vite\";\n\n/**\n * Where a captured transform/resolve failure rides from Vite's connect stack to\n * the Fastify hook that mounted it.\n *\n * A `Symbol.for` key on the raw `IncomingMessage` rather than a `WeakMap`\n * because the two halves live in different modules and are wired at different\n * times; the request object is the only thing they provably share, and the\n * symbol cannot collide with a Vite/Fastify/user property.\n *\n * Exported so a test can stage a captured failure without booting a vite\n * server, and so the two halves cannot drift onto two different keys.\n */\nexport const DEV_TRANSFORM_ERROR_BODY = Symbol.for(\"warlock.web.devTransformErrorBody\");\n\n/**\n * Re-exported so a dev-only caller can reach the cookie commit seam through\n * this module without a second import path into `./buffered-response`, which\n * is where the implementation actually lives.\n */\nexport { applyBufferedCookie, type BufferedCookie } from \"./buffered-response\";\n\ntype DevTransformErrorCarrier = { [DEV_TRANSFORM_ERROR_BODY]?: string };\n\n/**\n * The status a refused module now answers with.\n *\n * NOT the 404 this replaces. That 404 was never a decision about the module —\n * it is what an unmatched URL gets once vite has declined it, and which of the\n * two framework answers you see depends only on whether the app declares a\n * catch-all page: with one (v5/app does — `path: \"*\"`) the request lands in the\n * page pipeline, matches no route, and `./render-page.ts:604` returns\n * `{ html: \"\", status: 404 }` for `./create-page-route-handler.ts:147` to write\n * as an empty `text/html` body; without one it is `core/src/router/router.ts:879`.\n * Either way \"the module does not exist\" is precisely the wrong thing to tell a\n * developer whose module exists and was refused. 500 is the status VITE ITSELF writes for\n * this exact condition when it is not in middleware mode\n * (`node_modules/vite/dist/node/chunks/config.js:9528`), so this adopts that\n * convention rather than inventing a third one.\n */\nexport const DEV_TRANSFORM_ERROR_STATUS = 500;\n\n/**\n * `buildErrorMessage` as vite exports it. Declared structurally so this module\n * needs no value import of vite — vite is an optional, dev-only peer and a\n * production install does not carry it.\n */\nexport type BuildErrorMessage = (\n error: Error,\n args?: string[],\n includeStack?: boolean,\n) => string;\n\n/**\n * The dev error transport was constructed while the process is hosting a\n * PRODUCTION build. Refused by name at construction rather than degraded,\n * because everything this transport does — file paths, source frames, plugin\n * names — is exactly what a production response must never carry.\n */\nexport class DevErrorTransportInProductionError extends Error {\n public constructor() {\n super(\n \"The dev error transport was constructed with `Application.runtimeStrategy === \" +\n '\"production\"`. It exists only to put a Vite transform failure in front of a ' +\n \"developer and its response body carries absolute file paths and source frames, \" +\n \"so it must never be mounted on a production-hosted server.\",\n );\n this.name = \"DevErrorTransportInProductionError\";\n }\n}\n\n/**\n * Render a refused module's failure as the plain-text body the browser gets.\n *\n * Formatting is DELEGATED to vite's own exported `buildErrorMessage`, not\n * reimplemented: it is the same function vite uses to print the failure to the\n * terminal, so the text a developer reads in the network panel and the text\n * they read in the terminal cannot drift. Two things are added around it —\n * `error.name`, which vite's terminal path replaces with a fixed\n * \"Internal server error:\" prefix and which is the single most useful token for\n * a named gate refusal (`ProjectionAmbiguityError`), and the `cause` chain,\n * which vite does not walk.\n *\n * `stripVTControlCharacters` is not optional: `buildErrorMessage` colours its\n * output with picocolors, which is ON whenever the dev server owns a TTY, and\n * raw ANSI escapes in an HTTP body are noise. Vite strips them the same way for\n * the overlay payload (`config.js:9490-9497`).\n *\n * The stack is deliberately omitted (`includeStack: false`). A gate refusal's\n * stack points into the gate, not into the developer's code; the fields that\n * locate the problem — plugin, file, line, source frame — are what\n * `buildErrorMessage` puts there without it.\n */\nexport function formatDevTransformError(\n error: unknown,\n buildErrorMessage: BuildErrorMessage,\n): string {\n const failure =\n error instanceof Error ? error : new Error(typeof error === \"string\" ? error : String(error));\n\n const lines = [\n stripVTControlCharacters(\n buildErrorMessage(failure, [`${failure.name}: ${failure.message}`], false),\n ),\n ];\n\n // Walk the `cause` chain. A gate may wrap a parser failure, and the wrapped\n // message is usually the one naming the actual syntax that was refused.\n let cause = (failure as { cause?: unknown }).cause;\n\n while (cause instanceof Error) {\n lines.push(` Caused by: ${cause.name}: ${stripVTControlCharacters(cause.message)}`);\n cause = (cause as { cause?: unknown }).cause;\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\n/**\n * DEV-ONLY. Capture the transform/resolve failure that vite is about to throw\n * away, so the request that caused it can answer with it.\n *\n * WHY THIS IS A PLUGIN AND NOT A `middlewares.use(...)` CALL — this is the\n * whole defect, and it is an ordering fact, not a style choice:\n *\n * - Vite mounts its own error handler LAST, built as\n * `errorMiddleware(server, !!middlewareMode)`\n * (`node_modules/vite/dist/node/chunks/config.js:25705`).\n * - In middleware mode that `allowNext` flag is `true`, and the handler then\n * logs the error to the TERMINAL and calls `next()` — with no error\n * (`config.js:9525-9527`).\n * - connect only routes an error to a 4-arity handler while an error is in\n * flight (`config.js:10611-10626`), so `next()` clears it: every layer after\n * that point, INCLUDING the `done` callback `./web-connector.ts` hands the\n * stack, is called as if the request had simply gone unhandled. The\n * framework then answers the only way it can for a URL it does not know — a\n * 404, empty (see {@link DEV_TRANSFORM_ERROR_STATUS} for which of the two\n * produces it).\n * - Anything registered with `vite.middlewares.use(...)` after `createServer()`\n * resolves lands AFTER that handler and is therefore unreachable. A\n * `configureServer` POST hook does not: vite runs post hooks at\n * `config.js:25700`, five lines BEFORE it mounts its error handler.\n *\n * So this sits between the failure and vite's logger. It captures, then calls\n * `next(error)` and lets vite's own handler run exactly as before — the\n * terminal message and the `hot.send({ type: \"error\" })` overlay push\n * (`config.js:9511-9521`) are unchanged. This transport ADDS a reader; it\n * replaces nothing.\n *\n * @param isProductionRuntime the connector's own hosting-mode signal\n * (`./web-connector.ts:122`) — passed in rather than re-derived so there\n * is one definition of \"this process is Vite-hosted\", not two.\n */\nexport function devErrorTransportPlugin(options: {\n isProductionRuntime: () => boolean;\n buildErrorMessage: BuildErrorMessage;\n}): Plugin {\n const { isProductionRuntime, buildErrorMessage } = options;\n\n if (isProductionRuntime()) {\n throw new DevErrorTransportInProductionError();\n }\n\n const capture: Connect.ErrorHandleFunction = (error, request, _response, next) => {\n // Re-asserted per request, not just at construction: `runtimeStrategy` is\n // process state and a transport that leaks source frames is not something\n // to hold open on a boot-time reading alone. In production this layer is a\n // pass-through and vite's handler behaves exactly as it does today.\n if (isProductionRuntime()) return next(error);\n\n (request as DevTransformErrorCarrier)[DEV_TRANSFORM_ERROR_BODY] = formatDevTransformError(\n error,\n buildErrorMessage,\n );\n\n next(error);\n };\n\n return {\n name: \"warlock:dev-error-transport\",\n // Belt to the `isProductionRuntime` braces: this plugin has no business in\n // a `vite build` graph either.\n apply: \"serve\",\n configureServer(server) {\n // RETURNING a function is what makes this a POST hook — the ordering the\n // note above depends on. Mounting inline here would land the layer BEFORE\n // vite's transform middleware, where no error has been thrown yet.\n return () => {\n server.middlewares.use(capture);\n };\n },\n };\n}\n\n/**\n * Answer the request with the failure {@link devErrorTransportPlugin} captured,\n * if there was one. Returns `false` when there was not, which is the normal\n * case and means \"carry on down the framework's own path\".\n *\n * Called from the Fastify `onRequest` hook that mounts vite\n * (`./web-connector.ts:321`), in the `done` callback — i.e. at the one moment\n * where connect has finished, vite has declined to answer, and the framework is\n * about to 404. Writing to the raw `ServerResponse` rather than through Fastify\n * is what the mount already does for every response vite serves, so this stays\n * on the same side of the seam.\n */\nexport function sendCapturedDevError(request: IncomingMessage, response: ServerResponse): boolean {\n const body = (request as DevTransformErrorCarrier)[DEV_TRANSFORM_ERROR_BODY];\n\n if (typeof body !== \"string\") return false;\n\n // A middleware further down may have answered already (vite serves plenty of\n // requests itself). Never write twice; the captured body is then just dropped.\n if (response.headersSent || response.writableEnded) return false;\n\n response.statusCode = DEV_TRANSFORM_ERROR_STATUS;\n response.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n // A refusal is a fact about the CURRENT source. Caching it would survive the\n // edit that fixes it.\n response.setHeader(\"cache-control\", \"no-store\");\n response.end(body);\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,MAAa,2BAA2B,OAAO,IAAI,mCAAmC;;;;;;;;;;;;;;;;;AA2BtF,MAAa,6BAA6B;;;;;;;AAmB1C,IAAa,qCAAb,cAAwD,MAAM;CAC5D,AAAO,cAAc;EACnB,MACE,uSAIF;EACA,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,wBACd,OACA,mBACQ;CACR,MAAM,UACJ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK,CAAC;CAE9F,MAAM,QAAQ,CACZ,yBACE,kBAAkB,SAAS,CAAC,GAAG,QAAQ,KAAK,IAAI,QAAQ,SAAS,GAAG,KAAK,CAC3E,CACF;CAIA,IAAI,QAAS,QAAgC;CAE7C,OAAO,iBAAiB,OAAO;EAC7B,MAAM,KAAK,gBAAgB,MAAM,KAAK,IAAI,yBAAyB,MAAM,OAAO,GAAG;EACnF,QAAS,MAA8B;CACzC;CAEA,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,wBAAwB,SAG7B;CACT,MAAM,EAAE,qBAAqB,sBAAsB;CAEnD,IAAI,oBAAoB,GACtB,MAAM,IAAI,mCAAmC;CAG/C,MAAM,WAAwC,OAAO,SAAS,WAAW,SAAS;EAKhF,IAAI,oBAAoB,GAAG,OAAO,KAAK,KAAK;EAE5C,AAAC,QAAqC,4BAA4B,wBAChE,OACA,iBACF;EAEA,KAAK,KAAK;CACZ;CAEA,OAAO;EACL,MAAM;EAGN,OAAO;EACP,gBAAgB,QAAQ;GAItB,aAAa;IACX,OAAO,YAAY,IAAI,OAAO;GAChC;EACF;CACF;AACF;;;;;;;;;;;;;AAcA,SAAgB,qBAAqB,SAA0B,UAAmC;CAChG,MAAM,OAAQ,QAAqC;CAEnD,IAAI,OAAO,SAAS,UAAU,OAAO;CAIrC,IAAI,SAAS,eAAe,SAAS,eAAe,OAAO;CAE3D,SAAS;CACT,SAAS,UAAU,gBAAgB,2BAA2B;CAG9D,SAAS,UAAU,iBAAiB,UAAU;CAC9C,SAAS,IAAI,IAAI;CAEjB,OAAO;AACT"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ServerErrorPageProps } from "../props.mjs";
|
|
2
|
+
import { MetadataOutput } from "../metadata.mjs";
|
|
3
|
+
//#region ../web/src/server/error-page.d.ts
|
|
4
|
+
/** Server-only shape of an application-owned `error.page.tsx` namespace. */
|
|
5
|
+
type ErrorPageModule = {
|
|
6
|
+
register?: () => unknown;
|
|
7
|
+
default?: unknown;
|
|
8
|
+
metadata?: MetadataOutput | ((props: ServerErrorPageProps) => MetadataOutput);
|
|
9
|
+
};
|
|
10
|
+
/** Deliberately lazy: normal requests never even load error.page.tsx. */
|
|
11
|
+
type ErrorPageModuleLoader = () => Promise<ErrorPageModule>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ErrorPageModuleLoader };
|
|
14
|
+
//# sourceMappingURL=error-page.d.mts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ERROR_PAGE_METADATA } from "./resolve-page-metadata.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../web/src/server/error-page.ts
|
|
4
|
+
function serializePageError(thrown) {
|
|
5
|
+
if (thrown instanceof Error) return {
|
|
6
|
+
name: thrown.name || "Error",
|
|
7
|
+
message: thrown.message,
|
|
8
|
+
...process.env.NODE_ENV !== "production" && typeof thrown.stack === "string" ? { stack: thrown.stack } : {}
|
|
9
|
+
};
|
|
10
|
+
let message;
|
|
11
|
+
try {
|
|
12
|
+
message = typeof thrown === "string" ? thrown : String(thrown);
|
|
13
|
+
} catch {
|
|
14
|
+
message = "An unexpected error occurred.";
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
name: "Error",
|
|
18
|
+
message
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Error-page metadata improves the safe framework default; it cannot remove noindex. */
|
|
22
|
+
function resolveErrorPageMetadata(module, props) {
|
|
23
|
+
const own = typeof module.metadata === "function" ? module.metadata(props) : module.metadata;
|
|
24
|
+
return {
|
|
25
|
+
...ERROR_PAGE_METADATA,
|
|
26
|
+
...own,
|
|
27
|
+
robots: own?.robots ?? ERROR_PAGE_METADATA.robots
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function hydrationErrorPageProps(props, serializableError = props.error) {
|
|
31
|
+
return {
|
|
32
|
+
error: serializePageError(serializableError),
|
|
33
|
+
status: props.status
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { hydrationErrorPageProps, resolveErrorPageMetadata };
|
|
39
|
+
//# sourceMappingURL=error-page.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-page.mjs","names":[],"sources":["../../../../../../../web/src/server/error-page.ts"],"sourcesContent":["import type { SerializedErrorPageProps, SerializedPageError } from \"../components/document-context\";\nimport { ERROR_PAGE_METADATA } from \"./resolve-page-metadata\";\nimport type { MetadataOutput } from \"../metadata\";\nimport type { ServerErrorPageProps } from \"../props\";\n\n/** Server-only shape of an application-owned `error.page.tsx` namespace. */\nexport type ErrorPageModule = {\n register?: () => unknown;\n default?: unknown;\n metadata?: MetadataOutput | ((props: ServerErrorPageProps) => MetadataOutput);\n};\n\n/** Deliberately lazy: normal requests never even load error.page.tsx. */\nexport type ErrorPageModuleLoader = () => Promise<ErrorPageModule>;\n\nexport function serializePageError(thrown: unknown): SerializedPageError {\n if (thrown instanceof Error) {\n return {\n name: thrown.name || \"Error\",\n message: thrown.message,\n ...(process.env.NODE_ENV !== \"production\" && typeof thrown.stack === \"string\"\n ? { stack: thrown.stack }\n : {}),\n };\n }\n\n let message: string;\n try {\n message = typeof thrown === \"string\" ? thrown : String(thrown);\n } catch {\n message = \"An unexpected error occurred.\";\n }\n\n return {\n name: \"Error\",\n message,\n };\n}\n\n/** Error-page metadata improves the safe framework default; it cannot remove noindex. */\nexport function resolveErrorPageMetadata(\n module: ErrorPageModule,\n props: ServerErrorPageProps,\n): MetadataOutput {\n const own = typeof module.metadata === \"function\" ? module.metadata(props) : module.metadata;\n\n return { ...ERROR_PAGE_METADATA, ...own, robots: own?.robots ?? ERROR_PAGE_METADATA.robots };\n}\n\nexport function hydrationErrorPageProps(\n props: ServerErrorPageProps,\n serializableError: unknown = props.error,\n): SerializedErrorPageProps {\n return { error: serializePageError(serializableError), status: props.status };\n}\n"],"mappings":";;;AAeA,SAAgB,mBAAmB,QAAsC;CACvE,IAAI,kBAAkB,OACpB,OAAO;EACL,MAAM,OAAO,QAAQ;EACrB,SAAS,OAAO;EAChB,GAAI,QAAQ,IAAI,aAAa,gBAAgB,OAAO,OAAO,UAAU,WACjE,EAAE,OAAO,OAAO,MAAM,IACtB,CAAC;CACP;CAGF,IAAI;CACJ,IAAI;EACF,UAAU,OAAO,WAAW,WAAW,SAAS,OAAO,MAAM;CAC/D,QAAQ;EACN,UAAU;CACZ;CAEA,OAAO;EACL,MAAM;EACN;CACF;AACF;;AAGA,SAAgB,yBACd,QACA,OACgB;CAChB,MAAM,MAAM,OAAO,OAAO,aAAa,aAAa,OAAO,SAAS,KAAK,IAAI,OAAO;CAEpF,OAAO;EAAE,GAAG;EAAqB,GAAG;EAAK,QAAQ,KAAK,UAAU,oBAAoB;CAAO;AAC7F;AAEA,SAAgB,wBACd,OACA,oBAA6B,MAAM,OACT;CAC1B,OAAO;EAAE,OAAO,mBAAmB,iBAAiB;EAAG,QAAQ,MAAM;CAAO;AAC9E"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { BufferedCookie, BufferedHeader, LoaderShortCircuitKind, PageResponseCommit } from "./settle-page-response.mjs";
|
|
2
|
+
import { ExecutePageRequestOptions, PageBoundaryDesignation, PageContextRunner, PageDataBundle, PageErrorRecord, PageLevelName, PageRouteEntry, PageRouteMatch, PageShortCircuit, PageTripleModule, PipelineLoader, PipelineLoaderContext, PipelineMiddleware, PipelineStore } from "./execute-page-request.types.mjs";
|
|
3
|
+
import { connectPageContext } from "./page-context.mjs";
|
|
4
|
+
import { Response } from "@warlock.js/core";
|
|
5
|
+
|
|
3
6
|
//#region ../web/src/server/execute-page-request.d.ts
|
|
4
|
-
declare function executePageRequest<TResult = PageDataBundle>(options: ExecutePageRequestOptions<TResult>): Promise<TResult | undefined>;
|
|
7
|
+
declare function executePageRequest<TResult = PageDataBundle>(options: ExecutePageRequestOptions<TResult>): Promise<TResult | Response | undefined>;
|
|
5
8
|
//#endregion
|
|
6
9
|
export { executePageRequest };
|
|
7
10
|
//# sourceMappingURL=execute-page-request.d.mts.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { enterSharedScope, sealShared
|
|
2
|
-
import { createBufferedResponse, isLoaderShortCircuit } from "./buffered-response.mjs";
|
|
3
|
-
import { connectPageContext, connectPageSharedScope, enterAdditionalSharedScope, requireRunner } from "./page-context.mjs";
|
|
4
|
-
import { matchRoute } from "./match-page-route.mjs";
|
|
1
|
+
import { enterSharedScope, sealShared } from "../shared.mjs";
|
|
5
2
|
import { resolvePageMetadata } from "./resolve-page-metadata.mjs";
|
|
3
|
+
import { connectPageContext, enterAdditionalSharedScope, requireRunner } from "./page-context.mjs";
|
|
4
|
+
import { matchRoute } from "./match-page-route.mjs";
|
|
6
5
|
import { resolveValidationData } from "./resolve-validation-data.mjs";
|
|
7
|
-
import { LEVEL_ORDER, buildErrorRecord, commitBuffers, designateBoundary } from "./settle-page-response.mjs";
|
|
6
|
+
import { LEVEL_ORDER, buildErrorRecord, commitBuffers, createBufferedResponse, createLevelBuffer, designateBoundary, isLoaderShortCircuit } from "./settle-page-response.mjs";
|
|
7
|
+
import { Response } from "@warlock.js/core";
|
|
8
8
|
import { v } from "@warlock.js/seal";
|
|
9
9
|
|
|
10
10
|
//#region ../web/src/server/execute-page-request.ts
|
|
@@ -28,9 +28,9 @@ async function executePageRequest(options) {
|
|
|
28
28
|
request,
|
|
29
29
|
response
|
|
30
30
|
};
|
|
31
|
-
return
|
|
31
|
+
return runner.run(store, async () => {
|
|
32
32
|
enterSharedScope(store);
|
|
33
|
-
enterAdditionalSharedScope(store);
|
|
33
|
+
/* @__PURE__ */ enterAdditionalSharedScope(store);
|
|
34
34
|
const finish = async (bundle) => options.finish ? await options.finish(bundle) : bundle;
|
|
35
35
|
const bundle = { route: {
|
|
36
36
|
name: matched.entry.name,
|
|
@@ -47,7 +47,7 @@ async function executePageRequest(options) {
|
|
|
47
47
|
});
|
|
48
48
|
} catch (thrown) {
|
|
49
49
|
bundle.error = buildErrorRecord(thrown, designateBoundary(level, triple), pathname);
|
|
50
|
-
|
|
50
|
+
response.setStatusCode(500);
|
|
51
51
|
return finish(bundle);
|
|
52
52
|
}
|
|
53
53
|
if (output !== void 0) {
|
|
@@ -64,7 +64,7 @@ async function executePageRequest(options) {
|
|
|
64
64
|
if (validation?.schema) {
|
|
65
65
|
const data = resolveValidationData(validation.validating, request);
|
|
66
66
|
const result = await v.validate(validation.schema, data);
|
|
67
|
-
if (result.isValid && result.data) request.setValidatedData
|
|
67
|
+
if (result.isValid && result.data) request.setValidatedData(result.data);
|
|
68
68
|
if (!result.isValid) {
|
|
69
69
|
bundle.shortCircuit = {
|
|
70
70
|
stage: "validation",
|
|
@@ -76,86 +76,85 @@ async function executePageRequest(options) {
|
|
|
76
76
|
}
|
|
77
77
|
const sealedShared = await sealShared(store);
|
|
78
78
|
bundle.shared = sealedShared;
|
|
79
|
-
const buffers = LEVEL_ORDER.map((level) => {
|
|
80
|
-
const { response: bufferedResponse, buffer } = createBufferedResponse();
|
|
81
|
-
return {
|
|
82
|
-
level,
|
|
83
|
-
buffer,
|
|
84
|
-
bufferedResponse
|
|
85
|
-
};
|
|
86
|
-
});
|
|
87
|
-
const settled = await Promise.allSettled(buffers.map(({ level, bufferedResponse }) => {
|
|
88
|
-
const loader = triple[level].loader;
|
|
89
|
-
return (async () => loader ? await loader({
|
|
90
|
-
request,
|
|
91
|
-
response: bufferedResponse,
|
|
92
|
-
shared
|
|
93
|
-
}) : void 0)();
|
|
94
|
-
}));
|
|
95
|
-
let firstError;
|
|
96
|
-
let firstSignal;
|
|
97
|
-
function toSignal(value) {
|
|
98
|
-
return isLoaderShortCircuit(value) ? value : void 0;
|
|
99
|
-
}
|
|
100
|
-
for (let index = 0; index < LEVEL_ORDER.length; index++) {
|
|
101
|
-
const result = settled[index];
|
|
102
|
-
if (result.status === "rejected") {
|
|
103
|
-
firstError = {
|
|
104
|
-
level: LEVEL_ORDER[index],
|
|
105
|
-
index,
|
|
106
|
-
error: result.reason
|
|
107
|
-
};
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
const signal = toSignal(result.value);
|
|
111
|
-
if (signal) {
|
|
112
|
-
firstSignal = {
|
|
113
|
-
level: LEVEL_ORDER[index],
|
|
114
|
-
index,
|
|
115
|
-
signal
|
|
116
|
-
};
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
79
|
const dataKeys = {
|
|
121
80
|
app: "appData",
|
|
122
81
|
layout: "layoutData",
|
|
123
82
|
page: "pageData"
|
|
124
83
|
};
|
|
125
|
-
const
|
|
84
|
+
const buffers = {
|
|
85
|
+
app: createLevelBuffer(),
|
|
86
|
+
layout: createLevelBuffer(),
|
|
87
|
+
page: createLevelBuffer()
|
|
88
|
+
};
|
|
89
|
+
let signalIndex = -1;
|
|
90
|
+
let signalKind;
|
|
91
|
+
let signalThrown;
|
|
92
|
+
let signalCircuit;
|
|
126
93
|
for (let index = 0; index < LEVEL_ORDER.length; index++) {
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
94
|
+
const level = LEVEL_ORDER[index];
|
|
95
|
+
const loader = triple[level].loader;
|
|
96
|
+
if (!loader) continue;
|
|
97
|
+
let value;
|
|
98
|
+
try {
|
|
99
|
+
value = await loader({
|
|
100
|
+
request,
|
|
101
|
+
response: createBufferedResponse(buffers[level]),
|
|
102
|
+
shared: sealedShared
|
|
103
|
+
});
|
|
104
|
+
} catch (thrown) {
|
|
105
|
+
signalIndex = index;
|
|
106
|
+
signalKind = "throw";
|
|
107
|
+
signalThrown = thrown;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
if (value instanceof Response) return value;
|
|
111
|
+
if (isLoaderShortCircuit(value)) {
|
|
112
|
+
signalIndex = index;
|
|
113
|
+
signalKind = "shortCircuit";
|
|
114
|
+
signalCircuit = value;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
bundle[dataKeys[level]] = value;
|
|
133
118
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
119
|
+
let committedLevels;
|
|
120
|
+
/** Set only when a THROW escalated to the app boundary — forces 500. */
|
|
121
|
+
let forcedStatusCode;
|
|
122
|
+
if (signalIndex === -1) committedLevels = [...LEVEL_ORDER];
|
|
123
|
+
else if (signalKind === "throw") {
|
|
124
|
+
committedLevels = LEVEL_ORDER.slice(0, signalIndex);
|
|
125
|
+
const boundary = designateBoundary(LEVEL_ORDER[signalIndex], triple);
|
|
126
|
+
bundle.error = buildErrorRecord(signalThrown, boundary, pathname);
|
|
127
|
+
if (boundary.boundaryLevel === "app") {
|
|
128
|
+
response.setStatusCode(500);
|
|
129
|
+
forcedStatusCode = 500;
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
committedLevels = LEVEL_ORDER.slice(0, signalIndex + 1);
|
|
133
|
+
const circuit = signalCircuit;
|
|
139
134
|
bundle.shortCircuit = {
|
|
140
135
|
stage: "loaders",
|
|
141
|
-
level:
|
|
142
|
-
kind:
|
|
143
|
-
statusCode:
|
|
144
|
-
url:
|
|
145
|
-
body:
|
|
136
|
+
level: LEVEL_ORDER[signalIndex],
|
|
137
|
+
kind: circuit.kind,
|
|
138
|
+
statusCode: circuit.statusCode,
|
|
139
|
+
url: circuit.url,
|
|
140
|
+
body: circuit.body
|
|
146
141
|
};
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
142
|
+
}
|
|
143
|
+
bundle.commit = commitBuffers(response, buffers, committedLevels);
|
|
144
|
+
if (forcedStatusCode !== void 0) bundle.commit.statusCode = forcedStatusCode;
|
|
145
|
+
if (bundle.shortCircuit) return finish(bundle);
|
|
146
|
+
const resolved = resolvePageMetadata({
|
|
147
|
+
metadata: triple.page.metadata,
|
|
148
|
+
data: bundle.pageData,
|
|
149
|
+
error: bundle.error?.error,
|
|
150
|
+
failed: Boolean(bundle.error),
|
|
151
|
+
shared: sealedShared
|
|
152
|
+
});
|
|
153
|
+
bundle.metadata = resolved.metadata;
|
|
154
|
+
if (resolved.thrown !== void 0) {
|
|
155
|
+
const boundary = designateBoundary("page", triple);
|
|
156
|
+
bundle.error = buildErrorRecord(resolved.thrown, boundary, bundle.route.path);
|
|
157
|
+
if (boundary.boundaryLevel === "app") response.setStatusCode(500);
|
|
159
158
|
}
|
|
160
159
|
return finish(bundle);
|
|
161
160
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute-page-request.mjs","names":[],"sources":["../../../../../../../web/src/server/execute-page-request.ts"],"sourcesContent":["import { v } from \"@warlock.js/seal\";\nimport { createBufferedResponse, isLoaderShortCircuit } from \"./buffered-response\";\nimport type { LoaderShortCircuitSignal } from \"./buffered-response\";\nimport { enterSharedScope, sealShared, shared } from \"../shared\";\nimport { enterAdditionalSharedScope, requireRunner } from \"./page-context\";\nimport { matchRoute } from \"./match-page-route\";\nimport { resolvePageMetadata } from \"./resolve-page-metadata\";\nimport { resolveValidationData } from \"./resolve-validation-data\";\nimport {\n buildErrorRecord,\n commitBuffers,\n designateBoundary,\n LEVEL_ORDER,\n} from \"./settle-page-response\";\nimport type {\n ExecutePageRequestOptions,\n PageDataBundle,\n PageLevelName,\n PageRouteMatch,\n PipelineResponse,\n PipelineStore,\n} from \"./execute-page-request.types\";\n\n/**\n * Pipeline stages 1–8 of a page request; stages 9–10 (render/emit) are the\n * render slice's, and this ends at the DATA BUNDLE.\n *\n * The founding sentence governs the shape — a page route is an ordinary Warlock\n * route whose handler renders React instead of returning JSON — so every stage\n * mirrors what core already does for an API request: core's own ALS store and\n * `run`, core's middleware short-circuit rule (`output !== undefined`), and\n * `validateAll`'s validation with its 422, recorded as a designation because\n * sending is the emit's job.\n *\n * The pieces live beside this file: `page-context.ts` (stage 2 wiring),\n * `match-page-route.ts` (stage 1), `resolve-validation-data.ts` (stage 4),\n * `settle-page-response.ts` (stage 7), `resolve-page-metadata.ts` (stage 8).\n */\n\nexport * from \"./execute-page-request.types\";\nexport { connectPageContext, connectPageSharedScope } from \"./page-context\";\nexport { buildErrorRecord, designateBoundary } from \"./settle-page-response\";\n\nexport async function executePageRequest<TResult = PageDataBundle>(\n options: ExecutePageRequestOptions<TResult>,\n): Promise<TResult | undefined> {\n const runner = requireRunner();\n\n // ── stage 1 · MATCH ─────────────────────────────────────────────────────\n const [pathname, queryString] = options.url.split(\"?\");\n const matched = matchRoute(pathname, options.routes);\n\n if (!matched) return undefined;\n\n const query = Object.fromEntries(new URLSearchParams(queryString ?? \"\"));\n const match: PageRouteMatch = { entry: matched.entry, params: matched.params, query };\n const { triple } = matched.entry;\n\n const { request, response } = options.createHttp(match);\n\n // ── stage 2 · CONTEXT ───────────────────────────────────────────────────\n // Core's own store shape, built by core's own `buildStore` when the runner\n // carries one. `enterSharedScope` keys the shared target by this exact object.\n const store: PipelineStore = runner.buildStore\n ? runner.buildStore({ request, response })\n : { request, response };\n\n return await runner.run(store, async () => {\n enterSharedScope(store);\n enterAdditionalSharedScope(store);\n\n const finish = async (bundle: PageDataBundle): Promise<TResult> =>\n options.finish ? await options.finish(bundle) : (bundle as TResult);\n\n const bundle: PageDataBundle = {\n route: {\n name: matched.entry.name,\n path: matched.entry.path,\n params: match.params,\n query,\n },\n };\n\n // ── stage 3 · MIDDLEWARE ──────────────────────────────────────────────\n // Sequential, outermost first. Middleware gets the REAL request/response —\n // it runs alone, before any parallelism — and is the only writer of `shared`.\n for (const level of LEVEL_ORDER) {\n for (const middleware of triple[level].middleware ?? []) {\n let output: unknown;\n\n try {\n output = await middleware({ request, response });\n } catch (thrown) {\n /*\n A middleware throw means NOTHING was served: no loaders ran, no\n buffers exist. The \"a nested boundary keeps the committed status\"\n rule assumes the page was substantially served, so it cannot apply\n here — the framework forces 500 whichever level the boundary\n designates to.\n */\n bundle.error = buildErrorRecord(thrown, designateBoundary(level, triple), pathname);\n bundle.commit = commitBuffers(response, [], 500);\n\n return finish(bundle);\n }\n\n if (output !== undefined) {\n bundle.shortCircuit = {\n stage: \"middleware\",\n level,\n value: output,\n statusCode: (response as PipelineResponse & { statusCode?: number }).statusCode,\n };\n\n return finish(bundle);\n }\n }\n }\n\n // ── stage 4 · VALIDATION ──────────────────────────────────────────────\n // Failure stops before the seal: a 422 designation in the bundle, since\n // sending is the emit slice's job.\n const validation = triple.page.validation;\n\n if (validation?.schema) {\n const data = resolveValidationData(validation.validating, request);\n const result = await v.validate(validation.schema, data);\n\n if (result.isValid && result.data) {\n request.setValidatedData?.(result.data);\n }\n\n if (!result.isValid) {\n bundle.shortCircuit = { stage: \"validation\", status: 422, errors: result.errors };\n\n return finish(bundle);\n }\n }\n\n // ── stage 5 · SEAL shared ─────────────────────────────────────────────\n // From here `shared` is immutable and identical for every reader. The\n // bundle carries `sealShared`'s RETURN, never a proxy re-read.\n const sealedShared = await sealShared(store);\n\n bundle.shared = sealedShared;\n\n // ── stage 6 · LOADERS ─────────────────────────────────────────────────\n // Parallel, each with its OWN buffered response. Loaders never read each\n // other and cannot write `shared` — it is sealed, and a write throws.\n const buffers = LEVEL_ORDER.map(level => {\n const { response: bufferedResponse, buffer } = createBufferedResponse();\n\n return { level, buffer, bufferedResponse };\n });\n\n const settled = await Promise.allSettled(\n buffers.map(({ level, bufferedResponse }) => {\n const loader = triple[level].loader;\n\n return (async () =>\n loader ? await loader({ request, response: bufferedResponse, shared }) : undefined)();\n }),\n );\n\n // ── stage 7 · SETTLE / COMMIT ─────────────────────────────────────────\n // Walk root→leaf for the first abnormal outcome. A throw discards the\n // throwing layer's buffer and everything below it, and the framework owns\n // the status. A redirect/notFound commits the signalling layer's own buffer\n // and discards its siblings below.\n let firstError: { level: PageLevelName; index: number; error: unknown } | undefined;\n let firstSignal:\n | { level: PageLevelName; index: number; signal: LoaderShortCircuitSignal }\n | undefined;\n\n function toSignal(value: unknown) {\n return isLoaderShortCircuit(value) ? value : undefined;\n }\n\n for (let index = 0; index < LEVEL_ORDER.length; index++) {\n const result = settled[index];\n\n if (result.status === \"rejected\") {\n firstError = { level: LEVEL_ORDER[index], index, error: result.reason };\n break;\n }\n\n const signal = toSignal(result.value);\n\n if (signal) {\n firstSignal = { level: LEVEL_ORDER[index], index, signal };\n break;\n }\n }\n\n const dataKeys: Record<PageLevelName, \"appData\" | \"layoutData\" | \"pageData\"> = {\n app: \"appData\",\n layout: \"layoutData\",\n page: \"pageData\",\n };\n\n // A throw elsewhere never costs a healthy layer its data. Levels leafward\n // of a short-circuit go, because they will not render.\n const dataCutoff = firstSignal ? firstSignal.index : LEVEL_ORDER.length;\n\n for (let index = 0; index < LEVEL_ORDER.length; index++) {\n const result = settled[index];\n\n if (result.status !== \"fulfilled\") continue;\n if (firstError && index === firstError.index) continue;\n if (index > dataCutoff) continue;\n if (toSignal(result.value)) continue;\n\n bundle[dataKeys[LEVEL_ORDER[index]]] = result.value;\n }\n\n if (firstError) {\n const boundary = designateBoundary(firstError.level, triple);\n\n bundle.error = buildErrorRecord(firstError.error, boundary, pathname);\n\n // Commit strictly rootward of the throw. A NESTED boundary keeps the\n // committed status — the page was substantially served; only the ROOT\n // boundary forces 5xx.\n bundle.commit = commitBuffers(\n response,\n buffers.slice(0, firstError.index),\n boundary.boundaryLevel === \"app\" ? 500 : undefined,\n );\n } else if (firstSignal) {\n bundle.shortCircuit = {\n stage: \"loaders\",\n level: firstSignal.level,\n kind: firstSignal.signal.kind,\n statusCode: firstSignal.signal.statusCode,\n url: firstSignal.signal.url,\n body: firstSignal.signal.body,\n };\n bundle.commit = commitBuffers(response, buffers.slice(0, firstSignal.index + 1));\n } else {\n bundle.commit = commitBuffers(response, buffers);\n }\n\n // ── stage 8 · METADATA ────────────────────────────────────────────────\n // Skipped entirely after a loader short-circuit: a redirect/notFound answer\n // has no page render to describe.\n if (!bundle.shortCircuit) {\n const resolved = resolvePageMetadata({\n metadata: triple.page.metadata,\n data: bundle.pageData,\n error: bundle.error?.error,\n failed: Boolean(bundle.error),\n shared: sealedShared,\n });\n\n bundle.metadata = resolved.metadata;\n\n /*\n The page's own metadata function threw with no earlier error to mask.\n Record it exactly as a loader throw, so the boundary renders and the\n framework keeps the status — rather than letting it escape stage 8,\n where nothing is left to catch it and stage 7's buffers are already\n committed.\n */\n if (resolved.thrown !== undefined) {\n bundle.error = buildErrorRecord(\n resolved.thrown,\n designateBoundary(\"page\", triple),\n bundle.route.path,\n );\n }\n }\n\n return finish(bundle);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"execute-page-request.mjs","names":[],"sources":["../../../../../../../web/src/server/execute-page-request.ts"],"sourcesContent":["import { Response } from \"@warlock.js/core\";\nimport { v } from \"@warlock.js/seal\";\nimport { enterSharedScope, sealShared } from \"../shared\";\nimport { enterAdditionalSharedScope, requireRunner } from \"./page-context\";\nimport { matchRoute } from \"./match-page-route\";\nimport { resolvePageMetadata } from \"./resolve-page-metadata\";\nimport { resolveValidationData } from \"./resolve-validation-data\";\nimport {\n buildErrorRecord,\n commitBuffers,\n createBufferedResponse,\n createLevelBuffer,\n designateBoundary,\n isLoaderShortCircuit,\n LEVEL_ORDER,\n type LevelBuffer,\n type PageResponseCommit,\n} from \"./settle-page-response\";\nimport type {\n ExecutePageRequestOptions,\n PageDataBundle,\n PageLevelName,\n PageRouteMatch,\n PipelineStore,\n} from \"./execute-page-request.types\";\n\nexport * from \"./execute-page-request.types\";\nexport { connectPageContext } from \"./page-context\";\nexport {\n buildErrorRecord,\n designateBoundary,\n type BufferedCookie,\n type BufferedHeader,\n type LoaderShortCircuitKind,\n type PageResponseCommit,\n} from \"./settle-page-response\";\n\n/** Widens `PageDataBundle` with the two fields stage 6/7 populate. */\ntype Bundle = PageDataBundle & {\n commit?: PageResponseCommit;\n shortCircuit?: PageDataBundle[\"shortCircuit\"] & {\n kind?: \"redirect\" | \"notFound\";\n url?: string;\n body?: unknown;\n };\n};\n\nexport async function executePageRequest<TResult = PageDataBundle>(\n options: ExecutePageRequestOptions<TResult>,\n): Promise<TResult | Response | undefined> {\n const runner = requireRunner();\n const [pathname, queryString] = options.url.split(\"?\");\n const matched = matchRoute(pathname, options.routes);\n\n if (!matched) return undefined;\n\n const query = Object.fromEntries(new URLSearchParams(queryString ?? \"\"));\n const match: PageRouteMatch = { entry: matched.entry, params: matched.params, query };\n const { triple } = matched.entry;\n const { request, response } = options.createHttp(match);\n const store: PipelineStore = runner.buildStore\n ? runner.buildStore({ request, response })\n : { request, response };\n\n return runner.run(store, async () => {\n enterSharedScope(store);\n enterAdditionalSharedScope(store);\n\n const finish = async (bundle: PageDataBundle): Promise<TResult> =>\n options.finish ? await options.finish(bundle) : (bundle as TResult);\n\n const bundle: Bundle = {\n route: {\n name: matched.entry.name,\n path: matched.entry.path,\n params: match.params,\n query,\n },\n };\n\n for (const level of LEVEL_ORDER) {\n for (const middleware of triple[level].middleware ?? []) {\n let output: unknown;\n\n try {\n output = await middleware({ request, response });\n } catch (thrown) {\n bundle.error = buildErrorRecord(thrown, designateBoundary(level, triple), pathname);\n response.setStatusCode(500);\n return finish(bundle);\n }\n\n if (output !== undefined) {\n bundle.shortCircuit = {\n stage: \"middleware\",\n level,\n value: output,\n statusCode: response.statusCode,\n };\n return finish(bundle);\n }\n }\n }\n\n const validation = triple.page.validation;\n\n if (validation?.schema) {\n const data = resolveValidationData(validation.validating, request);\n const result = await v.validate(validation.schema, data);\n\n if (result.isValid && result.data) {\n request.setValidatedData(result.data);\n }\n\n if (!result.isValid) {\n bundle.shortCircuit = { stage: \"validation\", status: 422, errors: result.errors };\n return finish(bundle);\n }\n }\n\n const sealedShared = await sealShared(store);\n bundle.shared = sealedShared;\n\n const dataKeys: Record<PageLevelName, \"appData\" | \"layoutData\" | \"pageData\"> = {\n app: \"appData\",\n layout: \"layoutData\",\n page: \"pageData\",\n };\n\n // Stage 6 — LOADERS, root to leaf. Every level gets its OWN buffer (never\n // the live response), and a terminal result or throw prevents every lower\n // loader from starting.\n const buffers: Record<PageLevelName, LevelBuffer> = {\n app: createLevelBuffer(),\n layout: createLevelBuffer(),\n page: createLevelBuffer(),\n };\n\n let signalIndex = -1;\n let signalKind: \"throw\" | \"shortCircuit\" | undefined;\n let signalThrown: unknown;\n let signalCircuit:\n | { kind: \"redirect\" | \"notFound\"; statusCode: number; url?: string; body?: unknown }\n | undefined;\n\n for (let index = 0; index < LEVEL_ORDER.length; index++) {\n const level = LEVEL_ORDER[index];\n const loader = triple[level].loader;\n\n if (!loader) continue;\n\n let value: unknown;\n\n try {\n value = await loader({\n request,\n response: createBufferedResponse(buffers[level]),\n shared: sealedShared,\n });\n } catch (thrown) {\n signalIndex = index;\n signalKind = \"throw\";\n signalThrown = thrown;\n break;\n }\n\n if (value instanceof Response) return value;\n\n if (isLoaderShortCircuit(value)) {\n signalIndex = index;\n signalKind = \"shortCircuit\";\n signalCircuit = value;\n break;\n }\n\n bundle[dataKeys[level]] = value;\n }\n\n let committedLevels: PageLevelName[];\n /** Set only when a THROW escalated to the app boundary — forces 500. */\n let forcedStatusCode: number | undefined;\n\n if (signalIndex === -1) {\n committedLevels = [...LEVEL_ORDER];\n } else if (signalKind === \"throw\") {\n // The throwing level's buffer is discarded; lower levels never ran.\n committedLevels = LEVEL_ORDER.slice(0, signalIndex);\n\n const boundary = designateBoundary(LEVEL_ORDER[signalIndex], triple);\n bundle.error = buildErrorRecord(signalThrown, boundary, pathname);\n\n if (boundary.boundaryLevel === \"app\") {\n response.setStatusCode(500);\n forcedStatusCode = 500;\n }\n } else {\n // Short-circuit: the signalling level's OWN buffer commits too\n // (inclusive); lower levels never ran.\n committedLevels = LEVEL_ORDER.slice(0, signalIndex + 1);\n\n const circuit = signalCircuit!;\n\n bundle.shortCircuit = {\n stage: \"loaders\",\n level: LEVEL_ORDER[signalIndex],\n kind: circuit.kind,\n statusCode: circuit.statusCode,\n url: circuit.url,\n body: circuit.body,\n } as unknown as PageDataBundle[\"shortCircuit\"];\n }\n\n bundle.commit = commitBuffers(response, buffers, committedLevels);\n\n // Forced AFTER the fold: an app-boundary escalation forces 500\n // regardless of what the surviving (rootward) buffers happened to set —\n // it is the framework's answer, not a loader's.\n if (forcedStatusCode !== undefined) bundle.commit.statusCode = forcedStatusCode;\n\n // Stage 8 — METADATA. Skipped entirely for a short-circuit (there is no\n // page to describe); a throw still runs it, same as before this stage 6/7\n // rewrite (a boundary still needs a title/robots answer).\n if (bundle.shortCircuit) {\n return finish(bundle);\n }\n\n const resolved = resolvePageMetadata({\n metadata: triple.page.metadata,\n data: bundle.pageData,\n error: bundle.error?.error,\n failed: Boolean(bundle.error),\n shared: sealedShared,\n });\n\n bundle.metadata = resolved.metadata;\n\n if (resolved.thrown !== undefined) {\n const boundary = designateBoundary(\"page\", triple);\n bundle.error = buildErrorRecord(resolved.thrown, boundary, bundle.route.path);\n\n if (boundary.boundaryLevel === \"app\") response.setStatusCode(500);\n }\n\n return finish(bundle);\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,67 +1,37 @@
|
|
|
1
1
|
import { MetadataOutput, PageMetadata } from "../metadata.mjs";
|
|
2
|
+
import { SerializedErrorPageProps } from "../components/document-context.mjs";
|
|
2
3
|
import { SharedStore } from "../shared.mjs";
|
|
3
4
|
import { SharedContext } from "../index.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { BufferedResponse } from "./settle-page-response.mjs";
|
|
6
|
+
import { HttpContext, Request, Response } from "@warlock.js/core";
|
|
6
7
|
import { BaseValidator } from "@warlock.js/seal";
|
|
7
8
|
|
|
8
9
|
//#region ../web/src/server/execute-page-request.types.d.ts
|
|
9
|
-
/** At runtime this IS core's `RequestContextStore` (request-context.ts:10-13). */
|
|
10
10
|
type PipelineStore = SharedStore & {
|
|
11
|
-
request:
|
|
12
|
-
response:
|
|
11
|
+
request: Request;
|
|
12
|
+
response: Response;
|
|
13
13
|
};
|
|
14
|
-
/**
|
|
15
|
-
* Core's `requestContext` satisfies this as-is — `run`/`getStore` are the
|
|
16
|
-
* inherited `Context` delegates and `buildStore` is `RequestContext.buildStore`,
|
|
17
|
-
* the same function core's http path feeds through `contextManager.buildStores`.
|
|
18
|
-
*/
|
|
19
14
|
type PageContextRunner = {
|
|
20
15
|
run<T>(store: PipelineStore, callback: () => Promise<T>): Promise<T>;
|
|
21
16
|
getStore(): PipelineStore | undefined;
|
|
22
17
|
buildStore?(payload?: Record<string, any>): PipelineStore;
|
|
23
18
|
};
|
|
24
19
|
type PageLevelName = "app" | "layout" | "page";
|
|
20
|
+
type PipelineMiddleware = (ctx: HttpContext) => unknown | Promise<unknown>;
|
|
25
21
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* `query`/`params` are core's own parses (request.ts:1010,1026) and the ONLY
|
|
31
|
-
* ones the pipeline reads. Stage 4 used to take them from a match object built
|
|
32
|
-
* by re-parsing the URL — `resolve-validation-data.ts` records what that cost.
|
|
33
|
-
*/
|
|
34
|
-
type PipelineRequest = WebRequest & {
|
|
35
|
-
body?: Record<string, unknown>;
|
|
36
|
-
headers?: Record<string, unknown>;
|
|
37
|
-
query?: Record<string, unknown>;
|
|
38
|
-
params?: Record<string, unknown>;
|
|
39
|
-
setValidatedData?(data: Record<string, unknown>): void;
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Standalone rather than based on `WebResponse`: that facade's
|
|
43
|
-
* `redirect`/`notFound` return the branded short-circuit signal, which core's
|
|
44
|
-
* real `Response` never carries — basing this on it would reject the very
|
|
45
|
-
* instances the seam exists to admit.
|
|
22
|
+
* A loader never sees the live `Response` — stage 6 hands it a per-level
|
|
23
|
+
* `BufferedResponse` (`execute-page-request.ts`, `createBufferedResponse`),
|
|
24
|
+
* so its own type says that, rather than the core `Response` a middleware
|
|
25
|
+
* receives.
|
|
46
26
|
*/
|
|
47
|
-
type
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
setStatusCode(statusCode: number): PipelineResponse;
|
|
51
|
-
parse(value: unknown): Promise<unknown>;
|
|
52
|
-
};
|
|
53
|
-
/** Pass-through is `undefined` — core's exact rule (`Request.executeMiddleware`). */
|
|
54
|
-
type PipelineMiddleware = (ctx: {
|
|
55
|
-
request: PipelineRequest;
|
|
56
|
-
response: PipelineResponse;
|
|
57
|
-
}) => unknown | Promise<unknown>;
|
|
58
|
-
type PipelineLoader = (ctx: {
|
|
59
|
-
request: PipelineRequest;
|
|
60
|
-
response: BufferedWebResponse;
|
|
27
|
+
type PipelineLoaderContext = {
|
|
28
|
+
request: Request;
|
|
29
|
+
response: BufferedResponse;
|
|
61
30
|
shared: SharedContext;
|
|
62
|
-
}
|
|
63
|
-
|
|
31
|
+
};
|
|
32
|
+
type PipelineLoader = (ctx: PipelineLoaderContext) => unknown | Promise<unknown>;
|
|
64
33
|
type PageTripleModule = {
|
|
34
|
+
register?: () => unknown;
|
|
65
35
|
route?: string | {
|
|
66
36
|
readonly path: string;
|
|
67
37
|
readonly name?: string;
|
|
@@ -72,7 +42,7 @@ type PageTripleModule = {
|
|
|
72
42
|
validating?: readonly string[];
|
|
73
43
|
};
|
|
74
44
|
loader?: PipelineLoader;
|
|
75
|
-
metadata?: PageMetadata<PipelineLoader>;
|
|
45
|
+
metadata?: PageMetadata<PipelineLoader>;
|
|
76
46
|
default?: unknown;
|
|
77
47
|
ErrorBoundary?: unknown;
|
|
78
48
|
};
|
|
@@ -91,54 +61,27 @@ type PageRouteMatch = {
|
|
|
91
61
|
query: Record<string, string>;
|
|
92
62
|
};
|
|
93
63
|
type ExecutePageRequestOptions<TResult = PageDataBundle> = {
|
|
94
|
-
|
|
64
|
+
url: string;
|
|
95
65
|
routes: readonly PageRouteEntry[];
|
|
96
|
-
|
|
97
|
-
* Construct the Request/Response pair for this match, mirroring core's
|
|
98
|
-
* `handleRoute` body (router.ts:924-932).
|
|
99
|
-
*/
|
|
100
|
-
createHttp(match: PageRouteMatch): {
|
|
101
|
-
request: PipelineRequest;
|
|
102
|
-
response: PipelineResponse;
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* The render seam — continues stages 9-10 inside the exact ALS store and
|
|
106
|
-
* shared scope that middleware and loaders used.
|
|
107
|
-
*/
|
|
66
|
+
createHttp(match: PageRouteMatch): HttpContext;
|
|
108
67
|
finish?(bundle: PageDataBundle): TResult | Promise<TResult>;
|
|
109
68
|
};
|
|
110
69
|
type PageBoundaryDesignation = {
|
|
111
|
-
throwingLevel: PageLevelName;
|
|
70
|
+
throwingLevel: PageLevelName;
|
|
112
71
|
boundaryLevel: PageLevelName;
|
|
113
72
|
};
|
|
114
|
-
type PageResponseCommit = {
|
|
115
|
-
/** Final per-key state, root→leaf: a leafward write wins its key. */headers: BufferedHeader[];
|
|
116
|
-
cookies: BufferedCookie[];
|
|
117
|
-
statusCode?: number;
|
|
118
|
-
committedLevels: PageLevelName[];
|
|
119
|
-
};
|
|
120
73
|
type PageShortCircuit = {
|
|
121
74
|
stage: "middleware";
|
|
122
75
|
level: PageLevelName;
|
|
123
|
-
value: unknown;
|
|
76
|
+
value: unknown;
|
|
124
77
|
statusCode?: number;
|
|
125
78
|
} | {
|
|
126
79
|
stage: "validation";
|
|
127
80
|
status: number;
|
|
128
81
|
errors: unknown;
|
|
129
|
-
} | {
|
|
130
|
-
stage: "loaders";
|
|
131
|
-
level: PageLevelName;
|
|
132
|
-
kind: "redirect" | "notFound";
|
|
133
|
-
statusCode: number;
|
|
134
|
-
url?: string;
|
|
135
|
-
body?: unknown;
|
|
136
82
|
};
|
|
137
|
-
/**
|
|
138
|
-
* What a throw becomes once it enters the bundle. `scrubbed` says whether
|
|
139
|
-
* `error` is the raw thrown value or a production surrogate standing in for it.
|
|
140
|
-
*/
|
|
141
83
|
type PageErrorRecord = {
|
|
84
|
+
/** Never serialized; preserves the actual thrown value for server error.page.tsx. */originalError: unknown;
|
|
142
85
|
error: unknown;
|
|
143
86
|
boundary: PageBoundaryDesignation;
|
|
144
87
|
digest: string;
|
|
@@ -149,26 +92,24 @@ type PageDataBundle = {
|
|
|
149
92
|
name: string;
|
|
150
93
|
path: string;
|
|
151
94
|
params: Record<string, string>;
|
|
152
|
-
/**
|
|
153
|
-
* ⚠ Re-parsed from the URL by `match-page-route.ts`, NOT core's parse, so it
|
|
154
|
-
* is flat and last-wins: `?tags=a&tags=b&filter[status]=active` arrives as
|
|
155
|
-
* `{ tags: "b", "filter[status]": "active" }`.
|
|
156
|
-
*
|
|
157
|
-
* Nothing in `web` reads it — stage 4 was the last consumer. It survives
|
|
158
|
-
* only as part of this public type and goes away with the second matcher.
|
|
159
|
-
* **Read `request.query`.**
|
|
160
|
-
*/
|
|
161
95
|
query: Record<string, string>;
|
|
162
96
|
};
|
|
163
97
|
appData?: unknown;
|
|
164
98
|
layoutData?: unknown;
|
|
165
|
-
pageData?: unknown;
|
|
99
|
+
pageData?: unknown;
|
|
166
100
|
shared?: Readonly<SharedContext>;
|
|
167
101
|
metadata?: MetadataOutput;
|
|
168
|
-
commit?: PageResponseCommit;
|
|
169
102
|
shortCircuit?: PageShortCircuit;
|
|
170
103
|
error?: PageErrorRecord;
|
|
104
|
+
/**
|
|
105
|
+
* Selected only for the framework-owned application error-page terminal —
|
|
106
|
+
* already the JSON-safe shape (`hydrationErrorPageProps` produces this, not
|
|
107
|
+
* the raw `ErrorPageProps` an authored `error.page.tsx` renders from during
|
|
108
|
+
* SSR), because this field's only consumer is the hydration payload
|
|
109
|
+
* (`build-hydration-payload.ts`), never a component prop.
|
|
110
|
+
*/
|
|
111
|
+
errorPage?: SerializedErrorPageProps;
|
|
171
112
|
};
|
|
172
113
|
//#endregion
|
|
173
|
-
export { ExecutePageRequestOptions, PageBoundaryDesignation, PageContextRunner, PageDataBundle, PageErrorRecord, PageLevelName,
|
|
114
|
+
export { ExecutePageRequestOptions, PageBoundaryDesignation, PageContextRunner, PageDataBundle, PageErrorRecord, PageLevelName, PageRouteEntry, PageRouteMatch, PageShortCircuit, PageTripleModule, PipelineLoader, PipelineLoaderContext, PipelineMiddleware, PipelineStore };
|
|
174
115
|
//# sourceMappingURL=execute-page-request.types.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CLIENT_ASSET_URL_PREFIX } from "./client-asset-url-prefix.mjs";
|
|
2
2
|
import { HYDRATION_CLIENT_ENTRY_NAME } from "../vite/hydration-entries.mjs";
|
|
3
|
-
import path from "node:path";
|
|
4
3
|
import { readFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
5
|
|
|
6
6
|
//#region ../web/src/server/hydration-client-url.ts
|
|
7
7
|
/**
|
package/esm/server/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { composeRoutePath } from "../routing/compose-route-path.mjs";
|
|
2
|
-
import { connectSharedStore } from "../shared.mjs";
|
|
3
2
|
import { PAYLOAD_SCRIPT_ID, escapePayload } from "../components/document-context.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { connectSharedStore } from "../shared.mjs";
|
|
4
|
+
import { BufferedCookie, BufferedHeader, LoaderShortCircuitKind, PageResponseCommit } from "./settle-page-response.mjs";
|
|
5
|
+
import { ExecutePageRequestOptions, PageBoundaryDesignation, PageContextRunner, PageDataBundle, PageLevelName, PageRouteEntry, PageRouteMatch, PageShortCircuit, PageTripleModule, PipelineLoader, PipelineMiddleware, PipelineStore } from "./execute-page-request.types.mjs";
|
|
6
|
+
import { connectPageContext } from "./page-context.mjs";
|
|
7
7
|
import { executePageRequest } from "./execute-page-request.mjs";
|
|
8
8
|
import { InstallPageRoutesOptions, InstalledPageRoute, LayoutModuleShape, PageModuleShape, PageRouteExport, installPageRoutes } from "./install-page-routes.mjs";
|
|
9
9
|
import { PageRoutesRegistry, RenderPageOptions, RenderPageRequestOptions, RenderedPage, connectPageRoutes, renderPage, renderPageRequest } from "./render-page.mjs";
|
|
@@ -11,4 +11,4 @@ import { PageModuleNotInManifestError, createPageModuleLoader } from "./create-p
|
|
|
11
11
|
import { InstallPageRoutesFromManifestOptions, InstalledManifestPageRoute, PageRouteHandlerFactory, installPageRoutesFromManifest } from "./install-page-routes-from-manifest.mjs";
|
|
12
12
|
import { DuplicateNotFoundPageError, NOT_FOUND_PAGE_FILENAME, NOT_FOUND_ROUTE_NAME, NOT_FOUND_ROUTE_PATH, NotFoundPageDeclaresRouteError, NotFoundRouteHandlerOptions, RegisteredRouteShape, UnmatchedRequestKind, acceptsHtmlExplicitly, classifyUnmatchedRequest, createNotFoundRouteHandler, frameworkDefaultNotFoundDocument, isNotFoundPageFile } from "./not-found-page.mjs";
|
|
13
13
|
import { VITE_DIRECT_CSS_QUERY, devStylesheetUrls, productionStylesheetUrls } from "./stylesheet-urls.mjs";
|
|
14
|
-
export { type BufferedCookie, type BufferedHeader,
|
|
14
|
+
export { type BufferedCookie, type BufferedHeader, DuplicateNotFoundPageError, type ExecutePageRequestOptions, type InstallPageRoutesFromManifestOptions, type InstallPageRoutesOptions, type InstalledManifestPageRoute, type InstalledPageRoute, type LayoutModuleShape, type LoaderShortCircuitKind, NOT_FOUND_PAGE_FILENAME, NOT_FOUND_ROUTE_NAME, NOT_FOUND_ROUTE_PATH, NotFoundPageDeclaresRouteError, type NotFoundRouteHandlerOptions, PAYLOAD_SCRIPT_ID, type PageBoundaryDesignation, type PageContextRunner, type PageDataBundle, type PageLevelName, PageModuleNotInManifestError, type PageModuleShape, type PageResponseCommit, type PageRouteEntry, type PageRouteExport, type PageRouteHandlerFactory, type PageRouteMatch, type PageRoutesRegistry, type PageShortCircuit, type PageTripleModule, type PipelineLoader, type PipelineMiddleware, type PipelineStore, type RegisteredRouteShape, type RenderPageOptions, type RenderPageRequestOptions, type RenderedPage, type UnmatchedRequestKind, VITE_DIRECT_CSS_QUERY, acceptsHtmlExplicitly, classifyUnmatchedRequest, composeRoutePath, connectPageContext, connectPageRoutes, connectSharedStore, createNotFoundRouteHandler, createPageModuleLoader, devStylesheetUrls, escapePayload, executePageRequest, frameworkDefaultNotFoundDocument, installPageRoutes, installPageRoutesFromManifest, isNotFoundPageFile, productionStylesheetUrls, renderPage, renderPageRequest };
|