@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
package/src/zones.ts
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/web/zones` - which side of the app a source file belongs to.
|
|
3
|
+
*
|
|
4
|
+
* One classifier, used by both client builds, both dev servers and `nifra check`, so they cannot
|
|
5
|
+
* disagree about what may reach a browser. It classifies a RESOLVED file by its real path: the
|
|
6
|
+
* bundler has already applied tsconfig `paths`, aliases and package `exports`, and a workspace
|
|
7
|
+
* package linked through `node_modules` resolves to a path outside any `node_modules` directory, so
|
|
8
|
+
* it is first-party and must say which side it is on.
|
|
9
|
+
*
|
|
10
|
+
* Default-deny: a first-party file that no rule places is an error, never "probably fine".
|
|
11
|
+
*/
|
|
12
|
+
import { closeSync, existsSync, openSync, readFileSync, readSync, realpathSync } from "node:fs"
|
|
13
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
|
|
14
|
+
import { SERVER_FN_MODULE } from "./internal/server-boundary.ts"
|
|
15
|
+
|
|
16
|
+
export type Zone =
|
|
17
|
+
/** A page or special file under `routes/` - the frontend half of a route. */
|
|
18
|
+
| "route-frontend"
|
|
19
|
+
/** A route's `x.backend.ts` twin. */
|
|
20
|
+
| "route-backend"
|
|
21
|
+
| "frontend"
|
|
22
|
+
| "backend"
|
|
23
|
+
| "shared"
|
|
24
|
+
/** A server-function module (`*.fn.ts`); the browser only ever receives its generated RPC stub. */
|
|
25
|
+
| "fn"
|
|
26
|
+
/** Framework output (the client entry, the server composition, generated types). */
|
|
27
|
+
| "generated"
|
|
28
|
+
/** A third-party package, or a workspace package declaring `"nifra": { "environment": "library" }`. */
|
|
29
|
+
| "library"
|
|
30
|
+
|
|
31
|
+
export type Classification =
|
|
32
|
+
| {
|
|
33
|
+
readonly zone: Zone
|
|
34
|
+
/** The package a `library` file belongs to. */
|
|
35
|
+
readonly packageName?: string
|
|
36
|
+
/** A `library` package's own declaration, when it made one. */
|
|
37
|
+
readonly declared?: PackageEnvironment
|
|
38
|
+
/** A third-party file's path inside `node_modules`: `drizzle-orm/node-postgres/index.js`. */
|
|
39
|
+
readonly modulePath?: string
|
|
40
|
+
}
|
|
41
|
+
| { readonly zone: "error"; readonly reason: string }
|
|
42
|
+
|
|
43
|
+
export type PackageEnvironment = "frontend" | "backend" | "shared" | "library"
|
|
44
|
+
const PACKAGE_ENVIRONMENTS: ReadonlySet<string> = new Set([
|
|
45
|
+
"frontend",
|
|
46
|
+
"backend",
|
|
47
|
+
"shared",
|
|
48
|
+
"library",
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
/** Zones whose code may ship to a browser. `fn` is listed because its stub is what ships. */
|
|
52
|
+
export const BROWSER_ZONES: ReadonlySet<Zone> = new Set([
|
|
53
|
+
"route-frontend",
|
|
54
|
+
"frontend",
|
|
55
|
+
"shared",
|
|
56
|
+
"fn",
|
|
57
|
+
"generated",
|
|
58
|
+
"library",
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
const SERVER_ZONES: ReadonlySet<Zone> = new Set(["route-backend", "backend", "fn"])
|
|
62
|
+
const FRONTEND_ZONES: ReadonlySet<Zone> = new Set(["route-frontend", "frontend"])
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Third-party packages that never belong in a browser bundle: database drivers, ORMs' server
|
|
66
|
+
* entries, mailers and server SDKs. A package reaching a `node:` builtin is caught separately; this
|
|
67
|
+
* list covers the ones that bundle "fine" and leak credentials or query code instead.
|
|
68
|
+
*/
|
|
69
|
+
export const SERVER_PACKAGES: readonly string[] = [
|
|
70
|
+
"@libsql/client",
|
|
71
|
+
"@neondatabase/serverless",
|
|
72
|
+
"@planetscale/database",
|
|
73
|
+
"@prisma/client",
|
|
74
|
+
"@sendgrid/mail",
|
|
75
|
+
"@vercel/postgres",
|
|
76
|
+
"argon2",
|
|
77
|
+
"bcrypt",
|
|
78
|
+
"bcryptjs",
|
|
79
|
+
"better-sqlite3",
|
|
80
|
+
"drizzle-kit",
|
|
81
|
+
"firebase-admin",
|
|
82
|
+
"ioredis",
|
|
83
|
+
"jsonwebtoken",
|
|
84
|
+
"knex",
|
|
85
|
+
"mongodb",
|
|
86
|
+
"mongoose",
|
|
87
|
+
"mysql",
|
|
88
|
+
"mysql2",
|
|
89
|
+
"nodemailer",
|
|
90
|
+
"pg",
|
|
91
|
+
"pg-pool",
|
|
92
|
+
"postgres",
|
|
93
|
+
"prisma",
|
|
94
|
+
"redis",
|
|
95
|
+
"resend",
|
|
96
|
+
"sequelize",
|
|
97
|
+
"server-only",
|
|
98
|
+
"sqlite3",
|
|
99
|
+
"stripe",
|
|
100
|
+
"twilio",
|
|
101
|
+
"typeorm",
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
/** `drizzle-orm` is a browser-safe query builder until one of its driver entries is imported. */
|
|
105
|
+
const SERVER_SUBPATHS =
|
|
106
|
+
/^drizzle-orm\/(?:node-postgres|postgres-js|mysql2|better-sqlite3|libsql|bun-sqlite|neon-serverless|neon-http|vercel-postgres|planetscale-serverless|d1|pglite)(?:\/|$)/
|
|
107
|
+
|
|
108
|
+
/** The nifra marker a module imports to refuse the browser outright. */
|
|
109
|
+
export const BACKEND_ONLY_MARKER = "@nifrajs/web/backend-only"
|
|
110
|
+
|
|
111
|
+
const SOURCE = "(?:[cm]?[jt]sx?|svelte|vue|mdx)"
|
|
112
|
+
/** A route's backend half: `x.backend.ts`. Script extensions only; JSX belongs to the frontend half. */
|
|
113
|
+
export const ROUTE_BACKEND_FILE = /\.backend\.(?:[cm]?[jt]s)$/
|
|
114
|
+
const ZONE_SUFFIX = new RegExp(`\\.(frontend|backend|shared|fn)\\.${SOURCE}$`)
|
|
115
|
+
const LEGACY_SERVER_SUFFIX = new RegExp(`\\.server\\.${SOURCE}$`)
|
|
116
|
+
|
|
117
|
+
const ZONE_FOLDERS: Readonly<Record<string, Zone>> = {
|
|
118
|
+
frontend: "frontend",
|
|
119
|
+
backend: "backend",
|
|
120
|
+
shared: "shared",
|
|
121
|
+
public: "frontend",
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface ZoneClassifierOptions {
|
|
125
|
+
/** The app root: the directory holding `routes/`, `frontend/`, `backend/` and `shared/`. */
|
|
126
|
+
readonly appRoot: string
|
|
127
|
+
/** The routes directory (default `<appRoot>/routes`). */
|
|
128
|
+
readonly routesDir?: string
|
|
129
|
+
/** Extra absolute files treated as `generated` - a build's own entry modules. */
|
|
130
|
+
readonly generatedFiles?: readonly string[]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface ZoneClassifier {
|
|
134
|
+
readonly appRoot: string
|
|
135
|
+
/** Classify a file by its absolute (or appRoot-relative) path. Results are cached per path. */
|
|
136
|
+
classify(file: string): Classification
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const toPosix = (path: string): string => path.replaceAll("\\", "/")
|
|
140
|
+
|
|
141
|
+
// `generateServerManifest`'s first line. The manifest composes both halves of every route, so it
|
|
142
|
+
// imports frontend code from wherever the build wrote it - next to the server entry, often backend/.
|
|
143
|
+
const GENERATED_SERVER_MANIFEST = "// GENERATED by @nifrajs/web generateServerManifest"
|
|
144
|
+
const SERVER_MANIFEST_FILE = /^server-manifest\.[cm]?[jt]s$/
|
|
145
|
+
|
|
146
|
+
function isGeneratedServerManifest(file: string): boolean {
|
|
147
|
+
let fd: number | undefined
|
|
148
|
+
try {
|
|
149
|
+
fd = openSync(file, "r")
|
|
150
|
+
const head = Buffer.alloc(GENERATED_SERVER_MANIFEST.length)
|
|
151
|
+
const read = readSync(fd, head, 0, head.length, 0)
|
|
152
|
+
return head.toString("utf8", 0, read) === GENERATED_SERVER_MANIFEST
|
|
153
|
+
} catch {
|
|
154
|
+
return false
|
|
155
|
+
} finally {
|
|
156
|
+
if (fd !== undefined) closeSync(fd)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The real path of a file, or the normalized input when it does not exist on disk. */
|
|
161
|
+
function realFile(path: string): string {
|
|
162
|
+
try {
|
|
163
|
+
return toPosix(realpathSync.native(path))
|
|
164
|
+
} catch {
|
|
165
|
+
// A path that does not exist yet keeps its directory's real spelling, so it still compares with
|
|
166
|
+
// the paths around it: a Windows short name or a symlinked temp dir spells one directory two ways.
|
|
167
|
+
const parent = dirname(path)
|
|
168
|
+
if (parent === path) return toPosix(path)
|
|
169
|
+
const real = realFile(parent)
|
|
170
|
+
return `${real.endsWith("/") ? real : `${real}/`}${basename(path)}`
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const isInside = (child: string, parent: string): boolean =>
|
|
175
|
+
child === parent || child.startsWith(parent.endsWith("/") ? parent : `${parent}/`)
|
|
176
|
+
|
|
177
|
+
/** `@scope/name` or `name` from the LAST `node_modules/` segment of a path. */
|
|
178
|
+
function nodeModulesPackage(path: string): string | undefined {
|
|
179
|
+
const at = path.lastIndexOf("/node_modules/")
|
|
180
|
+
if (at === -1) return undefined
|
|
181
|
+
const parts = path.slice(at + "/node_modules/".length).split("/")
|
|
182
|
+
const first = parts[0]
|
|
183
|
+
if (first === undefined || first === "") return undefined
|
|
184
|
+
return first.startsWith("@") && parts[1] !== undefined ? `${first}/${parts[1]}` : first
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
interface PackageInfo {
|
|
188
|
+
readonly dir: string
|
|
189
|
+
readonly name: string | undefined
|
|
190
|
+
readonly environment: string | undefined
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Create a classifier for one app. Cheap to create; it caches per file and per package directory,
|
|
195
|
+
* so build plugins and dev servers keep one for the life of a build or a session.
|
|
196
|
+
*/
|
|
197
|
+
export function createZoneClassifier(options: ZoneClassifierOptions): ZoneClassifier {
|
|
198
|
+
const appRoot = realFile(resolve(options.appRoot))
|
|
199
|
+
const routesDir = realFile(resolve(options.routesDir ?? join(options.appRoot, "routes")))
|
|
200
|
+
const generated = new Set((options.generatedFiles ?? []).map((file) => realFile(resolve(file))))
|
|
201
|
+
const cache = new Map<string, Classification>()
|
|
202
|
+
const packages = new Map<string, PackageInfo | null>()
|
|
203
|
+
|
|
204
|
+
/** The nearest `package.json` at or above `dir`, cached by directory. */
|
|
205
|
+
const packageFor = (dir: string): PackageInfo | undefined => {
|
|
206
|
+
const seen: string[] = []
|
|
207
|
+
let current = dir
|
|
208
|
+
for (;;) {
|
|
209
|
+
const cached = packages.get(current)
|
|
210
|
+
if (cached !== undefined) {
|
|
211
|
+
for (const visited of seen) packages.set(visited, cached)
|
|
212
|
+
return cached ?? undefined
|
|
213
|
+
}
|
|
214
|
+
seen.push(current)
|
|
215
|
+
const manifest = join(current, "package.json")
|
|
216
|
+
if (existsSync(manifest)) {
|
|
217
|
+
let parsed: { name?: unknown; nifra?: { environment?: unknown } } = {}
|
|
218
|
+
try {
|
|
219
|
+
parsed = JSON.parse(readFileSync(manifest, "utf8")) as typeof parsed
|
|
220
|
+
} catch {
|
|
221
|
+
// An unreadable manifest declares nothing; the caller reports the missing declaration.
|
|
222
|
+
}
|
|
223
|
+
const info: PackageInfo = {
|
|
224
|
+
dir: current,
|
|
225
|
+
name: typeof parsed.name === "string" ? parsed.name : undefined,
|
|
226
|
+
environment:
|
|
227
|
+
typeof parsed.nifra?.environment === "string" ? parsed.nifra.environment : undefined,
|
|
228
|
+
}
|
|
229
|
+
for (const visited of seen) packages.set(visited, info)
|
|
230
|
+
return info
|
|
231
|
+
}
|
|
232
|
+
const parent = toPosix(dirname(current))
|
|
233
|
+
if (parent === current) {
|
|
234
|
+
for (const visited of seen) packages.set(visited, null)
|
|
235
|
+
return undefined
|
|
236
|
+
}
|
|
237
|
+
current = parent
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const classifyUncached = (file: string): Classification => {
|
|
242
|
+
if (generated.has(file)) return { zone: "generated" }
|
|
243
|
+
const thirdParty = nodeModulesPackage(file)
|
|
244
|
+
if (thirdParty !== undefined) {
|
|
245
|
+
const info = packageFor(toPosix(dirname(file)))
|
|
246
|
+
const declared =
|
|
247
|
+
info?.environment !== undefined && PACKAGE_ENVIRONMENTS.has(info.environment)
|
|
248
|
+
? (info.environment as PackageEnvironment)
|
|
249
|
+
: undefined
|
|
250
|
+
return {
|
|
251
|
+
zone: "library",
|
|
252
|
+
packageName: thirdParty,
|
|
253
|
+
modulePath: file.slice(file.lastIndexOf("/node_modules/") + "/node_modules/".length),
|
|
254
|
+
...(declared ? { declared } : {}),
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const base = file.slice(file.lastIndexOf("/") + 1)
|
|
258
|
+
if (isInside(file, appRoot)) {
|
|
259
|
+
if (SERVER_MANIFEST_FILE.test(base) && isGeneratedServerManifest(file)) {
|
|
260
|
+
return { zone: "generated" }
|
|
261
|
+
}
|
|
262
|
+
if (LEGACY_SERVER_SUFFIX.test(base)) {
|
|
263
|
+
return {
|
|
264
|
+
zone: "error",
|
|
265
|
+
reason: `"${relativeTo(appRoot, file)}" uses the retired ".server" suffix. Rename it to ".backend" (or move it under backend/); nifra no longer empties .server modules in the browser build`,
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const rel = relativeTo(appRoot, file)
|
|
269
|
+
const top = rel.split("/")[0] ?? ""
|
|
270
|
+
if (top.startsWith(".nifra")) return { zone: "generated" }
|
|
271
|
+
const suffix = ZONE_SUFFIX.exec(base)?.[1] as
|
|
272
|
+
| "frontend"
|
|
273
|
+
| "backend"
|
|
274
|
+
| "shared"
|
|
275
|
+
| "fn"
|
|
276
|
+
| undefined
|
|
277
|
+
// Only a script module is replaced by the calls a browser makes; any other ".fn" file would
|
|
278
|
+
// ship to the browser exactly as written.
|
|
279
|
+
if (suffix === "fn" && !SERVER_FN_MODULE.test(base)) {
|
|
280
|
+
return {
|
|
281
|
+
zone: "error",
|
|
282
|
+
reason: `"${rel}" is named as a server function, but only a script module (.ts, .js) can be one - the browser build would ship this file as it is. Rename it`,
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (isInside(file, routesDir)) {
|
|
286
|
+
if (ROUTE_BACKEND_FILE.test(base)) return { zone: "route-backend" }
|
|
287
|
+
if (suffix === "fn") return { zone: "fn" }
|
|
288
|
+
if (suffix === "backend") return { zone: "route-backend" }
|
|
289
|
+
if (suffix === "shared") return { zone: "shared" }
|
|
290
|
+
return { zone: "route-frontend" }
|
|
291
|
+
}
|
|
292
|
+
const folder = ZONE_FOLDERS[top]
|
|
293
|
+
if (folder !== undefined) {
|
|
294
|
+
if (suffix === undefined || suffix === folder) return { zone: folder }
|
|
295
|
+
if (suffix === "fn" && folder === "backend") return { zone: "fn" }
|
|
296
|
+
return {
|
|
297
|
+
zone: "error",
|
|
298
|
+
reason: `"${rel}" sits in ${top}/ but its ".${suffix}" suffix says otherwise. A file belongs to one side: move it, or drop the suffix`,
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (suffix !== undefined) return { zone: suffix === "fn" ? "fn" : suffix }
|
|
302
|
+
return { zone: "error", reason: unzonedReason(rel) }
|
|
303
|
+
}
|
|
304
|
+
// First-party code outside the app: a workspace package, or a relative import out of the app.
|
|
305
|
+
const info = packageFor(toPosix(dirname(file)))
|
|
306
|
+
const environment = info?.environment
|
|
307
|
+
if (environment !== undefined && PACKAGE_ENVIRONMENTS.has(environment)) {
|
|
308
|
+
return environment === "library"
|
|
309
|
+
? {
|
|
310
|
+
zone: "library",
|
|
311
|
+
...(info?.name ? { packageName: info.name } : {}),
|
|
312
|
+
declared: "library",
|
|
313
|
+
}
|
|
314
|
+
: { zone: environment as Zone }
|
|
315
|
+
}
|
|
316
|
+
const where = info?.name ?? info?.dir ?? dirname(file)
|
|
317
|
+
return {
|
|
318
|
+
zone: "error",
|
|
319
|
+
reason: `"${file}" is first-party code outside the app (${where}) and declares no side. Add "nifra": { "environment": "frontend" | "backend" | "shared" | "library" } to ${info === undefined ? "its package.json" : `${info.dir}/package.json`}`,
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
appRoot,
|
|
325
|
+
classify(input: string): Classification {
|
|
326
|
+
const absolute = isAbsolute(input) ? input : resolve(appRoot, input)
|
|
327
|
+
const file = realFile(absolute)
|
|
328
|
+
const cached = cache.get(file)
|
|
329
|
+
if (cached !== undefined) return cached
|
|
330
|
+
const result = classifyUncached(file)
|
|
331
|
+
cache.set(file, result)
|
|
332
|
+
return result
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function relativeTo(root: string, file: string): string {
|
|
338
|
+
return toPosix(relative(root, file)).split(sep).join("/")
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const RETIRED_ROOT_FILES: Readonly<Record<string, string>> = {
|
|
342
|
+
"backend.ts": "backend/app.ts",
|
|
343
|
+
"framework.ts": "backend/framework.ts",
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function unzonedReason(rel: string): string {
|
|
347
|
+
const moved = RETIRED_ROOT_FILES[rel]
|
|
348
|
+
if (moved !== undefined) {
|
|
349
|
+
return `"${rel}" moved: it lives at ${moved} now (run \`nifra migrate layout\`)`
|
|
350
|
+
}
|
|
351
|
+
return `"${rel}" is in no zone. Every file a build loads lives under routes/, frontend/, backend/ or shared/, or carries a .frontend/.backend/.shared suffix`
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** The package name a bare specifier names, or `undefined` for a relative path, a builtin, a
|
|
355
|
+
* subpath import (`#x`) or a bundler-virtual id. */
|
|
356
|
+
export function specifierPackage(specifier: string): string | undefined {
|
|
357
|
+
if (/^(?:\.|\/|#|\0|[a-z]+:)/i.test(specifier)) return undefined
|
|
358
|
+
if (specifier.startsWith("@") && !/^@[^/]+\/[^/]/.test(specifier)) return undefined
|
|
359
|
+
const parts = specifier.split("/")
|
|
360
|
+
return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0]
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Why a classified file may not ship to a browser, or `undefined` when it may. A `drizzle-orm` driver
|
|
365
|
+
* entry is recognized by the import as written (`specifier`) or by the file's path in the package.
|
|
366
|
+
*/
|
|
367
|
+
export function browserDenial(
|
|
368
|
+
classification: Classification,
|
|
369
|
+
specifier?: string,
|
|
370
|
+
): string | undefined {
|
|
371
|
+
if (classification.zone === "error") return classification.reason
|
|
372
|
+
if (!BROWSER_ZONES.has(classification.zone)) {
|
|
373
|
+
return classification.zone === "route-backend"
|
|
374
|
+
? "it is a route's backend half, which runs on the server only"
|
|
375
|
+
: "it is backend code"
|
|
376
|
+
}
|
|
377
|
+
if (classification.zone !== "library") return undefined
|
|
378
|
+
const name = classification.packageName
|
|
379
|
+
if (classification.declared === "backend") return `package "${name}" declares itself backend-only`
|
|
380
|
+
if (name !== undefined && SERVER_PACKAGES.includes(name)) {
|
|
381
|
+
return `package "${name}" is server code (a driver, server SDK or credential helper)`
|
|
382
|
+
}
|
|
383
|
+
if (specifier !== undefined && SERVER_SUBPATHS.test(specifier)) {
|
|
384
|
+
return `"${specifier}" is a database driver entry`
|
|
385
|
+
}
|
|
386
|
+
const driver = classification.modulePath?.match(SERVER_SUBPATHS)?.[0]
|
|
387
|
+
if (driver !== undefined) return `"${driver.replace(/\/$/, "")}" is a database driver entry`
|
|
388
|
+
return undefined
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Whether a module in `from` may import (with a value import) a module in `to`. */
|
|
392
|
+
export function importAllowed(from: Zone, to: Zone): boolean {
|
|
393
|
+
if (from === "generated" || from === "library") return true
|
|
394
|
+
if (to === "generated" || to === "library") return true
|
|
395
|
+
if (FRONTEND_ZONES.has(from)) return FRONTEND_ZONES.has(to) || to === "shared" || to === "fn"
|
|
396
|
+
if (SERVER_ZONES.has(from)) return !FRONTEND_ZONES.has(to)
|
|
397
|
+
// shared
|
|
398
|
+
return to === "shared"
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** The message for an import {@link importAllowed} refuses. */
|
|
402
|
+
export function importRuleMessage(fromFile: string, from: Zone, toFile: string, to: Zone): string {
|
|
403
|
+
const side = (zone: Zone): string =>
|
|
404
|
+
zone === "route-frontend"
|
|
405
|
+
? "a route's frontend half"
|
|
406
|
+
: zone === "route-backend"
|
|
407
|
+
? "a route's backend half"
|
|
408
|
+
: zone === "fn"
|
|
409
|
+
? "a server-function module"
|
|
410
|
+
: `${zone} code`
|
|
411
|
+
const hint =
|
|
412
|
+
from === "shared"
|
|
413
|
+
? "shared code runs on both sides, so it may import only shared code and third-party packages"
|
|
414
|
+
: SERVER_ZONES.has(from)
|
|
415
|
+
? "backend code may not import frontend code; move what both need into shared/"
|
|
416
|
+
: "frontend code may not import backend code; reach it through a loader, an action or a *.fn.ts server function"
|
|
417
|
+
return `${fromFile} (${side(from)}) imports ${toFile} (${side(to)}): ${hint}. A type-only import (\`import type\`) is allowed`
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export { isSensitiveFieldName } from "./internal/output-guard.ts"
|
|
421
|
+
export { privateEnvReads, privateEnvReason } from "./internal/private-env.ts"
|
|
422
|
+
export {
|
|
423
|
+
publicScanFiles,
|
|
424
|
+
type SecretExemption,
|
|
425
|
+
type SecretFinding,
|
|
426
|
+
type SecretRule,
|
|
427
|
+
type SecretScanFile,
|
|
428
|
+
type SecretScanInput,
|
|
429
|
+
scanForSecrets,
|
|
430
|
+
} from "./internal/secret-scan.ts"
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@nifrajs/web/plugins/vite-server-only` - the Vite half of the `.server` convention.
|
|
3
|
-
*
|
|
4
|
-
* A module named `*.server.ts(x)` is server-only: the Bun client build empties it so its import
|
|
5
|
-
* subtree - `node:` builtins, native modules, secrets - never reaches a browser. This does the same
|
|
6
|
-
* for Vite, in both the dev server and the production build.
|
|
7
|
-
*
|
|
8
|
-
* ## Why this exists separately
|
|
9
|
-
*
|
|
10
|
-
* The convention was implemented once, as a `Bun.build` plugin, which meant it held in exactly one of
|
|
11
|
-
* the four pipelines. `nifra build` emptied the module; `nifra dev` (Vite), a Vite production build,
|
|
12
|
-
* and `nifra dev --bun` all shipped it whole. A guard that holds in one pipeline and not the others is
|
|
13
|
-
* worse than none, because the file name reads as protection everywhere.
|
|
14
|
-
*
|
|
15
|
-
* `*.fn` already had this shape - a Bun plugin, a Vite plugin, and a refusal in the one pipeline that
|
|
16
|
-
* cannot transform. `.server` now matches it.
|
|
17
|
-
*
|
|
18
|
-
* ## Why `transform` and not `load`
|
|
19
|
-
*
|
|
20
|
-
* Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
|
|
21
|
-
* `transform` receives the source Vite already read, and returning replacement code discards the
|
|
22
|
-
* original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts
|
|
23
|
-
* this ahead of framework transforms, which have no business seeing server code either.
|
|
24
|
-
*/
|
|
25
|
-
import { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement } from "../internal/server-boundary.js";
|
|
26
|
-
export { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement };
|
|
27
|
-
/** The slice of a Vite/Rollup plugin this returns. Structural, so `vite` stays an optional peer. */
|
|
28
|
-
export interface ServerOnlyEmptyPlugin {
|
|
29
|
-
readonly name: string;
|
|
30
|
-
readonly enforce: "pre";
|
|
31
|
-
/** Applied only to the CLIENT build; the server build keeps the real module. */
|
|
32
|
-
readonly applyToEnvironment?: (environment: {
|
|
33
|
-
readonly name: string;
|
|
34
|
-
}) => boolean;
|
|
35
|
-
transform(code: string, id: string, options?: {
|
|
36
|
-
readonly ssr?: boolean;
|
|
37
|
-
}): {
|
|
38
|
-
code: string;
|
|
39
|
-
map: null;
|
|
40
|
-
} | null;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Empty every `*.server` module in the client build.
|
|
44
|
-
*
|
|
45
|
-
* Client-only by construction: nifra passes this to the client build and the dev server, never to the
|
|
46
|
-
* SSR build, where the real module is what runs.
|
|
47
|
-
*/
|
|
48
|
-
export declare function viteServerOnlyEmpty(): ServerOnlyEmptyPlugin;
|
|
49
|
-
//# sourceMappingURL=vite-server-only.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vite-server-only.d.ts","sourceRoot":"","sources":["../../src/plugins/vite-server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EAC1B,MAAM,gCAAgC,CAAA;AAIvC,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAA;AAEjF,oGAAoG;AACpG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB,gFAAgF;IAChF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IACjF,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACtC;AAKD;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,qBAAqB,CAiB3D"}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@nifrajs/web/plugins/vite-server-only` - the Vite half of the `.server` convention.
|
|
3
|
-
*
|
|
4
|
-
* A module named `*.server.ts(x)` is server-only: the Bun client build empties it so its import
|
|
5
|
-
* subtree - `node:` builtins, native modules, secrets - never reaches a browser. This does the same
|
|
6
|
-
* for Vite, in both the dev server and the production build.
|
|
7
|
-
*
|
|
8
|
-
* ## Why this exists separately
|
|
9
|
-
*
|
|
10
|
-
* The convention was implemented once, as a `Bun.build` plugin, which meant it held in exactly one of
|
|
11
|
-
* the four pipelines. `nifra build` emptied the module; `nifra dev` (Vite), a Vite production build,
|
|
12
|
-
* and `nifra dev --bun` all shipped it whole. A guard that holds in one pipeline and not the others is
|
|
13
|
-
* worse than none, because the file name reads as protection everywhere.
|
|
14
|
-
*
|
|
15
|
-
* `*.fn` already had this shape - a Bun plugin, a Vite plugin, and a refusal in the one pipeline that
|
|
16
|
-
* cannot transform. `.server` now matches it.
|
|
17
|
-
*
|
|
18
|
-
* ## Why `transform` and not `load`
|
|
19
|
-
*
|
|
20
|
-
* Vite resolves and reads the file itself; taking `load` would mean re-implementing its resolution.
|
|
21
|
-
* `transform` receives the source Vite already read, and returning replacement code discards the
|
|
22
|
-
* original along with its imports - so the server subtree is never followed. `enforce: "pre"` puts
|
|
23
|
-
* this ahead of framework transforms, which have no business seeing server code either.
|
|
24
|
-
*/
|
|
25
|
-
import { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement, } from "../internal/server-boundary.js";
|
|
26
|
-
// Re-exported so a caller wiring its own bundler gets the matcher from the same owner the pipelines
|
|
27
|
-
// use, rather than writing a fifth copy of the regex.
|
|
28
|
-
export { SERVER_ONLY_MODULE, SERVER_ONLY_REPLACEMENT, viteServerOnlyReplacement };
|
|
29
|
-
/** Strips a Vite id's `?query` and `#hash` so the suffix test sees a plain path. */
|
|
30
|
-
const bare = (id) => id.split("?")[0]?.split("#")[0] ?? id;
|
|
31
|
-
/**
|
|
32
|
-
* Empty every `*.server` module in the client build.
|
|
33
|
-
*
|
|
34
|
-
* Client-only by construction: nifra passes this to the client build and the dev server, never to the
|
|
35
|
-
* SSR build, where the real module is what runs.
|
|
36
|
-
*/
|
|
37
|
-
export function viteServerOnlyEmpty() {
|
|
38
|
-
return {
|
|
39
|
-
name: "nifra:server-only-empty",
|
|
40
|
-
enforce: "pre",
|
|
41
|
-
// Vite 6+ environments: the SSR environment must keep the real module. Older Vite ignores this and
|
|
42
|
-
// relies on nifra only registering the plugin on the client side.
|
|
43
|
-
applyToEnvironment: (environment) => environment.name === "client",
|
|
44
|
-
transform(code, id, options) {
|
|
45
|
-
// Vite 5 ignores `applyToEnvironment` but supplies this transform context for SSR.
|
|
46
|
-
if (options?.ssr === true)
|
|
47
|
-
return null;
|
|
48
|
-
const path = bare(id);
|
|
49
|
-
if (!SERVER_ONLY_MODULE.test(path))
|
|
50
|
-
return null;
|
|
51
|
-
// `map: null` is honest rather than lazy: the replacement shares no lines with the source, so any
|
|
52
|
-
// mapping would point a debugger at unrelated code.
|
|
53
|
-
return { code: viteServerOnlyReplacement(code), map: null };
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=vite-server-only.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vite-server-only.js","sourceRoot":"","sources":["../../src/plugins/vite-server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAA;AAEvC,oGAAoG;AACpG,sDAAsD;AACtD,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAA;AAejF,oFAAoF;AACpF,MAAM,IAAI,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,KAAK;QACd,mGAAmG;QACnG,kEAAkE;QAClE,kBAAkB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ;QAClE,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO;YACzB,mFAAmF;YACnF,IAAI,OAAO,EAAE,GAAG,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAA;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;YACrB,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC/C,kGAAkG;YAClG,oDAAoD;YACpD,OAAO,EAAE,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QAC7D,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/server-only.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@nifrajs/web/server-only` - the **explicit, opt-in poison-import marker** (Next's `import
|
|
3
|
-
* "server-only"`). A module of PURE server logic that carries no `node:` import - a secret-bearing
|
|
4
|
-
* constant, a server-only API call - can't be caught by nifra's other two client-leak guards: the
|
|
5
|
-
* `.server` convention (which empties `*.server` modules in the client build) needs the file to be
|
|
6
|
-
* NAMED `*.server`, and the node-builtin guard only fires when a `node:` import actually reaches a
|
|
7
|
-
* client chunk. This marker closes that gap: drop a side-effect import at the top of the module -
|
|
8
|
-
*
|
|
9
|
-
* import "@nifrajs/web/server-only"
|
|
10
|
-
*
|
|
11
|
-
* - and the CLIENT build ({@link buildClient}) FAILS LOUD, naming the import chain, if that module
|
|
12
|
-
* ever lands in a browser chunk. On the SERVER build/runtime it's a harmless no-op (this empty
|
|
13
|
-
* module), so the same file runs untouched server-side.
|
|
14
|
-
*
|
|
15
|
-
* Pair it with the type-level {@link ServerOnly} brand (from `@nifrajs/web`) on the module's exports
|
|
16
|
-
* to signal intent to readers + the compiler; this runtime import is what actually fails the build.
|
|
17
|
-
*
|
|
18
|
-
* @see ServerOnly - the type-level intent marker that complements this runtime import.
|
|
19
|
-
*/
|
|
20
|
-
export {};
|
|
21
|
-
//# sourceMappingURL=server-only.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server-only.d.ts","sourceRoot":"","sources":["../src/server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,OAAO,EAAE,CAAA"}
|
package/dist/server-only.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `@nifrajs/web/server-only` - the **explicit, opt-in poison-import marker** (Next's `import
|
|
3
|
-
* "server-only"`). A module of PURE server logic that carries no `node:` import - a secret-bearing
|
|
4
|
-
* constant, a server-only API call - can't be caught by nifra's other two client-leak guards: the
|
|
5
|
-
* `.server` convention (which empties `*.server` modules in the client build) needs the file to be
|
|
6
|
-
* NAMED `*.server`, and the node-builtin guard only fires when a `node:` import actually reaches a
|
|
7
|
-
* client chunk. This marker closes that gap: drop a side-effect import at the top of the module -
|
|
8
|
-
*
|
|
9
|
-
* import "@nifrajs/web/server-only"
|
|
10
|
-
*
|
|
11
|
-
* - and the CLIENT build ({@link buildClient}) FAILS LOUD, naming the import chain, if that module
|
|
12
|
-
* ever lands in a browser chunk. On the SERVER build/runtime it's a harmless no-op (this empty
|
|
13
|
-
* module), so the same file runs untouched server-side.
|
|
14
|
-
*
|
|
15
|
-
* Pair it with the type-level {@link ServerOnly} brand (from `@nifrajs/web`) on the module's exports
|
|
16
|
-
* to signal intent to readers + the compiler; this runtime import is what actually fails the build.
|
|
17
|
-
*
|
|
18
|
-
* @see ServerOnly - the type-level intent marker that complements this runtime import.
|
|
19
|
-
*/
|
|
20
|
-
// Intentionally empty. The marker is the IMPORT itself: `buildClient` detects any module that imports
|
|
21
|
-
// this specifier and lands in a client chunk (via the same Bun metafile graph the node-builtin guard
|
|
22
|
-
// walks) and fails the build. There is no value to export and nothing to run - on the server it's a
|
|
23
|
-
// no-op so the marked module behaves normally; on the client the build never completes.
|
|
24
|
-
export {};
|
|
25
|
-
//# sourceMappingURL=server-only.js.map
|
package/dist/server-only.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server-only.js","sourceRoot":"","sources":["../src/server-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,sGAAsG;AACtG,qGAAqG;AACrG,oGAAoG;AACpG,wFAAwF;AACxF,OAAO,EAAE,CAAA"}
|