@nifrajs/web 2.14.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boundary.d.ts +158 -0
- package/dist/boundary.d.ts.map +1 -0
- package/dist/boundary.js +237 -0
- package/dist/boundary.js.map +1 -0
- package/dist/build-plan.d.ts +228 -0
- package/dist/build-plan.d.ts.map +1 -0
- package/dist/build-plan.js +514 -0
- package/dist/build-plan.js.map +1 -0
- package/dist/build-vite.d.ts +2 -1
- package/dist/build-vite.d.ts.map +1 -1
- package/dist/build-vite.js +5 -3
- package/dist/build-vite.js.map +1 -1
- package/dist/build.d.ts +2 -197
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +92 -515
- package/dist/build.js.map +1 -1
- package/dist/chunk-names.d.ts.map +1 -1
- package/dist/chunk-names.js +3 -2
- package/dist/chunk-names.js.map +1 -1
- package/dist/dev-ssr-graph.d.ts.map +1 -1
- package/dist/dev-ssr-graph.js +63 -28
- package/dist/dev-ssr-graph.js.map +1 -1
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +9 -2
- package/dist/diagnostic.js.map +1 -1
- package/dist/fonts-google.d.ts.map +1 -1
- package/dist/fonts-google.js +3 -1
- package/dist/fonts-google.js.map +1 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +2 -1
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +16 -589
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -1824
- package/dist/index.js.map +1 -1
- package/dist/internal/codegen.d.ts +68 -0
- package/dist/internal/codegen.d.ts.map +1 -0
- package/dist/internal/codegen.js +317 -0
- package/dist/internal/codegen.js.map +1 -0
- package/dist/internal/fetcher-runtime.d.ts +19 -0
- package/dist/internal/fetcher-runtime.d.ts.map +1 -0
- package/dist/internal/fetcher-runtime.js +26 -0
- package/dist/internal/fetcher-runtime.js.map +1 -0
- package/dist/internal/head-merge.d.ts +2 -0
- package/dist/internal/head-merge.d.ts.map +1 -1
- package/dist/internal/head-merge.js +17 -1
- package/dist/internal/head-merge.js.map +1 -1
- package/dist/internal/identity-policy.d.ts +51 -0
- package/dist/internal/identity-policy.d.ts.map +1 -0
- package/dist/internal/identity-policy.js +67 -0
- package/dist/internal/identity-policy.js.map +1 -0
- package/dist/internal/parity.d.ts +104 -8
- package/dist/internal/parity.d.ts.map +1 -1
- package/dist/internal/parity.js +472 -56
- package/dist/internal/parity.js.map +1 -1
- package/dist/internal/query-runtime.d.ts +8 -0
- package/dist/internal/query-runtime.d.ts.map +1 -0
- package/dist/internal/query-runtime.js +29 -0
- package/dist/internal/query-runtime.js.map +1 -0
- package/dist/internal/render-document.d.ts +372 -0
- package/dist/internal/render-document.d.ts.map +1 -0
- package/dist/internal/render-document.js +744 -0
- package/dist/internal/render-document.js.map +1 -0
- package/dist/internal/runtime-contract.d.ts +48 -0
- package/dist/internal/runtime-contract.d.ts.map +1 -0
- package/dist/internal/runtime-contract.js +27 -0
- package/dist/internal/runtime-contract.js.map +1 -0
- package/dist/internal/web-app.d.ts +171 -0
- package/dist/internal/web-app.d.ts.map +1 -0
- package/dist/internal/web-app.js +844 -0
- package/dist/internal/web-app.js.map +1 -0
- package/dist/islands.d.ts +50 -2
- package/dist/islands.d.ts.map +1 -1
- package/dist/islands.js +96 -36
- package/dist/islands.js.map +1 -1
- package/dist/manifest.d.ts +41 -6
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js.map +1 -1
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +22 -9
- package/dist/module-graph.js.map +1 -1
- package/dist/nano.d.ts +125 -0
- package/dist/nano.d.ts.map +1 -0
- package/dist/nano.js +190 -0
- package/dist/nano.js.map +1 -0
- package/dist/plugins/css-modules.js +2 -2
- package/dist/plugins/css-modules.js.map +1 -1
- package/dist/plugins/kit.d.ts +12 -0
- package/dist/plugins/kit.d.ts.map +1 -1
- package/dist/plugins/kit.js +62 -12
- package/dist/plugins/kit.js.map +1 -1
- package/dist/plugins/postcss.js +2 -2
- package/dist/plugins/postcss.js.map +1 -1
- package/dist/plugins/scss.js +2 -2
- package/dist/plugins/scss.js.map +1 -1
- package/dist/plugins/stylex.d.ts +70 -0
- package/dist/plugins/stylex.d.ts.map +1 -0
- package/dist/plugins/stylex.js +166 -0
- package/dist/plugins/stylex.js.map +1 -0
- package/dist/plugins/svg.js +2 -2
- package/dist/plugins/svg.js.map +1 -1
- package/dist/render-seam.d.ts +5 -0
- package/dist/render-seam.d.ts.map +1 -1
- package/dist/render-seam.js +10 -0
- package/dist/render-seam.js.map +1 -1
- package/dist/router.d.ts +11 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +254 -47
- package/dist/router.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +34 -16
- package/dist/vite.js.map +1 -1
- package/package.json +49 -2
- package/src/boundary.ts +407 -0
- package/src/build-plan.ts +741 -0
- package/src/build-vite.ts +6 -7
- package/src/build.ts +120 -730
- package/src/chunk-names.ts +3 -2
- package/src/dev-ssr-graph.ts +63 -28
- package/src/diagnostic.ts +14 -2
- package/src/fonts-google.ts +3 -1
- package/src/fs.ts +2 -1
- package/src/index.ts +107 -2553
- package/src/internal/codegen.ts +400 -0
- package/src/internal/fetcher-runtime.ts +36 -0
- package/src/internal/head-merge.ts +19 -1
- package/src/internal/identity-policy.ts +94 -0
- package/src/internal/parity.ts +603 -71
- package/src/internal/query-runtime.ts +31 -0
- package/src/internal/render-document.ts +1116 -0
- package/src/internal/runtime-contract.ts +54 -0
- package/src/internal/web-app.ts +1114 -0
- package/src/islands.ts +106 -40
- package/src/manifest.ts +50 -3
- package/src/module-graph.ts +23 -9
- package/src/nano.ts +246 -0
- package/src/plugins/css-modules.ts +2 -2
- package/src/plugins/kit.ts +65 -12
- package/src/plugins/postcss.ts +2 -2
- package/src/plugins/scss.ts +2 -2
- package/src/plugins/stylex.ts +298 -0
- package/src/plugins/svg.ts +2 -2
- package/src/render-seam.ts +6 -0
- package/src/router.ts +293 -51
- package/src/vite.ts +41 -15
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-neutral async boundaries. A boundary owns one named piece of UI data; the web core owns
|
|
3
|
+
* lifecycle, concurrency, and isolation while each adapter decides how its `render` result is mounted.
|
|
4
|
+
* The contract deliberately contains no React/Svelte/Vue/Preact types.
|
|
5
|
+
*/
|
|
6
|
+
/** Request-scoped inputs available to dynamic and intercepting boundaries. */
|
|
7
|
+
export interface BoundaryRequestCtx {
|
|
8
|
+
readonly request: Request;
|
|
9
|
+
readonly params: Readonly<Record<string, string>>;
|
|
10
|
+
readonly api: unknown;
|
|
11
|
+
readonly env: unknown;
|
|
12
|
+
readonly draft: boolean;
|
|
13
|
+
readonly search: Readonly<Record<string, unknown>>;
|
|
14
|
+
readonly signal: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
/** Build-safe inputs available to a static boundary. It intentionally has no request/session/params API. */
|
|
17
|
+
export interface StaticCtx {
|
|
18
|
+
readonly phase: "build";
|
|
19
|
+
readonly origin?: string;
|
|
20
|
+
}
|
|
21
|
+
export type BoundaryMode = "static" | "dynamic" | {
|
|
22
|
+
readonly intercept: string;
|
|
23
|
+
};
|
|
24
|
+
interface BoundaryBase<Data, UI> {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly render: (data: Data) => UI;
|
|
27
|
+
readonly fallback?: UI;
|
|
28
|
+
/** Adapter-provided error slot name. The neutral core never imports a UI component. */
|
|
29
|
+
readonly errorId?: string;
|
|
30
|
+
}
|
|
31
|
+
export type StaticBoundary<Data, UI> = BoundaryBase<Data, UI> & {
|
|
32
|
+
readonly mode: "static";
|
|
33
|
+
readonly load?: (ctx: StaticCtx) => Data | Promise<Data>;
|
|
34
|
+
};
|
|
35
|
+
export type DynamicBoundary<Data, UI> = BoundaryBase<Data, UI> & {
|
|
36
|
+
readonly mode: "dynamic";
|
|
37
|
+
readonly load?: (ctx: BoundaryRequestCtx) => Data | Promise<Data>;
|
|
38
|
+
};
|
|
39
|
+
export type InterceptBoundary<Data, UI> = BoundaryBase<Data, UI> & {
|
|
40
|
+
readonly mode: {
|
|
41
|
+
readonly intercept: string;
|
|
42
|
+
};
|
|
43
|
+
readonly load?: (ctx: BoundaryRequestCtx) => Data | Promise<Data>;
|
|
44
|
+
};
|
|
45
|
+
/** A discriminated union: the `mode` selects the context type accepted by `load`. */
|
|
46
|
+
export type Boundary<Data, UI> = StaticBoundary<Data, UI> | DynamicBoundary<Data, UI> | InterceptBoundary<Data, UI>;
|
|
47
|
+
/**
|
|
48
|
+
* Erased runtime registration shape used by a route/layout manifest. This is intentionally a
|
|
49
|
+
* structural union rather than `Boundary<never, unknown>`: a registration keeps its loader's
|
|
50
|
+
* concrete data type at the declaration site, and function-parameter variance must not turn that
|
|
51
|
+
* loader into `Promise<never>` when the manifest erases it.
|
|
52
|
+
*/
|
|
53
|
+
type ErasedBoundaryBase = {
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly render: (data: never) => unknown;
|
|
56
|
+
readonly fallback?: unknown;
|
|
57
|
+
readonly errorId?: string;
|
|
58
|
+
};
|
|
59
|
+
export type BoundaryRegistration = (ErasedBoundaryBase & {
|
|
60
|
+
readonly mode: "static";
|
|
61
|
+
readonly load?: (ctx: StaticCtx) => unknown | Promise<unknown>;
|
|
62
|
+
}) | (ErasedBoundaryBase & {
|
|
63
|
+
readonly mode: "dynamic";
|
|
64
|
+
readonly load?: (ctx: BoundaryRequestCtx) => unknown | Promise<unknown>;
|
|
65
|
+
}) | (ErasedBoundaryBase & {
|
|
66
|
+
readonly mode: {
|
|
67
|
+
readonly intercept: string;
|
|
68
|
+
};
|
|
69
|
+
readonly load?: (ctx: BoundaryRequestCtx) => unknown | Promise<unknown>;
|
|
70
|
+
});
|
|
71
|
+
/** Neutral manifest descriptor: no framework component crosses the web core boundary. */
|
|
72
|
+
export interface BoundaryDescriptor {
|
|
73
|
+
readonly name: string;
|
|
74
|
+
readonly mode: BoundaryMode;
|
|
75
|
+
readonly hasLoad: boolean;
|
|
76
|
+
readonly errorId?: string;
|
|
77
|
+
}
|
|
78
|
+
export type BoundaryStatus = "unresolved" | "pending" | "ready" | "error";
|
|
79
|
+
export interface BoundaryError {
|
|
80
|
+
readonly name: string;
|
|
81
|
+
readonly message: string;
|
|
82
|
+
}
|
|
83
|
+
/** Serializable boundary state passed to the adapter's render seam. */
|
|
84
|
+
export interface BoundaryState {
|
|
85
|
+
readonly name: string;
|
|
86
|
+
readonly mode: BoundaryMode;
|
|
87
|
+
readonly status: BoundaryStatus;
|
|
88
|
+
readonly data?: unknown;
|
|
89
|
+
readonly error?: BoundaryError;
|
|
90
|
+
readonly errorId?: string;
|
|
91
|
+
}
|
|
92
|
+
export type BoundaryStates = Readonly<Record<string, BoundaryState>>;
|
|
93
|
+
/** A dynamic load that has started but is not part of the initial render barrier. */
|
|
94
|
+
export interface PendingBoundary {
|
|
95
|
+
readonly name: string;
|
|
96
|
+
readonly mode: BoundaryMode;
|
|
97
|
+
readonly errorId?: string;
|
|
98
|
+
readonly promise: Promise<unknown>;
|
|
99
|
+
}
|
|
100
|
+
/** Runtime handles for a concurrent boundary batch. `initial` is renderable immediately. */
|
|
101
|
+
export interface DynamicBoundaryBatch {
|
|
102
|
+
readonly initial: BoundaryStates;
|
|
103
|
+
readonly pending: ReadonlyArray<PendingBoundary>;
|
|
104
|
+
/** Resolves to final per-boundary states for non-streaming consumers and tests. */
|
|
105
|
+
readonly complete: Promise<BoundaryStates>;
|
|
106
|
+
}
|
|
107
|
+
/** Public in-memory reference cache for build-safe static boundary values. It holds no payload outside
|
|
108
|
+
* the process and is intentionally not a durable or tenant-aware cache implementation. */
|
|
109
|
+
export interface StaticBoundaryCache {
|
|
110
|
+
get(boundary: BoundaryRegistration): Promise<unknown> | undefined;
|
|
111
|
+
set(boundary: BoundaryRegistration, value: Promise<unknown>): void;
|
|
112
|
+
}
|
|
113
|
+
export declare class MemoryStaticBoundaryCache implements StaticBoundaryCache {
|
|
114
|
+
#private;
|
|
115
|
+
get(boundary: BoundaryRegistration): Promise<unknown> | undefined;
|
|
116
|
+
set(boundary: BoundaryRegistration, value: Promise<unknown>): void;
|
|
117
|
+
}
|
|
118
|
+
export interface StaticBoundaryImportEdge {
|
|
119
|
+
readonly from: string;
|
|
120
|
+
readonly to: string;
|
|
121
|
+
}
|
|
122
|
+
export interface StaticBoundaryRoot {
|
|
123
|
+
readonly name: string;
|
|
124
|
+
readonly module: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Enforce the second half of the static-boundary safety boundary. A build adapter supplies the
|
|
128
|
+
* transitive module graph and the modules it has classified as request-scoped; a static root that
|
|
129
|
+
* reaches one fails before any shared shell is emitted. Keeping this check graph-shaped makes it
|
|
130
|
+
* usable by Bun/Vite without making the neutral web runtime depend on either bundler.
|
|
131
|
+
*/
|
|
132
|
+
export declare function assertStaticBoundaryImports(roots: readonly StaticBoundaryRoot[], edges: readonly StaticBoundaryImportEdge[], requestScopedModules: ReadonlySet<string>): void;
|
|
133
|
+
/** Validate and serialize the neutral boundary manifest. Duplicate names fail closed at startup. */
|
|
134
|
+
export declare function boundaryDescriptors(boundaries: readonly BoundaryRegistration[]): ReadonlyArray<BoundaryDescriptor>;
|
|
135
|
+
/**
|
|
136
|
+
* Resolve all dynamic boundaries concurrently. Each receives a fresh frozen context object; one
|
|
137
|
+
* failure becomes that boundary's error state and never rejects a sibling or exposes sibling data.
|
|
138
|
+
* Static/intercepting modes remain `unresolved` until their phase-specific runtime owns them.
|
|
139
|
+
*/
|
|
140
|
+
export declare function resolveDynamicBoundaries(boundaries: readonly BoundaryRegistration[], context: BoundaryRequestCtx): Promise<BoundaryStates>;
|
|
141
|
+
/**
|
|
142
|
+
* Start all dynamic boundary loads at once without making the page wait for the slowest sibling.
|
|
143
|
+
* The initial states contain `status: "pending"`; callers that support deferred values can attach
|
|
144
|
+
* each `pending.promise` to its own slot. `complete` still provides settled, isolated states to
|
|
145
|
+
* non-streaming callers, preserving the simple reference API.
|
|
146
|
+
*/
|
|
147
|
+
export declare function startDynamicBoundaries(boundaries: readonly BoundaryRegistration[], context: BoundaryRequestCtx): DynamicBoundaryBatch;
|
|
148
|
+
/**
|
|
149
|
+
* Resolve only explicitly annotated static boundaries with a request-free build context. Dynamic
|
|
150
|
+
* and intercepting boundaries remain unresolved. Values are cached by boundary object identity in
|
|
151
|
+
* the supplied in-memory cache, so a worker instance does not repeat a build-safe computation per
|
|
152
|
+
* request. A rejected static load becomes that slot's error state; it never fabricates shared data.
|
|
153
|
+
*/
|
|
154
|
+
export declare function resolveStaticBoundaries(boundaries: readonly BoundaryRegistration[], context: StaticCtx, cache?: StaticBoundaryCache): Promise<BoundaryStates>;
|
|
155
|
+
/** Stable mode label for adapter registries and diagnostics. */
|
|
156
|
+
export declare function boundaryModeKey(mode: BoundaryMode): string;
|
|
157
|
+
export {};
|
|
158
|
+
//# sourceMappingURL=boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"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"}
|
package/dist/boundary.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-neutral async boundaries. A boundary owns one named piece of UI data; the web core owns
|
|
3
|
+
* lifecycle, concurrency, and isolation while each adapter decides how its `render` result is mounted.
|
|
4
|
+
* The contract deliberately contains no React/Svelte/Vue/Preact types.
|
|
5
|
+
*/
|
|
6
|
+
export class MemoryStaticBoundaryCache {
|
|
7
|
+
#values = new WeakMap();
|
|
8
|
+
get(boundary) {
|
|
9
|
+
return this.#values.get(boundary);
|
|
10
|
+
}
|
|
11
|
+
set(boundary, value) {
|
|
12
|
+
this.#values.set(boundary, value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Enforce the second half of the static-boundary safety boundary. A build adapter supplies the
|
|
17
|
+
* transitive module graph and the modules it has classified as request-scoped; a static root that
|
|
18
|
+
* reaches one fails before any shared shell is emitted. Keeping this check graph-shaped makes it
|
|
19
|
+
* usable by Bun/Vite without making the neutral web runtime depend on either bundler.
|
|
20
|
+
*/
|
|
21
|
+
export function assertStaticBoundaryImports(roots, edges, requestScopedModules) {
|
|
22
|
+
const next = new Map();
|
|
23
|
+
for (const edge of edges) {
|
|
24
|
+
const children = next.get(edge.from);
|
|
25
|
+
if (children === undefined)
|
|
26
|
+
next.set(edge.from, [edge.to]);
|
|
27
|
+
else
|
|
28
|
+
children.push(edge.to);
|
|
29
|
+
}
|
|
30
|
+
for (const root of roots) {
|
|
31
|
+
const seen = new Set([root.module]);
|
|
32
|
+
const queue = [root.module];
|
|
33
|
+
for (let cursor = 0; cursor < queue.length; cursor++) {
|
|
34
|
+
const module = queue[cursor];
|
|
35
|
+
if (module === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
if (requestScopedModules.has(module)) {
|
|
38
|
+
throw new TypeError(`[nifra/web] static boundary "${root.name}" reaches request-scoped module "${module}"`);
|
|
39
|
+
}
|
|
40
|
+
for (const child of next.get(module) ?? []) {
|
|
41
|
+
if (!seen.has(child)) {
|
|
42
|
+
seen.add(child);
|
|
43
|
+
queue.push(child);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const BOUNDARY_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
50
|
+
const MAX_INTERCEPT_PATH = 2048;
|
|
51
|
+
const modeKey = (mode) => typeof mode === "string" ? mode : `intercept:${mode.intercept}`;
|
|
52
|
+
const assertMode = (mode) => {
|
|
53
|
+
if (mode === "static" || mode === "dynamic")
|
|
54
|
+
return;
|
|
55
|
+
if (typeof mode !== "object" ||
|
|
56
|
+
mode === null ||
|
|
57
|
+
typeof mode.intercept !== "string" ||
|
|
58
|
+
mode.intercept.length === 0 ||
|
|
59
|
+
mode.intercept.length > MAX_INTERCEPT_PATH ||
|
|
60
|
+
!mode.intercept.startsWith("/") ||
|
|
61
|
+
mode.intercept.startsWith("//")) {
|
|
62
|
+
throw new TypeError("[nifra/web] boundary intercept must be a same-origin path");
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/** Validate and serialize the neutral boundary manifest. Duplicate names fail closed at startup. */
|
|
66
|
+
export function boundaryDescriptors(boundaries) {
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
return boundaries.map((boundary) => {
|
|
69
|
+
if (!BOUNDARY_NAME.test(boundary.name) || seen.has(boundary.name)) {
|
|
70
|
+
throw new TypeError(`[nifra/web] invalid or duplicate boundary name "${boundary.name}"`);
|
|
71
|
+
}
|
|
72
|
+
assertMode(boundary.mode);
|
|
73
|
+
seen.add(boundary.name);
|
|
74
|
+
return {
|
|
75
|
+
name: boundary.name,
|
|
76
|
+
mode: boundary.mode,
|
|
77
|
+
hasLoad: boundary.load !== undefined,
|
|
78
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const scopedContext = (context) => Object.freeze({
|
|
83
|
+
request: context.request,
|
|
84
|
+
params: Object.freeze({ ...context.params }),
|
|
85
|
+
api: context.api,
|
|
86
|
+
env: context.env,
|
|
87
|
+
draft: context.draft,
|
|
88
|
+
search: Object.freeze({ ...context.search }),
|
|
89
|
+
signal: context.signal,
|
|
90
|
+
});
|
|
91
|
+
/**
|
|
92
|
+
* Redact a failed load into the client-visible slot. Boundary states are serialized into the document
|
|
93
|
+
* (`window.__NIFRA_BOUNDARIES__`), so the thrown error's own message never crosses: a driver or fetch
|
|
94
|
+
* failure carries hosts, credentials, and query text that a page is not allowed to publish. An `Error`
|
|
95
|
+
* subclass name is withheld for the same reason - it names the internal library. The real error goes to
|
|
96
|
+
* a generic server-console event, the same split `deferred.ts` uses for a rejected deferred value. The
|
|
97
|
+
* log deliberately carries only the failure kind: exception messages can contain credentials, query
|
|
98
|
+
* text, or user data and must not be copied into an operational sink by default.
|
|
99
|
+
*
|
|
100
|
+
* A boundary that wants to show the user something specific catches its own failure inside `load` and
|
|
101
|
+
* returns that as data; the framework never guesses which parts of an exception are publishable.
|
|
102
|
+
*/
|
|
103
|
+
const boundaryError = (error) => {
|
|
104
|
+
if (error !== undefined) {
|
|
105
|
+
console.error("[nifra/web] boundary load failed", {
|
|
106
|
+
kind: error instanceof Error ? "error" : typeof error,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return { name: "Error", message: "Boundary failed" };
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Resolve all dynamic boundaries concurrently. Each receives a fresh frozen context object; one
|
|
113
|
+
* failure becomes that boundary's error state and never rejects a sibling or exposes sibling data.
|
|
114
|
+
* Static/intercepting modes remain `unresolved` until their phase-specific runtime owns them.
|
|
115
|
+
*/
|
|
116
|
+
export async function resolveDynamicBoundaries(boundaries, context) {
|
|
117
|
+
return startDynamicBoundaries(boundaries, context).complete;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Start all dynamic boundary loads at once without making the page wait for the slowest sibling.
|
|
121
|
+
* The initial states contain `status: "pending"`; callers that support deferred values can attach
|
|
122
|
+
* each `pending.promise` to its own slot. `complete` still provides settled, isolated states to
|
|
123
|
+
* non-streaming callers, preserving the simple reference API.
|
|
124
|
+
*/
|
|
125
|
+
export function startDynamicBoundaries(boundaries, context) {
|
|
126
|
+
// Validation only: a bad name, a duplicate, or a malformed intercept path throws here rather than
|
|
127
|
+
// producing a half-registered batch. It cannot return holes, so no slot below tests for one.
|
|
128
|
+
boundaryDescriptors(boundaries);
|
|
129
|
+
const initial = {};
|
|
130
|
+
const pending = [];
|
|
131
|
+
for (const boundary of boundaries) {
|
|
132
|
+
const base = {
|
|
133
|
+
name: boundary.name,
|
|
134
|
+
mode: boundary.mode,
|
|
135
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
136
|
+
};
|
|
137
|
+
if (boundary.mode !== "dynamic") {
|
|
138
|
+
initial[boundary.name] = { ...base, status: "unresolved" };
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (boundary.load === undefined) {
|
|
142
|
+
initial[boundary.name] = { ...base, status: "ready" };
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
// Promise.resolve().then() converts a synchronous throw into this boundary's rejection, so it
|
|
146
|
+
// cannot abort siblings or escape before the caller attaches the deferred consumer.
|
|
147
|
+
const promise = Promise.resolve().then(() => boundary.load?.(scopedContext(context)));
|
|
148
|
+
pending.push({
|
|
149
|
+
name: boundary.name,
|
|
150
|
+
mode: boundary.mode,
|
|
151
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
152
|
+
promise,
|
|
153
|
+
});
|
|
154
|
+
initial[boundary.name] = { ...base, status: "pending" };
|
|
155
|
+
}
|
|
156
|
+
const complete = Promise.all(boundaries.map(async (boundary) => {
|
|
157
|
+
const base = {
|
|
158
|
+
name: boundary.name,
|
|
159
|
+
mode: boundary.mode,
|
|
160
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
161
|
+
};
|
|
162
|
+
if (boundary.mode !== "dynamic")
|
|
163
|
+
return { ...base, status: "unresolved" };
|
|
164
|
+
if (boundary.load === undefined)
|
|
165
|
+
return { ...base, status: "ready" };
|
|
166
|
+
const load = pending.find((entry) => entry.name === boundary.name);
|
|
167
|
+
if (load === undefined)
|
|
168
|
+
return { ...base, status: "error", error: boundaryError(undefined) };
|
|
169
|
+
try {
|
|
170
|
+
const data = await load.promise;
|
|
171
|
+
return { ...base, status: "ready", ...(data !== undefined ? { data } : {}) };
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return { ...base, status: "error", error: boundaryError(error) };
|
|
175
|
+
}
|
|
176
|
+
})).then((states) => Object.fromEntries(states.map((state) => [state.name, state])));
|
|
177
|
+
return { initial, pending, complete };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Resolve only explicitly annotated static boundaries with a request-free build context. Dynamic
|
|
181
|
+
* and intercepting boundaries remain unresolved. Values are cached by boundary object identity in
|
|
182
|
+
* the supplied in-memory cache, so a worker instance does not repeat a build-safe computation per
|
|
183
|
+
* request. A rejected static load becomes that slot's error state; it never fabricates shared data.
|
|
184
|
+
*/
|
|
185
|
+
export async function resolveStaticBoundaries(boundaries, context, cache = DEFAULT_STATIC_CACHE) {
|
|
186
|
+
// Validation only - see `startDynamicBoundaries`; the descriptor list has no holes to test for.
|
|
187
|
+
boundaryDescriptors(boundaries);
|
|
188
|
+
const states = await Promise.all(boundaries.map(async (boundary) => {
|
|
189
|
+
if (boundary.mode !== "static") {
|
|
190
|
+
return {
|
|
191
|
+
name: boundary.name,
|
|
192
|
+
mode: boundary.mode,
|
|
193
|
+
status: "unresolved",
|
|
194
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (boundary.load === undefined) {
|
|
198
|
+
return {
|
|
199
|
+
name: boundary.name,
|
|
200
|
+
mode: boundary.mode,
|
|
201
|
+
status: "ready",
|
|
202
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
let value = cache.get(boundary);
|
|
206
|
+
if (value === undefined) {
|
|
207
|
+
value = Promise.resolve().then(() => boundary.load?.(context));
|
|
208
|
+
cache.set(boundary, value);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const data = await value;
|
|
212
|
+
return {
|
|
213
|
+
name: boundary.name,
|
|
214
|
+
mode: boundary.mode,
|
|
215
|
+
status: "ready",
|
|
216
|
+
...(data !== undefined ? { data } : {}),
|
|
217
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
return {
|
|
222
|
+
name: boundary.name,
|
|
223
|
+
mode: boundary.mode,
|
|
224
|
+
status: "error",
|
|
225
|
+
error: boundaryError(error),
|
|
226
|
+
...(boundary.errorId !== undefined ? { errorId: boundary.errorId } : {}),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}));
|
|
230
|
+
return Object.fromEntries(states.map((state) => [state.name, state]));
|
|
231
|
+
}
|
|
232
|
+
const DEFAULT_STATIC_CACHE = new MemoryStaticBoundaryCache();
|
|
233
|
+
/** Stable mode label for adapter registries and diagnostics. */
|
|
234
|
+
export function boundaryModeKey(mode) {
|
|
235
|
+
return modeKey(mode);
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.js","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { ClientModuleGraph } from "./module-graph.js";
|
|
2
|
+
export interface BuildManifest {
|
|
3
|
+
/** URL of the client entry module (content-hashed). */
|
|
4
|
+
readonly entry: string;
|
|
5
|
+
/** URLs of every emitted asset (entry + chunks) - for serving + preloading. */
|
|
6
|
+
readonly assets: readonly string[];
|
|
7
|
+
/** `routeId → [layout chunk URLs…, own chunk URL]` - the chunks a route needs, for `createWebApp`'s
|
|
8
|
+
* `routePreload` (`<link rel="modulepreload">` the matched route alongside the entry). Each route +
|
|
9
|
+
* layout is also a build entrypoint, so it gets a named chunk the bootstrap's lazy import dedupes to. */
|
|
10
|
+
readonly routes: Readonly<Record<string, readonly string[]>>;
|
|
11
|
+
/** URL paths copied from `publicDir`, sorted. Lets the server entry serve them without scanning a
|
|
12
|
+
* directory per request, and lets an adapter that needs a file list (CDN upload, platform static
|
|
13
|
+
* assets) consume one. Omitted when there is no `public/`. */
|
|
14
|
+
readonly publicFiles?: readonly string[];
|
|
15
|
+
/** The app's bundled, content-hashed stylesheet(s) - the bootstrap's **aggregate** CSS (every
|
|
16
|
+
* `import './x.css'` reachable from the app). The complete stylesheet regardless of which file
|
|
17
|
+
* imported the CSS; the always-safe fallback `createWebApp` links when a route has no per-route entry
|
|
18
|
+
* in {@link routeStyles}. Omitted when the app imports no CSS. */
|
|
19
|
+
readonly css?: readonly string[];
|
|
20
|
+
/** `routeId → [chain CSS URLs]` - only the stylesheets the matched route's layout chain + own file
|
|
21
|
+
* actually use (Bun emits a per-entrypoint CSS bundle per route/layout, with shared-component CSS
|
|
22
|
+
* inlined into each consumer). `createWebApp` links these instead of the aggregate, so a page ships
|
|
23
|
+
* only its own CSS. A route is omitted (→ aggregate fallback) when its `[name]` collides with another
|
|
24
|
+
* route's basename (ambiguous CSS↔route) or the build emitted orphan shared-chunk CSS - correctness
|
|
25
|
+
* over minimality. Absent entirely when the app imports no CSS. */
|
|
26
|
+
readonly routeStyles?: Readonly<Record<string, readonly string[]>>;
|
|
27
|
+
}
|
|
28
|
+
/** The built worker bundle - point your `wrangler.toml`'s `main` at `worker`. */
|
|
29
|
+
export interface ServerBuild {
|
|
30
|
+
/** Path to the bundled, self-contained worker entry. */
|
|
31
|
+
readonly worker: string;
|
|
32
|
+
/** Paths of every emitted output (entry + any code-split chunks) - what to ship to the platform. */
|
|
33
|
+
readonly outputs: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
/** A deploy target `nifra build --target <t>` can emit. `static` is pure SSG (no server). */
|
|
36
|
+
export declare const BUILD_TARGETS: readonly ["bun", "node", "deno", "cf-pages", "vercel", "static"];
|
|
37
|
+
export type BuildTarget = (typeof BUILD_TARGETS)[number];
|
|
38
|
+
/** A type guard narrowing an arbitrary string to a {@link BuildTarget}. */
|
|
39
|
+
export declare function isBuildTarget(value: string): value is BuildTarget;
|
|
40
|
+
export type ServerBuildTarget = "browser" | "node" | "bun";
|
|
41
|
+
export interface StaticBuildTargetPlan {
|
|
42
|
+
readonly target: "static";
|
|
43
|
+
readonly kind: "static";
|
|
44
|
+
readonly serverTarget: undefined;
|
|
45
|
+
readonly outputFile: undefined;
|
|
46
|
+
readonly run: string;
|
|
47
|
+
}
|
|
48
|
+
export interface ServerBuildTargetPlan {
|
|
49
|
+
readonly target: Exclude<BuildTarget, "static">;
|
|
50
|
+
readonly kind: "server";
|
|
51
|
+
readonly serverTarget: ServerBuildTarget;
|
|
52
|
+
/** The worker's final filename inside the assembled deploy directory. */
|
|
53
|
+
readonly outputFile: "_worker.js" | "index.js" | "server.js";
|
|
54
|
+
readonly run: string;
|
|
55
|
+
}
|
|
56
|
+
export type BuildTargetPlan = StaticBuildTargetPlan | ServerBuildTargetPlan;
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the target-specific deploy shape before any bundling starts. Keeping this decision pure
|
|
59
|
+
* means Bun and Vite strategies share the same output filename, server target, and hand-off text;
|
|
60
|
+
* the filesystem emitter only has to execute the plan.
|
|
61
|
+
*/
|
|
62
|
+
export declare function planBuildTarget(target: BuildTarget, outDir: string): BuildTargetPlan;
|
|
63
|
+
/**
|
|
64
|
+
* A build-tool STRATEGY for {@link buildTargetWith} - the two bundling steps, and nothing else. Everything
|
|
65
|
+
* around them (server-entry codegen, deploy assembly, prerender, size report) is bundler-agnostic and
|
|
66
|
+
* lives in `buildTargetWith`, so a second bundler (Vite) is this interface, not a second orchestrator.
|
|
67
|
+
*
|
|
68
|
+
* Plugin lists are `readonly unknown[]` because a Bun plugin and a Vite plugin are different types; each
|
|
69
|
+
* strategy casts to its own. `buildTargetWith` only forwards them.
|
|
70
|
+
*/
|
|
71
|
+
export interface Bundler {
|
|
72
|
+
/** Build the client bundle → the shared {@link BuildManifest}. */
|
|
73
|
+
buildClient(input: {
|
|
74
|
+
readonly routesDir: string;
|
|
75
|
+
readonly outDir: string;
|
|
76
|
+
readonly clientModule: string;
|
|
77
|
+
readonly plugins?: readonly unknown[];
|
|
78
|
+
readonly conditions?: readonly string[];
|
|
79
|
+
readonly define?: Readonly<Record<string, string>>;
|
|
80
|
+
readonly publicDir?: string | false;
|
|
81
|
+
readonly publicEnvPrefix?: string;
|
|
82
|
+
/** Project root (Vite needs it; the Bun strategy ignores it). */
|
|
83
|
+
readonly root?: string;
|
|
84
|
+
}): Promise<BuildManifest>;
|
|
85
|
+
/** Build the server worker → the shared {@link ServerBuild}. */
|
|
86
|
+
buildServer(input: {
|
|
87
|
+
readonly routesDir: string;
|
|
88
|
+
readonly serverEntry: string;
|
|
89
|
+
readonly outDir: string;
|
|
90
|
+
readonly clientEntry: string;
|
|
91
|
+
readonly target: "browser" | "node" | "bun";
|
|
92
|
+
readonly plugins?: readonly unknown[];
|
|
93
|
+
readonly define?: Readonly<Record<string, string>>;
|
|
94
|
+
readonly root?: string;
|
|
95
|
+
}): Promise<ServerBuild>;
|
|
96
|
+
}
|
|
97
|
+
/** One `node:`-builtin-in-the-client finding: the offending builtin, the emitted chunk it landed in,
|
|
98
|
+
* and the shortest USER-module import chain that pulled it there (entry → … → builtin). */
|
|
99
|
+
export interface NodeBuiltinFinding {
|
|
100
|
+
readonly builtin: string;
|
|
101
|
+
readonly chunk: string;
|
|
102
|
+
/** The shortest import path from a user entry to the builtin, as a list of display labels:
|
|
103
|
+
* `[entryFile, ...as-written specifiers along the way, builtin]`, e.g.
|
|
104
|
+
* `["routes/article/[slug].tsx", "../data.ts", "../db/client.ts", "postgres", "node:tls"]`. The
|
|
105
|
+
* entry is its graph key (the route file); each hop is the import's *as-written* specifier; the tail
|
|
106
|
+
* is the builtin. Empty only if the builtin module isn't reachable from any traced input (it always
|
|
107
|
+
* is when flagged). */
|
|
108
|
+
readonly chain: readonly string[];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Scan a build's metafile for any `node:` builtin that a USER module pulled into a CLIENT output
|
|
112
|
+
* chunk, returning a sorted, deduped list of {@link NodeBuiltinFinding}s. Three graph facts combine so
|
|
113
|
+
* the report is precise AND actionable:
|
|
114
|
+
* 1. **What the user wrote** - only builtins imported by a NON-`node:` input count, so Bun's own
|
|
115
|
+
* polyfill chain (`node:crypto` → `node:buffer`/`node:stream`/…) doesn't bury the real cause.
|
|
116
|
+
* 2. **Where it landed** - the chunk is read from the per-output `inputs`, so the error names the
|
|
117
|
+
* emitted file to look at.
|
|
118
|
+
* 3. **How it got there** - the shortest import chain from a user entry to the builtin
|
|
119
|
+
* (`shortestBuiltinChain`), so the error points straight at the offending `import` line instead of
|
|
120
|
+
* leaving the dev to grep the dependency tree (the DX gap this closes).
|
|
121
|
+
* Graph-based (never the emitted text), so it survives minification and can't be fooled by a string
|
|
122
|
+
* literal that merely contains `"node:crypto"`. Pure + exported for unit testing. Empty ⇒ clean.
|
|
123
|
+
*/
|
|
124
|
+
export declare function detectNodeBuiltinsInClient(graph: ClientModuleGraph): ReadonlyArray<NodeBuiltinFinding>;
|
|
125
|
+
/** The marker specifier an author imports to opt a module into the client-leak guard. Matched on the
|
|
126
|
+
* import edge's *as-written* `original` first (the robust signal: it's exactly what the author typed,
|
|
127
|
+
* before Bun resolves it to `src/server-only.ts` / `dist/server-only.js`). */
|
|
128
|
+
export declare const SERVER_ONLY_MARKER = "@nifrajs/web/server-only";
|
|
129
|
+
/** One `server-only`-module-in-the-client finding: the offending module (the as-written marker-import
|
|
130
|
+
* chain's tail before the marker), the emitted chunk it landed in, and the shortest USER-module import
|
|
131
|
+
* chain that pulled it there (entry → … → the server-only module). */
|
|
132
|
+
export interface ServerOnlyFinding {
|
|
133
|
+
/** The emitted client chunk the server-only module landed in (basename). */
|
|
134
|
+
readonly chunk: string;
|
|
135
|
+
/** The shortest import path from a user entry to the server-only module, as display labels:
|
|
136
|
+
* `[entryFile, ...as-written specifiers…, "<module> (marked server-only)"]`. Mirrors
|
|
137
|
+
* {@link NodeBuiltinFinding.chain}; the tail names the marked module so the message reads
|
|
138
|
+
* `routes/x.tsx → ../secrets.ts (marked server-only)`. */
|
|
139
|
+
readonly chain: readonly string[];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Scan a build's metafile for any module that opts into the `server-only` marker (a side-effect
|
|
143
|
+
* `import "@nifrajs/web/server-only"`) yet landed in a CLIENT output chunk, returning a sorted, deduped
|
|
144
|
+
* list of {@link ServerOnlyFinding}s. Mirrors {@link detectNodeBuiltinsInClient}: it reads the SAME
|
|
145
|
+
* graph facts - which inputs import the marker (the "marked" modules), which chunk each landed in (the
|
|
146
|
+
* per-output `inputs`), and the shortest import chain from a user entry to it. The marker module ITSELF
|
|
147
|
+
* (which imports nothing) is excluded - only the modules that *opt in* are flagged. Pure + exported for
|
|
148
|
+
* unit testing. Empty ⇒ clean.
|
|
149
|
+
*/
|
|
150
|
+
export declare function detectServerOnlyInClient(graph: ClientModuleGraph): ReadonlyArray<ServerOnlyFinding>;
|
|
151
|
+
/** The build-failing message for `node:` builtins that reached the client bundle. `undefined` ⇒ clean. */
|
|
152
|
+
export declare function formatNodeBuiltinLeak(findings: ReadonlyArray<NodeBuiltinFinding>): string | undefined;
|
|
153
|
+
/** The build-failing message for `server-only`-marked modules that reached the client. `undefined` ⇒ clean. */
|
|
154
|
+
export declare function formatServerOnlyLeak(findings: ReadonlyArray<ServerOnlyFinding>): string | undefined;
|
|
155
|
+
/** One emitted chunk's measured size, in raw bytes + gzipped bytes (over-the-wire weight). */
|
|
156
|
+
export interface ChunkSize {
|
|
157
|
+
/** The emitted file's basename (e.g. `index-abc123.js`). */
|
|
158
|
+
readonly name: string;
|
|
159
|
+
/** Raw byte length of the file. */
|
|
160
|
+
readonly bytes: number;
|
|
161
|
+
/** Gzipped byte length (what the client actually downloads, modulo brotli). */
|
|
162
|
+
readonly gzip: number;
|
|
163
|
+
}
|
|
164
|
+
/** A whole build's size report - every chunk (largest first) + the totals. */
|
|
165
|
+
export interface SizeReport {
|
|
166
|
+
/** Per-chunk sizes, sorted biggest gzip first (the regression you want to see at the top). */
|
|
167
|
+
readonly chunks: readonly ChunkSize[];
|
|
168
|
+
/** Sum of every chunk's raw bytes. */
|
|
169
|
+
readonly totalBytes: number;
|
|
170
|
+
/** Sum of every chunk's gzip bytes. */
|
|
171
|
+
readonly totalGzip: number;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Aggregate a list of measured chunks into a {@link SizeReport}: sort biggest-gzip-first (ties broken
|
|
175
|
+
* by raw bytes, then name for stable output) and sum the totals. Pure - the measurement (reading the
|
|
176
|
+
* file + gzipping it) happens in the orchestrator; this is the deterministic, unit-testable core.
|
|
177
|
+
*/
|
|
178
|
+
export declare function aggregateSizeReport(chunks: readonly ChunkSize[]): SizeReport;
|
|
179
|
+
/** Human-readable byte count: `B`/`KB`/`MB` with one decimal above 1 KB (e.g. `12.3 KB`). Pure. */
|
|
180
|
+
export declare function formatBytes(bytes: number): string;
|
|
181
|
+
/**
|
|
182
|
+
* Render a {@link SizeReport} as a terse aligned table (biggest first) with a totals row - the text
|
|
183
|
+
* `nifra build --report` prints. Pure (string in, string out) so the formatting is unit-testable.
|
|
184
|
+
*/
|
|
185
|
+
export declare function renderSizeReport(report: SizeReport): string;
|
|
186
|
+
/** A drift finding between a committed server-manifest and the live `routes/` tree. */
|
|
187
|
+
export interface ManifestDrift {
|
|
188
|
+
/** Route files present in `routes/` but ABSENT from the committed manifest (the manifest is stale -
|
|
189
|
+
* the new route won't be served by the worker). */
|
|
190
|
+
readonly missing: readonly string[];
|
|
191
|
+
/** Route files the committed manifest imports that no longer exist in `routes/` (a deleted/renamed
|
|
192
|
+
* route still wired into the worker - a build/runtime break). */
|
|
193
|
+
readonly extra: readonly string[];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Extract the route-relative file list a committed server-manifest declares, as the same
|
|
197
|
+
* `routes/`-relative keys `discoverRoutes` produces (e.g. `docs/index.tsx`). Reads the route map's KEYS,
|
|
198
|
+
* which carry the file extension and no directory prefix - exactly discovery's shape - so the result is
|
|
199
|
+
* independent of the specifier prefix the manifest happened to import with. The `@nifrajs/web` import and
|
|
200
|
+
* the baked `clientEntry`/`styles`/`routeStyles` lines carry no route-map entry and are ignored.
|
|
201
|
+
*
|
|
202
|
+
* `_routesPrefix` is accepted for call-site compatibility but no longer needed: the keys are already
|
|
203
|
+
* prefix-free. Pure - operates on source text.
|
|
204
|
+
*/
|
|
205
|
+
export declare function parseManifestRouteFiles(source: string, _routesPrefix?: string): string[];
|
|
206
|
+
/** The baked `clientEntry` URL in a committed server-manifest, or `undefined` if absent. Pure. */
|
|
207
|
+
export declare function parseManifestClientEntry(source: string): string | undefined;
|
|
208
|
+
/** The baked top-level `styles` array in a committed server-manifest (empty if absent/unparseable). Pure. */
|
|
209
|
+
export declare function parseManifestStyles(source: string): string[];
|
|
210
|
+
/** The baked per-route `routeStyles` map in a committed server-manifest (empty if absent/unparseable). Pure. */
|
|
211
|
+
export declare function parseManifestRouteStyles(source: string): Record<string, string[]>;
|
|
212
|
+
/**
|
|
213
|
+
* Diff the route files a committed server-manifest imports against the files freshly discovered in
|
|
214
|
+
* `routes/`. Returns the `missing` (in routes/, not in manifest - stale manifest) and `extra` (in
|
|
215
|
+
* manifest, gone from routes/ - dangling import) sets. Empty arrays ⇒ in sync. Pure - the caller
|
|
216
|
+
* supplies both file lists (the committed source is parsed via {@link parseManifestRouteFiles}; the
|
|
217
|
+
* fresh list comes from `discoverRoutes`). Lists need not be pre-sorted; the result is sorted.
|
|
218
|
+
*/
|
|
219
|
+
export declare function diffManifestRoutes(manifestFiles: readonly string[], discoveredFiles: readonly string[]): ManifestDrift;
|
|
220
|
+
/** True when a drift report is clean (no missing + no extra routes). */
|
|
221
|
+
export declare function isManifestInSync(drift: ManifestDrift): boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Format a {@link ManifestDrift} as a named, actionable error message, or `undefined` when in sync.
|
|
224
|
+
* Names the exact missing/extra routes + the one fix (regenerate the manifest by re-running the build).
|
|
225
|
+
* `manifestPath` is shown for the dev to locate the stale file. Pure.
|
|
226
|
+
*/
|
|
227
|
+
export declare function formatManifestDrift(drift: ManifestDrift, manifestPath?: string): string | undefined;
|
|
228
|
+
//# sourceMappingURL=build-plan.d.ts.map
|