@nifrajs/web 3.3.0 → 4.0.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/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 +35 -6
- package/dist/build-plan.d.ts.map +1 -1
- package/dist/build-plan.js +89 -35
- package/dist/build-plan.js.map +1 -1
- package/dist/build-vite.d.ts +18 -0
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +97 -17
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +72 -23
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +411 -98
- 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 +21 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +226 -15
- 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/css-contract.d.ts +21 -0
- package/dist/css-contract.d.ts.map +1 -0
- package/dist/css-contract.js +28 -0
- package/dist/css-contract.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 +15 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -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 +3 -0
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +151 -30
- 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 +69 -0
- package/dist/internal/page-execution.d.ts.map +1 -0
- package/dist/internal/page-execution.js +1021 -0
- package/dist/internal/page-execution.js.map +1 -0
- 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 +37 -21
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +104 -65
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/request-url.d.ts +3 -0
- package/dist/internal/request-url.d.ts.map +1 -0
- package/dist/internal/request-url.js +14 -0
- package/dist/internal/request-url.js.map +1 -0
- 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 +485 -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 +56 -8
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +215 -772
- 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 +301 -39
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts +3 -0
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +37 -9
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +221 -14
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +386 -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 +72 -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 +131 -48
- 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/trusted-html.d.ts +34 -0
- package/dist/trusted-html.d.ts.map +1 -0
- package/dist/trusted-html.js +35 -0
- package/dist/trusted-html.js.map +1 -0
- 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 +187 -36
- 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 +113 -36
- package/src/build-vite.ts +124 -15
- package/src/build.ts +535 -102
- package/src/bun-dev-entry.ts +1 -1
- package/src/cdn.ts +216 -0
- package/src/client.ts +251 -18
- package/src/conformance.ts +16 -3
- package/src/csp.ts +194 -0
- package/src/css-contract.ts +44 -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 +57 -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 +180 -29
- 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 +1371 -0
- 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 +153 -73
- package/src/internal/request-url.ts +14 -0
- package/src/internal/response-controls.ts +284 -0
- package/src/internal/runtime-contract.ts +10 -15
- package/src/internal/secret-scan.ts +623 -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 +290 -879
- package/src/internal/zone-graph.ts +593 -0
- package/src/isr.ts +366 -39
- package/src/llms-txt.ts +44 -8
- package/src/manifest.ts +671 -17
- 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 +110 -0
- package/src/prerender.ts +49 -3
- package/src/public-dir.ts +135 -49
- 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/trusted-html.ts +44 -0
- package/src/vitals.ts +44 -0
- package/src/vite.ts +207 -36
- 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,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private environment reads in browser-reachable code. Browser code (a route's frontend half,
|
|
3
|
+
* `frontend/`, `shared/`) may read `NODE_ENV`, the bundler's own `import.meta.env` flags and variables
|
|
4
|
+
* named with the public prefix; any other `process.env`, `import.meta.env`, `Bun.env` or `Deno.env`
|
|
5
|
+
* read is a secret on its way to a browser, or a value that is silently `undefined` there.
|
|
6
|
+
*
|
|
7
|
+
* Detection works on tokens, not text, so a string or comment that mentions `process.env.X` is not a
|
|
8
|
+
* read. TypeScript and JSX go through Bun's transpiler first: JSX prose becomes string literals and type
|
|
9
|
+
* positions disappear. SFC and MDX files contribute only their code: scripts, markup expressions and
|
|
10
|
+
* ESM lines.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs"
|
|
13
|
+
import { delimitedSpans, withoutComments, withoutMatches } from "./html-spans.ts"
|
|
14
|
+
|
|
15
|
+
const IMPORT_META_FLAGS = new Set(["MODE", "DEV", "PROD", "SSR", "BASE_URL"])
|
|
16
|
+
|
|
17
|
+
/** Words after which a `/` starts a regular expression rather than a division. */
|
|
18
|
+
const BEFORE_EXPRESSION = new Set([
|
|
19
|
+
"return",
|
|
20
|
+
"typeof",
|
|
21
|
+
"case",
|
|
22
|
+
"do",
|
|
23
|
+
"else",
|
|
24
|
+
"in",
|
|
25
|
+
"of",
|
|
26
|
+
"new",
|
|
27
|
+
"delete",
|
|
28
|
+
"void",
|
|
29
|
+
"throw",
|
|
30
|
+
"yield",
|
|
31
|
+
"await",
|
|
32
|
+
"instanceof",
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
type Token =
|
|
36
|
+
| { readonly kind: "id" | "punct" | "string"; readonly value: string }
|
|
37
|
+
| { readonly kind: "other" }
|
|
38
|
+
|
|
39
|
+
/** JavaScript tokens, with strings, template text, comments and regular expressions skipped. */
|
|
40
|
+
function tokenize(code: string): Token[] {
|
|
41
|
+
const tokens: Token[] = []
|
|
42
|
+
// One entry per open `${`: how many `{` are open inside it.
|
|
43
|
+
const templates: number[] = []
|
|
44
|
+
let i = 0
|
|
45
|
+
const readTemplate = (from: number): number => {
|
|
46
|
+
let j = from
|
|
47
|
+
while (j < code.length) {
|
|
48
|
+
const c = code[j]
|
|
49
|
+
if (c === "\\") j += 2
|
|
50
|
+
else if (c === "`") {
|
|
51
|
+
tokens.push({ kind: "other" })
|
|
52
|
+
return j + 1
|
|
53
|
+
} else if (c === "$" && code[j + 1] === "{") {
|
|
54
|
+
templates.push(0)
|
|
55
|
+
return j + 2
|
|
56
|
+
} else j++
|
|
57
|
+
}
|
|
58
|
+
return j
|
|
59
|
+
}
|
|
60
|
+
const regexAllowed = (): boolean => {
|
|
61
|
+
const prev = tokens.at(-1)
|
|
62
|
+
if (prev === undefined) return true
|
|
63
|
+
if (prev.kind === "id") return BEFORE_EXPRESSION.has(prev.value)
|
|
64
|
+
if (prev.kind === "punct") return prev.value !== ")" && prev.value !== "]" && prev.value !== "}"
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
while (i < code.length) {
|
|
68
|
+
const c = code[i] as string
|
|
69
|
+
if (/\s/.test(c)) {
|
|
70
|
+
i++
|
|
71
|
+
} else if (c === "/" && code[i + 1] === "/") {
|
|
72
|
+
const end = code.indexOf("\n", i)
|
|
73
|
+
i = end === -1 ? code.length : end
|
|
74
|
+
} else if (c === "/" && code[i + 1] === "*") {
|
|
75
|
+
const end = code.indexOf("*/", i + 2)
|
|
76
|
+
i = end === -1 ? code.length : end + 2
|
|
77
|
+
} else if (c === "'" || c === '"') {
|
|
78
|
+
let j = i + 1
|
|
79
|
+
let value = ""
|
|
80
|
+
while (j < code.length && code[j] !== c && code[j] !== "\n") {
|
|
81
|
+
if (code[j] === "\\") {
|
|
82
|
+
value += code[j + 1] ?? ""
|
|
83
|
+
j += 2
|
|
84
|
+
} else value += code[j++]
|
|
85
|
+
}
|
|
86
|
+
tokens.push({ kind: "string", value })
|
|
87
|
+
i = j + 1
|
|
88
|
+
} else if (c === "`") {
|
|
89
|
+
i = readTemplate(i + 1)
|
|
90
|
+
} else if (c === "}" && templates.at(-1) === 0) {
|
|
91
|
+
templates.pop()
|
|
92
|
+
i = readTemplate(i + 1)
|
|
93
|
+
} else if (/[A-Za-z_$\u0080-]/.test(c)) {
|
|
94
|
+
let j = i + 1
|
|
95
|
+
while (j < code.length && /[\w$\u0080-]/.test(code[j] as string)) j++
|
|
96
|
+
tokens.push({ kind: "id", value: code.slice(i, j) })
|
|
97
|
+
i = j
|
|
98
|
+
} else if (/\d/.test(c) || (c === "." && /\d/.test(code[i + 1] ?? ""))) {
|
|
99
|
+
let j = i + 1
|
|
100
|
+
while (j < code.length && /[\w.]/.test(code[j] as string)) j++
|
|
101
|
+
tokens.push({ kind: "other" })
|
|
102
|
+
i = j
|
|
103
|
+
} else if (c === "/" && regexAllowed()) {
|
|
104
|
+
let j = i + 1
|
|
105
|
+
let inClass = false
|
|
106
|
+
while (j < code.length && code[j] !== "\n") {
|
|
107
|
+
const d = code[j]
|
|
108
|
+
if (d === "\\") j++
|
|
109
|
+
else if (d === "[") inClass = true
|
|
110
|
+
else if (d === "]") inClass = false
|
|
111
|
+
else if (d === "/" && !inClass) break
|
|
112
|
+
j++
|
|
113
|
+
}
|
|
114
|
+
j++
|
|
115
|
+
while (j < code.length && /[a-z]/i.test(code[j] as string)) j++
|
|
116
|
+
tokens.push({ kind: "other" })
|
|
117
|
+
i = j
|
|
118
|
+
} else if (c === "?" && code[i + 1] === "." && !/\d/.test(code[i + 2] ?? "")) {
|
|
119
|
+
// Optional chaining reads the same property: `process?.env?.X` is `process.env.X`.
|
|
120
|
+
tokens.push({ kind: "punct", value: "." })
|
|
121
|
+
i += 2
|
|
122
|
+
} else {
|
|
123
|
+
const open = templates.length - 1
|
|
124
|
+
if (c === "{" && open >= 0) templates[open] = (templates[open] as number) + 1
|
|
125
|
+
if (c === "}" && open >= 0) templates[open] = (templates[open] as number) - 1
|
|
126
|
+
tokens.push({ kind: "punct", value: c })
|
|
127
|
+
i++
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return tokens
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** One environment read: what it looks like in source, and the variable it names, when it names one. */
|
|
134
|
+
interface EnvRead {
|
|
135
|
+
readonly expression: string
|
|
136
|
+
readonly name?: string
|
|
137
|
+
/** `import.meta.env`, whose bundler flags (`MODE`, `DEV`, ...) are public. */
|
|
138
|
+
readonly importMeta: boolean
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function envReads(tokens: readonly Token[]): EnvRead[] {
|
|
142
|
+
const reads: EnvRead[] = []
|
|
143
|
+
const is = (at: number, kind: "id" | "punct", value: string): boolean => {
|
|
144
|
+
const token = tokens[at]
|
|
145
|
+
return token !== undefined && token.kind === kind && token.value === value
|
|
146
|
+
}
|
|
147
|
+
const stringAt = (at: number): string | undefined => {
|
|
148
|
+
const token = tokens[at]
|
|
149
|
+
return token?.kind === "string" ? token.value : undefined
|
|
150
|
+
}
|
|
151
|
+
const idAt = (at: number): string | undefined => {
|
|
152
|
+
const token = tokens[at]
|
|
153
|
+
return token?.kind === "id" ? token.value : undefined
|
|
154
|
+
}
|
|
155
|
+
/** A property read after an env object that ends at `at`: `.X` or `["X"]`. */
|
|
156
|
+
const member = (base: string, at: number, importMeta: boolean): EnvRead => {
|
|
157
|
+
const property = is(at, "punct", ".") ? idAt(at + 1) : undefined
|
|
158
|
+
if (property !== undefined)
|
|
159
|
+
return { expression: `${base}.${property}`, name: property, importMeta }
|
|
160
|
+
const key = is(at, "punct", "[") && is(at + 2, "punct", "]") ? stringAt(at + 1) : undefined
|
|
161
|
+
if (key !== undefined)
|
|
162
|
+
return { expression: `${base}[${JSON.stringify(key)}]`, name: key, importMeta }
|
|
163
|
+
return { expression: base, importMeta }
|
|
164
|
+
}
|
|
165
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
166
|
+
const head = idAt(i)
|
|
167
|
+
if (
|
|
168
|
+
(head === "process" || head === "Bun") &&
|
|
169
|
+
is(i + 1, "punct", ".") &&
|
|
170
|
+
is(i + 2, "id", "env")
|
|
171
|
+
) {
|
|
172
|
+
reads.push(member(`${head}.env`, i + 3, false))
|
|
173
|
+
} else if (
|
|
174
|
+
head === "import" &&
|
|
175
|
+
is(i + 1, "punct", ".") &&
|
|
176
|
+
is(i + 2, "id", "meta") &&
|
|
177
|
+
is(i + 3, "punct", ".") &&
|
|
178
|
+
is(i + 4, "id", "env")
|
|
179
|
+
) {
|
|
180
|
+
reads.push(member("import.meta.env", i + 5, true))
|
|
181
|
+
} else if (head === "Deno" && is(i + 1, "punct", ".") && is(i + 2, "id", "env")) {
|
|
182
|
+
const method = is(i + 3, "punct", ".") ? idAt(i + 4) : undefined
|
|
183
|
+
const name =
|
|
184
|
+
(method === "get" || method === "has") && is(i + 5, "punct", "(")
|
|
185
|
+
? stringAt(i + 6)
|
|
186
|
+
: undefined
|
|
187
|
+
reads.push(
|
|
188
|
+
name !== undefined && is(i + 7, "punct", ")")
|
|
189
|
+
? { expression: `Deno.env.${method}(${JSON.stringify(name)})`, name, importMeta: false }
|
|
190
|
+
: {
|
|
191
|
+
expression: method === undefined ? "Deno.env" : `Deno.env.${method}()`,
|
|
192
|
+
importMeta: false,
|
|
193
|
+
},
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return reads
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// A closing tag ends at any space, slash or `>` after its name, as a browser and the compilers read it.
|
|
201
|
+
const SCRIPT_ELEMENT = /<script\b[^>]*>([\s\S]*?)<\/script(?=[\s/>])[^>]*>/gi
|
|
202
|
+
const STYLE_ELEMENT = /<style\b[^>]*>[\s\S]*?<\/style(?=[\s/>])[^>]*>/gi
|
|
203
|
+
|
|
204
|
+
/** The code of a Svelte or Vue file: its scripts as modules, its markup expressions one by one. */
|
|
205
|
+
function sfcCode(
|
|
206
|
+
source: string,
|
|
207
|
+
vue: boolean,
|
|
208
|
+
): { readonly scripts: string[]; readonly expressions: string[] } {
|
|
209
|
+
const scripts: string[] = []
|
|
210
|
+
const withoutScripts = withoutMatches(source, SCRIPT_ELEMENT, (match) => {
|
|
211
|
+
scripts.push(match[1] ?? "")
|
|
212
|
+
})
|
|
213
|
+
const markup = withoutComments(withoutMatches(withoutScripts, STYLE_ELEMENT))
|
|
214
|
+
const expressions: string[] = []
|
|
215
|
+
if (vue) {
|
|
216
|
+
expressions.push(...delimitedSpans(markup, "{{", "}}"))
|
|
217
|
+
for (const match of markup.matchAll(/\s(?::|@|v-|#)[\w:.[\]-]*\s*=\s*("([^"]*)"|'([^']*)')/g))
|
|
218
|
+
expressions.push(match[2] ?? match[3] ?? "")
|
|
219
|
+
} else {
|
|
220
|
+
expressions.push(...braceExpressions(markup))
|
|
221
|
+
}
|
|
222
|
+
return { scripts, expressions }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Every top-level `{...}` region of markup, braces balanced. */
|
|
226
|
+
function braceExpressions(markup: string): string[] {
|
|
227
|
+
const out: string[] = []
|
|
228
|
+
let depth = 0
|
|
229
|
+
let start = -1
|
|
230
|
+
for (let i = 0; i < markup.length; i++) {
|
|
231
|
+
if (markup[i] === "{") {
|
|
232
|
+
if (depth++ === 0) start = i + 1
|
|
233
|
+
} else if (markup[i] === "}" && depth > 0 && --depth === 0) {
|
|
234
|
+
out.push(markup.slice(start, i))
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return out
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** The code of an MDX file: its import/export lines and `{...}` expressions, never its prose or code. */
|
|
241
|
+
function mdxCode(source: string): string[] {
|
|
242
|
+
const text = source
|
|
243
|
+
.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, "")
|
|
244
|
+
.replace(/`[^`\n]*`/g, "")
|
|
245
|
+
const esm = text.split("\n").filter((line) => /^(?:import|export)\b/.test(line))
|
|
246
|
+
return [...esm, ...braceExpressions(text)]
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const SCRIPT = /\.(?:[cm]?[jt]sx?)$/
|
|
250
|
+
|
|
251
|
+
function transpile(code: string, loader: "ts" | "tsx" | "js" | "jsx"): string {
|
|
252
|
+
if (typeof Bun === "undefined") return code
|
|
253
|
+
try {
|
|
254
|
+
return new Bun.Transpiler({ loader }).transformSync(code)
|
|
255
|
+
} catch {
|
|
256
|
+
// Code the transpiler cannot parse fails the real build anyway; scan it as written.
|
|
257
|
+
return code
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const loaderFor = (file: string): "ts" | "tsx" | "js" | "jsx" =>
|
|
262
|
+
/\.[cm]?tsx$/.test(file)
|
|
263
|
+
? "tsx"
|
|
264
|
+
: /\.[cm]?ts$/.test(file)
|
|
265
|
+
? "ts"
|
|
266
|
+
: /\.jsx$/.test(file)
|
|
267
|
+
? "jsx"
|
|
268
|
+
: "js"
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The private environment reads in one browser-reachable file, as written (`process.env.SECRET`).
|
|
272
|
+
* `publicPrefix` is the app's public-env prefix; `""` makes every variable but `NODE_ENV` private.
|
|
273
|
+
*/
|
|
274
|
+
export function privateEnvReads(file: string, source: string, publicPrefix: string): string[] {
|
|
275
|
+
if (!/\benv\b/.test(source)) return []
|
|
276
|
+
const chunks: string[] = []
|
|
277
|
+
if (SCRIPT.test(file)) chunks.push(transpile(source, loaderFor(file)))
|
|
278
|
+
else if (/\.(?:svelte|vue)$/.test(file)) {
|
|
279
|
+
const { scripts, expressions } = sfcCode(source, file.endsWith(".vue"))
|
|
280
|
+
chunks.push(...scripts.map((script) => transpile(script, "ts")), ...expressions)
|
|
281
|
+
} else if (/\.mdx?$/.test(file)) chunks.push(...mdxCode(source))
|
|
282
|
+
else return []
|
|
283
|
+
const allowed = (read: EnvRead): boolean => {
|
|
284
|
+
if (read.name === undefined) return false
|
|
285
|
+
if (read.name === "NODE_ENV") return true
|
|
286
|
+
if (read.importMeta && IMPORT_META_FLAGS.has(read.name)) return true
|
|
287
|
+
return publicPrefix !== "" && read.name.startsWith(publicPrefix)
|
|
288
|
+
}
|
|
289
|
+
const found = new Set<string>()
|
|
290
|
+
for (const chunk of chunks) {
|
|
291
|
+
if (!/\benv\b/.test(chunk)) continue
|
|
292
|
+
for (const read of envReads(tokenize(chunk))) if (!allowed(read)) found.add(read.expression)
|
|
293
|
+
}
|
|
294
|
+
return [...found].sort()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** The denial reason for a file's private environment reads. */
|
|
298
|
+
export function privateEnvReason(reads: readonly string[], publicPrefix: string): string {
|
|
299
|
+
const allowed = publicPrefix === "" ? "NODE_ENV" : `NODE_ENV and variables named ${publicPrefix}*`
|
|
300
|
+
return (
|
|
301
|
+
`it reads private environment ${reads.length === 1 ? "variable" : "variables"} ${reads.join(", ")}. ` +
|
|
302
|
+
`Browser code may read only ${allowed}; read the rest in a loader, an action or under backend/`
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** The denial reason for one file's source, or `undefined` when it reads no private variable. */
|
|
307
|
+
export function privateEnvDenial(
|
|
308
|
+
file: string,
|
|
309
|
+
source: string,
|
|
310
|
+
publicPrefix: string,
|
|
311
|
+
): string | undefined {
|
|
312
|
+
const reads = privateEnvReads(file, source, publicPrefix)
|
|
313
|
+
return reads.length === 0 ? undefined : privateEnvReason(reads, publicPrefix)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** A per-file check for a finished build, reading each file once. */
|
|
317
|
+
export function privateEnvCheck(publicPrefix: string): (file: string) => string | undefined {
|
|
318
|
+
const seen = new Map<string, string | undefined>()
|
|
319
|
+
return (file) => {
|
|
320
|
+
if (!seen.has(file)) {
|
|
321
|
+
let source: string | undefined
|
|
322
|
+
try {
|
|
323
|
+
source = readFileSync(file, "utf8")
|
|
324
|
+
} catch {
|
|
325
|
+
source = undefined
|
|
326
|
+
}
|
|
327
|
+
seen.set(
|
|
328
|
+
file,
|
|
329
|
+
source === undefined ? undefined : privateEnvDenial(file, source, publicPrefix),
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
return seen.get(file)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { sep } from "node:path"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Percent-encode one path segment exactly the way a browser encodes it into a request URL.
|
|
5
|
+
*
|
|
6
|
+
* NOT `encodeURIComponent`. That escapes the sub-delimiters `, @ + = & ; $`, which a browser sends raw -
|
|
7
|
+
* so a file named `report,2026.csv` would be recorded as `/report%2C2026.csv` while the request arrives
|
|
8
|
+
* as `/report,2026.csv`, the allowlist lookup misses, and the file 404s in production only. `encodeURI`
|
|
9
|
+
* agrees with `URL.pathname` on every character except `?` and `#`, which terminate a path and so must
|
|
10
|
+
* be escaped explicitly here.
|
|
11
|
+
*/
|
|
12
|
+
function encodePathSegment(segment: string): string {
|
|
13
|
+
return encodeURI(segment).replace(
|
|
14
|
+
/[?#]/g,
|
|
15
|
+
(char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The URL path a browser requests for a file at `rel` (OS separators) under the public directory. */
|
|
20
|
+
export function publicUrlPath(rel: string): string {
|
|
21
|
+
return `/${rel.split(sep).map(encodePathSegment).join("/")}`
|
|
22
|
+
}
|