@nifrajs/web 4.0.0 → 5.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/README.md +11 -0
- package/dist/build-vite.d.ts +19 -0
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +31 -10
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +8 -10
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +90 -31
- package/dist/build.js.map +1 -1
- package/dist/cdn.d.ts +8 -0
- package/dist/cdn.d.ts.map +1 -1
- package/dist/cdn.js +14 -15
- package/dist/cdn.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +49 -26
- package/dist/client.js.map +1 -1
- package/dist/deferred.d.ts +2 -2
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +28 -7
- package/dist/deferred.js.map +1 -1
- package/dist/dev-feed.d.ts.map +1 -1
- package/dist/dev-feed.js +24 -5
- package/dist/dev-feed.js.map +1 -1
- package/dist/dev-session.d.ts.map +1 -1
- package/dist/dev-session.js +4 -14
- package/dist/dev-session.js.map +1 -1
- package/dist/dev.d.ts +7 -17
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +13 -1
- package/dist/dev.js.map +1 -1
- package/dist/draft.d.ts +8 -5
- package/dist/draft.d.ts.map +1 -1
- package/dist/draft.js +30 -13
- package/dist/draft.js.map +1 -1
- package/dist/fonts-google.d.ts.map +1 -1
- package/dist/fonts-google.js +7 -0
- package/dist/fonts-google.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/codegen.d.ts +1 -0
- package/dist/internal/codegen.d.ts.map +1 -1
- package/dist/internal/codegen.js +56 -29
- package/dist/internal/codegen.js.map +1 -1
- package/dist/internal/disposable-dir.d.ts +8 -0
- package/dist/internal/disposable-dir.d.ts.map +1 -0
- package/dist/internal/disposable-dir.js +25 -0
- package/dist/internal/disposable-dir.js.map +1 -0
- package/dist/internal/entry-chunks.d.ts +9 -0
- package/dist/internal/entry-chunks.d.ts.map +1 -0
- package/dist/internal/entry-chunks.js +15 -0
- package/dist/internal/entry-chunks.js.map +1 -0
- package/dist/internal/evidence-traversal.d.ts +12 -0
- package/dist/internal/evidence-traversal.d.ts.map +1 -0
- package/dist/internal/evidence-traversal.js +48 -0
- package/dist/internal/evidence-traversal.js.map +1 -0
- package/dist/internal/head-merge.d.ts +6 -0
- package/dist/internal/head-merge.d.ts.map +1 -1
- package/dist/internal/head-merge.js +9 -0
- package/dist/internal/head-merge.js.map +1 -1
- package/dist/internal/js-string.d.ts +2 -0
- package/dist/internal/js-string.d.ts.map +1 -1
- package/dist/internal/js-string.js +5 -1
- package/dist/internal/js-string.js.map +1 -1
- package/dist/internal/mount-shadow.js +1 -1
- package/dist/internal/mount-shadow.js.map +1 -1
- package/dist/internal/output-guard.d.ts +7 -0
- package/dist/internal/output-guard.d.ts.map +1 -1
- package/dist/internal/output-guard.js +80 -8
- package/dist/internal/output-guard.js.map +1 -1
- package/dist/internal/page-execution.d.ts +6 -0
- package/dist/internal/page-execution.d.ts.map +1 -1
- package/dist/internal/page-execution.js +377 -108
- package/dist/internal/page-execution.js.map +1 -1
- package/dist/internal/progressive-cookies.d.ts +14 -0
- package/dist/internal/progressive-cookies.d.ts.map +1 -0
- package/dist/internal/progressive-cookies.js +43 -0
- package/dist/internal/progressive-cookies.js.map +1 -0
- package/dist/internal/public-env.d.ts +2 -0
- package/dist/internal/public-env.d.ts.map +1 -0
- package/dist/internal/public-env.js +13 -0
- package/dist/internal/public-env.js.map +1 -0
- package/dist/internal/render-document.d.ts +1 -0
- package/dist/internal/render-document.d.ts.map +1 -1
- package/dist/internal/render-document.js +28 -5
- package/dist/internal/render-document.js.map +1 -1
- package/dist/internal/response-controls.d.ts +2 -0
- package/dist/internal/response-controls.d.ts.map +1 -1
- package/dist/internal/response-controls.js +26 -16
- package/dist/internal/response-controls.js.map +1 -1
- package/dist/internal/revive-dates.d.ts +16 -0
- package/dist/internal/revive-dates.d.ts.map +1 -0
- package/dist/internal/revive-dates.js +32 -0
- package/dist/internal/revive-dates.js.map +1 -0
- package/dist/internal/route-path.d.ts +8 -0
- package/dist/internal/route-path.d.ts.map +1 -0
- package/dist/internal/route-path.js +8 -0
- package/dist/internal/route-path.js.map +1 -0
- package/dist/internal/secret-scan.d.ts.map +1 -1
- package/dist/internal/secret-scan.js +105 -19
- package/dist/internal/secret-scan.js.map +1 -1
- package/dist/internal/server-boundary.d.ts.map +1 -1
- package/dist/internal/server-boundary.js +33 -5
- package/dist/internal/server-boundary.js.map +1 -1
- package/dist/internal/web-app.d.ts +21 -16
- package/dist/internal/web-app.d.ts.map +1 -1
- package/dist/internal/web-app.js +73 -21
- package/dist/internal/web-app.js.map +1 -1
- package/dist/internal/wire-date.d.ts +3 -0
- package/dist/internal/wire-date.d.ts.map +1 -0
- package/dist/internal/wire-date.js +14 -0
- package/dist/internal/wire-date.js.map +1 -0
- package/dist/internal/zone-graph.d.ts +4 -0
- package/dist/internal/zone-graph.d.ts.map +1 -1
- package/dist/internal/zone-graph.js +54 -14
- package/dist/internal/zone-graph.js.map +1 -1
- package/dist/isr.d.ts +58 -2
- package/dist/isr.d.ts.map +1 -1
- package/dist/isr.js +296 -39
- package/dist/isr.js.map +1 -1
- package/dist/llms-txt.d.ts +2 -0
- package/dist/llms-txt.d.ts.map +1 -1
- package/dist/llms-txt.js +29 -3
- package/dist/llms-txt.js.map +1 -1
- package/dist/manifest.d.ts +28 -4
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +14 -3
- package/dist/manifest.js.map +1 -1
- package/dist/open-resolved.d.ts +18 -0
- package/dist/open-resolved.d.ts.map +1 -0
- package/dist/open-resolved.js +62 -0
- package/dist/open-resolved.js.map +1 -0
- package/dist/plugins/kit.d.ts +1 -0
- package/dist/plugins/kit.d.ts.map +1 -1
- package/dist/plugins/kit.js +26 -11
- package/dist/plugins/kit.js.map +1 -1
- package/dist/plugins/vite-leak-guard.d.ts +3 -1
- package/dist/plugins/vite-leak-guard.d.ts.map +1 -1
- package/dist/plugins/vite-leak-guard.js +4 -0
- package/dist/plugins/vite-leak-guard.js.map +1 -1
- package/dist/plugins/zone-guard.d.ts.map +1 -1
- package/dist/plugins/zone-guard.js +10 -2
- package/dist/plugins/zone-guard.js.map +1 -1
- package/dist/public-dir.d.ts +8 -1
- package/dist/public-dir.d.ts.map +1 -1
- package/dist/public-dir.js +17 -6
- package/dist/public-dir.js.map +1 -1
- package/dist/query.d.ts +13 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +89 -26
- package/dist/query.js.map +1 -1
- package/dist/render-seam.d.ts +2 -0
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +1 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/route-types.d.ts.map +1 -1
- package/dist/route-types.js +20 -9
- package/dist/route-types.js.map +1 -1
- package/dist/router.d.ts +9 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +39 -46
- package/dist/router.js.map +1 -1
- package/dist/server-components/build-info.d.ts +3 -0
- package/dist/server-components/build-info.d.ts.map +1 -0
- package/dist/server-components/build-info.js +7 -0
- package/dist/server-components/build-info.js.map +1 -0
- package/dist/server-components/build-target.d.ts +12 -0
- package/dist/server-components/build-target.d.ts.map +1 -0
- package/dist/server-components/build-target.js +153 -0
- package/dist/server-components/build-target.js.map +1 -0
- package/dist/server-components/build.d.ts +24 -0
- package/dist/server-components/build.d.ts.map +1 -0
- package/dist/server-components/build.js +366 -0
- package/dist/server-components/build.js.map +1 -0
- package/dist/server-components/bundle-proof.d.ts +3 -0
- package/dist/server-components/bundle-proof.d.ts.map +1 -0
- package/dist/server-components/bundle-proof.js +23 -0
- package/dist/server-components/bundle-proof.js.map +1 -0
- package/dist/server-components/client.d.ts +46 -0
- package/dist/server-components/client.d.ts.map +1 -0
- package/dist/server-components/client.js +594 -0
- package/dist/server-components/client.js.map +1 -0
- package/dist/server-components/definition.d.ts +11 -0
- package/dist/server-components/definition.d.ts.map +1 -0
- package/dist/server-components/definition.js +94 -0
- package/dist/server-components/definition.js.map +1 -0
- package/dist/server-components/dev-http.d.ts +5 -0
- package/dist/server-components/dev-http.d.ts.map +1 -0
- package/dist/server-components/dev-http.js +71 -0
- package/dist/server-components/dev-http.js.map +1 -0
- package/dist/server-components/error.d.ts +9 -0
- package/dist/server-components/error.d.ts.map +1 -0
- package/dist/server-components/error.js +24 -0
- package/dist/server-components/error.js.map +1 -0
- package/dist/server-components/frames.d.ts +7 -0
- package/dist/server-components/frames.d.ts.map +1 -0
- package/dist/server-components/frames.js +169 -0
- package/dist/server-components/frames.js.map +1 -0
- package/dist/server-components/html-policy.d.ts +6 -0
- package/dist/server-components/html-policy.d.ts.map +1 -0
- package/dist/server-components/html-policy.js +75 -0
- package/dist/server-components/html-policy.js.map +1 -0
- package/dist/server-components/html.d.ts +4 -0
- package/dist/server-components/html.d.ts.map +1 -0
- package/dist/server-components/html.js +67 -0
- package/dist/server-components/html.js.map +1 -0
- package/dist/server-components/islands.d.ts +44 -0
- package/dist/server-components/islands.d.ts.map +1 -0
- package/dist/server-components/islands.js +42 -0
- package/dist/server-components/islands.js.map +1 -0
- package/dist/server-components/native.d.ts +4 -0
- package/dist/server-components/native.d.ts.map +1 -0
- package/dist/server-components/native.js +81 -0
- package/dist/server-components/native.js.map +1 -0
- package/dist/server-components/portal.d.ts +6 -0
- package/dist/server-components/portal.d.ts.map +1 -0
- package/dist/server-components/portal.js +35 -0
- package/dist/server-components/portal.js.map +1 -0
- package/dist/server-components/props.d.ts +3 -0
- package/dist/server-components/props.d.ts.map +1 -0
- package/dist/server-components/props.js +14 -0
- package/dist/server-components/props.js.map +1 -0
- package/dist/server-components/protocol.d.ts +44 -0
- package/dist/server-components/protocol.d.ts.map +1 -0
- package/dist/server-components/protocol.js +139 -0
- package/dist/server-components/protocol.js.map +1 -0
- package/dist/server-components/range.d.ts +24 -0
- package/dist/server-components/range.d.ts.map +1 -0
- package/dist/server-components/range.js +263 -0
- package/dist/server-components/range.js.map +1 -0
- package/dist/server-components/registry.d.ts +6 -0
- package/dist/server-components/registry.d.ts.map +1 -0
- package/dist/server-components/registry.js +30 -0
- package/dist/server-components/registry.js.map +1 -0
- package/dist/server-components/response.d.ts +4 -0
- package/dist/server-components/response.d.ts.map +1 -0
- package/dist/server-components/response.js +32 -0
- package/dist/server-components/response.js.map +1 -0
- package/dist/server-components/router-integration.d.ts +9 -0
- package/dist/server-components/router-integration.d.ts.map +1 -0
- package/dist/server-components/router-integration.js +29 -0
- package/dist/server-components/router-integration.js.map +1 -0
- package/dist/server-components/runtime.d.ts +14 -0
- package/dist/server-components/runtime.d.ts.map +1 -0
- package/dist/server-components/runtime.js +276 -0
- package/dist/server-components/runtime.js.map +1 -0
- package/dist/server-components/scheduler.d.ts +14 -0
- package/dist/server-components/scheduler.d.ts.map +1 -0
- package/dist/server-components/scheduler.js +74 -0
- package/dist/server-components/scheduler.js.map +1 -0
- package/dist/server-components/schema.d.ts +4 -0
- package/dist/server-components/schema.d.ts.map +1 -0
- package/dist/server-components/schema.js +77 -0
- package/dist/server-components/schema.js.map +1 -0
- package/dist/server-components/server-entry.d.ts +12 -0
- package/dist/server-components/server-entry.d.ts.map +1 -0
- package/dist/server-components/server-entry.js +90 -0
- package/dist/server-components/server-entry.js.map +1 -0
- package/dist/server-components/source.d.ts +15 -0
- package/dist/server-components/source.d.ts.map +1 -0
- package/dist/server-components/source.js +269 -0
- package/dist/server-components/source.js.map +1 -0
- package/dist/server-components/state.d.ts +4 -0
- package/dist/server-components/state.d.ts.map +1 -0
- package/dist/server-components/state.js +123 -0
- package/dist/server-components/state.js.map +1 -0
- package/dist/server-components/stream.d.ts +6 -0
- package/dist/server-components/stream.d.ts.map +1 -0
- package/dist/server-components/stream.js +70 -0
- package/dist/server-components/stream.js.map +1 -0
- package/dist/server-components/trusted-html.d.ts +3 -0
- package/dist/server-components/trusted-html.d.ts.map +1 -0
- package/dist/server-components/trusted-html.js +14 -0
- package/dist/server-components/trusted-html.js.map +1 -0
- package/dist/server-components/types.d.ts +107 -0
- package/dist/server-components/types.d.ts.map +1 -0
- package/dist/server-components/types.js +2 -0
- package/dist/server-components/types.js.map +1 -0
- package/dist/server-components/typescript.d.ts +11 -0
- package/dist/server-components/typescript.d.ts.map +1 -0
- package/dist/server-components/typescript.js +28 -0
- package/dist/server-components/typescript.js.map +1 -0
- package/dist/server-components/value.d.ts +5 -0
- package/dist/server-components/value.d.ts.map +1 -0
- package/dist/server-components/value.js +77 -0
- package/dist/server-components/value.js.map +1 -0
- package/dist/server-components/vite.d.ts +12 -0
- package/dist/server-components/vite.d.ts.map +1 -0
- package/dist/server-components/vite.js +197 -0
- package/dist/server-components/vite.js.map +1 -0
- package/dist/server-components.d.ts +5 -0
- package/dist/server-components.d.ts.map +1 -0
- package/dist/server-components.js +4 -0
- package/dist/server-components.js.map +1 -0
- package/dist/service-worker.d.ts +8 -1
- package/dist/service-worker.d.ts.map +1 -1
- package/dist/service-worker.js +20 -6
- package/dist/service-worker.js.map +1 -1
- package/dist/vite.d.ts +12 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +77 -17
- package/dist/vite.js.map +1 -1
- package/dist/zones.d.ts +40 -0
- package/dist/zones.d.ts.map +1 -1
- package/dist/zones.js +170 -6
- package/dist/zones.js.map +1 -1
- package/package.json +97 -4
- package/src/build-vite.ts +31 -9
- package/src/build.ts +109 -31
- package/src/cdn.ts +18 -12
- package/src/client.ts +48 -23
- package/src/deferred.ts +27 -9
- package/src/dev-feed.ts +24 -5
- package/src/dev-session.ts +4 -13
- package/src/dev.ts +29 -1
- package/src/draft.ts +35 -14
- package/src/fonts-google.ts +10 -0
- package/src/index.ts +3 -2
- package/src/internal/codegen.ts +60 -29
- package/src/internal/disposable-dir.ts +24 -0
- package/src/internal/entry-chunks.ts +18 -0
- package/src/internal/evidence-traversal.ts +57 -0
- package/src/internal/head-merge.ts +16 -0
- package/src/internal/js-string.ts +6 -1
- package/src/internal/mount-shadow.ts +1 -1
- package/src/internal/output-guard.ts +88 -7
- package/src/internal/page-execution.ts +506 -108
- package/src/internal/progressive-cookies.ts +60 -0
- package/src/internal/public-env.ts +14 -0
- package/src/internal/render-document.ts +26 -11
- package/src/internal/response-controls.ts +31 -14
- package/src/internal/revive-dates.ts +35 -0
- package/src/internal/route-path.ts +8 -0
- package/src/internal/secret-scan.ts +104 -17
- package/src/internal/server-boundary.ts +29 -5
- package/src/internal/web-app.ts +116 -37
- package/src/internal/wire-date.ts +16 -0
- package/src/internal/zone-graph.ts +59 -19
- package/src/isr.ts +351 -43
- package/src/llms-txt.ts +32 -3
- package/src/manifest.ts +43 -7
- package/src/open-resolved.ts +64 -0
- package/src/plugins/kit.ts +31 -11
- package/src/plugins/vite-leak-guard.ts +5 -1
- package/src/plugins/zone-guard.ts +11 -2
- package/src/public-dir.ts +27 -8
- package/src/query.ts +107 -26
- package/src/render-seam.ts +2 -0
- package/src/route-types.ts +34 -7
- package/src/router.ts +58 -55
- package/src/server-components/build-info.ts +11 -0
- package/src/server-components/build-target.ts +194 -0
- package/src/server-components/build.ts +419 -0
- package/src/server-components/bundle-proof.ts +33 -0
- package/src/server-components/client.ts +663 -0
- package/src/server-components/definition.ts +125 -0
- package/src/server-components/dev-http.ts +75 -0
- package/src/server-components/error.ts +24 -0
- package/src/server-components/frames.ts +181 -0
- package/src/server-components/html-policy.ts +94 -0
- package/src/server-components/html.ts +81 -0
- package/src/server-components/islands.ts +111 -0
- package/src/server-components/native.ts +101 -0
- package/src/server-components/portal.ts +42 -0
- package/src/server-components/props.ts +19 -0
- package/src/server-components/protocol.ts +167 -0
- package/src/server-components/range.ts +313 -0
- package/src/server-components/registry.ts +34 -0
- package/src/server-components/response.ts +44 -0
- package/src/server-components/router-integration.ts +40 -0
- package/src/server-components/runtime.ts +376 -0
- package/src/server-components/scheduler.ts +84 -0
- package/src/server-components/schema.ts +69 -0
- package/src/server-components/server-entry.ts +98 -0
- package/src/server-components/source.ts +324 -0
- package/src/server-components/state.ts +143 -0
- package/src/server-components/stream.ts +71 -0
- package/src/server-components/trusted-html.ts +21 -0
- package/src/server-components/types.ts +138 -0
- package/src/server-components/typescript.ts +39 -0
- package/src/server-components/value.ts +82 -0
- package/src/server-components/vite.ts +242 -0
- package/src/server-components.ts +32 -0
- package/src/service-worker.ts +24 -7
- package/src/vite.ts +68 -17
- package/src/zones.ts +208 -7
package/src/internal/web-app.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { emitRequestErrorLog } from "@nifrajs/core/edge-kit"
|
|
2
|
+
import { jsonLogger } from "@nifrajs/core/logger"
|
|
1
3
|
import {
|
|
2
4
|
type BackendEvidenceProvider,
|
|
3
5
|
type BackendMount,
|
|
@@ -16,12 +18,15 @@ import { CACHE_CHANNEL } from "../isr.ts"
|
|
|
16
18
|
import { generateLlmsTxt } from "../llms-txt.ts"
|
|
17
19
|
import type { Manifest } from "../manifest.ts"
|
|
18
20
|
import type { RenderAdapter } from "../render-seam.ts"
|
|
19
|
-
import { PRERENDERED_LIST_PATH } from "../router.ts"
|
|
21
|
+
import { createMatcher, PRERENDERED_LIST_PATH } from "../router.ts"
|
|
22
|
+
import type { EvidenceAncestry } from "./evidence-traversal.ts"
|
|
20
23
|
import { formatShadowedPages, shadowedPages } from "./mount-shadow.ts"
|
|
21
24
|
import { createPageRequestExecutor, type NonceResolver } from "./page-execution.ts"
|
|
25
|
+
import { requestPattern } from "./route-path.ts"
|
|
22
26
|
|
|
23
27
|
export type { NonceResolver } from "./page-execution.ts"
|
|
24
28
|
export interface CreateWebAppOptions<Env = unknown> {
|
|
29
|
+
readonly serverComponents?: import("../server-components/types.ts").ServerComponentRuntime
|
|
25
30
|
readonly adapter: RenderAdapter
|
|
26
31
|
readonly manifest: Manifest
|
|
27
32
|
/** URL of the built client entry (module script) injected into every page. */
|
|
@@ -78,9 +83,10 @@ export interface CreateWebAppOptions<Env = unknown> {
|
|
|
78
83
|
*
|
|
79
84
|
* **Why the timing matters.** `beforeHandle`, `afterHandle`, `around`, `derive`/`decorate`, and
|
|
80
85
|
* `onError` are snapshotted into each route AS IT IS DECLARED, and `createWebApp` declares every
|
|
81
|
-
* page (plus the `/*` catch-all) before it returns - so a caller's `app.use(…)` afterwards
|
|
82
|
-
* them to
|
|
83
|
-
*
|
|
86
|
+
* page (plus the `/*` catch-all) before it returns - so a caller's `app.use(…)` afterwards would
|
|
87
|
+
* bind them to no page, and outside production it throws (`HOOK_AFTER_PAGES`). `requestId()` is
|
|
88
|
+
* exactly that case: it is a `derive`, so applied late it would leave every page without
|
|
89
|
+
* `c.requestId`. A `group()` is its own scope and stays allowed. Route assurance evidence is order-scoped the same
|
|
84
90
|
* way, so a late `securityHeaders()` leaves the pages unable to PROVE the header to `nifra assure`
|
|
85
91
|
* even though it does still set it.
|
|
86
92
|
*
|
|
@@ -101,10 +107,11 @@ export interface CreateWebAppOptions<Env = unknown> {
|
|
|
101
107
|
* per-route via `@nifrajs/client`'s `createRoutes`). Opaque to the core.
|
|
102
108
|
*
|
|
103
109
|
* **Auto-mount.** Every `inProcessClient(backend)` exposes the symbol-keyed platform-aware backend
|
|
104
|
-
* mount interface from `@nifrajs/core/mount`; `createWebApp` also serves
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
110
|
+
* mount interface from `@nifrajs/core/mount`; given an {@link apiPrefix}, `createWebApp` also serves
|
|
111
|
+
* that backend over HTTP there: a request whose pathname starts with the prefix is dispatched before
|
|
112
|
+
* page routing with the same `env`/`waitUntil` platform context, and the backend's `Response` is
|
|
113
|
+
* returned untouched. The mount runs in `nifra dev` too. Without `apiPrefix` the backend is
|
|
114
|
+
* loader-only.
|
|
108
115
|
*
|
|
109
116
|
* **Request-scoped loader calls.** While a page renders, its loaders, actions and boundaries get a
|
|
110
117
|
* `ctx.api` bound to that request's platform identity: a backend handler reached through it sees the
|
|
@@ -114,12 +121,12 @@ export interface CreateWebAppOptions<Env = unknown> {
|
|
|
114
121
|
* does not lose it. Headers do NOT travel: the page request's `cookie` and `authorization` are never
|
|
115
122
|
* copied, so a loader call is anonymous unless the loader passes headers on the call itself. */
|
|
116
123
|
readonly api?: unknown
|
|
117
|
-
/** HTTP path prefix the {@link api} backend is
|
|
124
|
+
/** HTTP path prefix the {@link api} backend is mounted at, such as `"/api"`. A request whose
|
|
118
125
|
* pathname is exactly the prefix or starts with `prefix + "/"` is dispatched to the backend before
|
|
119
126
|
* page routing; the backend therefore defines its routes at the **full** path (`server().post("/api/
|
|
120
|
-
* sync", …)`), matching the in-process `inProcessClient` call sites.
|
|
121
|
-
*
|
|
122
|
-
*
|
|
127
|
+
* sync", …)`), matching the in-process `inProcessClient` call sites. Unset or `""`, nothing is
|
|
128
|
+
* mounted: the app serves pages only and `api` stays a loader-only `ctx.api`. Mounting is also a
|
|
129
|
+
* no-op when `api` does not expose the symbol mount.
|
|
123
130
|
*
|
|
124
131
|
* A page file under the prefix (`routes/api/report.tsx`) could never render - the backend answers
|
|
125
132
|
* first and its 404 is final - so `createWebApp` throws, naming the file. The same holds for every
|
|
@@ -146,7 +153,8 @@ export interface CreateWebAppOptions<Env = unknown> {
|
|
|
146
153
|
* Tried longest-path-first and BEFORE the `api` mount, so a mount at `/api/auth` wins over a backend
|
|
147
154
|
* at `/api` no matter which was declared first. `stripPrefix` is the per-mount form of {@link apiStrip}:
|
|
148
155
|
* leave it off to pass the full path through. `opaque` states why a mount whose routes nifra cannot
|
|
149
|
-
* read (better-auth's handler) is outside assurance; it is then listed as a known gap
|
|
156
|
+
* read (better-auth's handler) is outside assurance; it is then listed as a known gap, with any
|
|
157
|
+
* routes it declares reported as declared.
|
|
150
158
|
*/
|
|
151
159
|
readonly mounts?: ReadonlyArray<{
|
|
152
160
|
readonly path: string
|
|
@@ -154,7 +162,7 @@ export interface CreateWebAppOptions<Env = unknown> {
|
|
|
154
162
|
readonly stripPrefix?: boolean
|
|
155
163
|
readonly priority?: number
|
|
156
164
|
readonly fallbackOn?: 404
|
|
157
|
-
readonly opaque?:
|
|
165
|
+
readonly opaque?: MountOptions<Env>["opaque"]
|
|
158
166
|
}>
|
|
159
167
|
/** Secret for **draft / preview mode** (see `enableDraft`). When set, a request carrying a valid
|
|
160
168
|
* signed `__nifra_draft` cookie gets `ctx.draft === true` in loaders/actions (else always `false`).
|
|
@@ -204,8 +212,9 @@ export interface CreateWebAppOptions<Env = unknown> {
|
|
|
204
212
|
readonly staticBoundaryCache?: import("../boundary.ts").StaticBoundaryCache
|
|
205
213
|
/** Observe every loader/action failure - for error-reporting plugins (Sentry-style). Called for
|
|
206
214
|
* real throws (not control-flow `Response`s like `redirect`), **before** the nearest `_error`
|
|
207
|
-
* boundary renders / a soft-nav 500 / a rethrow
|
|
208
|
-
*
|
|
215
|
+
* boundary renders / a soft-nav 500 / a rethrow. A failure a boundary answers is also written to the
|
|
216
|
+
* server's `logger` (under its `errorLogDetail`) whether or not this is set, exactly as an unhandled
|
|
217
|
+
* error is. Observation only; its own throws are swallowed so a faulty reporter can't break
|
|
209
218
|
* rendering. (`beforeLoader` is intentionally omitted - the core HTTP hooks already cover
|
|
210
219
|
* pre-request work.) */
|
|
211
220
|
readonly onLoaderError?: (
|
|
@@ -258,10 +267,14 @@ function backendMountOf<Env>(api: unknown): MountableApp<Env> | undefined {
|
|
|
258
267
|
return mount
|
|
259
268
|
}
|
|
260
269
|
|
|
261
|
-
|
|
270
|
+
/** A provider a web app composes: it also takes the chain of web providers above it, which a
|
|
271
|
+
* provider that composes nothing ignores. */
|
|
272
|
+
type ComposedEvidenceProvider = (ancestry?: EvidenceAncestry) => ReturnType<BackendEvidenceProvider>
|
|
273
|
+
|
|
274
|
+
function evidenceProviderOf(value: unknown): ComposedEvidenceProvider | undefined {
|
|
262
275
|
if ((typeof value !== "object" && typeof value !== "function") || value === null) return undefined
|
|
263
276
|
const provider = (value as { [NIFRA_BACKEND_EVIDENCE]?: unknown })[NIFRA_BACKEND_EVIDENCE]
|
|
264
|
-
return typeof provider === "function" ? (provider as
|
|
277
|
+
return typeof provider === "function" ? (provider as ComposedEvidenceProvider) : undefined
|
|
265
278
|
}
|
|
266
279
|
|
|
267
280
|
function mountPathPrefix(path: string): string {
|
|
@@ -274,6 +287,15 @@ function mountPathPrefix(path: string): string {
|
|
|
274
287
|
return withoutWildcard.slice(0, end)
|
|
275
288
|
}
|
|
276
289
|
|
|
290
|
+
/** The server's own error log, for page failures an `_error` boundary answers before it sees them. */
|
|
291
|
+
function boundaryErrorLog(
|
|
292
|
+
options: ServerOptions | undefined,
|
|
293
|
+
): (error: unknown, request: Request) => void {
|
|
294
|
+
const logger = options?.logger ?? jsonLogger()
|
|
295
|
+
const detail = options?.errorLogDetail ?? "full"
|
|
296
|
+
return (error, request) => emitRequestErrorLog(logger, detail, error, { req: request })
|
|
297
|
+
}
|
|
298
|
+
|
|
277
299
|
/**
|
|
278
300
|
* Build a nifra app from a route manifest: every route SSRs its layout chain via `renderPage`,
|
|
279
301
|
* and a wildcard catch-all renders `_404` (or a plain 404). Reuses @nifrajs/core's router +
|
|
@@ -306,6 +328,38 @@ export function createWebApp<Env = unknown>(
|
|
|
306
328
|
// `beforeHandle`, assurance evidence) actually bind to them - see the `use` option. A caller
|
|
307
329
|
// cannot do this after the fact: by the time this function returns, the routes are declared.
|
|
308
330
|
options.use?.(app)
|
|
331
|
+
if (options.serverComponents !== undefined) {
|
|
332
|
+
const runtime = options.serverComponents
|
|
333
|
+
const match = createMatcher(
|
|
334
|
+
manifest.routes
|
|
335
|
+
.filter((route) => runtime.owns(route.id))
|
|
336
|
+
.map((route) => ({ routeId: route.id, pattern: route.pattern })),
|
|
337
|
+
)
|
|
338
|
+
const privateRoute = (request: Request): boolean =>
|
|
339
|
+
request.headers.has("x-nifra-component") || match(new URL(request.url).pathname) !== undefined
|
|
340
|
+
Object.defineProperty(app, Symbol.for("nifra.web.private-route"), { value: privateRoute })
|
|
341
|
+
app.onResponse((response, request) => {
|
|
342
|
+
if (!privateRoute(request)) return response
|
|
343
|
+
const headers = new Headers(response.headers)
|
|
344
|
+
headers.set("cache-control", "private, no-store")
|
|
345
|
+
const vary = new Set(
|
|
346
|
+
(headers.get("vary") ?? "")
|
|
347
|
+
.split(",")
|
|
348
|
+
.map((name) => name.trim().toLowerCase())
|
|
349
|
+
.filter(Boolean),
|
|
350
|
+
)
|
|
351
|
+
vary.add("x-nifra-component")
|
|
352
|
+
vary.add("x-nifra-data")
|
|
353
|
+
headers.set("vary", [...vary].join(", "))
|
|
354
|
+
headers.delete("x-nifra-isr-revalidate")
|
|
355
|
+
headers.delete("x-nifra-isr-tags")
|
|
356
|
+
return new Response(response.body, {
|
|
357
|
+
status: response.status,
|
|
358
|
+
statusText: response.statusText,
|
|
359
|
+
headers,
|
|
360
|
+
})
|
|
361
|
+
})
|
|
362
|
+
}
|
|
309
363
|
// A resolver created by `createNonceResolver({ header })` carries a paired response hook. Register it
|
|
310
364
|
// after caller middleware so its request-specific CSP value is the last default applied to a page.
|
|
311
365
|
const nonceResponse = options.nonce?.onResponse
|
|
@@ -322,15 +376,15 @@ export function createWebApp<Env = unknown>(
|
|
|
322
376
|
cacheChannel.enabled = true
|
|
323
377
|
},
|
|
324
378
|
})
|
|
325
|
-
//
|
|
379
|
+
// Mount the in-process backend over HTTP at `apiPrefix`, when one is given, BEFORE page routing.
|
|
326
380
|
// Core's pre-route mount seam runs before the page wildcard `/*`; parent request hooks run first,
|
|
327
381
|
// child request hooks run inside the mount, and parent response hooks still wrap the result.
|
|
328
382
|
// The mount handles every HTTP method and preserves backend 404 responses instead of letting the
|
|
329
383
|
// page catch-all render them. We dispatch the same `Request` object (its body unread), so
|
|
330
384
|
// streamed/large bodies pass through untouched. The backend defines full prefixed paths
|
|
331
|
-
// (`server().post("/api/sync", …)`) unless `apiStrip` is enabled.
|
|
332
|
-
//
|
|
333
|
-
const apiPrefix = options.apiPrefix ?? "
|
|
385
|
+
// (`server().post("/api/sync", …)`) unless `apiStrip` is enabled. Without a prefix, or with a
|
|
386
|
+
// non-mountable `api`, the backend is loader-only and pages-only apps keep the no-mount fast path.
|
|
387
|
+
const apiPrefix = options.apiPrefix ?? ""
|
|
334
388
|
const apiStrip = options.apiStrip === true
|
|
335
389
|
const mountedApi = backendMountOf<Env>(api)
|
|
336
390
|
// Longest path first, so a more specific mount (`/api/auth`) is tried before a broader one (`/api`)
|
|
@@ -361,6 +415,22 @@ export function createWebApp<Env = unknown>(
|
|
|
361
415
|
? undefined
|
|
362
416
|
: servedPrerenderedList(options.prerenderedPaths)
|
|
363
417
|
const pageExecutor = createPageRequestExecutor<Env>({
|
|
418
|
+
...(options.serverComponents === undefined
|
|
419
|
+
? {}
|
|
420
|
+
: { serverComponents: options.serverComponents }),
|
|
421
|
+
...(options.serverComponents === undefined
|
|
422
|
+
? {}
|
|
423
|
+
: {
|
|
424
|
+
rerenderOwnerGET: async (
|
|
425
|
+
request: Request,
|
|
426
|
+
context: import("./page-execution.ts").PageRouteContext<Env>,
|
|
427
|
+
) =>
|
|
428
|
+
app.fetch(request, {
|
|
429
|
+
env: context.env,
|
|
430
|
+
...(context.waitUntil === undefined ? {} : { waitUntil: context.waitUntil }),
|
|
431
|
+
...(context.clientIp === undefined ? {} : { clientIp: context.clientIp }),
|
|
432
|
+
}),
|
|
433
|
+
}),
|
|
364
434
|
adapter,
|
|
365
435
|
manifest,
|
|
366
436
|
clientEntry,
|
|
@@ -384,18 +454,30 @@ export function createWebApp<Env = unknown>(
|
|
|
384
454
|
? {}
|
|
385
455
|
: { nonce: cspNonceResolver(options.csp) as NonceResolver<Env>, csp: options.csp }),
|
|
386
456
|
...(options.onLoaderError === undefined ? {} : { onLoaderError: options.onLoaderError }),
|
|
457
|
+
logBoundaryError: boundaryErrorLog(options.server),
|
|
387
458
|
cacheChannel,
|
|
388
459
|
})
|
|
389
460
|
|
|
390
461
|
for (const route of manifest.routes) {
|
|
391
|
-
|
|
392
|
-
app.register(
|
|
462
|
+
const pattern = requestPattern(route.pattern)
|
|
463
|
+
app.register(
|
|
464
|
+
"GET",
|
|
465
|
+
pattern,
|
|
466
|
+
options.serverComponents?.owns(route.id)
|
|
467
|
+
? { capabilities: options.serverComponents.capabilities(route.id) }
|
|
468
|
+
: undefined,
|
|
469
|
+
pageExecutor.get(route),
|
|
470
|
+
)
|
|
471
|
+
app.register("POST", pattern, undefined, pageExecutor.post(route))
|
|
393
472
|
}
|
|
394
473
|
// llms.txt & llms-full.txt describe what a visitor can reach: the pages, and the backend only when it
|
|
395
474
|
// is served over HTTP - a backend the loaders call in-process has no public surface to describe. An
|
|
396
475
|
// app route at either path takes precedence, and each text is built once per app.
|
|
397
476
|
if (options.llmsTxt !== false) {
|
|
398
|
-
const llmsOptions = {
|
|
477
|
+
const llmsOptions = {
|
|
478
|
+
includeLocalGuidelines: options.publishLocalGuidelines === true,
|
|
479
|
+
apiPathPrefix: apiStrip ? mountPathPrefix(apiPrefix) : "",
|
|
480
|
+
}
|
|
399
481
|
const publicApi = mountedApi !== undefined && apiPrefix !== "" ? api : undefined
|
|
400
482
|
for (const [path, full] of [
|
|
401
483
|
["/llms.txt", false],
|
|
@@ -439,13 +521,9 @@ export function createWebApp<Env = unknown>(
|
|
|
439
521
|
// absent.
|
|
440
522
|
app.register("GET", "/*", undefined, pageExecutor.fallback)
|
|
441
523
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
throw new Error("[nifra/web] cyclic composed assurance evidence mount")
|
|
446
|
-
}
|
|
447
|
-
evidenceComposing = true
|
|
448
|
-
try {
|
|
524
|
+
const evidenceProvider: ComposedEvidenceProvider = async (passed) => {
|
|
525
|
+
const { traverseEvidence } = await import("./evidence-traversal.ts")
|
|
526
|
+
return traverseEvidence(evidenceProvider, passed, async (ancestry) => {
|
|
449
527
|
const { composeProjectEvidence, snapshotProjectEvidence } = await import(
|
|
450
528
|
"@nifrajs/core/evidence"
|
|
451
529
|
)
|
|
@@ -464,7 +542,7 @@ export function createWebApp<Env = unknown>(
|
|
|
464
542
|
)
|
|
465
543
|
}
|
|
466
544
|
parts.push({
|
|
467
|
-
evidence: await provider.call(mount.app),
|
|
545
|
+
evidence: await provider.call(mount.app, ancestry),
|
|
468
546
|
...(mount.stripPrefix === true ? { pathPrefix: mountPathPrefix(mount.path) } : {}),
|
|
469
547
|
})
|
|
470
548
|
}
|
|
@@ -476,14 +554,13 @@ export function createWebApp<Env = unknown>(
|
|
|
476
554
|
)
|
|
477
555
|
}
|
|
478
556
|
parts.push({
|
|
479
|
-
evidence: await provider.call(api),
|
|
557
|
+
evidence: await provider.call(api, ancestry),
|
|
558
|
+
// biome-ignore lint/plugin/noConditionalEmptyObjectSpread: Evidence paths acquire a prefix only when the mounted backend strips it.
|
|
480
559
|
...(apiStrip ? { pathPrefix: mountPathPrefix(apiPrefix) } : {}),
|
|
481
560
|
})
|
|
482
561
|
}
|
|
483
562
|
return composeProjectEvidence(parts)
|
|
484
|
-
}
|
|
485
|
-
evidenceComposing = false
|
|
486
|
-
}
|
|
563
|
+
})
|
|
487
564
|
}
|
|
488
565
|
Object.defineProperty(app, NIFRA_BACKEND_EVIDENCE, {
|
|
489
566
|
configurable: false,
|
|
@@ -491,6 +568,8 @@ export function createWebApp<Env = unknown>(
|
|
|
491
568
|
value: evidenceProvider,
|
|
492
569
|
writable: false,
|
|
493
570
|
})
|
|
571
|
+
// Core's dev-time hook audit reads this: an order-scoped hook added from here on skips every page.
|
|
572
|
+
Object.defineProperty(app, Symbol.for("nifra.web.pagesDeclared"), { value: true })
|
|
494
573
|
|
|
495
574
|
return app
|
|
496
575
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `t.date()` across the wire, server half. JSON has no dates, so the output guard hands the page a
|
|
3
|
+
* `wireDate()`: a real `Date` for the server render that serializes as `{ "__nifra_date": iso }`. The
|
|
4
|
+
* browser half (`revive-dates.ts`) rebuilds those tags, so both renders see the same value.
|
|
5
|
+
*/
|
|
6
|
+
import { DATE_TAG } from "./revive-dates.ts"
|
|
7
|
+
|
|
8
|
+
const WIRE_JSON: PropertyDescriptor = {
|
|
9
|
+
value(this: Date): Readonly<Record<string, string>> {
|
|
10
|
+
return { [DATE_TAG]: this.toISOString() }
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** A `Date` that crosses JSON tagged, so the browser can tell it from a string that looks like one. */
|
|
15
|
+
export const wireDate = (time: number): Date =>
|
|
16
|
+
Object.defineProperty(new Date(time), "toJSON", WIRE_JSON)
|
|
@@ -60,6 +60,38 @@ export interface ZoneLeak {
|
|
|
60
60
|
readonly reason: string
|
|
61
61
|
/** Shortest import path from an entry: `[entry, ...as-written specifiers]`. */
|
|
62
62
|
readonly chain: readonly string[]
|
|
63
|
+
/** The undeclared package the refusal is about; its files share one line in the report. */
|
|
64
|
+
readonly package?: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const leakLine = (leak: ZoneLeak, module = leak.module): string =>
|
|
68
|
+
` - ${module}: ${leak.reason}${leak.chain.length > 1 ? `\n via ${leak.chain.join(" → ")}` : ""}`
|
|
69
|
+
|
|
70
|
+
/** One line per leak, except that the files of one undeclared package share a single line. */
|
|
71
|
+
function leakLines(leaks: readonly ZoneLeak[]): string[] {
|
|
72
|
+
const byPackage = new Map<string, ZoneLeak[]>()
|
|
73
|
+
for (const leak of leaks) {
|
|
74
|
+
if (leak.package === undefined) continue
|
|
75
|
+
const group = byPackage.get(leak.package)
|
|
76
|
+
if (group === undefined) byPackage.set(leak.package, [leak])
|
|
77
|
+
else group.push(leak)
|
|
78
|
+
}
|
|
79
|
+
const lines: string[] = []
|
|
80
|
+
for (const leak of leaks) {
|
|
81
|
+
const group = leak.package === undefined ? undefined : byPackage.get(leak.package)
|
|
82
|
+
if (group === undefined || group.length < 2) {
|
|
83
|
+
lines.push(leakLine(leak))
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
if (group[0] !== leak) continue
|
|
87
|
+
const shown = group
|
|
88
|
+
.slice(0, 3)
|
|
89
|
+
.map((item) => item.module)
|
|
90
|
+
.join(", ")
|
|
91
|
+
const more = group.length > 3 ? ` and ${group.length - 3} more` : ""
|
|
92
|
+
lines.push(leakLine(leak, `${group.length} files (${shown}${more})`))
|
|
93
|
+
}
|
|
94
|
+
return lines
|
|
63
95
|
}
|
|
64
96
|
|
|
65
97
|
export interface ClientGraphVerdict {
|
|
@@ -71,6 +103,8 @@ export interface ClientGraphVerdict {
|
|
|
71
103
|
}
|
|
72
104
|
|
|
73
105
|
export interface VerifyClientGraphOptions {
|
|
106
|
+
/** Exact modules certified by the component compiler in this generation. */
|
|
107
|
+
readonly componentModules?: ReadonlySet<string>
|
|
74
108
|
readonly classifier: ZoneClassifier
|
|
75
109
|
readonly sourceOf: (id: string) => ModuleSource
|
|
76
110
|
/** The private-env denial for a browser-code file (see `privateEnvCheck`); unchecked when absent. */
|
|
@@ -192,6 +226,7 @@ export function verifyClientGraph(
|
|
|
192
226
|
}
|
|
193
227
|
|
|
194
228
|
const denied = new Map<string, string>()
|
|
229
|
+
const deniedPackage = new Map<string, string>()
|
|
195
230
|
for (const id of [...Object.keys(graph.modules), ...dropped]) {
|
|
196
231
|
const source = sourceOf(id)
|
|
197
232
|
const file = source.kind === "builtin" ? undefined : source.file
|
|
@@ -202,6 +237,8 @@ export function verifyClientGraph(
|
|
|
202
237
|
if (reason === undefined && BROWSER_CODE.has(classification.zone))
|
|
203
238
|
reason = options.privateEnv?.(file)
|
|
204
239
|
if (reason !== undefined) denied.set(id, reason)
|
|
240
|
+
if (classification.zone === "error" && classification.package !== undefined)
|
|
241
|
+
deniedPackage.set(id, classification.package)
|
|
205
242
|
}
|
|
206
243
|
|
|
207
244
|
// Chains start at a route file where they can: a lazily split module is an entry of its own chunk,
|
|
@@ -225,11 +262,15 @@ export function verifyClientGraph(
|
|
|
225
262
|
: entries
|
|
226
263
|
const chains = denied.size === 0 ? new Map<string, string[]>() : shortestChains(roots, edges)
|
|
227
264
|
const leaks = [...denied]
|
|
228
|
-
.map(([id, reason]) =>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
265
|
+
.map(([id, reason]): ZoneLeak => {
|
|
266
|
+
const leak = {
|
|
267
|
+
module: labelOf(id),
|
|
268
|
+
reason,
|
|
269
|
+
chain: (chains.get(id) ?? [id]).map((label, i) => (i === 0 ? labelOf(label) : label)),
|
|
270
|
+
}
|
|
271
|
+
const pkg = deniedPackage.get(id)
|
|
272
|
+
return pkg === undefined ? leak : { ...leak, package: pkg }
|
|
273
|
+
})
|
|
233
274
|
.sort((a, b) => codeUnitOrder(a.module, b.module))
|
|
234
275
|
|
|
235
276
|
const misplacedExports: string[] = []
|
|
@@ -333,10 +374,14 @@ export function verifyServerGraph(
|
|
|
333
374
|
readonly module: string
|
|
334
375
|
readonly reason: string
|
|
335
376
|
readonly tail?: string
|
|
377
|
+
readonly package?: string
|
|
336
378
|
}> = []
|
|
337
379
|
for (const [id, module] of Object.entries(graph.modules)) {
|
|
338
380
|
const from = classOf(id)
|
|
339
|
-
if (from?.zone === "error")
|
|
381
|
+
if (from?.zone === "error") {
|
|
382
|
+
const at = { at: id, module: labelOf(id), reason: from.reason }
|
|
383
|
+
found.push(from.package === undefined ? at : { ...at, package: from.package })
|
|
384
|
+
}
|
|
340
385
|
const file = fileOf(id)
|
|
341
386
|
const env =
|
|
342
387
|
from !== undefined && file !== undefined && BROWSER_CODE.has(from.zone)
|
|
@@ -364,7 +409,9 @@ export function verifyServerGraph(
|
|
|
364
409
|
const to = classOf(target)
|
|
365
410
|
if (from === undefined || to === undefined || from.zone === "error" || to.zone === "error")
|
|
366
411
|
continue
|
|
367
|
-
|
|
412
|
+
const componentBridge =
|
|
413
|
+
to.zone === "component" && options.componentModules?.has(fileOf(target) ?? "") === true
|
|
414
|
+
if (!componentBridge && !importAllowed(from.zone, to.zone)) {
|
|
368
415
|
found.push({
|
|
369
416
|
at: id,
|
|
370
417
|
module: labelOf(target),
|
|
@@ -386,9 +433,10 @@ export function verifyServerGraph(
|
|
|
386
433
|
]
|
|
387
434
|
const chains = shortestChains(entries, edges)
|
|
388
435
|
const violations = found
|
|
389
|
-
.map(({ at, module, reason, tail }) => {
|
|
436
|
+
.map(({ at, module, reason, tail, package: pkg }): ZoneLeak => {
|
|
390
437
|
const chain = (chains.get(at) ?? [at]).map((label, i) => (i === 0 ? labelOf(label) : label))
|
|
391
|
-
|
|
438
|
+
const violation = { module, reason, chain: tail === undefined ? chain : [...chain, tail] }
|
|
439
|
+
return pkg === undefined ? violation : { ...violation, package: pkg }
|
|
392
440
|
})
|
|
393
441
|
.sort((a, b) => codeUnitOrder(a.module, b.module) || codeUnitOrder(a.reason, b.reason))
|
|
394
442
|
return { violations }
|
|
@@ -397,11 +445,7 @@ export function verifyServerGraph(
|
|
|
397
445
|
/** The build-failing message for a server graph that breaks the zone rules. */
|
|
398
446
|
export function formatServerGraphVerdict(verdict: ServerGraphVerdict): string | undefined {
|
|
399
447
|
if (verdict.violations.length === 0) return undefined
|
|
400
|
-
|
|
401
|
-
(v) =>
|
|
402
|
-
` - ${v.module}: ${v.reason}${v.chain.length > 1 ? `\n via ${v.chain.join(" → ")}` : ""}`,
|
|
403
|
-
)
|
|
404
|
-
return `[nifra/web] the server build breaks the zone rules:\n${lines.join("\n")}`
|
|
448
|
+
return `[nifra/web] the server build breaks the zone rules:\n${leakLines(verdict.violations).join("\n")}`
|
|
405
449
|
}
|
|
406
450
|
|
|
407
451
|
/** One emitted file, as the accounting sees it. */
|
|
@@ -561,12 +605,8 @@ const indent = (lines: readonly string[]): string => lines.map((line) => ` - ${
|
|
|
561
605
|
export function formatClientGraphVerdict(verdict: ClientGraphVerdict): string | undefined {
|
|
562
606
|
const sections: string[] = []
|
|
563
607
|
if (verdict.leaks.length > 0) {
|
|
564
|
-
const lines = verdict.leaks.map(
|
|
565
|
-
(leak) =>
|
|
566
|
-
` - ${leak.module}: ${leak.reason}${leak.chain.length > 1 ? `\n via ${leak.chain.join(" → ")}` : ""}`,
|
|
567
|
-
)
|
|
568
608
|
sections.push(
|
|
569
|
-
`[nifra/web] the browser build reached code that may not ship to a browser:\n${
|
|
609
|
+
`[nifra/web] the browser build reached code that may not ship to a browser:\n${leakLines(verdict.leaks).join("\n")}\n` +
|
|
570
610
|
"Reach backend code through a route's backend half (x.backend.ts) or a *.fn.ts server function; " +
|
|
571
611
|
"code both sides need belongs in shared/.",
|
|
572
612
|
)
|