@nifrajs/web 3.5.0 → 4.0.2
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/dist/backend-only.d.ts +15 -0
- package/dist/backend-only.d.ts.map +1 -0
- package/dist/backend-only.js +16 -0
- package/dist/backend-only.js.map +1 -0
- package/dist/boundary.d.ts +5 -0
- package/dist/boundary.d.ts.map +1 -1
- package/dist/boundary.js +11 -2
- package/dist/boundary.js.map +1 -1
- package/dist/build-plan.d.ts +18 -6
- package/dist/build-plan.d.ts.map +1 -1
- package/dist/build-plan.js +82 -34
- package/dist/build-plan.js.map +1 -1
- package/dist/build-vite.d.ts +4 -0
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +68 -13
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +57 -23
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +356 -85
- package/dist/build.js.map +1 -1
- package/dist/bun-dev-entry.d.ts +1 -1
- package/dist/bun-dev-entry.js +1 -1
- package/dist/cdn.d.ts +74 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/cdn.js +132 -0
- package/dist/cdn.js.map +1 -0
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +136 -35
- package/dist/client.js.map +1 -1
- package/dist/conformance.d.ts +4 -3
- package/dist/conformance.d.ts.map +1 -1
- package/dist/conformance.js +12 -3
- package/dist/conformance.js.map +1 -1
- package/dist/csp.d.ts +93 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +122 -0
- package/dist/csp.js.map +1 -0
- package/dist/dev-client.d.ts +76 -0
- package/dist/dev-client.d.ts.map +1 -0
- package/dist/dev-client.js +280 -0
- package/dist/dev-client.js.map +1 -0
- package/dist/dev-diagnostics.d.ts +26 -2
- package/dist/dev-diagnostics.d.ts.map +1 -1
- package/dist/dev-diagnostics.js +38 -7
- package/dist/dev-diagnostics.js.map +1 -1
- package/dist/dev-error.d.ts +4 -13
- package/dist/dev-error.d.ts.map +1 -1
- package/dist/dev-error.js +46 -4
- package/dist/dev-error.js.map +1 -1
- package/dist/dev-feed.d.ts +298 -0
- package/dist/dev-feed.d.ts.map +1 -0
- package/dist/dev-feed.js +797 -0
- package/dist/dev-feed.js.map +1 -0
- package/dist/dev-indicator.d.ts +38 -0
- package/dist/dev-indicator.d.ts.map +1 -0
- package/dist/dev-indicator.js +93 -0
- package/dist/dev-indicator.js.map +1 -0
- package/dist/dev-session.d.ts +77 -0
- package/dist/dev-session.d.ts.map +1 -0
- package/dist/dev-session.js +580 -0
- package/dist/dev-session.js.map +1 -0
- package/dist/dev-sourcemap.d.ts +27 -0
- package/dist/dev-sourcemap.d.ts.map +1 -0
- package/dist/dev-sourcemap.js +235 -0
- package/dist/dev-sourcemap.js.map +1 -0
- package/dist/dev-ssr-graph.d.ts.map +1 -1
- package/dist/dev-ssr-graph.js +13 -5
- package/dist/dev-ssr-graph.js.map +1 -1
- package/dist/dev.d.ts +15 -2
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +69 -44
- package/dist/dev.js.map +1 -1
- package/dist/diagnostic-catalog.d.ts +35 -0
- package/dist/diagnostic-catalog.d.ts.map +1 -0
- package/dist/diagnostic-catalog.js +203 -0
- package/dist/diagnostic-catalog.js.map +1 -0
- package/dist/diagnostic-prompt.d.ts +49 -0
- package/dist/diagnostic-prompt.d.ts.map +1 -0
- package/dist/diagnostic-prompt.js +303 -0
- package/dist/diagnostic-prompt.js.map +1 -0
- package/dist/diagnostic.d.ts +17 -23
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +31 -39
- package/dist/diagnostic.js.map +1 -1
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +4 -1
- package/dist/draft.js.map +1 -1
- package/dist/fn.d.ts +5 -0
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +13 -3
- package/dist/fn.js.map +1 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +4 -2
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +13 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -12
- package/dist/index.js.map +1 -1
- package/dist/internal/cdn/cloudflare.d.ts +48 -0
- package/dist/internal/cdn/cloudflare.d.ts.map +1 -0
- package/dist/internal/cdn/cloudflare.js +94 -0
- package/dist/internal/cdn/cloudflare.js.map +1 -0
- package/dist/internal/cdn/core.d.ts +91 -0
- package/dist/internal/cdn/core.d.ts.map +1 -0
- package/dist/internal/cdn/core.js +220 -0
- package/dist/internal/cdn/core.js.map +1 -0
- package/dist/internal/cdn/fastly.d.ts +17 -0
- package/dist/internal/cdn/fastly.d.ts.map +1 -0
- package/dist/internal/cdn/fastly.js +43 -0
- package/dist/internal/cdn/fastly.js.map +1 -0
- package/dist/internal/cdn/vercel.d.ts +31 -0
- package/dist/internal/cdn/vercel.d.ts.map +1 -0
- package/dist/internal/cdn/vercel.js +56 -0
- package/dist/internal/cdn/vercel.js.map +1 -0
- package/dist/internal/code-unit-order.d.ts +4 -0
- package/dist/internal/code-unit-order.d.ts.map +1 -0
- package/dist/internal/code-unit-order.js +6 -0
- package/dist/internal/code-unit-order.js.map +1 -0
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +141 -25
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/dev-csp.d.ts +24 -0
- package/dist/internal/dev-csp.d.ts.map +1 -0
- package/dist/internal/dev-csp.js +160 -0
- package/dist/internal/dev-csp.js.map +1 -0
- package/dist/internal/dev-reserved.d.ts +25 -0
- package/dist/internal/dev-reserved.d.ts.map +1 -0
- package/dist/internal/dev-reserved.js +39 -0
- package/dist/internal/dev-reserved.js.map +1 -0
- package/dist/internal/html-spans.d.ts +10 -0
- package/dist/internal/html-spans.d.ts.map +1 -0
- package/dist/internal/html-spans.js +46 -0
- package/dist/internal/html-spans.js.map +1 -0
- package/dist/internal/loading-runtime.d.ts +44 -0
- package/dist/internal/loading-runtime.d.ts.map +1 -0
- package/dist/internal/loading-runtime.js +206 -0
- package/dist/internal/loading-runtime.js.map +1 -0
- package/dist/internal/matches-runtime.d.ts +8 -0
- package/dist/internal/matches-runtime.d.ts.map +1 -0
- package/dist/internal/matches-runtime.js +61 -0
- package/dist/internal/matches-runtime.js.map +1 -0
- package/dist/internal/mount-shadow.d.ts +36 -0
- package/dist/internal/mount-shadow.d.ts.map +1 -0
- package/dist/internal/mount-shadow.js +65 -0
- package/dist/internal/mount-shadow.js.map +1 -0
- package/dist/internal/node-builtins.d.ts +3 -0
- package/dist/internal/node-builtins.d.ts.map +1 -0
- package/dist/internal/node-builtins.js +79 -0
- package/dist/internal/node-builtins.js.map +1 -0
- package/dist/internal/output-guard.d.ts +65 -0
- package/dist/internal/output-guard.d.ts.map +1 -0
- package/dist/internal/output-guard.js +630 -0
- package/dist/internal/output-guard.js.map +1 -0
- package/dist/internal/page-execution.d.ts +20 -8
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +571 -105
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/parity.d.ts +21 -0
- package/dist/internal/parity.d.ts.map +1 -1
- package/dist/internal/parity.js +104 -22
- package/dist/internal/parity.js.map +1 -1
- package/dist/internal/private-env.d.ts +12 -0
- package/dist/internal/private-env.d.ts.map +1 -0
- package/dist/internal/private-env.js +335 -0
- package/dist/internal/private-env.js.map +1 -0
- package/dist/internal/public-url.d.ts +3 -0
- package/dist/internal/public-url.d.ts.map +1 -0
- package/dist/internal/public-url.js +18 -0
- package/dist/internal/public-url.js.map +1 -0
- package/dist/internal/render-document.d.ts +25 -19
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +77 -61
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/response-controls.d.ts +75 -0
- package/dist/internal/response-controls.d.ts.map +1 -0
- package/dist/internal/response-controls.js +256 -0
- package/dist/internal/response-controls.js.map +1 -0
- package/dist/internal/runtime-contract.d.ts +11 -16
- package/dist/internal/runtime-contract.d.ts.map +1 -1
- package/dist/internal/runtime-contract.js.map +1 -1
- package/dist/internal/secret-scan.d.ts +104 -0
- package/dist/internal/secret-scan.d.ts.map +1 -0
- package/dist/internal/secret-scan.js +503 -0
- package/dist/internal/secret-scan.js.map +1 -0
- package/dist/internal/server-boundary.d.ts +6 -29
- package/dist/internal/server-boundary.d.ts.map +1 -1
- package/dist/internal/server-boundary.js +6 -66
- package/dist/internal/server-boundary.js.map +1 -1
- package/dist/internal/source-map.d.ts +47 -0
- package/dist/internal/source-map.d.ts.map +1 -0
- package/dist/internal/source-map.js +225 -0
- package/dist/internal/source-map.js.map +1 -0
- package/dist/internal/target-compat.d.ts +27 -0
- package/dist/internal/target-compat.d.ts.map +1 -0
- package/dist/internal/target-compat.js +77 -0
- package/dist/internal/target-compat.js.map +1 -0
- package/dist/internal/timing-safe-equal.d.ts +6 -0
- package/dist/internal/timing-safe-equal.d.ts.map +1 -1
- package/dist/internal/timing-safe-equal.js +10 -0
- package/dist/internal/timing-safe-equal.js.map +1 -1
- package/dist/internal/vitals-runtime.d.ts +18 -0
- package/dist/internal/vitals-runtime.d.ts.map +1 -0
- package/dist/internal/vitals-runtime.js +38 -0
- package/dist/internal/vitals-runtime.js.map +1 -0
- package/dist/internal/web-app.d.ts +38 -9
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +199 -59
- package/dist/internal/web-app.js.map +1 -1
- package/dist/internal/zone-graph.d.ts +89 -0
- package/dist/internal/zone-graph.d.ts.map +1 -0
- package/dist/internal/zone-graph.js +492 -0
- package/dist/internal/zone-graph.js.map +1 -0
- package/dist/isr.d.ts +83 -6
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +263 -36
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +31 -6
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +220 -13
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +394 -9
- package/dist/manifest.js.map +1 -1
- package/dist/module-graph.d.ts +26 -5
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +17 -7
- package/dist/module-graph.js.map +1 -1
- package/dist/navigation.d.ts +17 -3
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +11 -5
- package/dist/navigation.js.map +1 -1
- package/dist/nonce.d.ts +47 -0
- package/dist/nonce.d.ts.map +1 -0
- package/dist/nonce.js +63 -0
- package/dist/nonce.js.map +1 -0
- package/dist/plugins/css-modules.d.ts.map +1 -1
- package/dist/plugins/css-modules.js +4 -3
- package/dist/plugins/css-modules.js.map +1 -1
- package/dist/plugins/kit.d.ts +9 -0
- package/dist/plugins/kit.d.ts.map +1 -1
- package/dist/plugins/kit.js +18 -0
- package/dist/plugins/kit.js.map +1 -1
- package/dist/plugins/svg.d.ts +22 -3
- package/dist/plugins/svg.d.ts.map +1 -1
- package/dist/plugins/svg.js +112 -1
- package/dist/plugins/svg.js.map +1 -1
- package/dist/plugins/vite-leak-guard.d.ts +107 -3
- package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
- package/dist/plugins/vite-leak-guard.js +378 -17
- package/dist/plugins/vite-leak-guard.js.map +1 -1
- package/dist/plugins/vite-zone-guard.d.ts +61 -0
- package/dist/plugins/vite-zone-guard.d.ts.map +1 -0
- package/dist/plugins/vite-zone-guard.js +208 -0
- package/dist/plugins/vite-zone-guard.js.map +1 -0
- package/dist/plugins/zone-guard.d.ts +27 -0
- package/dist/plugins/zone-guard.d.ts.map +1 -0
- package/dist/plugins/zone-guard.js +80 -0
- package/dist/plugins/zone-guard.js.map +1 -0
- package/dist/prerender.d.ts +10 -0
- package/dist/prerender.d.ts.map +1 -1
- package/dist/prerender.js +35 -3
- package/dist/prerender.js.map +1 -1
- package/dist/public-dir.d.ts +9 -0
- package/dist/public-dir.d.ts.map +1 -1
- package/dist/public-dir.js +67 -9
- package/dist/public-dir.js.map +1 -1
- package/dist/pwa-manifest.d.ts +68 -0
- package/dist/pwa-manifest.d.ts.map +1 -0
- package/dist/pwa-manifest.js +178 -0
- package/dist/pwa-manifest.js.map +1 -0
- package/dist/query.js +4 -4
- package/dist/query.js.map +1 -1
- package/dist/render-seam.d.ts +42 -2
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +6 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/route-manifest.d.ts +3 -0
- package/dist/route-manifest.d.ts.map +1 -1
- package/dist/route-manifest.js +26 -3
- package/dist/route-manifest.js.map +1 -1
- package/dist/route-types.d.ts +24 -0
- package/dist/route-types.d.ts.map +1 -0
- package/dist/route-types.js +195 -0
- package/dist/route-types.js.map +1 -0
- package/dist/router.d.ts +28 -6
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +129 -40
- package/dist/router.js.map +1 -1
- package/dist/vitals.d.ts +33 -0
- package/dist/vitals.d.ts.map +1 -0
- package/dist/vitals.js +16 -0
- package/dist/vitals.js.map +1 -0
- package/dist/vite.d.ts +16 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +248 -43
- package/dist/vite.js.map +1 -1
- package/dist/zones.d.ts +70 -0
- package/dist/zones.d.ts.map +1 -0
- package/dist/zones.js +365 -0
- package/dist/zones.js.map +1 -0
- package/package.json +67 -14
- package/src/backend-only.ts +16 -0
- package/src/boundary.ts +18 -5
- package/src/build-plan.ts +90 -35
- package/src/build-vite.ts +77 -11
- package/src/build.ts +466 -96
- package/src/bun-dev-entry.ts +1 -1
- package/src/cdn.ts +216 -0
- package/src/client.ts +135 -36
- package/src/conformance.ts +16 -3
- package/src/csp.ts +194 -0
- package/src/dev-client.ts +349 -0
- package/src/dev-diagnostics.ts +60 -7
- package/src/dev-error.ts +49 -5
- package/src/dev-feed.ts +1140 -0
- package/src/dev-indicator.ts +113 -0
- package/src/dev-session.ts +729 -0
- package/src/dev-sourcemap.ts +281 -0
- package/src/dev-ssr-graph.ts +16 -5
- package/src/dev.ts +86 -43
- package/src/diagnostic-catalog.ts +243 -0
- package/src/diagnostic-prompt.ts +355 -0
- package/src/diagnostic.ts +63 -66
- package/src/draft.ts +4 -1
- package/src/fn.ts +19 -3
- package/src/fs.ts +13 -2
- package/src/index.ts +42 -10
- package/src/internal/cdn/cloudflare.ts +146 -0
- package/src/internal/cdn/core.ts +316 -0
- package/src/internal/cdn/fastly.ts +62 -0
- package/src/internal/cdn/vercel.ts +95 -0
- package/src/internal/code-unit-order.ts +5 -0
- package/src/internal/codegen.ts +161 -24
- package/src/internal/dev-csp.ts +176 -0
- package/src/internal/dev-reserved.ts +52 -0
- package/src/internal/html-spans.ts +50 -0
- package/src/internal/loading-runtime.ts +244 -0
- package/src/internal/matches-runtime.ts +62 -0
- package/src/internal/mount-shadow.ts +88 -0
- package/src/internal/node-builtins.ts +79 -0
- package/src/internal/output-guard.ts +701 -0
- package/src/internal/page-execution.ts +795 -185
- package/src/internal/parity.ts +150 -36
- package/src/internal/private-env.ts +334 -0
- package/src/internal/public-url.ts +22 -0
- package/src/internal/render-document.ts +114 -67
- package/src/internal/response-controls.ts +284 -0
- package/src/internal/runtime-contract.ts +10 -15
- package/src/internal/secret-scan.ts +638 -0
- package/src/internal/server-boundary.ts +6 -69
- package/src/internal/source-map.ts +256 -0
- package/src/internal/target-compat.ts +92 -0
- package/src/internal/timing-safe-equal.ts +13 -0
- package/src/internal/vitals-runtime.ts +53 -0
- package/src/internal/web-app.ts +262 -84
- package/src/internal/zone-graph.ts +593 -0
- package/src/isr.ts +325 -37
- package/src/llms-txt.ts +28 -4
- package/src/manifest.ts +682 -16
- package/src/module-graph.ts +53 -9
- package/src/navigation.ts +29 -6
- package/src/nonce.ts +104 -0
- package/src/plugins/css-modules.ts +5 -3
- package/src/plugins/kit.ts +24 -0
- package/src/plugins/svg.ts +135 -4
- package/src/plugins/vite-leak-guard.ts +529 -16
- package/src/plugins/vite-zone-guard.ts +263 -0
- package/src/plugins/zone-guard.ts +119 -0
- package/src/prerender.ts +49 -3
- package/src/public-dir.ts +69 -8
- package/src/pwa-manifest.ts +226 -0
- package/src/query.ts +4 -4
- package/src/render-seam.ts +46 -1
- package/src/route-manifest.ts +15 -3
- package/src/route-types.ts +264 -0
- package/src/router.ts +163 -63
- package/src/vitals.ts +44 -0
- package/src/vite.ts +257 -42
- package/src/zones.ts +430 -0
- package/dist/plugins/vite-server-only.d.ts +0 -49
- package/dist/plugins/vite-server-only.d.ts.map +0 -1
- package/dist/plugins/vite-server-only.js +0 -57
- package/dist/plugins/vite-server-only.js.map +0 -1
- package/dist/server-only.d.ts +0 -21
- package/dist/server-only.d.ts.map +0 -1
- package/dist/server-only.js +0 -25
- package/dist/server-only.js.map +0 -1
- package/src/plugins/vite-server-only.ts +0 -75
- package/src/server-only.ts +0 -25
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zone guard for Vite's dev server. A dev server serves source by URL, so the guard has to hold
|
|
3
|
+
* before any byte is sent, whatever the URL form:
|
|
4
|
+
*
|
|
5
|
+
* - a middleware ahead of Vite's own answers 403 for any request that maps to a file the browser may
|
|
6
|
+
* not have: a direct path, `/@fs/`, `?raw`, `?url`, `?import`, a `.map`, a pre-bundled dependency;
|
|
7
|
+
* - `resolveId` refuses a browser import of backend code at the importer, so the overlay names the line
|
|
8
|
+
* instead of the browser hitting a bare 403;
|
|
9
|
+
* - `transform` refuses whatever reaches the client environment by another route.
|
|
10
|
+
*
|
|
11
|
+
* The SSR environment loads the same files legitimately and is never checked here.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
14
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
15
|
+
import { privateEnvDenial } from "../internal/private-env.js";
|
|
16
|
+
import { browserDenial, createZoneClassifier, SERVER_PACKAGES, specifierPackage, } from "../zones.js";
|
|
17
|
+
/** Zones whose code runs in a browser, so may read only public environment variables. */
|
|
18
|
+
const BROWSER_CODE = new Set(["route-frontend", "frontend", "shared"]);
|
|
19
|
+
const stripQuery = (id) => id.replace(/[?#].*$/, "");
|
|
20
|
+
/** Query forms that hand a module's source, or a URL to it, to the browser untransformed. */
|
|
21
|
+
const RAW_QUERY = /(?:^|&)(?:raw|url|inline)(?:[&=]|$)/;
|
|
22
|
+
const isFile = (path) => {
|
|
23
|
+
try {
|
|
24
|
+
return statSync(path).isFile();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export function viteZoneGuard(options) {
|
|
31
|
+
const classifier = createZoneClassifier(options);
|
|
32
|
+
let root = resolve(options.appRoot);
|
|
33
|
+
const display = (file) => {
|
|
34
|
+
const rel = relative(classifier.appRoot, file).replaceAll("\\", "/");
|
|
35
|
+
return rel.startsWith("..") || isAbsolute(rel) ? file : rel;
|
|
36
|
+
};
|
|
37
|
+
const isClient = (context, ssr) => ssr !== true && (context.environment?.name ?? "client") === "client";
|
|
38
|
+
/** What a pre-bundled dependency file was built from, per Vite's `_metadata.json`. */
|
|
39
|
+
const depsSources = new Map();
|
|
40
|
+
const depSource = (file) => {
|
|
41
|
+
const dir = dirname(file);
|
|
42
|
+
const metadata = join(dir, "_metadata.json");
|
|
43
|
+
let mtime;
|
|
44
|
+
try {
|
|
45
|
+
mtime = statSync(metadata).mtimeMs;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
let cached = depsSources.get(dir);
|
|
51
|
+
if (cached === undefined || cached.mtime !== mtime) {
|
|
52
|
+
const sources = new Map();
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(readFileSync(metadata, "utf8"));
|
|
55
|
+
for (const dep of Object.values(parsed.optimized ?? {})) {
|
|
56
|
+
if (dep.src !== undefined && dep.file !== undefined) {
|
|
57
|
+
sources.set(resolve(dir, dep.file), resolve(dir, dep.src));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// An unreadable metadata file maps nothing; the request then classifies as the cache file.
|
|
63
|
+
}
|
|
64
|
+
cached = { mtime, sources };
|
|
65
|
+
depsSources.set(dir, cached);
|
|
66
|
+
}
|
|
67
|
+
return cached.sources.get(file);
|
|
68
|
+
};
|
|
69
|
+
/** The file a request URL serves, or `undefined` when it names none (an app route, a Vite internal). */
|
|
70
|
+
const fileForUrl = (url) => {
|
|
71
|
+
let path;
|
|
72
|
+
try {
|
|
73
|
+
path = decodeURIComponent(stripQuery(url));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
let file;
|
|
79
|
+
if (path.startsWith("/@fs/"))
|
|
80
|
+
file = path.slice("/@fs".length).replace(/^\/([a-zA-Z]:)/, "$1");
|
|
81
|
+
else if (path.startsWith("/@id/")) {
|
|
82
|
+
const id = path.slice("/@id/".length);
|
|
83
|
+
if (!isAbsolute(id))
|
|
84
|
+
return undefined;
|
|
85
|
+
file = id;
|
|
86
|
+
}
|
|
87
|
+
else if (path.startsWith("/@"))
|
|
88
|
+
return undefined;
|
|
89
|
+
else
|
|
90
|
+
file = join(root, path);
|
|
91
|
+
for (const candidate of [file, file.replace(/\.map$/, "")]) {
|
|
92
|
+
// One spelling per file: Vite names it `C:/app/x.ts` on Windows, `join` names it `C:\app\x.ts`.
|
|
93
|
+
if (isFile(candidate))
|
|
94
|
+
return resolve(candidate);
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
};
|
|
98
|
+
const middleware = (req, res, next) => {
|
|
99
|
+
const url = req.url ?? "/";
|
|
100
|
+
const file = fileForUrl(url);
|
|
101
|
+
if (file === undefined)
|
|
102
|
+
return next();
|
|
103
|
+
const source = file.replaceAll("\\", "/").includes("/node_modules/.vite/")
|
|
104
|
+
? (depSource(file) ?? file)
|
|
105
|
+
: file;
|
|
106
|
+
const classification = classifier.classify(source);
|
|
107
|
+
let reason = browserDenial(classification);
|
|
108
|
+
const query = url.includes("?") ? url.slice(url.indexOf("?") + 1) : "";
|
|
109
|
+
if (reason === undefined && classification.zone === "fn" && RAW_QUERY.test(query)) {
|
|
110
|
+
reason = "a server-function module reaches the browser only as its generated stub";
|
|
111
|
+
}
|
|
112
|
+
if (reason === undefined)
|
|
113
|
+
return next();
|
|
114
|
+
res.statusCode = 403;
|
|
115
|
+
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
116
|
+
res.setHeader("cache-control", "no-store");
|
|
117
|
+
res.end(`[nifra] ${display(source)} may not reach the browser: ${reason}\n`);
|
|
118
|
+
};
|
|
119
|
+
const refusals = new Map();
|
|
120
|
+
const refuse = (context, file, reason, importer) => {
|
|
121
|
+
const message = `[nifra/web] ${display(file)} may not reach the browser${importer === undefined ? "" : ` (imported by ${display(importer)})`}: ${reason}`;
|
|
122
|
+
refusals.set(resolve(importer ?? file), message);
|
|
123
|
+
return context.error(new Error(message));
|
|
124
|
+
};
|
|
125
|
+
/** A bare specifier's package, classified through its manifest so a workspace link is first-party. */
|
|
126
|
+
const packageClassification = (name, importer) => {
|
|
127
|
+
let dir = dirname(importer);
|
|
128
|
+
for (;;) {
|
|
129
|
+
const manifest = join(dir, "node_modules", name, "package.json");
|
|
130
|
+
if (existsSync(manifest))
|
|
131
|
+
return classifier.classify(manifest);
|
|
132
|
+
const parent = dirname(dir);
|
|
133
|
+
if (parent === dir)
|
|
134
|
+
return { zone: "library", packageName: name };
|
|
135
|
+
dir = parent;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
name: "nifra:zone-guard",
|
|
140
|
+
enforce: "pre",
|
|
141
|
+
refusalFor(url) {
|
|
142
|
+
const file = fileForUrl(url);
|
|
143
|
+
return file === undefined ? undefined : refusals.get(file);
|
|
144
|
+
},
|
|
145
|
+
fileForUrl,
|
|
146
|
+
// A pre-bundled copy would serve a server package under `.vite/deps/` instead of its own path.
|
|
147
|
+
config: () => ({ optimizeDeps: { exclude: [...SERVER_PACKAGES] } }),
|
|
148
|
+
configResolved(config) {
|
|
149
|
+
root = config.root;
|
|
150
|
+
},
|
|
151
|
+
configureServer(server) {
|
|
152
|
+
// Registered directly (not in a returned post hook), so it runs ahead of Vite's own middlewares.
|
|
153
|
+
server.middlewares.use(middleware);
|
|
154
|
+
},
|
|
155
|
+
async resolveId(source, importer, resolveOptions) {
|
|
156
|
+
if (importer === undefined || resolveOptions?.scan === true)
|
|
157
|
+
return null;
|
|
158
|
+
if (!isClient(this, resolveOptions?.ssr))
|
|
159
|
+
return null;
|
|
160
|
+
const importerFile = stripQuery(importer);
|
|
161
|
+
if (!isAbsolute(importerFile))
|
|
162
|
+
return null;
|
|
163
|
+
if (source.startsWith(".")) {
|
|
164
|
+
const target = resolve(dirname(importerFile), stripQuery(source));
|
|
165
|
+
if (isFile(target)) {
|
|
166
|
+
const reason = browserDenial(classifier.classify(target));
|
|
167
|
+
if (reason !== undefined)
|
|
168
|
+
refuse(this, target, reason, importerFile);
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
const name = specifierPackage(source);
|
|
173
|
+
if (name !== undefined) {
|
|
174
|
+
const reason = browserDenial(packageClassification(name, importerFile), source);
|
|
175
|
+
if (reason !== undefined)
|
|
176
|
+
refuse(this, source, reason, importerFile);
|
|
177
|
+
}
|
|
178
|
+
// An alias, a tsconfig path or a package export: what the specifier resolves to decides.
|
|
179
|
+
const resolved = await this.resolve(source, importer, { ...resolveOptions, skipSelf: true });
|
|
180
|
+
const target = resolved === null || resolved.external ? undefined : stripQuery(resolved.id);
|
|
181
|
+
if (target !== undefined && isAbsolute(target) && isFile(target)) {
|
|
182
|
+
const reason = browserDenial(classifier.classify(target), source);
|
|
183
|
+
if (reason !== undefined)
|
|
184
|
+
refuse(this, target, reason, importerFile);
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
},
|
|
188
|
+
transform(code, id, transformOptions) {
|
|
189
|
+
if (!isClient(this, transformOptions?.ssr))
|
|
190
|
+
return null;
|
|
191
|
+
const file = stripQuery(id);
|
|
192
|
+
if (!isAbsolute(file) || !isFile(file))
|
|
193
|
+
return null;
|
|
194
|
+
// A fresh transform re-resolves every import, so an earlier refusal is stale until it recurs.
|
|
195
|
+
refusals.delete(resolve(file));
|
|
196
|
+
const classification = classifier.classify(file);
|
|
197
|
+
// A `pre` transform sees the file as written, so the env check reads the source the author wrote.
|
|
198
|
+
const reason = browserDenial(classification) ??
|
|
199
|
+
(BROWSER_CODE.has(classification.zone) && id === file
|
|
200
|
+
? privateEnvDenial(file, code, options.publicEnvPrefix ?? "PUBLIC_")
|
|
201
|
+
: undefined);
|
|
202
|
+
if (reason !== undefined)
|
|
203
|
+
refuse(this, file, reason);
|
|
204
|
+
return null;
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=vite-zone-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-zone-guard.js","sourceRoot":"","sources":["../../src/plugins/vite-zone-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zone guard for Bun's bundler: every file the browser bundle loads is classified, and one that may
|
|
3
|
+
* not reach a browser stops the build (or the dev request) before its code is bundled.
|
|
4
|
+
*
|
|
5
|
+
* The client build runs it in `record` mode and reports every refusal together with its import chain,
|
|
6
|
+
* read from the finished graph; the dev server runs it in `throw` mode, where there is no finished graph
|
|
7
|
+
* and the refusal must stop the response.
|
|
8
|
+
*/
|
|
9
|
+
import type { BunPlugin } from "bun";
|
|
10
|
+
import { type ZoneClassifier } from "../zones.js";
|
|
11
|
+
export interface ZoneGuardOptions {
|
|
12
|
+
/** The app root: the directory holding `routes/`, `frontend/`, `backend/` and `shared/`. */
|
|
13
|
+
readonly appRoot: string;
|
|
14
|
+
/** The routes directory (default `<appRoot>/routes`). */
|
|
15
|
+
readonly routesDir?: string;
|
|
16
|
+
/** The build's own generated entry modules. */
|
|
17
|
+
readonly generatedFiles?: readonly string[];
|
|
18
|
+
/** Collect refusals instead of throwing; the caller reports them with chains after the build. */
|
|
19
|
+
readonly onDenied?: (file: string, reason: string) => void;
|
|
20
|
+
/** Share a classifier with post-build verification. */
|
|
21
|
+
readonly classifier?: ZoneClassifier;
|
|
22
|
+
/** The public-env prefix browser code may read (default `"PUBLIC_"`). Checked here only in throw
|
|
23
|
+
* mode; a build checks the finished graph instead. */
|
|
24
|
+
readonly publicEnvPrefix?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function zoneGuardPlugin(options: ZoneGuardOptions): BunPlugin;
|
|
27
|
+
//# sourceMappingURL=zone-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-guard.d.ts","sourceRoot":"","sources":["../../src/plugins/zone-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAEpC,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,aAAa,CAAA;AAuBpB,MAAM,WAAW,gBAAgB;IAC/B,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C,iGAAiG;IACjG,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1D,uDAAuD;IACvD,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,CAAA;IACpC;0DACsD;IACtD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAKD,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA0DpE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zone guard for Bun's bundler: every file the browser bundle loads is classified, and one that may
|
|
3
|
+
* not reach a browser stops the build (or the dev request) before its code is bundled.
|
|
4
|
+
*
|
|
5
|
+
* The client build runs it in `record` mode and reports every refusal together with its import chain,
|
|
6
|
+
* read from the finished graph; the dev server runs it in `throw` mode, where there is no finished graph
|
|
7
|
+
* and the refusal must stop the response.
|
|
8
|
+
*/
|
|
9
|
+
import { statSync } from "node:fs";
|
|
10
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
11
|
+
import { privateEnvDenial } from "../internal/private-env.js";
|
|
12
|
+
import { BACKEND_ONLY_MARKER, browserDenial, createZoneClassifier, } from "../zones.js";
|
|
13
|
+
const SOURCE_EXTENSIONS = "[cm]?[jt]sx?|svelte|vue|mdx|astro|css|scss|sass|less|json|jsonc|toml|ya?ml|txt|md";
|
|
14
|
+
/** Source files Bun loads through a loader that tolerates a declining `onLoad`. Every other file
|
|
15
|
+
* (images, fonts, wasm, and a `.pem` or `.sql` imported `with { type: "text" }`) goes through
|
|
16
|
+
* `onResolve`: a file-loader asset panics Bun 1.4 on a declining `onLoad`. */
|
|
17
|
+
const SOURCE_FILE = new RegExp(`\\.(?:${SOURCE_EXTENSIONS})$`);
|
|
18
|
+
const ZONE_SUFFIXES = "frontend|backend|shared|fn";
|
|
19
|
+
/**
|
|
20
|
+
* A relative or absolute import whose last segment has an extension, other than a source file. A zone
|
|
21
|
+
* suffix is no extension: `"./calendar.shared"` imports `calendar.shared.ts`.
|
|
22
|
+
* Source files are excluded by the FILTER, not declined by the handler: on Bun's dev server a filter
|
|
23
|
+
* match alone changes how the import resolves, even when the handler declines (see
|
|
24
|
+
* `internal/dev-reserved.ts`), and a source file is the onLoad hook's to judge anyway.
|
|
25
|
+
*/
|
|
26
|
+
const NON_SOURCE_PATH = new RegExp(`^(?:\\.|\\/|[A-Za-z]:[\\\\/])(?![^?#]*\\.(?:${SOURCE_EXTENSIONS}|${ZONE_SUFFIXES})(?:[?#]|$)).*\\.[^./\\\\?#]+(?:[?#].*)?$`);
|
|
27
|
+
const isFile = (path) => statSync(path, { throwIfNoEntry: false })?.isFile() ?? false;
|
|
28
|
+
/** Zones whose code runs in a browser, so may read only public environment variables. */
|
|
29
|
+
const BROWSER_CODE = new Set(["route-frontend", "frontend", "shared"]);
|
|
30
|
+
export function zoneGuardPlugin(options) {
|
|
31
|
+
const classifier = options.classifier ?? createZoneClassifier(options);
|
|
32
|
+
const display = (file) => {
|
|
33
|
+
const rel = relative(classifier.appRoot, file).replaceAll("\\", "/");
|
|
34
|
+
return rel.startsWith("..") || isAbsolute(rel) ? file : rel;
|
|
35
|
+
};
|
|
36
|
+
const refuse = (file, reason, importer) => {
|
|
37
|
+
if (options.onDenied !== undefined) {
|
|
38
|
+
options.onDenied(file, reason);
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const via = importer === undefined || importer === "" ? "" : ` (imported by ${display(importer)})`;
|
|
42
|
+
throw new Error(`[nifra/web] ${display(file)} may not reach the browser${via}: ${reason}`);
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
name: "nifra-zone-guard",
|
|
46
|
+
setup(build) {
|
|
47
|
+
build.onLoad({ filter: SOURCE_FILE }, async (args) => {
|
|
48
|
+
if (args.namespace !== "file")
|
|
49
|
+
return undefined;
|
|
50
|
+
const classification = classifier.classify(args.path);
|
|
51
|
+
let reason = browserDenial(classification);
|
|
52
|
+
if (reason === undefined &&
|
|
53
|
+
options.onDenied === undefined &&
|
|
54
|
+
BROWSER_CODE.has(classification.zone)) {
|
|
55
|
+
const source = await Bun.file(args.path).text();
|
|
56
|
+
reason = privateEnvDenial(args.path, source, options.publicEnvPrefix ?? "PUBLIC_");
|
|
57
|
+
}
|
|
58
|
+
return reason === undefined ? undefined : refuse(args.path, reason);
|
|
59
|
+
});
|
|
60
|
+
build.onResolve({ filter: NON_SOURCE_PATH }, (args) => {
|
|
61
|
+
// An entry point (a dev server's HTML page) is the build's own choice, not an import.
|
|
62
|
+
if (args.importer === "")
|
|
63
|
+
return undefined;
|
|
64
|
+
const file = resolve(dirname(args.importer), args.path.replace(/[?#].*$/, ""));
|
|
65
|
+
// No such file means an extensionless import of a dotted source name (`./date.utils`): Bun resolves
|
|
66
|
+
// it to the source file, which onLoad judges. Judging the bare path would refuse a file never loaded.
|
|
67
|
+
if (!isFile(file))
|
|
68
|
+
return undefined;
|
|
69
|
+
const reason = browserDenial(classifier.classify(file));
|
|
70
|
+
return reason === undefined ? undefined : refuse(file, reason, args.importer);
|
|
71
|
+
});
|
|
72
|
+
// In record mode the finished graph reports these, with chains; only the dev server needs them here.
|
|
73
|
+
if (options.onDenied !== undefined)
|
|
74
|
+
return;
|
|
75
|
+
build.onResolve({ filter: /^(?:node|bun):/ }, (args) => refuse(args.path, "Node and Bun built-ins run on the server only; use them in a route's backend half or under backend/", args.importer));
|
|
76
|
+
build.onResolve({ filter: new RegExp(`^${BACKEND_ONLY_MARKER.replaceAll("/", "\\/")}$`) }, (args) => refuse(args.importer, `it imports "${BACKEND_ONLY_MARKER}", which marks it backend-only`));
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=zone-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-guard.js","sourceRoot":"","sources":["../../src/plugins/zone-guard.ts"],"names":[],"mappings":"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"}
|
package/dist/prerender.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SecretExemption } from "./internal/secret-scan.js";
|
|
1
2
|
import { type RouteEntry } from "./manifest.js";
|
|
2
3
|
/** Minimal app surface the driver needs - just a fetch handler (a built `createWebApp`). */
|
|
3
4
|
export interface PrerenderApp {
|
|
@@ -15,6 +16,15 @@ export interface PrerenderOptions {
|
|
|
15
16
|
/** Origin for the synthetic request URL. Default `http://localhost`. Only the path is meaningful;
|
|
16
17
|
* loaders see this as `request.url`, so set it if a loader builds absolute URLs from the origin. */
|
|
17
18
|
readonly origin?: string;
|
|
19
|
+
/** The secret scan every page and `_data.json` passes before it is written: what looks like a
|
|
20
|
+
* credential, or the value of a non-public build environment variable, fails the prerender. */
|
|
21
|
+
readonly secrets?: {
|
|
22
|
+
/** Default: the build environment (`Bun.env`, else `process.env`). */
|
|
23
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
24
|
+
/** Default `"PUBLIC_"`. */
|
|
25
|
+
readonly publicEnvPrefix?: string;
|
|
26
|
+
readonly exemptions?: readonly SecretExemption[];
|
|
27
|
+
};
|
|
18
28
|
}
|
|
19
29
|
export interface PrerenderEntry {
|
|
20
30
|
/** The route pattern that was prerendered (e.g. `/`, `/about`). */
|
package/dist/prerender.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"AAYA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAGjE,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B;4EACwE;IACxE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAA;IAC1B;oGACgG;IAChG,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,kGAAkG;IAClG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;wGACoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"prerender.d.ts","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"AAYA,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAA;AACnG,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAGjE,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B;4EACwE;IACxE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAA;IAC1B;oGACgG;IAChG,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,kGAAkG;IAClG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;wGACoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB;mGAC+F;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,sEAAsE;QACtE,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;QAC3D,2BAA2B;QAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;QACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;KACjD,CAAA;CACF;AAED,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;mGAC+F;IAC/F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,cAAc,EAAE,CAAA;IAC/C,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC/E;8FAC0F;IAC1F,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,CAAA;CAC5D;AAUD,0FAA0F;AAC1F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAOD;+FAC+F;AAC/F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAOD;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA6HzF;AAED;oFACoF;AACpF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,4BAA4B;IAC3C;uGACmG;IACnG,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACzC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,qBAAqB,CAOvB"}
|
package/dist/prerender.js
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { dirname, resolve, sep } from "node:path";
|
|
13
|
+
import { assertNoSecrets, buildEnvironment } from "./internal/secret-scan.js";
|
|
13
14
|
import { fillRoutePattern } from "./manifest.js";
|
|
14
|
-
import { DATA_HEADER } from "./router.js";
|
|
15
|
+
import { DATA_HEADER, PRERENDERED_GLOBAL, PRERENDERED_LIST_PATH } from "./router.js";
|
|
15
16
|
/** Strip trailing `/` runs by index scan - an unanchored-start `/\/+$/` replace backtracks
|
|
16
17
|
* quadratically on a long slash run, and route patterns are input here. */
|
|
17
18
|
function trimTrailingSlashes(path) {
|
|
@@ -25,6 +26,8 @@ export function htmlFileFor(pattern) {
|
|
|
25
26
|
const trimmed = trimTrailingSlashes(pattern.replace(/^\/+/, ""));
|
|
26
27
|
return trimmed === "" ? "index.html" : `${trimmed}/index.html`;
|
|
27
28
|
}
|
|
29
|
+
// The handover of a page whose prerendered set is served as a list rather than inlined.
|
|
30
|
+
const PRERENDERED_LIST_URL = new RegExp(`"${PRERENDERED_GLOBAL}":"(${PRERENDERED_LIST_PATH.replaceAll(".", "\\.")}\\?v=[0-9a-f]+)"`);
|
|
28
31
|
/** The static loader-data file next to a route's `index.html`: `/` → `_data.json`, `/a/b` →
|
|
29
32
|
* `a/b/_data.json`. The client fetches it on soft-nav into a prerendered route (no worker). */
|
|
30
33
|
export function dataFileFor(pattern) {
|
|
@@ -47,8 +50,16 @@ export async function prerenderRoutes(options) {
|
|
|
47
50
|
const skipped = [];
|
|
48
51
|
const fallbacks = {};
|
|
49
52
|
const outRoot = resolve(options.outDir);
|
|
53
|
+
let listUrl;
|
|
54
|
+
const secretScan = {
|
|
55
|
+
env: options.secrets?.env ?? buildEnvironment(),
|
|
56
|
+
publicEnvPrefix: options.secrets?.publicEnvPrefix ?? "PUBLIC_",
|
|
57
|
+
...(options.secrets?.exemptions ? { exemptions: options.secrets.exemptions } : {}),
|
|
58
|
+
};
|
|
59
|
+
const assertClean = (name, text) => assertNoSecrets({ ...secretScan, artifacts: [{ name, text }] });
|
|
50
60
|
// Render one concrete path → drain the streamed document to bytes → write its index.html. Shared by
|
|
51
|
-
// static (one path) and dynamic (one path per getStaticPaths entry) routes
|
|
61
|
+
// static (one path) and dynamic (one path per getStaticPaths entry) routes. A failed render is a skip;
|
|
62
|
+
// a credential in the output fails the whole prerender.
|
|
52
63
|
const renderPath = async (path) => {
|
|
53
64
|
const res = await options.app.fetch(new Request(`${origin}${path}`));
|
|
54
65
|
if (!res.ok) {
|
|
@@ -57,12 +68,14 @@ export async function prerenderRoutes(options) {
|
|
|
57
68
|
return;
|
|
58
69
|
}
|
|
59
70
|
const html = await res.text();
|
|
71
|
+
listUrl ??= PRERENDERED_LIST_URL.exec(html)?.[1];
|
|
60
72
|
const file = htmlFileFor(path);
|
|
61
73
|
const abs = outputPath(outRoot, file);
|
|
62
74
|
if (abs === undefined) {
|
|
63
75
|
skipped.push({ path, reason: "unsafe output path" });
|
|
64
76
|
return;
|
|
65
77
|
}
|
|
78
|
+
assertClean(file, html);
|
|
66
79
|
mkdirSync(dirname(abs), { recursive: true });
|
|
67
80
|
writeFileSync(abs, html);
|
|
68
81
|
// Also emit the loader data as a static `_data.json` next to it, so a client soft-nav into this
|
|
@@ -80,7 +93,9 @@ export async function prerenderRoutes(options) {
|
|
|
80
93
|
dataFile = undefined;
|
|
81
94
|
}
|
|
82
95
|
else {
|
|
83
|
-
|
|
96
|
+
const data = await dataRes.text();
|
|
97
|
+
assertClean(dataFile, data);
|
|
98
|
+
writeFileSync(dataAbs, data);
|
|
84
99
|
}
|
|
85
100
|
}
|
|
86
101
|
else {
|
|
@@ -125,6 +140,23 @@ export async function prerenderRoutes(options) {
|
|
|
125
140
|
}
|
|
126
141
|
await renderPath(route.pattern);
|
|
127
142
|
}
|
|
143
|
+
// A set too large to inline is handed over as the URL of a list the app serves; the static output
|
|
144
|
+
// has to carry the file those pages fetch.
|
|
145
|
+
if (listUrl !== undefined) {
|
|
146
|
+
const listRes = await options.app.fetch(new Request(`${origin}${listUrl}`));
|
|
147
|
+
const listFile = PRERENDERED_LIST_PATH.slice(1);
|
|
148
|
+
const listAbs = outputPath(outRoot, listFile);
|
|
149
|
+
if (listRes.ok && listAbs !== undefined) {
|
|
150
|
+
const text = await listRes.text();
|
|
151
|
+
assertClean(listFile, text);
|
|
152
|
+
mkdirSync(dirname(listAbs), { recursive: true });
|
|
153
|
+
writeFileSync(listAbs, text);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
await listRes.body?.cancel();
|
|
157
|
+
skipped.push({ path: PRERENDERED_LIST_PATH, reason: `list returned HTTP ${listRes.status}` });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
128
160
|
return { prerendered, skipped, fallbacks };
|
|
129
161
|
}
|
|
130
162
|
/**
|
package/dist/prerender.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prerender.js","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAmB,MAAM,eAAe,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"prerender.js","sourceRoot":"","sources":["../src/prerender.ts"],"names":[],"mappings":"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"}
|
package/dist/public-dir.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface ServePublicDirOptions {
|
|
|
15
15
|
/** Optional encoded URL-path allowlist. When present, route misses avoid a filesystem probe. */
|
|
16
16
|
readonly files?: ReadonlySet<string>;
|
|
17
17
|
}
|
|
18
|
+
export declare const IMMUTABLE = "public, max-age=31536000, immutable";
|
|
19
|
+
export declare const ONE_DAY = "public, max-age=86400";
|
|
20
|
+
/**
|
|
21
|
+
* Media types by extension. A table rather than `Bun.file(path).type`: this handler also runs on the
|
|
22
|
+
* Node and Deno adapters, where `Bun` does not exist and every hit would throw. Unknown extensions
|
|
23
|
+
* (and extensionless files such as ACME tokens) are served as `application/octet-stream`, which
|
|
24
|
+
* together with `nosniff` keeps the browser from guessing a more dangerous type.
|
|
25
|
+
*/
|
|
26
|
+
export declare const CONTENT_TYPES: Readonly<Record<string, string>>;
|
|
18
27
|
/**
|
|
19
28
|
* Resolve a URL pathname to an absolute path **confined** to `root`, or `undefined` if it escapes.
|
|
20
29
|
*
|
package/dist/public-dir.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-dir.d.ts","sourceRoot":"","sources":["../src/public-dir.ts"],"names":[],"mappings":"AAqBA;4FAC4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,oGAAoG;IACpG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,cAAc,CAAA;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;
|
|
1
|
+
{"version":3,"file":"public-dir.d.ts","sourceRoot":"","sources":["../src/public-dir.ts"],"names":[],"mappings":"AAqBA;4FAC4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,oGAAoG;IACpG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,cAAc,CAAA;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;AAED,eAAO,MAAM,SAAS,wCAAwC,CAAA;AAC9D,eAAO,MAAM,OAAO,0BAA0B,CAAA;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuC1D,CAAA;AA2CD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAapF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAuGrD"}
|
package/dist/public-dir.js
CHANGED
|
@@ -14,12 +14,70 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { constants as FS } from "node:fs";
|
|
16
16
|
import { open, realpath } from "node:fs/promises";
|
|
17
|
-
import { normalize, resolve, sep } from "node:path";
|
|
17
|
+
import { extname, normalize, resolve, sep } from "node:path";
|
|
18
18
|
import { Readable } from "node:stream";
|
|
19
19
|
import { parseByteRange } from "@nifrajs/core/range";
|
|
20
20
|
import { pathnameOf } from "@nifrajs/core/server";
|
|
21
|
-
const IMMUTABLE = "public, max-age=31536000, immutable";
|
|
22
|
-
const ONE_DAY = "public, max-age=86400";
|
|
21
|
+
export const IMMUTABLE = "public, max-age=31536000, immutable";
|
|
22
|
+
export const ONE_DAY = "public, max-age=86400";
|
|
23
|
+
/**
|
|
24
|
+
* Media types by extension. A table rather than `Bun.file(path).type`: this handler also runs on the
|
|
25
|
+
* Node and Deno adapters, where `Bun` does not exist and every hit would throw. Unknown extensions
|
|
26
|
+
* (and extensionless files such as ACME tokens) are served as `application/octet-stream`, which
|
|
27
|
+
* together with `nosniff` keeps the browser from guessing a more dangerous type.
|
|
28
|
+
*/
|
|
29
|
+
export const CONTENT_TYPES = {
|
|
30
|
+
".html": "text/html; charset=utf-8",
|
|
31
|
+
".htm": "text/html; charset=utf-8",
|
|
32
|
+
".css": "text/css; charset=utf-8",
|
|
33
|
+
".js": "text/javascript; charset=utf-8",
|
|
34
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
35
|
+
".cjs": "text/javascript; charset=utf-8",
|
|
36
|
+
".json": "application/json; charset=utf-8",
|
|
37
|
+
".map": "application/json; charset=utf-8",
|
|
38
|
+
".webmanifest": "application/manifest+json",
|
|
39
|
+
".txt": "text/plain; charset=utf-8",
|
|
40
|
+
".md": "text/markdown; charset=utf-8",
|
|
41
|
+
".csv": "text/csv; charset=utf-8",
|
|
42
|
+
".xml": "application/xml",
|
|
43
|
+
".rss": "application/rss+xml",
|
|
44
|
+
".atom": "application/atom+xml",
|
|
45
|
+
".svg": "image/svg+xml",
|
|
46
|
+
".png": "image/png",
|
|
47
|
+
".jpg": "image/jpeg",
|
|
48
|
+
".jpeg": "image/jpeg",
|
|
49
|
+
".gif": "image/gif",
|
|
50
|
+
".webp": "image/webp",
|
|
51
|
+
".avif": "image/avif",
|
|
52
|
+
".ico": "image/x-icon",
|
|
53
|
+
".bmp": "image/bmp",
|
|
54
|
+
".woff": "font/woff",
|
|
55
|
+
".woff2": "font/woff2",
|
|
56
|
+
".ttf": "font/ttf",
|
|
57
|
+
".otf": "font/otf",
|
|
58
|
+
".wasm": "application/wasm",
|
|
59
|
+
".pdf": "application/pdf",
|
|
60
|
+
".zip": "application/zip",
|
|
61
|
+
".mp4": "video/mp4",
|
|
62
|
+
".webm": "video/webm",
|
|
63
|
+
".ogg": "audio/ogg",
|
|
64
|
+
".mp3": "audio/mpeg",
|
|
65
|
+
".wav": "audio/wav",
|
|
66
|
+
".m4a": "audio/mp4",
|
|
67
|
+
".vtt": "text/vtt; charset=utf-8",
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Whether a confined path crosses a dot-segment other than `.well-known`. The build copies `public/`
|
|
71
|
+
* with dotfiles included, so an editor or VCS artifact (`.env`, `.git/`, `.DS_Store`) dropped there
|
|
72
|
+
* would otherwise be downloadable. Checked on the resolved path, so `%2e` encodings are covered.
|
|
73
|
+
*/
|
|
74
|
+
function isHiddenPath(root, abs) {
|
|
75
|
+
for (const segment of abs.slice(root.length + 1).split(sep)) {
|
|
76
|
+
if (segment.startsWith(".") && segment !== ".well-known")
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
23
81
|
/** HTTP dates carry second precision; comparing raw milliseconds makes every file look stale. */
|
|
24
82
|
function seconds(time) {
|
|
25
83
|
return Math.floor(time / 1000);
|
|
@@ -101,7 +159,7 @@ export function servePublicDir(options) {
|
|
|
101
159
|
if (options.files !== undefined && !options.files.has(pathname))
|
|
102
160
|
return undefined;
|
|
103
161
|
const abs = resolvePublicPath(root, pathname);
|
|
104
|
-
if (abs === undefined)
|
|
162
|
+
if (abs === undefined || isHiddenPath(root, abs))
|
|
105
163
|
return undefined;
|
|
106
164
|
const [resolvedRoot, resolvedFile] = await Promise.all([
|
|
107
165
|
rootReal,
|
|
@@ -123,17 +181,17 @@ export function servePublicDir(options) {
|
|
|
123
181
|
if (!stat.isFile())
|
|
124
182
|
return undefined;
|
|
125
183
|
const size = stat.size;
|
|
126
|
-
const file = Bun.file(resolvedFile);
|
|
127
184
|
const headers = new Headers({
|
|
128
185
|
"cache-control": pathname.startsWith(hashedPrefix) ? hashed : assets,
|
|
129
186
|
// Advertised unconditionally: a client that never sees `accept-ranges` will not attempt a seek,
|
|
130
187
|
// so a video or audio file under `public/` is scrubbable only once this header is present.
|
|
131
188
|
"accept-ranges": "bytes",
|
|
189
|
+
// Never let a client sniff a served file into a more dangerous type (an upload-like `.txt`
|
|
190
|
+
// rendered as HTML, say). The declared type below is authoritative.
|
|
191
|
+
"x-content-type-options": "nosniff",
|
|
192
|
+
// Set explicitly rather than inferred from the body, so HEAD (a null body) carries it too.
|
|
193
|
+
"content-type": CONTENT_TYPES[extname(resolvedFile).toLowerCase()] ?? "application/octet-stream",
|
|
132
194
|
});
|
|
133
|
-
// `new Response(file)` used to infer the media type, which meant HEAD - built from a null body -
|
|
134
|
-
// silently lost it. Setting it here is what makes the two methods agree.
|
|
135
|
-
if (file.type !== "")
|
|
136
|
-
headers.set("content-type", file.type);
|
|
137
195
|
const lastModified = Number.isFinite(stat.mtimeMs) && stat.mtimeMs > 0 ? stat.mtimeMs : undefined;
|
|
138
196
|
if (lastModified !== undefined) {
|
|
139
197
|
headers.set("last-modified", new Date(lastModified).toUTCString());
|