@chidchanun/bcp 0.1.9 → 0.1.10
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 +41 -0
- package/README.md +141 -8
- package/docs/releasing.md +34 -19
- package/docs/route-guards.md +240 -0
- package/docs/updating.md +130 -0
- package/package.json +1 -1
- package/packages/bundler/src/server-production-guards.ts +497 -0
- package/packages/bundler/src/server-production-middleware.ts +33 -8
- package/packages/cli/src/args.ts +58 -0
- package/packages/cli/src/index.ts +41 -13
- package/packages/cli/src/update.ts +860 -0
- package/packages/client/src/index.tsx +1 -0
- package/packages/client/src/loader-data.tsx +171 -41
- package/packages/server/src/page-guard.ts +529 -0
- package/packages/server/src/page-loader.ts +317 -20
- package/packages/server/src/standalone-production-runtime-v2-guard.ts +815 -0
- package/packages/server/src/standalone-production-runtime-v3.ts +1 -1
package/docs/updating.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Updating BCP Framework
|
|
2
|
+
|
|
3
|
+
BCP includes a project updater that can move an existing application to a newer published framework version and refresh the active package-manager lockfile.
|
|
4
|
+
|
|
5
|
+
## First update from 0.1.9 or older
|
|
6
|
+
|
|
7
|
+
BCP versions published before the updater do not know the `update` command. Run the newest CLI explicitly once:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @chidchanun/bcp@latest update
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The updater adds this project script when the application does not already define its own `update` script:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"scripts": {
|
|
18
|
+
"update": "bcp update"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Normal future updates can therefore use either:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bcp update
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or, without a global BCP installation:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm run update
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Update to latest
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bcp update
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This resolves `@chidchanun/bcp@latest` from npm, pins that exact resolved release in `package.json`, detects the project's package manager and runs its install command.
|
|
42
|
+
|
|
43
|
+
For example, if `latest` resolves to `0.1.10`, a generated application becomes:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"bcp": "npm:@chidchanun/bcp@0.1.10"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The exact pin is intentional. A later plain `npm install` should not silently move the framework to a release that the BCP updater did not explicitly resolve.
|
|
54
|
+
|
|
55
|
+
Generated applications use the dependency key `bcp` with an npm alias to `@chidchanun/bcp`. The updater also recognizes a direct `@chidchanun/bcp` dependency.
|
|
56
|
+
|
|
57
|
+
## Update to a specific version or dist-tag
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bcp update 0.1.10
|
|
61
|
+
bcp update next
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The target may be an exact published version or an npm dist-tag. The updater always records the exact version that the registry resolves.
|
|
65
|
+
|
|
66
|
+
## Check without changing files
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bcp update --check
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The command resolves the current `latest` target and prints the installed/declaration state without modifying `package.json` or the lockfile.
|
|
73
|
+
|
|
74
|
+
## Dry run
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bcp update 0.1.10 --dry-run
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
A dry run resolves the target and shows the dependency/script changes without writing files or installing packages.
|
|
81
|
+
|
|
82
|
+
## Project root
|
|
83
|
+
|
|
84
|
+
Use `--root` when updating a project from another directory:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
bcp update --root ./apps/admin
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Package-manager detection
|
|
91
|
+
|
|
92
|
+
BCP detects the package manager from its lockfile:
|
|
93
|
+
|
|
94
|
+
- `package-lock.json` -> npm
|
|
95
|
+
- `pnpm-lock.yaml` -> pnpm
|
|
96
|
+
- `yarn.lock` -> Yarn
|
|
97
|
+
- `bun.lock` / `bun.lockb` -> Bun
|
|
98
|
+
|
|
99
|
+
If no lockfile exists, BCP uses the invoking package-manager user agent when available and otherwise falls back to npm. If lockfiles from multiple package managers are present, the updater stops instead of guessing.
|
|
100
|
+
|
|
101
|
+
Registry version discovery currently uses npm's registry CLI (`npm view`) even when the project itself installs dependencies with pnpm, Yarn or Bun.
|
|
102
|
+
|
|
103
|
+
## Failure safety
|
|
104
|
+
|
|
105
|
+
Before installation BCP keeps the original `package.json` and detected lockfile in memory. If the package-manager install fails, those files are restored before the command exits with an error.
|
|
106
|
+
|
|
107
|
+
A package manager may still have touched `node_modules` before failing, so run the normal install command again after resolving the underlying package-manager or network problem.
|
|
108
|
+
|
|
109
|
+
## What the updater changes
|
|
110
|
+
|
|
111
|
+
The updater changes the framework dependency, adds `scripts.update = "bcp update"` when no update script exists, and refreshes the package-manager lockfile. An existing custom `update` script is never overwritten.
|
|
112
|
+
|
|
113
|
+
It does not overwrite application source code, environment files, authentication implementations, database schemas or custom configuration.
|
|
114
|
+
|
|
115
|
+
Features that were originally generator presets, such as JWT auth scaffolding, are not automatically injected into an existing application merely by updating the framework package.
|
|
116
|
+
|
|
117
|
+
## Recommended verification
|
|
118
|
+
|
|
119
|
+
After an update, run the checks used by your project, for example:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm run typecheck
|
|
123
|
+
npm run build
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Commit `package.json` and the lockfile together after verification.
|
|
127
|
+
|
|
128
|
+
## Release channels
|
|
129
|
+
|
|
130
|
+
Stable BCP releases use the npm `latest` dist-tag. A release maintainer can still publish an explicit pre-release channel with `BCP_DIST_TAG`, for example `BCP_DIST_TAG=next`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chidchanun/bcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
fileURLToPath,
|
|
5
|
+
} from "node:url";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
build,
|
|
9
|
+
} from "esbuild";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
createServerEnvironmentDefines,
|
|
13
|
+
} from "../../env/src/index.js";
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
Route,
|
|
17
|
+
} from "../../router/src/index.js";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
findPageGuardFiles,
|
|
21
|
+
} from "../../server/src/page-guard.js";
|
|
22
|
+
|
|
23
|
+
export async function buildProductionGuards(
|
|
24
|
+
pageRoutes: Route[],
|
|
25
|
+
rootDirectory: string,
|
|
26
|
+
serverDirectory: string
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
const outputFile =
|
|
29
|
+
path.join(
|
|
30
|
+
serverDirectory,
|
|
31
|
+
"guards.mjs"
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
fs.rmSync(
|
|
35
|
+
outputFile,
|
|
36
|
+
{
|
|
37
|
+
force: true,
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const appDirectory =
|
|
42
|
+
path.join(
|
|
43
|
+
rootDirectory,
|
|
44
|
+
"app"
|
|
45
|
+
);
|
|
46
|
+
const guardedRoutes =
|
|
47
|
+
pageRoutes
|
|
48
|
+
.map(
|
|
49
|
+
(route) => ({
|
|
50
|
+
route,
|
|
51
|
+
guardFiles:
|
|
52
|
+
findPageGuardFiles(
|
|
53
|
+
appDirectory,
|
|
54
|
+
route.filePath
|
|
55
|
+
),
|
|
56
|
+
})
|
|
57
|
+
)
|
|
58
|
+
.filter(
|
|
59
|
+
(entry) =>
|
|
60
|
+
entry.guardFiles.length > 0
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
guardedRoutes.length ===
|
|
65
|
+
0
|
|
66
|
+
) {
|
|
67
|
+
console.log(
|
|
68
|
+
"[BCP Build] Guards: none"
|
|
69
|
+
);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const entryDirectory =
|
|
74
|
+
path.join(
|
|
75
|
+
rootDirectory,
|
|
76
|
+
".bcp-framework",
|
|
77
|
+
"build",
|
|
78
|
+
".guard-entry"
|
|
79
|
+
);
|
|
80
|
+
const entryFile =
|
|
81
|
+
path.join(
|
|
82
|
+
entryDirectory,
|
|
83
|
+
"guards-entry.mjs"
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
fs.rmSync(
|
|
87
|
+
entryDirectory,
|
|
88
|
+
{
|
|
89
|
+
recursive: true,
|
|
90
|
+
force: true,
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
fs.mkdirSync(
|
|
94
|
+
entryDirectory,
|
|
95
|
+
{
|
|
96
|
+
recursive: true,
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
fs.writeFileSync(
|
|
101
|
+
entryFile,
|
|
102
|
+
createGuardEntry(
|
|
103
|
+
guardedRoutes,
|
|
104
|
+
rootDirectory,
|
|
105
|
+
entryDirectory
|
|
106
|
+
),
|
|
107
|
+
"utf8"
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const frameworkDirectory =
|
|
111
|
+
path.dirname(
|
|
112
|
+
fileURLToPath(
|
|
113
|
+
import.meta.url
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
const frameworkServerEntry =
|
|
117
|
+
path.resolve(
|
|
118
|
+
frameworkDirectory,
|
|
119
|
+
"../../client/src/server.ts"
|
|
120
|
+
);
|
|
121
|
+
const frameworkServerOnlyEntry =
|
|
122
|
+
path.resolve(
|
|
123
|
+
frameworkDirectory,
|
|
124
|
+
"../../client/src/server-only.mjs"
|
|
125
|
+
);
|
|
126
|
+
const frameworkCacheEntry =
|
|
127
|
+
path.resolve(
|
|
128
|
+
frameworkDirectory,
|
|
129
|
+
"../../cache/src/index.ts"
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await build({
|
|
134
|
+
absWorkingDir:
|
|
135
|
+
rootDirectory,
|
|
136
|
+
entryPoints: [
|
|
137
|
+
entryFile,
|
|
138
|
+
],
|
|
139
|
+
outfile:
|
|
140
|
+
outputFile,
|
|
141
|
+
bundle:
|
|
142
|
+
true,
|
|
143
|
+
platform:
|
|
144
|
+
"node",
|
|
145
|
+
format:
|
|
146
|
+
"esm",
|
|
147
|
+
target: [
|
|
148
|
+
"node24",
|
|
149
|
+
],
|
|
150
|
+
minify:
|
|
151
|
+
process.env
|
|
152
|
+
.BCP_BUILD_MINIFY !==
|
|
153
|
+
"0",
|
|
154
|
+
sourcemap:
|
|
155
|
+
process.env
|
|
156
|
+
.BCP_BUILD_SOURCE_MAPS ===
|
|
157
|
+
"1",
|
|
158
|
+
treeShaking:
|
|
159
|
+
true,
|
|
160
|
+
packages:
|
|
161
|
+
"external",
|
|
162
|
+
plugins: [
|
|
163
|
+
{
|
|
164
|
+
name:
|
|
165
|
+
"bcp-guard-runtime",
|
|
166
|
+
setup(buildApi) {
|
|
167
|
+
buildApi.onResolve(
|
|
168
|
+
{
|
|
169
|
+
filter:
|
|
170
|
+
/^bcp\/server$/,
|
|
171
|
+
},
|
|
172
|
+
() => ({
|
|
173
|
+
path:
|
|
174
|
+
frameworkServerEntry,
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
buildApi.onResolve(
|
|
178
|
+
{
|
|
179
|
+
filter:
|
|
180
|
+
/^bcp\/server-only$/,
|
|
181
|
+
},
|
|
182
|
+
() => ({
|
|
183
|
+
path:
|
|
184
|
+
frameworkServerOnlyEntry,
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
buildApi.onResolve(
|
|
188
|
+
{
|
|
189
|
+
filter:
|
|
190
|
+
/^bcp\/cache$/,
|
|
191
|
+
},
|
|
192
|
+
() => ({
|
|
193
|
+
path:
|
|
194
|
+
frameworkCacheEntry,
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
define:
|
|
201
|
+
createServerEnvironmentDefines(
|
|
202
|
+
"production"
|
|
203
|
+
),
|
|
204
|
+
legalComments:
|
|
205
|
+
"none",
|
|
206
|
+
logLevel:
|
|
207
|
+
"silent",
|
|
208
|
+
});
|
|
209
|
+
} finally {
|
|
210
|
+
fs.rmSync(
|
|
211
|
+
entryDirectory,
|
|
212
|
+
{
|
|
213
|
+
recursive: true,
|
|
214
|
+
force: true,
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log(
|
|
220
|
+
`[BCP Build] Guards: ${guardedRoutes.length} protected route(s) -> ${path.relative(rootDirectory, outputFile).replace(/\\/g, "/")}`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function createGuardEntry(
|
|
225
|
+
guardedRoutes: Array<{
|
|
226
|
+
route: Route;
|
|
227
|
+
guardFiles: string[];
|
|
228
|
+
}>,
|
|
229
|
+
rootDirectory: string,
|
|
230
|
+
entryDirectory: string
|
|
231
|
+
): string {
|
|
232
|
+
const frameworkDirectory =
|
|
233
|
+
path.dirname(
|
|
234
|
+
fileURLToPath(
|
|
235
|
+
import.meta.url
|
|
236
|
+
)
|
|
237
|
+
);
|
|
238
|
+
const pageGuardRuntime =
|
|
239
|
+
path.resolve(
|
|
240
|
+
frameworkDirectory,
|
|
241
|
+
"../../server/src/page-guard.ts"
|
|
242
|
+
);
|
|
243
|
+
const requestContextRuntime =
|
|
244
|
+
path.resolve(
|
|
245
|
+
frameworkDirectory,
|
|
246
|
+
"../../server/src/request-context.ts"
|
|
247
|
+
);
|
|
248
|
+
const routerRuntime =
|
|
249
|
+
path.resolve(
|
|
250
|
+
frameworkDirectory,
|
|
251
|
+
"../../router/src/index.ts"
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const imports: string[] = [
|
|
255
|
+
`import { executePageGuardFunctions } from ${JSON.stringify(
|
|
256
|
+
toImportSpecifier(
|
|
257
|
+
entryDirectory,
|
|
258
|
+
pageGuardRuntime
|
|
259
|
+
)
|
|
260
|
+
)};`,
|
|
261
|
+
`import { applyResponseCookies, runWithRequestContext } from ${JSON.stringify(
|
|
262
|
+
toImportSpecifier(
|
|
263
|
+
entryDirectory,
|
|
264
|
+
requestContextRuntime
|
|
265
|
+
)
|
|
266
|
+
)};`,
|
|
267
|
+
`import { matchRoute } from ${JSON.stringify(
|
|
268
|
+
toImportSpecifier(
|
|
269
|
+
entryDirectory,
|
|
270
|
+
routerRuntime
|
|
271
|
+
)
|
|
272
|
+
)};`,
|
|
273
|
+
];
|
|
274
|
+
const guardNameByFile =
|
|
275
|
+
new Map<string, string>();
|
|
276
|
+
let guardIndex =
|
|
277
|
+
0;
|
|
278
|
+
|
|
279
|
+
for (const entry of guardedRoutes) {
|
|
280
|
+
for (const guardFile of entry.guardFiles) {
|
|
281
|
+
if (
|
|
282
|
+
guardNameByFile.has(
|
|
283
|
+
guardFile
|
|
284
|
+
)
|
|
285
|
+
) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const guardName =
|
|
290
|
+
`Guard${guardIndex++}`;
|
|
291
|
+
guardNameByFile.set(
|
|
292
|
+
guardFile,
|
|
293
|
+
guardName
|
|
294
|
+
);
|
|
295
|
+
imports.push(
|
|
296
|
+
`import { guard as ${guardName} } from ${JSON.stringify(
|
|
297
|
+
toImportSpecifier(
|
|
298
|
+
entryDirectory,
|
|
299
|
+
guardFile
|
|
300
|
+
)
|
|
301
|
+
)};`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const definitions =
|
|
307
|
+
guardedRoutes.map(
|
|
308
|
+
({
|
|
309
|
+
route,
|
|
310
|
+
guardFiles,
|
|
311
|
+
}) => {
|
|
312
|
+
const guards =
|
|
313
|
+
guardFiles.map(
|
|
314
|
+
(guardFile) => {
|
|
315
|
+
const guardName =
|
|
316
|
+
guardNameByFile.get(
|
|
317
|
+
guardFile
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
if (!guardName) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`BCP Framework: guard import was not generated for "${guardFile}".`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const label =
|
|
327
|
+
path.relative(
|
|
328
|
+
rootDirectory,
|
|
329
|
+
guardFile
|
|
330
|
+
)
|
|
331
|
+
.replace(
|
|
332
|
+
/\\/g,
|
|
333
|
+
"/"
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
return `{
|
|
337
|
+
filePath: ${JSON.stringify(label)},
|
|
338
|
+
guard: ${guardName}
|
|
339
|
+
}`;
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
return `{
|
|
344
|
+
pathname: ${JSON.stringify(route.pathname)},
|
|
345
|
+
segments: ${JSON.stringify(route.segments)},
|
|
346
|
+
guards: [${guards.join(", ")}]
|
|
347
|
+
}`;
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
return `${imports.join("\n")}
|
|
352
|
+
|
|
353
|
+
const definitions = [
|
|
354
|
+
${definitions.join(",\n ")}
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
const routePatterns =
|
|
358
|
+
definitions.map(
|
|
359
|
+
(definition) => ({
|
|
360
|
+
pathname:
|
|
361
|
+
definition.pathname,
|
|
362
|
+
segments:
|
|
363
|
+
definition.segments,
|
|
364
|
+
filePath:
|
|
365
|
+
definition.pathname,
|
|
366
|
+
layouts: []
|
|
367
|
+
})
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
const definitionByPathname =
|
|
371
|
+
new Map(
|
|
372
|
+
definitions.map(
|
|
373
|
+
(definition) => [
|
|
374
|
+
definition.pathname,
|
|
375
|
+
definition
|
|
376
|
+
]
|
|
377
|
+
)
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
export const guardedPathnames =
|
|
381
|
+
definitions.map(
|
|
382
|
+
(definition) =>
|
|
383
|
+
definition.pathname
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
export async function evaluateRouteGuards(input) {
|
|
387
|
+
const target =
|
|
388
|
+
new URL(
|
|
389
|
+
input.url
|
|
390
|
+
);
|
|
391
|
+
const match =
|
|
392
|
+
matchRoute(
|
|
393
|
+
routePatterns,
|
|
394
|
+
target.pathname
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
if (!match) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const definition =
|
|
402
|
+
definitionByPathname.get(
|
|
403
|
+
match.route.pathname
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
if (!definition) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const request =
|
|
411
|
+
new Request(
|
|
412
|
+
target,
|
|
413
|
+
{
|
|
414
|
+
method:
|
|
415
|
+
input.method ||
|
|
416
|
+
"GET",
|
|
417
|
+
headers:
|
|
418
|
+
new Headers(
|
|
419
|
+
input.headers ||
|
|
420
|
+
[]
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
return runWithRequestContext(
|
|
426
|
+
request,
|
|
427
|
+
async () => {
|
|
428
|
+
const execution =
|
|
429
|
+
await executePageGuardFunctions(
|
|
430
|
+
definition.guards,
|
|
431
|
+
match.params,
|
|
432
|
+
target
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
if (execution.response) {
|
|
436
|
+
return {
|
|
437
|
+
kind:
|
|
438
|
+
"response",
|
|
439
|
+
response:
|
|
440
|
+
applyResponseCookies(
|
|
441
|
+
execution.response
|
|
442
|
+
)
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const cookieCarrier =
|
|
447
|
+
applyResponseCookies(
|
|
448
|
+
new Response(
|
|
449
|
+
null,
|
|
450
|
+
{
|
|
451
|
+
status: 204
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
kind:
|
|
458
|
+
"allow",
|
|
459
|
+
data:
|
|
460
|
+
execution.data,
|
|
461
|
+
setCookies:
|
|
462
|
+
cookieCarrier.headers.getSetCookie()
|
|
463
|
+
};
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
remoteAddress:
|
|
467
|
+
input.remoteAddress ||
|
|
468
|
+
null
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function toImportSpecifier(
|
|
476
|
+
fromDirectory: string,
|
|
477
|
+
filePath: string
|
|
478
|
+
): string {
|
|
479
|
+
let relative =
|
|
480
|
+
path.relative(
|
|
481
|
+
fromDirectory,
|
|
482
|
+
filePath
|
|
483
|
+
)
|
|
484
|
+
.replace(
|
|
485
|
+
/\\/g,
|
|
486
|
+
"/"
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
if (
|
|
490
|
+
!relative.startsWith(".")
|
|
491
|
+
) {
|
|
492
|
+
relative =
|
|
493
|
+
`./${relative}`;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return relative;
|
|
497
|
+
}
|
|
@@ -16,6 +16,10 @@ import {
|
|
|
16
16
|
findProjectMiddlewareFile,
|
|
17
17
|
} from "../../server/src/middleware-loader.js";
|
|
18
18
|
|
|
19
|
+
import {
|
|
20
|
+
routeHasPageGuard,
|
|
21
|
+
} from "../../server/src/page-guard.js";
|
|
22
|
+
|
|
19
23
|
import type {
|
|
20
24
|
ApiRoute,
|
|
21
25
|
Route,
|
|
@@ -29,6 +33,10 @@ import type {
|
|
|
29
33
|
PartialHydrationBuildManifest,
|
|
30
34
|
} from "./partial-hydration.js";
|
|
31
35
|
|
|
36
|
+
import {
|
|
37
|
+
buildProductionGuards,
|
|
38
|
+
} from "./server-production-guards.js";
|
|
39
|
+
|
|
32
40
|
import {
|
|
33
41
|
buildProductionServer as buildBaseProductionServer,
|
|
34
42
|
type ProductionServerBuildResult,
|
|
@@ -73,7 +81,8 @@ export async function buildProductionServer(
|
|
|
73
81
|
writeCacheManifest(
|
|
74
82
|
pageRoutes,
|
|
75
83
|
apiRoutes,
|
|
76
|
-
serverDirectory
|
|
84
|
+
serverDirectory,
|
|
85
|
+
rootDirectory
|
|
77
86
|
);
|
|
78
87
|
|
|
79
88
|
await buildProductionMiddleware(
|
|
@@ -81,6 +90,12 @@ export async function buildProductionServer(
|
|
|
81
90
|
serverDirectory
|
|
82
91
|
);
|
|
83
92
|
|
|
93
|
+
await buildProductionGuards(
|
|
94
|
+
pageRoutes,
|
|
95
|
+
rootDirectory,
|
|
96
|
+
serverDirectory
|
|
97
|
+
);
|
|
98
|
+
|
|
84
99
|
return {
|
|
85
100
|
...baseResult,
|
|
86
101
|
size:
|
|
@@ -174,11 +189,6 @@ async function bundleServerCacheRuntime(
|
|
|
174
189
|
serverFile
|
|
175
190
|
);
|
|
176
191
|
|
|
177
|
-
/*
|
|
178
|
-
* The base server build may have emitted an external map. The final
|
|
179
|
-
* cache-runtime rebundle uses an inline map so a stale server.mjs.map
|
|
180
|
-
* must not be shipped next to the rewritten bundle.
|
|
181
|
-
*/
|
|
182
192
|
fs.rmSync(
|
|
183
193
|
`${serverFile}.map`,
|
|
184
194
|
{
|
|
@@ -204,14 +214,29 @@ async function bundleServerCacheRuntime(
|
|
|
204
214
|
function writeCacheManifest(
|
|
205
215
|
pageRoutes: Route[],
|
|
206
216
|
apiRoutes: ApiRoute[],
|
|
207
|
-
serverDirectory: string
|
|
217
|
+
serverDirectory: string,
|
|
218
|
+
rootDirectory: string
|
|
208
219
|
): void {
|
|
220
|
+
const appDirectory =
|
|
221
|
+
path.join(
|
|
222
|
+
rootDirectory,
|
|
223
|
+
"app"
|
|
224
|
+
);
|
|
225
|
+
const cacheablePages =
|
|
226
|
+
pageRoutes.filter(
|
|
227
|
+
(route) =>
|
|
228
|
+
!routeHasPageGuard(
|
|
229
|
+
appDirectory,
|
|
230
|
+
route.filePath
|
|
231
|
+
)
|
|
232
|
+
);
|
|
233
|
+
|
|
209
234
|
const manifest:
|
|
210
235
|
CacheManifest = {
|
|
211
236
|
version: 1,
|
|
212
237
|
pages:
|
|
213
238
|
collectCacheRoutes(
|
|
214
|
-
|
|
239
|
+
cacheablePages
|
|
215
240
|
),
|
|
216
241
|
apiRoutes:
|
|
217
242
|
collectCacheRoutes(
|