@warlock.js/web 5.0.2 → 5.2.0
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 +250 -0
- package/README.md +126 -12
- package/esm/build/contribution.d.mts +11 -4
- package/esm/build/contribution.mjs +46 -22
- package/esm/build/contribution.mjs.map +1 -1
- package/esm/build/discover-pages.mjs +318 -44
- package/esm/build/discover-pages.mjs.map +1 -1
- package/esm/build/generate-client-registry.mjs +47 -13
- package/esm/build/generate-client-registry.mjs.map +1 -1
- package/esm/build/generate-pages-barrel.mjs +55 -13
- 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 +1 -1
- package/esm/build/read-route-exports.mjs.map +1 -1
- package/esm/client/build-hydrated-tree.mjs +40 -9
- package/esm/client/build-hydrated-tree.mjs.map +1 -1
- package/esm/client/hydrate-page.mjs +1 -1
- package/esm/client/hydrate-page.mjs.map +1 -1
- package/esm/client/navigation/current-route.d.mts +1 -1
- package/esm/client/navigation/current-route.mjs +1 -1
- package/esm/client/navigation/current-route.mjs.map +1 -1
- package/esm/client/navigation/fetch-page-data.mjs +1 -1
- package/esm/client/navigation/fetch-page-data.mjs.map +1 -1
- package/esm/client/navigation/navigation-root.mjs +44 -7
- package/esm/client/navigation/navigation-root.mjs.map +1 -1
- package/esm/client/navigation/prefetch.mjs +1 -1
- package/esm/client/navigation/prefetch.mjs.map +1 -1
- package/esm/client/navigation/refresh.d.mts +1 -1
- package/esm/client/navigation/refresh.mjs +1 -1
- package/esm/client/navigation/refresh.mjs.map +1 -1
- package/esm/client/navigation/scroll-to-fragment.mjs +26 -0
- package/esm/client/navigation/scroll-to-fragment.mjs.map +1 -0
- package/esm/client/navigation/verbs.d.mts +1 -1
- package/esm/client/navigation/verbs.mjs +1 -1
- package/esm/client/navigation/verbs.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.d.mts +1 -1
- package/esm/client/runtime/manifest.mjs +3 -2
- package/esm/client/runtime/manifest.mjs.map +1 -1
- package/esm/client/runtime/matcher.d.mts +13 -1
- package/esm/client/runtime/matcher.mjs +13 -1
- package/esm/client/runtime/matcher.mjs.map +1 -1
- package/esm/client/runtime/types.d.mts +15 -2
- package/esm/components/default-app.mjs +1 -1
- package/esm/components/default-app.mjs.map +1 -1
- package/esm/components/document-context.d.mts +40 -2
- package/esm/components/document-context.mjs +1 -1
- package/esm/components/document-context.mjs.map +1 -1
- package/esm/components/head.d.mts +1 -1
- package/esm/components/head.mjs +1 -1
- package/esm/components/head.mjs.map +1 -1
- package/esm/components/link.d.mts +1 -1
- package/esm/components/link.mjs +1 -1
- package/esm/components/link.mjs.map +1 -1
- package/esm/components/scripts.d.mts +1 -1
- package/esm/components/scripts.mjs +4 -2
- package/esm/components/scripts.mjs.map +1 -1
- package/esm/context.d.mts +6 -102
- package/esm/hydration/index.mjs +1 -1
- package/esm/hydration/index.mjs.map +1 -1
- package/esm/hydration-payload.d.mts +1 -1
- package/esm/hydration-payload.mjs +37 -8
- package/esm/hydration-payload.mjs.map +1 -1
- package/esm/index.d.mts +4 -3
- package/esm/loaders.d.mts +7 -36
- package/esm/metadata.d.mts +15 -1
- package/esm/metadata.mjs +45 -0
- package/esm/metadata.mjs.map +1 -0
- package/esm/props.d.mts +16 -8
- package/esm/route.d.mts +2 -17
- package/esm/routing/compose-route-path.d.mts +1 -1
- package/esm/routing/compose-route-path.mjs +1 -1
- package/esm/routing/compose-route-path.mjs.map +1 -1
- package/esm/routing/data-request.mjs +1 -1
- package/esm/routing/data-request.mjs.map +1 -1
- package/esm/routing/filesystem-route.mjs +45 -0
- package/esm/routing/filesystem-route.mjs.map +1 -0
- package/esm/routing/layout-policy.mjs +1 -1
- package/esm/routing/layout-policy.mjs.map +1 -1
- package/esm/routing/navigator.mjs +1 -1
- package/esm/routing/navigator.mjs.map +1 -1
- package/esm/routing/query-string.d.mts +1 -1
- package/esm/routing/query-string.mjs +1 -1
- package/esm/routing/query-string.mjs.map +1 -1
- package/esm/routing/route-identity.mjs +1 -1
- package/esm/routing/route-identity.mjs.map +1 -1
- package/esm/routing/route-table.d.mts +1 -1
- package/esm/routing/route-table.mjs +1 -1
- package/esm/routing/route-table.mjs.map +1 -1
- package/esm/routing/router-events.d.mts +1 -1
- package/esm/routing/router-events.mjs +1 -1
- package/esm/routing/router-events.mjs.map +1 -1
- package/esm/routing/url-fragment.mjs +120 -0
- package/esm/routing/url-fragment.mjs.map +1 -0
- 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 +2 -1
- package/esm/server/build-hydration-payload.mjs.map +1 -1
- package/esm/server/client-asset-url-prefix.d.mts +1 -1
- package/esm/server/client-asset-url-prefix.mjs +1 -1
- package/esm/server/client-asset-url-prefix.mjs.map +1 -1
- package/esm/server/create-page-module-loader.d.mts +1 -1
- package/esm/server/create-page-module-loader.mjs +2 -1
- package/esm/server/create-page-module-loader.mjs.map +1 -1
- package/esm/server/create-page-route-handler.d.mts +58 -6
- package/esm/server/create-page-route-handler.mjs +127 -40
- package/esm/server/create-page-route-handler.mjs.map +1 -1
- package/esm/server/dev-server.mjs +2 -13
- 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 +7 -4
- package/esm/server/execute-page-request.mjs +79 -80
- package/esm/server/execute-page-request.mjs.map +1 -1
- package/esm/server/execute-page-request.types.d.mts +33 -92
- package/esm/server/hydration-client-url.d.mts +1 -1
- package/esm/server/hydration-client-url.mjs +2 -2
- package/esm/server/hydration-client-url.mjs.map +1 -1
- package/esm/server/index.d.mts +6 -5
- package/esm/server/index.mjs +4 -4
- package/esm/server/install-page-routes-from-manifest.d.mts +17 -7
- package/esm/server/install-page-routes-from-manifest.mjs +91 -8
- package/esm/server/install-page-routes-from-manifest.mjs.map +1 -1
- package/esm/server/install-page-routes.d.mts +32 -13
- package/esm/server/install-page-routes.mjs +112 -32
- package/esm/server/install-page-routes.mjs.map +1 -1
- package/esm/server/install-production-page-routes.mjs +3 -31
- package/esm/server/install-production-page-routes.mjs.map +1 -1
- package/esm/server/match-page-route.mjs +2 -2
- package/esm/server/match-page-route.mjs.map +1 -1
- package/esm/server/not-found-page.d.mts +126 -0
- package/esm/server/not-found-page.mjs +157 -0
- package/esm/server/not-found-page.mjs.map +1 -0
- package/esm/server/page-context.d.mts +3 -9
- package/esm/server/page-context.mjs +3 -16
- 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 +25 -10
- package/esm/server/page-manifest.mjs +1 -1
- 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 +11 -14
- package/esm/server/render-page.mjs +154 -28
- package/esm/server/render-page.mjs.map +1 -1
- package/esm/server/resolve-page-metadata.mjs +2 -2
- package/esm/server/resolve-page-metadata.mjs.map +1 -1
- package/esm/server/resolve-validation-data.mjs +1 -1
- package/esm/server/resolve-validation-data.mjs.map +1 -1
- package/esm/server/settle-page-response.d.mts +58 -1
- package/esm/server/settle-page-response.mjs +111 -46
- package/esm/server/settle-page-response.mjs.map +1 -1
- package/esm/server/stylesheet-urls.d.mts +41 -30
- package/esm/server/stylesheet-urls.mjs +137 -52
- 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 +2 -3
- package/esm/server/web-connector-factory.mjs +6 -4
- package/esm/server/web-connector-factory.mjs.map +1 -1
- package/esm/server/web-connector.d.mts +1 -1
- package/esm/server/web-connector.mjs +233 -18
- package/esm/server/web-connector.mjs.map +1 -1
- package/esm/shared.d.mts +18 -3
- package/esm/shared.mjs +3 -7
- package/esm/shared.mjs.map +1 -1
- package/esm/validation.d.mts +1 -1
- package/esm/vite/app-convention-aliases.mjs +1 -1
- package/esm/vite/app-convention-aliases.mjs.map +1 -1
- package/esm/vite/build-client.d.mts +1 -1
- package/esm/vite/build-client.mjs +2 -2
- package/esm/vite/build-client.mjs.map +1 -1
- package/esm/vite/gate-a-resolve.d.mts +1 -1
- package/esm/vite/gate-a-resolve.mjs +67 -13
- package/esm/vite/gate-a-resolve.mjs.map +1 -1
- package/esm/vite/gate-b-secrets.d.mts +1 -1
- package/esm/vite/gate-b-secrets.mjs +35 -17
- package/esm/vite/gate-b-secrets.mjs.map +1 -1
- package/esm/vite/gate-c-verify.d.mts +1 -1
- package/esm/vite/gate-c-verify.mjs +6 -6
- package/esm/vite/gate-c-verify.mjs.map +1 -1
- package/esm/vite/hydration-entries.d.mts +1 -1
- package/esm/vite/hydration-entries.mjs +10 -6
- package/esm/vite/hydration-entries.mjs.map +1 -1
- package/esm/vite/index.d.mts +9 -6
- package/esm/vite/index.mjs +124 -11
- package/esm/vite/index.mjs.map +1 -1
- package/esm/vite/page-registry-plugin.d.mts +22 -3
- package/esm/vite/page-registry-plugin.mjs +247 -2
- package/esm/vite/page-registry-plugin.mjs.map +1 -1
- package/esm/vite/projection.d.mts +2 -2
- package/esm/vite/projection.mjs +38 -15
- package/esm/vite/projection.mjs.map +1 -1
- package/llms-full.txt +241 -49
- package/llms.txt +6 -2
- package/package.json +3 -3
- package/skills/add-web-to-an-app/SKILL.md +3 -3
- package/skills/create-a-page/SKILL.md +130 -7
- package/skills/load-page-data/SKILL.md +92 -12
- package/skills/navigate-on-the-client/SKILL.md +3 -1
- package/skills/serve-styles/SKILL.md +13 -12
- package/skills/use-layouts/SKILL.md +20 -8
- package/skills/write-the-root/SKILL.md +5 -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,13 +1,13 @@
|
|
|
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
|
-
//#region
|
|
10
|
+
//#region web/src/server/execute-page-request.ts
|
|
11
11
|
async function executePageRequest(options) {
|
|
12
12
|
const runner = requireRunner();
|
|
13
13
|
const [pathname, queryString] = options.url.split("?");
|
|
@@ -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":";;;;;;;;;;AA2CA,eAAsB,mBACpB,SAC8B;CAC9B,MAAM,SAAS,cAAc;CAG7B,MAAM,CAAC,UAAU,eAAe,QAAQ,IAAI,MAAM,GAAG;CACrD,MAAM,UAAU,WAAW,UAAU,QAAQ,MAAM;CAEnD,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,QAAQ,OAAO,YAAY,IAAI,gBAAgB,eAAe,EAAE,CAAC;CACvE,MAAM,QAAwB;EAAE,OAAO,QAAQ;EAAO,QAAQ,QAAQ;EAAQ;CAAM;CACpF,MAAM,EAAE,WAAW,QAAQ;CAE3B,MAAM,EAAE,SAAS,aAAa,QAAQ,WAAW,KAAK;CAKtD,MAAM,QAAuB,OAAO,aAChC,OAAO,WAAW;EAAE;EAAS;CAAS,CAAC,IACvC;EAAE;EAAS;CAAS;CAExB,OAAO,MAAM,OAAO,IAAI,OAAO,YAAY;EACzC,iBAAiB,KAAK;EACtB,2BAA2B,KAAK;EAEhC,MAAM,SAAS,OAAO,WACpB,QAAQ,SAAS,MAAM,QAAQ,OAAO,MAAM,IAAK;EAEnD,MAAM,SAAyB,EAC7B,OAAO;GACL,MAAM,QAAQ,MAAM;GACpB,MAAM,QAAQ,MAAM;GACpB,QAAQ,MAAM;GACd;EACF,EACF;EAKA,KAAK,MAAM,SAAS,aAClB,KAAK,MAAM,cAAc,OAAO,MAAM,CAAC,cAAc,CAAC,GAAG;GACvD,IAAI;GAEJ,IAAI;IACF,SAAS,MAAM,WAAW;KAAE;KAAS;IAAS,CAAC;GACjD,SAAS,QAAQ;IAQf,OAAO,QAAQ,iBAAiB,QAAQ,kBAAkB,OAAO,MAAM,GAAG,QAAQ;IAClF,OAAO,SAAS,cAAc,UAAU,CAAC,GAAG,GAAG;IAE/C,OAAO,OAAO,MAAM;GACtB;GAEA,IAAI,WAAW,QAAW;IACxB,OAAO,eAAe;KACpB,OAAO;KACP;KACA,OAAO;KACP,YAAa,SAAwD;IACvE;IAEA,OAAO,OAAO,MAAM;GACtB;EACF;EAMF,MAAM,aAAa,OAAO,KAAK;EAE/B,IAAI,YAAY,QAAQ;GACtB,MAAM,OAAO,sBAAsB,WAAW,YAAY,OAAO;GACjE,MAAM,SAAS,MAAM,EAAE,SAAS,WAAW,QAAQ,IAAI;GAEvD,IAAI,OAAO,WAAW,OAAO,MAC3B,QAAQ,mBAAmB,OAAO,IAAI;GAGxC,IAAI,CAAC,OAAO,SAAS;IACnB,OAAO,eAAe;KAAE,OAAO;KAAc,QAAQ;KAAK,QAAQ,OAAO;IAAO;IAEhF,OAAO,OAAO,MAAM;GACtB;EACF;EAKA,MAAM,eAAe,MAAM,WAAW,KAAK;EAE3C,OAAO,SAAS;EAKhB,MAAM,UAAU,YAAY,KAAI,UAAS;GACvC,MAAM,EAAE,UAAU,kBAAkB,WAAW,uBAAuB;GAEtE,OAAO;IAAE;IAAO;IAAQ;GAAiB;EAC3C,CAAC;EAED,MAAM,UAAU,MAAM,QAAQ,WAC5B,QAAQ,KAAK,EAAE,OAAO,uBAAuB;GAC3C,MAAM,SAAS,OAAO,MAAM,CAAC;GAE7B,QAAQ,YACN,SAAS,MAAM,OAAO;IAAE;IAAS,UAAU;IAAkB;GAAO,CAAC,IAAI,OAAS,CAAE;EACxF,CAAC,CACH;EAOA,IAAI;EACJ,IAAI;EAIJ,SAAS,SAAS,OAAgB;GAChC,OAAO,qBAAqB,KAAK,IAAI,QAAQ;EAC/C;EAEA,KAAK,IAAI,QAAQ,GAAG,QAAQ,YAAY,QAAQ,SAAS;GACvD,MAAM,SAAS,QAAQ;GAEvB,IAAI,OAAO,WAAW,YAAY;IAChC,aAAa;KAAE,OAAO,YAAY;KAAQ;KAAO,OAAO,OAAO;IAAO;IACtE;GACF;GAEA,MAAM,SAAS,SAAS,OAAO,KAAK;GAEpC,IAAI,QAAQ;IACV,cAAc;KAAE,OAAO,YAAY;KAAQ;KAAO;IAAO;IACzD;GACF;EACF;EAEA,MAAM,WAAyE;GAC7E,KAAK;GACL,QAAQ;GACR,MAAM;EACR;EAIA,MAAM,aAAa,cAAc,YAAY,QAAQ,YAAY;EAEjE,KAAK,IAAI,QAAQ,GAAG,QAAQ,YAAY,QAAQ,SAAS;GACvD,MAAM,SAAS,QAAQ;GAEvB,IAAI,OAAO,WAAW,aAAa;GACnC,IAAI,cAAc,UAAU,WAAW,OAAO;GAC9C,IAAI,QAAQ,YAAY;GACxB,IAAI,SAAS,OAAO,KAAK,GAAG;GAE5B,OAAO,SAAS,YAAY,WAAW,OAAO;EAChD;EAEA,IAAI,YAAY;GACd,MAAM,WAAW,kBAAkB,WAAW,OAAO,MAAM;GAE3D,OAAO,QAAQ,iBAAiB,WAAW,OAAO,UAAU,QAAQ;GAKpE,OAAO,SAAS,cACd,UACA,QAAQ,MAAM,GAAG,WAAW,KAAK,GACjC,SAAS,kBAAkB,QAAQ,MAAM,MAC3C;EACF,OAAO,IAAI,aAAa;GACtB,OAAO,eAAe;IACpB,OAAO;IACP,OAAO,YAAY;IACnB,MAAM,YAAY,OAAO;IACzB,YAAY,YAAY,OAAO;IAC/B,KAAK,YAAY,OAAO;IACxB,MAAM,YAAY,OAAO;GAC3B;GACA,OAAO,SAAS,cAAc,UAAU,QAAQ,MAAM,GAAG,YAAY,QAAQ,CAAC,CAAC;EACjF,OACE,OAAO,SAAS,cAAc,UAAU,OAAO;EAMjD,IAAI,CAAC,OAAO,cAAc;GACxB,MAAM,WAAW,oBAAoB;IACnC,UAAU,OAAO,KAAK;IACtB,MAAM,OAAO;IACb,OAAO,OAAO,OAAO;IACrB,QAAQ,QAAQ,OAAO,KAAK;IAC5B,QAAQ;GACV,CAAC;GAED,OAAO,WAAW,SAAS;GAS3B,IAAI,SAAS,WAAW,QACtB,OAAO,QAAQ,iBACb,SAAS,QACT,kBAAkB,QAAQ,MAAM,GAChC,OAAO,MAAM,IACf;EAEJ;EAEA,OAAO,OAAO,MAAM;CACtB,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"execute-page-request.mjs","names":[],"sources":["C:/Work/Warlock.js/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
|
-
//#region
|
|
9
|
-
/** At runtime this IS core's `RequestContextStore` (request-context.ts:10-13). */
|
|
9
|
+
//#region web/src/server/execute-page-request.types.d.ts
|
|
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,9 +1,9 @@
|
|
|
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
|
-
//#region
|
|
6
|
+
//#region web/src/server/hydration-client-url.ts
|
|
7
7
|
/**
|
|
8
8
|
* The ONE runtime manifest read in production: one boot-time JSON
|
|
9
9
|
* lookup of the `hydration` entry in `<clientDir>/.vite/manifest.json`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydration-client-url.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"hydration-client-url.mjs","names":[],"sources":["C:/Work/Warlock.js/web/src/server/hydration-client-url.ts"],"sourcesContent":["/**\n * The ONE runtime manifest read in production: one boot-time JSON\n * lookup of the `hydration` entry in `<clientDir>/.vite/manifest.json`,\n * producing the string `install-page-routes.ts:139` already accepts as\n * `hydrationClientModuleUrl`.\n *\n * NEVER FALLS BACK. Each failure is its own named error so the boot log says\n * which half of the build→runtime handoff broke: a missing\n * file means the client build never ran, malformed JSON means the artifact is\n * corrupt, and a missing entry means the build/runtime entry-name contract\n * drifted. \"Serve without hydration\" is not among the outcomes.\n */\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { HYDRATION_CLIENT_ENTRY_NAME } from \"../vite/hydration-entries\";\nimport { CLIENT_ASSET_URL_PREFIX } from \"./client-asset-url-prefix\";\n\nexport interface ResolveHydrationClientUrlOptions {\n /** Absolute path to the client build output dir: `<outdir>/client`. */\n clientDir: string;\n}\n\n/** The manifest file is absent — the client build never ran, or ran elsewhere. */\nexport class WebClientManifestMissingError extends Error {\n public constructor(manifestPath: string, cause: unknown) {\n super(\n `Cannot resolve the hydration client URL: the Vite client manifest \"${manifestPath}\" is missing or unreadable. ` +\n \"This app was started in production without its `warlock build` client artifacts.\",\n { cause },\n );\n this.name = \"WebClientManifestMissingError\";\n }\n}\n\n/** The manifest file exists but is not parsable JSON, or is not a JSON object. */\nexport class WebClientManifestMalformedError extends Error {\n public constructor(manifestPath: string, cause: unknown) {\n super(\n `Cannot resolve the hydration client URL: the Vite client manifest \"${manifestPath}\" is not valid JSON object content.`,\n { cause },\n );\n this.name = \"WebClientManifestMalformedError\";\n }\n}\n\n/**\n * The manifest parses but carries no usable `hydration` entry.\n *\n * The entry name is a shared contract between the build (`build-client.ts`\n * rollup input) and this runtime read; drift fails loudly HERE. Nobody adds a\n * default.\n */\nexport class WebClientManifestEntryMissingError extends Error {\n public constructor(manifestPath: string, entryName: string) {\n super(\n `Cannot resolve the hydration client URL: the Vite client manifest \"${manifestPath}\" has no \"${entryName}\" entry with a \"file\" string. ` +\n \"The build and runtime hydration entry names have drifted.\",\n );\n this.name = \"WebClientManifestEntryMissingError\";\n }\n}\n\n/**\n * The manifest names an entry file that does NOT live under the asset\n * directory `CLIENT_ASSET_URL_PREFIX` is mounted from.\n *\n * Serving it would 404 — the static route only exposes that one directory —\n * so the artifact is rejected at boot instead of at first page view.\n */\nexport class WebClientAssetPrefixViolationError extends Error {\n public constructor(manifestPath: string, file: string) {\n super(\n `Cannot resolve the hydration client URL: the Vite client manifest \"${manifestPath}\" points its entry at \"${file}\", ` +\n `which is not under the \"${CLIENT_ASSET_URL_PREFIX}\" asset directory this framework serves. ` +\n \"This client artifact was not produced by this framework's client build configuration. \" +\n \"Re-run the client build instead of hand-editing the manifest or the build output.\",\n );\n this.name = \"WebClientAssetPrefixViolationError\";\n }\n}\n\ntype ManifestEntry = { file?: unknown; name?: unknown; isEntry?: unknown };\n\n/**\n * Find the hydration entry in a parsed Vite manifest.\n *\n * VITE KEYS BY SOURCE PATH, NOT BY ENTRY NAME. A rollup input of\n * `{ hydration: \"src/hydration/index.ts\" }` produces the record\n * `\"src/hydration/index.ts\": { name: \"hydration\", isEntry: true, file: \"assets/hydration-<hash>.js\" }`\n * — the name the build and this module share lives in the `name` FIELD, and\n * there is no `\"hydration\"` key at all. Indexing the manifest by\n * {@link HYDRATION_CLIENT_ENTRY_NAME} therefore missed every real manifest and\n * reported an entry-name drift that had not happened.\n *\n * Matching on `isEntry` as well as `name` keeps this unambiguous: a shared\n * chunk can carry a `name` too, and only entries are addressable as a module\n * URL.\n */\nfunction findHydrationEntry(manifest: Record<string, ManifestEntry | undefined>): ManifestEntry | undefined {\n for (const entry of Object.values(manifest)) {\n if (\n entry !== undefined &&\n typeof entry === \"object\" &&\n entry.isEntry === true &&\n entry.name === HYDRATION_CLIENT_ENTRY_NAME\n ) {\n return entry;\n }\n }\n\n return undefined;\n}\n\nexport function resolveHydrationClientUrl(options: ResolveHydrationClientUrlOptions): string {\n const manifestPath = path.join(options.clientDir, \".vite\", \"manifest.json\");\n\n let raw: string;\n\n try {\n raw = readFileSync(manifestPath, \"utf-8\");\n } catch (error) {\n throw new WebClientManifestMissingError(manifestPath, error);\n }\n\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n throw new WebClientManifestMalformedError(manifestPath, error);\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new WebClientManifestMalformedError(\n manifestPath,\n new TypeError(\"The manifest root is not a JSON object.\"),\n );\n }\n\n const entry = findHydrationEntry(parsed as Record<string, ManifestEntry | undefined>);\n\n if (entry === undefined || typeof entry !== \"object\" || typeof entry.file !== \"string\") {\n throw new WebClientManifestEntryMissingError(manifestPath, HYDRATION_CLIENT_ENTRY_NAME);\n }\n\n const url = `/${entry.file}`;\n\n // The prefix is DERIVED from CLIENT_ASSET_URL_PREFIX, never restated: this\n // file must not carry a second copy of the literal the constant owns.\n if (!url.startsWith(`${CLIENT_ASSET_URL_PREFIX}/`)) {\n throw new WebClientAssetPrefixViolationError(manifestPath, entry.file);\n }\n\n // Past this point the returned URL starts with CLIENT_ASSET_URL_PREFIX by\n // construction — which is exactly what the static-file route mounting\n // `<clientDir>/assets` at that same imported symbol relies on.\n return url;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuBA,IAAa,gCAAb,cAAmD,MAAM;CACvD,AAAO,YAAY,cAAsB,OAAgB;EACvD,MACE,sEAAsE,aAAa,iHAEnF,EAAE,MAAM,CACV;EACA,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,kCAAb,cAAqD,MAAM;CACzD,AAAO,YAAY,cAAsB,OAAgB;EACvD,MACE,sEAAsE,aAAa,sCACnF,EAAE,MAAM,CACV;EACA,KAAK,OAAO;CACd;AACF;;;;;;;;AASA,IAAa,qCAAb,cAAwD,MAAM;CAC5D,AAAO,YAAY,cAAsB,WAAmB;EAC1D,MACE,sEAAsE,aAAa,YAAY,UAAU,wFAE3G;EACA,KAAK,OAAO;CACd;AACF;;;;;;;;AASA,IAAa,qCAAb,cAAwD,MAAM;CAC5D,AAAO,YAAY,cAAsB,MAAc;EACrD,MACE,sEAAsE,aAAa,yBAAyB,KAAK,6BACpF,wBAAwB,iNAGvD;EACA,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;;;;AAmBA,SAAS,mBAAmB,UAAgF;CAC1G,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,GACxC,IACE,UAAU,UACV,OAAO,UAAU,YACjB,MAAM,YAAY,QAClB,MAAM,sBAEN,OAAO;AAKb;AAEA,SAAgB,0BAA0B,SAAmD;CAC3F,MAAM,eAAe,KAAK,KAAK,QAAQ,WAAW,SAAS,eAAe;CAE1E,IAAI;CAEJ,IAAI;EACF,MAAM,aAAa,cAAc,OAAO;CAC1C,SAAS,OAAO;EACd,MAAM,IAAI,8BAA8B,cAAc,KAAK;CAC7D;CAEA,IAAI;CAEJ,IAAI;EACF,SAAS,KAAK,MAAM,GAAG;CACzB,SAAS,OAAO;EACd,MAAM,IAAI,gCAAgC,cAAc,KAAK;CAC/D;CAEA,IAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GACvE,MAAM,IAAI,gCACR,8BACA,IAAI,UAAU,yCAAyC,CACzD;CAGF,MAAM,QAAQ,mBAAmB,MAAmD;CAEpF,IAAI,UAAU,UAAa,OAAO,UAAU,YAAY,OAAO,MAAM,SAAS,UAC5E,MAAM,IAAI,mCAAmC,cAAc,2BAA2B;CAGxF,MAAM,MAAM,IAAI,MAAM;CAItB,IAAI,CAAC,IAAI,WAAW,aAA2B,EAAE,GAC/C,MAAM,IAAI,mCAAmC,cAAc,MAAM,IAAI;CAMvE,OAAO;AACT"}
|
package/esm/server/index.d.mts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
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";
|
|
10
10
|
import { PageModuleNotInManifestError, createPageModuleLoader } from "./create-page-module-loader.mjs";
|
|
11
11
|
import { InstallPageRoutesFromManifestOptions, InstalledManifestPageRoute, PageRouteHandlerFactory, installPageRoutesFromManifest } from "./install-page-routes-from-manifest.mjs";
|
|
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";
|
|
12
13
|
import { VITE_DIRECT_CSS_QUERY, devStylesheetUrls, productionStylesheetUrls } from "./stylesheet-urls.mjs";
|
|
13
|
-
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 };
|
package/esm/server/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { connectSharedStore } from "../shared.mjs";
|
|
2
2
|
import { PAYLOAD_SCRIPT_ID, escapePayload } from "../components/document-context.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { connectPageContext, connectPageSharedScope } from "./page-context.mjs";
|
|
3
|
+
import { connectPageContext } from "./page-context.mjs";
|
|
5
4
|
import { executePageRequest } from "./execute-page-request.mjs";
|
|
6
5
|
import { connectPageRoutes, renderPage, renderPageRequest } from "./render-page.mjs";
|
|
7
6
|
import { PageModuleNotInManifestError, createPageModuleLoader } from "./create-page-module-loader.mjs";
|
|
8
7
|
import { composeRoutePath } from "../routing/compose-route-path.mjs";
|
|
8
|
+
import { VITE_DIRECT_CSS_QUERY, devStylesheetUrls, productionStylesheetUrls } from "./stylesheet-urls.mjs";
|
|
9
|
+
import { DuplicateNotFoundPageError, NOT_FOUND_PAGE_FILENAME, NOT_FOUND_ROUTE_NAME, NOT_FOUND_ROUTE_PATH, NotFoundPageDeclaresRouteError, acceptsHtmlExplicitly, classifyUnmatchedRequest, createNotFoundRouteHandler, frameworkDefaultNotFoundDocument, isNotFoundPageFile } from "./not-found-page.mjs";
|
|
9
10
|
import { installPageRoutesFromManifest } from "./install-page-routes-from-manifest.mjs";
|
|
10
11
|
import { installPageRoutes } from "./install-page-routes.mjs";
|
|
11
|
-
import { VITE_DIRECT_CSS_QUERY, devStylesheetUrls, productionStylesheetUrls } from "./stylesheet-urls.mjs";
|
|
12
12
|
|
|
13
|
-
export {
|
|
13
|
+
export { DuplicateNotFoundPageError, NOT_FOUND_PAGE_FILENAME, NOT_FOUND_ROUTE_NAME, NOT_FOUND_ROUTE_PATH, NotFoundPageDeclaresRouteError, PAYLOAD_SCRIPT_ID, PageModuleNotInManifestError, VITE_DIRECT_CSS_QUERY, acceptsHtmlExplicitly, classifyUnmatchedRequest, composeRoutePath, connectPageContext, connectPageRoutes, connectSharedStore, createNotFoundRouteHandler, createPageModuleLoader, devStylesheetUrls, escapePayload, executePageRequest, frameworkDefaultNotFoundDocument, installPageRoutes, installPageRoutesFromManifest, isNotFoundPageFile, productionStylesheetUrls, renderPage, renderPageRequest };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { PageManifest } from "./page-manifest.mjs";
|
|
2
|
-
import { BufferedCookie } from "./buffered-response.mjs";
|
|
3
2
|
import { PageRouteHandler, PageRouteHandlerOptions } from "./create-page-route-handler.mjs";
|
|
4
|
-
import {
|
|
3
|
+
import { Router } from "@warlock.js/core";
|
|
5
4
|
|
|
6
|
-
//#region
|
|
5
|
+
//#region web/src/server/install-page-routes-from-manifest.d.ts
|
|
7
6
|
/**
|
|
8
7
|
* How a handler is built for one page. Defaults to `createPageRouteHandler`;
|
|
9
8
|
* taking it as an input keeps this module's own job — reading the manifest and
|
|
@@ -11,7 +10,8 @@ import { Response, Router } from "@warlock.js/core";
|
|
|
11
10
|
*/
|
|
12
11
|
type PageRouteHandlerFactory = (options: PageRouteHandlerOptions) => PageRouteHandler;
|
|
13
12
|
type InstalledManifestPageRoute = {
|
|
14
|
-
/** The
|
|
13
|
+
/** The canonical declared route path, before layout-prefix composition. */declaredPath: string; /** The composed path the route was registered on. */
|
|
14
|
+
path: string; /** The resolved route name; shared namespace with API routes. */
|
|
15
15
|
name: string; /** The page's manifest `sourceFile`. */
|
|
16
16
|
file: string; /** The layout's manifest `sourceFile`, when the page has one. */
|
|
17
17
|
layoutFile: string | undefined;
|
|
@@ -19,9 +19,19 @@ type InstalledManifestPageRoute = {
|
|
|
19
19
|
type InstallPageRoutesFromManifestOptions = {
|
|
20
20
|
router: Router; /** The table the generated production barrel provided at import time. */
|
|
21
21
|
manifest: PageManifest; /** Browser module loaded after the server-rendered application and payload. */
|
|
22
|
-
hydrationClientModuleUrl?: string;
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
hydrationClientModuleUrl?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Where the client build wrote its output — `productionStylesheetUrls`'s own
|
|
25
|
+
* `clientDir` argument, forwarded here rather than pre-read into a flat list:
|
|
26
|
+
* each registered handler needs its OWN chain
|
|
27
|
+
* (`[root, ...outer-to-inner matched layouts, page]`, matched by the
|
|
28
|
+
* manifest's own `sourceFile` ids), not one list shared by every page.
|
|
29
|
+
*
|
|
30
|
+
* OPTIONAL for the same reason `PageManifest.clientDir` is: a build that
|
|
31
|
+
* discovered zero pages emits no client bundle, so there is no directory to
|
|
32
|
+
* read stylesheets from — and no page that could need one either.
|
|
33
|
+
*/
|
|
34
|
+
clientDir?: string; /** Same helper `dev-server.ts` exports — passed in, never imported. */
|
|
25
35
|
createHandler?: PageRouteHandlerFactory;
|
|
26
36
|
};
|
|
27
37
|
/**
|