veryfront 0.1.1100 → 0.1.1102
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/esm/deno.js +1 -1
- package/esm/src/agent/factory.d.ts.map +1 -1
- package/esm/src/agent/factory.js +2 -0
- package/esm/src/agent/hosted/cloud-runtime-system-messages.d.ts +5 -0
- package/esm/src/agent/hosted/cloud-runtime-system-messages.d.ts.map +1 -1
- package/esm/src/agent/hosted/cloud-runtime-system-messages.js +3 -2
- package/esm/src/agent/runtime/agent-invocation-contract.d.ts +1 -0
- package/esm/src/agent/runtime/agent-invocation-contract.d.ts.map +1 -1
- package/esm/src/agent/runtime/agent-invocation-contract.js +1 -0
- package/esm/src/agent/runtime/effective-agent-system.d.ts +8 -0
- package/esm/src/agent/runtime/effective-agent-system.d.ts.map +1 -0
- package/esm/src/agent/runtime/effective-agent-system.js +9 -0
- package/esm/src/internal-agents/run-stream.d.ts +1 -0
- package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
- package/esm/src/internal-agents/run-stream.js +53 -0
- package/esm/src/internal-agents/run-system-prompt.d.ts +42 -0
- package/esm/src/internal-agents/run-system-prompt.d.ts.map +1 -0
- package/esm/src/internal-agents/run-system-prompt.js +109 -0
- package/esm/src/internal-agents/schema.d.ts +2 -0
- package/esm/src/internal-agents/schema.d.ts.map +1 -1
- package/esm/src/internal-agents/schema.js +1 -0
- package/esm/src/routing/api/context-builder.d.ts +23 -1
- package/esm/src/routing/api/context-builder.d.ts.map +1 -1
- package/esm/src/routing/api/context-builder.js +22 -1
- package/esm/src/routing/api/handler.d.ts +11 -1
- package/esm/src/routing/api/handler.d.ts.map +1 -1
- package/esm/src/routing/api/handler.js +46 -12
- package/esm/src/routing/api/module-loader/loader.d.ts +5 -0
- package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/loader.js +37 -2
- package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
- package/esm/src/server/dev-server/middleware.js +35 -7
- package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/agent-stream.handler.js +1 -0
- package/esm/src/server/handlers/request/ssr/error-page-fallback.js +20 -3
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts +0 -11
- package/esm/src/server/handlers/request/ssr/ssr.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/ssr/ssr.handler.js +23 -1
- package/esm/src/transforms/esm/degraded-artifact.d.ts +15 -0
- package/esm/src/transforms/esm/degraded-artifact.d.ts.map +1 -0
- package/esm/src/transforms/esm/degraded-artifact.js +22 -0
- package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache.js +47 -17
- package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
- package/esm/src/transforms/esm/import-parser.js +47 -1
- package/esm/src/transforms/esm/specifier-resolver.d.ts +21 -4
- package/esm/src/transforms/esm/specifier-resolver.d.ts.map +1 -1
- package/esm/src/transforms/esm/specifier-resolver.js +43 -23
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts +23 -0
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.d.ts.map +1 -0
- package/esm/src/transforms/import-rewriter/strategies/asset-strategy.js +49 -0
- package/esm/src/transforms/import-rewriter/strategies/index.d.ts +1 -0
- package/esm/src/transforms/import-rewriter/strategies/index.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/strategies/index.js +1 -0
- package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts.map +1 -1
- package/esm/src/transforms/import-rewriter/unified-rewriter.js +2 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.js +2 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts +19 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/nested-imports.js +23 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +5 -5
|
@@ -12,6 +12,33 @@ import { executeAppRoute, executePagesRoute } from "./route-executor.js";
|
|
|
12
12
|
import { withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
13
13
|
/** Max entries in the loaded-handler LRU cache */
|
|
14
14
|
const HANDLER_CACHE_MAX_ENTRIES = 256;
|
|
15
|
+
/** Longest sanitised load error a response body carries. */
|
|
16
|
+
const MAX_LOAD_ERROR_LENGTH = 300;
|
|
17
|
+
/**
|
|
18
|
+
* Roots that only ever identify the machine running the server: home
|
|
19
|
+
* directories and the temp directories bundling writes to.
|
|
20
|
+
*/
|
|
21
|
+
const MACHINE_PATH_PATTERN = /(?:file:\/\/)?\/(?:private\/)?(?:Users|home|root|var\/folders|var\/tmp|tmp)\/\S*/g;
|
|
22
|
+
/**
|
|
23
|
+
* Reduce a module load error to something safe to put in a response body.
|
|
24
|
+
*
|
|
25
|
+
* The message is worth returning, since it usually names a syntax or import
|
|
26
|
+
* error the developer can act on. The rest of it is not: a raw load error
|
|
27
|
+
* carries the temp directory the bundle was written to, absolute paths into the
|
|
28
|
+
* framework install, and a full stack trace. A path inside the project survives
|
|
29
|
+
* as a project-relative one, because that is the part that identifies the file
|
|
30
|
+
* to fix.
|
|
31
|
+
*/
|
|
32
|
+
export function sanitizeLoadErrorForResponse(message, projectDir) {
|
|
33
|
+
// A stack trace is never the actionable part.
|
|
34
|
+
let text = (message.split("\n")[0] ?? "").trim();
|
|
35
|
+
if (projectDir) {
|
|
36
|
+
const root = projectDir.replace(/\/+$/, "");
|
|
37
|
+
text = text.replaceAll(`file://${root}/`, "").replaceAll(`${root}/`, "");
|
|
38
|
+
}
|
|
39
|
+
text = text.replace(MACHINE_PATH_PATTERN, "<PATH>");
|
|
40
|
+
return text.length > MAX_LOAD_ERROR_LENGTH ? `${text.slice(0, MAX_LOAD_ERROR_LENGTH)}...` : text;
|
|
41
|
+
}
|
|
15
42
|
let injectedDeps = null;
|
|
16
43
|
/**
|
|
17
44
|
* Inject dependencies for testing. Pass null to reset to defaults.
|
|
@@ -31,7 +58,6 @@ export class APIRouteHandler {
|
|
|
31
58
|
projectDir;
|
|
32
59
|
router = new ApiRouteMatcher();
|
|
33
60
|
routeCache = new LRUCache({ maxEntries: HANDLER_CACHE_MAX_ENTRIES });
|
|
34
|
-
lastErrorMessage = null;
|
|
35
61
|
activeRequests = 0;
|
|
36
62
|
destroyRequested = false;
|
|
37
63
|
destroyed = false;
|
|
@@ -116,18 +142,27 @@ export class APIRouteHandler {
|
|
|
116
142
|
page: match.route.page,
|
|
117
143
|
params: match.params,
|
|
118
144
|
});
|
|
119
|
-
const handler = await this.loadHandler(match);
|
|
145
|
+
const { handler, errorMessage } = await this.loadHandler(match);
|
|
120
146
|
if (!handler) {
|
|
147
|
+
const msg = errorMessage ?? "Handler not found";
|
|
121
148
|
try {
|
|
122
|
-
|
|
149
|
+
// The full detail, paths and all, belongs in the log.
|
|
150
|
+
logger.error(`handler module failed to load: ${match.route.page}`, { reason: msg });
|
|
123
151
|
}
|
|
124
152
|
catch (e) {
|
|
125
153
|
logger.warn("API error log failed", e);
|
|
126
154
|
}
|
|
127
|
-
const msg = this.lastErrorMessage ?? "Handler not found";
|
|
128
155
|
if (msg.includes("Remote import blocked by allow-list"))
|
|
129
156
|
return badGateway(msg);
|
|
130
|
-
|
|
157
|
+
// The reason the module failed to load is the only useful thing here.
|
|
158
|
+
// Reporting a flat "Handler not found" for what is usually a syntax or
|
|
159
|
+
// import error sends people hunting for a routing problem that does
|
|
160
|
+
// not exist. Local development only, and sanitised: a raw load error
|
|
161
|
+
// carries temp directories, absolute paths and a stack trace, none of
|
|
162
|
+
// which belong in a response body.
|
|
163
|
+
return internalServerError(ctx?.isLocalProject
|
|
164
|
+
? sanitizeLoadErrorForResponse(msg, this.projectDir)
|
|
165
|
+
: "Handler not found");
|
|
131
166
|
}
|
|
132
167
|
// App Router routes are always named route.ts/js/tsx/jsx
|
|
133
168
|
// Pages Router routes have descriptive names like articles.ts
|
|
@@ -156,7 +191,7 @@ export class APIRouteHandler {
|
|
|
156
191
|
await this.ensureConfig(adapter);
|
|
157
192
|
const cached = this.routeCache.get(modulePath);
|
|
158
193
|
if (cached)
|
|
159
|
-
return cached;
|
|
194
|
+
return { handler: cached, errorMessage: null };
|
|
160
195
|
try {
|
|
161
196
|
const deps = getDeps();
|
|
162
197
|
const handler = await deps.loadHandlerModule({
|
|
@@ -168,16 +203,15 @@ export class APIRouteHandler {
|
|
|
168
203
|
// Only cache handlers that export at least one HTTP method.
|
|
169
204
|
// Empty objects ({}) from failed imports are truthy but useless —
|
|
170
205
|
// caching them would prevent retry after the user fixes the import.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return
|
|
206
|
+
const usable = handler && Object.keys(handler).length > 0 ? handler : null;
|
|
207
|
+
if (usable)
|
|
208
|
+
this.routeCache.set(modulePath, usable);
|
|
209
|
+
return { handler: usable, errorMessage: null };
|
|
175
210
|
}
|
|
176
211
|
catch (error) {
|
|
177
212
|
const msg = error instanceof Error ? error.message : String(error);
|
|
178
|
-
this.lastErrorMessage = msg;
|
|
179
213
|
logger.error(`[API] Failed to load handler for ${modulePath}: ${msg}`);
|
|
180
|
-
return null;
|
|
214
|
+
return { handler: null, errorMessage: msg };
|
|
181
215
|
}
|
|
182
216
|
}, { "api.modulePath": modulePath });
|
|
183
217
|
}
|
|
@@ -2,4 +2,9 @@ import type { APIRoute, LoadModuleOptions } from "./types.js";
|
|
|
2
2
|
export { generateCompiledBinaryRequireShim, getNodeExternalPackagesToResolve, loadVeryfrontExportsMap, resolveEsmUserDependencies, resolveNodePackageToFileUrl, rewriteCompiledBinaryUserDependencyImports, rewriteCompiledBinaryVeryfrontImports, rewriteDenoNodeBuiltinImports, rewriteDenoNpmDependencyImports, rewriteNodeExternalImports, } from "./external-import-rewriter.js";
|
|
3
3
|
export { toCjsDestructureBindings } from "./loader-helpers.js";
|
|
4
4
|
export declare function loadHandlerModule(options: LoadModuleOptions): Promise<APIRoute | null>;
|
|
5
|
+
/**
|
|
6
|
+
* True when a module failed to load because Deno could not resolve one of its
|
|
7
|
+
* import specifiers, rather than because the module itself is broken.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isSpecifierResolutionError(error: unknown): boolean;
|
|
5
10
|
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAkB9D,OAAO,EACL,iCAAiC,EACjC,gCAAgC,EAChC,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0CAA0C,EAC1C,qCAAqC,EACrC,6BAA6B,EAC7B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyBtF"}
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAkB9D,OAAO,EACL,iCAAiC,EACjC,gCAAgC,EAChC,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0CAA0C,EAC1C,qCAAqC,EACrC,6BAA6B,EAC7B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAyBtF;AA0DD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAIlE"}
|
|
@@ -45,8 +45,25 @@ async function loadModule(args) {
|
|
|
45
45
|
return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config);
|
|
46
46
|
}
|
|
47
47
|
const fileExistsLocally = await fs.exists(modulePath);
|
|
48
|
-
if (fileExistsLocally)
|
|
49
|
-
|
|
48
|
+
if (fileExistsLocally) {
|
|
49
|
+
try {
|
|
50
|
+
return await loadTSModuleDirect(modulePath);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
// A direct import shares the dev server's runtime context, which is what
|
|
54
|
+
// makes auto-discovery (agentRegistry and friends) work — but it leaves
|
|
55
|
+
// specifier resolution to Deno, which knows nothing about the project's
|
|
56
|
+
// `@/` alias. Bundling can resolve that import map path, so fall back to
|
|
57
|
+
// it rather than reporting a routing-shaped 500.
|
|
58
|
+
if (!isSpecifierResolutionError(error))
|
|
59
|
+
throw error;
|
|
60
|
+
logger.debug("Direct import could not resolve a specifier, bundling instead", {
|
|
61
|
+
modulePath,
|
|
62
|
+
error: error instanceof Error ? error.message : String(error),
|
|
63
|
+
});
|
|
64
|
+
return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
50
67
|
logger.debug(`File not local, using adapter-based loading: ${modulePath}`);
|
|
51
68
|
return loadAndTranspileModule(modulePath, projectDir, adapter, fs, config);
|
|
52
69
|
}
|
|
@@ -55,6 +72,24 @@ async function loadModule(args) {
|
|
|
55
72
|
* This allows the module to share the same runtime context as the dev server,
|
|
56
73
|
* enabling auto-discovery features like agentRegistry to work.
|
|
57
74
|
*/
|
|
75
|
+
/**
|
|
76
|
+
* Deno's resolver reports an unresolvable import as a TypeError whose message
|
|
77
|
+
* opens by naming the specifier it could not resolve. Anchoring on that opening
|
|
78
|
+
* is what keeps a module's own error out: a route that throws
|
|
79
|
+
* `Error("Cannot find module x")` at evaluation time is broken, and re-running
|
|
80
|
+
* it under bundling would evaluate broken code a second time.
|
|
81
|
+
*/
|
|
82
|
+
const SPECIFIER_RESOLUTION_MESSAGE = /^(?:Import "[^"]+" not a dependency|Module not found "[^"]+"|Relative import path "[^"]+" not prefixed)/;
|
|
83
|
+
/**
|
|
84
|
+
* True when a module failed to load because Deno could not resolve one of its
|
|
85
|
+
* import specifiers, rather than because the module itself is broken.
|
|
86
|
+
*/
|
|
87
|
+
export function isSpecifierResolutionError(error) {
|
|
88
|
+
// Every other error shape the direct import can produce belongs to the module.
|
|
89
|
+
if (!(error instanceof TypeError))
|
|
90
|
+
return false;
|
|
91
|
+
return SPECIFIER_RESOLUTION_MESSAGE.test(error.message.trimStart());
|
|
92
|
+
}
|
|
58
93
|
function loadTSModuleDirect(modulePath) {
|
|
59
94
|
const cacheBuster = `?v=${Date.now()}`;
|
|
60
95
|
const url = modulePath.startsWith("file://")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7D,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD,UAAU,qBAAqB;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAkFD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,kBAAkB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAM7D,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD,UAAU,qBAAqB;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAkFD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkC/B;AAqHD,wBAAsB,eAAe,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,EAAE,eAAe,EACvB,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GAClD,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -86,11 +86,11 @@ export async function loadMiddlewareFile(projectDir, adapter, options = {}) {
|
|
|
86
86
|
try {
|
|
87
87
|
logger.debug(`Loading ${middlewareFile}`);
|
|
88
88
|
if (isVirtualFilesystem(adapter.fs)) {
|
|
89
|
-
return await loadMiddlewareFromVirtualFS(middlewarePath, adapter, options.throwOnError === true);
|
|
89
|
+
return await loadMiddlewareFromVirtualFS(middlewarePath, adapter, options.throwOnError === true, middlewareFile);
|
|
90
90
|
}
|
|
91
91
|
const middlewareUrl = `file://${middlewarePath}?t=${Date.now()}-${dntShim.crypto.randomUUID()}`;
|
|
92
92
|
const middlewareModule = await import(middlewareUrl);
|
|
93
|
-
return normalizeMiddlewareExport(middlewareModule, options.throwOnError === true);
|
|
93
|
+
return normalizeMiddlewareExport(middlewareModule, options.throwOnError === true, middlewareFile);
|
|
94
94
|
}
|
|
95
95
|
catch (error) {
|
|
96
96
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -101,7 +101,7 @@ export async function loadMiddlewareFile(projectDir, adapter, options = {}) {
|
|
|
101
101
|
}
|
|
102
102
|
return [];
|
|
103
103
|
}
|
|
104
|
-
async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport) {
|
|
104
|
+
async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport, sourceFile = "middleware.ts") {
|
|
105
105
|
const fs = createFileSystem();
|
|
106
106
|
const content = await adapter.fs.readFile(middlewarePath);
|
|
107
107
|
const source = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
@@ -130,20 +130,48 @@ async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport
|
|
|
130
130
|
try {
|
|
131
131
|
await fs.writeTextFile(tempFile, js);
|
|
132
132
|
const middlewareModule = await import(`file://${tempFile}?v=${Date.now()}`);
|
|
133
|
-
return normalizeMiddlewareExport(middlewareModule, strictExport);
|
|
133
|
+
return normalizeMiddlewareExport(middlewareModule, strictExport, sourceFile);
|
|
134
134
|
}
|
|
135
135
|
finally {
|
|
136
136
|
await fs.remove(tempDir, { recursive: true });
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Explain what a root middleware file must export. When the module looks like it
|
|
141
|
+
* was written for Next.js, say so, because that is overwhelmingly why this
|
|
142
|
+
* fails. A bad root middleware takes down every route, so the message has
|
|
143
|
+
* to be enough to fix the file without reading the source.
|
|
144
|
+
*/
|
|
145
|
+
function invalidMiddlewareExport(middlewareModule, sourceFile) {
|
|
146
|
+
const named = middlewareModule && typeof middlewareModule === "object"
|
|
147
|
+
? Object.keys(middlewareModule)
|
|
148
|
+
: [];
|
|
149
|
+
const looksLikeNext = named.includes("middleware") &&
|
|
150
|
+
typeof middlewareModule.middleware === "function";
|
|
151
|
+
const detail = looksLikeNext
|
|
152
|
+
? `Found a named "middleware" export, which is the Next.js convention. ` +
|
|
153
|
+
`Veryfront expects a default export, and its middleware receives ` +
|
|
154
|
+
`(c, next), where c is a context carrying c.req, not the Request itself.`
|
|
155
|
+
: named.length > 0
|
|
156
|
+
? `Found export(s): ${named.join(", ")}.`
|
|
157
|
+
: `The module has no usable export.`;
|
|
158
|
+
return new TypeError(`Invalid middleware export in ${sourceFile}. ${detail}\n` +
|
|
159
|
+
`Expected a default export that is a middleware function, or a non-empty ` +
|
|
160
|
+
`array of them:\n\n` +
|
|
161
|
+
` export default async function middleware(c, next) {\n` +
|
|
162
|
+
` const response = await next();\n` +
|
|
163
|
+
` return response;\n` +
|
|
164
|
+
` }\n\n` +
|
|
165
|
+
`See docs/guides/middleware.md.`);
|
|
166
|
+
}
|
|
167
|
+
function normalizeMiddlewareExport(middlewareModule, strict = false, sourceFile = "middleware.ts") {
|
|
140
168
|
const exported = middlewareModule && typeof middlewareModule === "object" &&
|
|
141
169
|
"default" in middlewareModule
|
|
142
170
|
? middlewareModule.default
|
|
143
171
|
: middlewareModule;
|
|
144
172
|
if (Array.isArray(exported)) {
|
|
145
173
|
if (strict && (exported.length === 0 || exported.some((value) => typeof value !== "function"))) {
|
|
146
|
-
throw
|
|
174
|
+
throw invalidMiddlewareExport(middlewareModule, sourceFile);
|
|
147
175
|
}
|
|
148
176
|
return exported.filter((middleware) => typeof middleware === "function");
|
|
149
177
|
}
|
|
@@ -151,7 +179,7 @@ function normalizeMiddlewareExport(middlewareModule, strict = false) {
|
|
|
151
179
|
return [exported];
|
|
152
180
|
}
|
|
153
181
|
if (strict) {
|
|
154
|
-
throw
|
|
182
|
+
throw invalidMiddlewareExport(middlewareModule, sourceFile);
|
|
155
183
|
}
|
|
156
184
|
return [];
|
|
157
185
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAGpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAQhD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AA0BnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,+BAA+B,CAAC,YAAY,CAAC,CAAC;CACpF;AAkhBD,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAsBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAGpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAQhD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AA0BnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,+BAA+B,CAAC,YAAY,CAAC,CAAC;CACpF;AAkhBD,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAsBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA6NxE"}
|
|
@@ -533,6 +533,7 @@ export class AgentStreamHandler extends BaseHandler {
|
|
|
533
533
|
projectAgentSandbox: {
|
|
534
534
|
apiUrl: resolveVeryfrontApiBaseUrlFromHostEnv(),
|
|
535
535
|
authToken: apiAuthToken || undefined,
|
|
536
|
+
branchId: payload.runtimeTargetBranchId,
|
|
536
537
|
projectId: sourceScopedContext.projectId ?? null,
|
|
537
538
|
},
|
|
538
539
|
});
|
|
@@ -78,6 +78,23 @@ async function setCachedPath(cacheKey, path) {
|
|
|
78
78
|
}
|
|
79
79
|
errorPagePathCache.set(cacheKey, path);
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Whether a "this project has no error page" answer may be cached.
|
|
83
|
+
*
|
|
84
|
+
* In dev it may not. Nothing invalidates this cache when the filesystem
|
|
85
|
+
* changes, so caching the miss means a project that adds `pages/500.tsx` while
|
|
86
|
+
* the server is running keeps getting the dev overlay until restart, with no
|
|
87
|
+
* indication why. A miss costs one file probe on an error render, which is not
|
|
88
|
+
* a path worth optimising in dev.
|
|
89
|
+
*/
|
|
90
|
+
function canCacheMiss(ctx) {
|
|
91
|
+
return !ctx.isLocalProject;
|
|
92
|
+
}
|
|
93
|
+
async function setCachedMiss(cacheKey, ctx) {
|
|
94
|
+
if (!canCacheMiss(ctx))
|
|
95
|
+
return;
|
|
96
|
+
await setCachedPath(cacheKey, null);
|
|
97
|
+
}
|
|
81
98
|
async function deleteCachedPath(cacheKey) {
|
|
82
99
|
if (injectedCacheRepo) {
|
|
83
100
|
await injectedCacheRepo.delete(cacheKey);
|
|
@@ -104,7 +121,7 @@ async function tryLoadErrorPage(pagesDir, pageType, ctx, reactVersion) {
|
|
|
104
121
|
try {
|
|
105
122
|
const resolvedPath = await ctx.adapter.fs.resolveFile(basePath);
|
|
106
123
|
if (!resolvedPath) {
|
|
107
|
-
await
|
|
124
|
+
await setCachedMiss(cacheKey, ctx);
|
|
108
125
|
return null;
|
|
109
126
|
}
|
|
110
127
|
const fullPath = joinPath(ctx.projectDir, resolvedPath);
|
|
@@ -117,7 +134,7 @@ async function tryLoadErrorPage(pagesDir, pageType, ctx, reactVersion) {
|
|
|
117
134
|
catch (_) {
|
|
118
135
|
// expected: resolveFile may fail, fall through to extension probing
|
|
119
136
|
}
|
|
120
|
-
await
|
|
137
|
+
await setCachedMiss(cacheKey, ctx);
|
|
121
138
|
return null;
|
|
122
139
|
}
|
|
123
140
|
for (const ext of ERROR_PAGE_EXTENSIONS) {
|
|
@@ -136,7 +153,7 @@ async function tryLoadErrorPage(pagesDir, pageType, ctx, reactVersion) {
|
|
|
136
153
|
// expected: file with this extension doesn't exist
|
|
137
154
|
}
|
|
138
155
|
}
|
|
139
|
-
await
|
|
156
|
+
await setCachedMiss(cacheKey, ctx);
|
|
140
157
|
return null;
|
|
141
158
|
}
|
|
142
159
|
async function loadErrorComponent(filePath, ctx, reactVersion) {
|
|
@@ -15,17 +15,6 @@ import { type SSRServiceLike } from "../../../services/rendering/ssr.service.js"
|
|
|
15
15
|
* Config override (PRODUCTION_MODE) takes precedence.
|
|
16
16
|
*/
|
|
17
17
|
export declare function isProductionMode(ctx: HandlerContext, _url?: URL): boolean;
|
|
18
|
-
/**
|
|
19
|
-
* SSR Handler - Thin orchestration layer
|
|
20
|
-
*
|
|
21
|
-
* Responsibilities:
|
|
22
|
-
* - Route matching and quick rejections
|
|
23
|
-
* - Multi-project context setup
|
|
24
|
-
* - Response building with headers
|
|
25
|
-
* - Custom fallback handling (not-found, error pages)
|
|
26
|
-
*
|
|
27
|
-
* Business logic is delegated to SSRService.
|
|
28
|
-
*/
|
|
29
18
|
export declare class SSRHandler extends BaseHandler {
|
|
30
19
|
metadata: HandlerMetadata;
|
|
31
20
|
private ssrService;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr.handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/ssr/ssr.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAWxB,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,4CAA4C,CAAC;
|
|
1
|
+
{"version":3,"file":"ssr.handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/ssr/ssr.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAWxB,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,4CAA4C,CAAC;AAOpD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAKzE;AA+BD,qBAAa,UAAW,SAAQ,WAAW;IACzC,QAAQ,EAAE,eAAe,CAIvB;IAEF,OAAO,CAAC,UAAU,CAAiB;gBAEvB,UAAU,CAAC,EAAE,cAAc;IAKvC,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA8BjE,OAAO,CAAC,qBAAqB;IAmF7B,OAAO,CAAC,iBAAiB;IAkEzB,OAAO,CAAC,cAAc;YAgBR,cAAc;YA6Bd,sBAAsB;YAgBtB,aAAa;CAU5B"}
|
|
@@ -19,6 +19,7 @@ import { tryNotFoundFallback } from "./not-found-fallback.js";
|
|
|
19
19
|
import { tryErrorPageFallback } from "./error-page-fallback.js";
|
|
20
20
|
import { SSRService, } from "../../../services/rendering/ssr.service.js";
|
|
21
21
|
import { ErrorPages } from "../../../utils/error-html.js";
|
|
22
|
+
import { VeryfrontError } from "../../../../errors/index.js";
|
|
22
23
|
import { buildSSRResponse } from "./ssr-response-builder.js";
|
|
23
24
|
const logger = serverLogger.component("ssr");
|
|
24
25
|
/**
|
|
@@ -43,6 +44,23 @@ export function isProductionMode(ctx, _url) {
|
|
|
43
44
|
*
|
|
44
45
|
* Business logic is delegated to SSRService.
|
|
45
46
|
*/
|
|
47
|
+
/**
|
|
48
|
+
* True for errors raised while compiling or resolving project source, as
|
|
49
|
+
* opposed to errors thrown by the running application.
|
|
50
|
+
*
|
|
51
|
+
* Module-load failures arrive wrapped in a RUNTIME-category `render-error`,
|
|
52
|
+
* which loses the original category, so they are identified by their
|
|
53
|
+
* `criticalFailures` context instead: a page module that could not be *loaded*
|
|
54
|
+
* always failed to compile or resolve.
|
|
55
|
+
*/
|
|
56
|
+
function isBuildError(error) {
|
|
57
|
+
if (!(error instanceof VeryfrontError))
|
|
58
|
+
return false;
|
|
59
|
+
if (error.category === "BUILD" || error.category === "MODULE")
|
|
60
|
+
return true;
|
|
61
|
+
const context = error.context;
|
|
62
|
+
return Array.isArray(context?.criticalFailures) && context.criticalFailures.length > 0;
|
|
63
|
+
}
|
|
46
64
|
export class SSRHandler extends BaseHandler {
|
|
47
65
|
metadata = {
|
|
48
66
|
name: "SSRHandler",
|
|
@@ -182,7 +200,11 @@ export class SSRHandler extends BaseHandler {
|
|
|
182
200
|
if (result.errorType === "not-found") {
|
|
183
201
|
return this.handleNotFound(req, ctx, slug, nonce);
|
|
184
202
|
}
|
|
185
|
-
|
|
203
|
+
const isServerError = result.errorType === "server-error" ||
|
|
204
|
+
result.errorType === "runtime";
|
|
205
|
+
// Project error pages should beat the dev overlay for runtime errors.
|
|
206
|
+
// Build/import errors stay visible because their overlay is actionable.
|
|
207
|
+
if (isServerError && !(result.showDevOverlay && isBuildError(result.error))) {
|
|
186
208
|
const customResponse = await this.tryCustomErrorFallback(req, ctx, result, nonce);
|
|
187
209
|
if (customResponse)
|
|
188
210
|
return customResponse;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Degradation marking for cached HTTP module artifacts.
|
|
3
|
+
*
|
|
4
|
+
* A module whose lazy dependency could not be prefetched is still usable for
|
|
5
|
+
* the render that produced it, but it must never be mistaken for a complete
|
|
6
|
+
* artifact on a later render. The marker travels with the code, so any reader
|
|
7
|
+
* of the on-disk cache can tell the two apart.
|
|
8
|
+
*
|
|
9
|
+
* @module transforms/esm/degraded-artifact
|
|
10
|
+
*/
|
|
11
|
+
/** Mark bundle code as produced from an incomplete dependency prefetch. */
|
|
12
|
+
export declare function markDegradedArtifact(code: string): string;
|
|
13
|
+
/** Return whether bundle code was produced from an incomplete prefetch. */
|
|
14
|
+
export declare function isDegradedArtifact(code: string): boolean;
|
|
15
|
+
//# sourceMappingURL=degraded-artifact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degraded-artifact.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/degraded-artifact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Degradation marking for cached HTTP module artifacts.
|
|
3
|
+
*
|
|
4
|
+
* A module whose lazy dependency could not be prefetched is still usable for
|
|
5
|
+
* the render that produced it, but it must never be mistaken for a complete
|
|
6
|
+
* artifact on a later render. The marker travels with the code, so any reader
|
|
7
|
+
* of the on-disk cache can tell the two apart.
|
|
8
|
+
*
|
|
9
|
+
* @module transforms/esm/degraded-artifact
|
|
10
|
+
*/
|
|
11
|
+
/** Preserved comment format that survives minification */
|
|
12
|
+
const VF_DEGRADED_MARKER = "\n/*! @vf-degraded */\n";
|
|
13
|
+
/** Mark bundle code as produced from an incomplete dependency prefetch. */
|
|
14
|
+
export function markDegradedArtifact(code) {
|
|
15
|
+
if (isDegradedArtifact(code))
|
|
16
|
+
return code;
|
|
17
|
+
return `${code}${VF_DEGRADED_MARKER}`;
|
|
18
|
+
}
|
|
19
|
+
/** Return whether bundle code was produced from an incomplete prefetch. */
|
|
20
|
+
export function isDegradedArtifact(code) {
|
|
21
|
+
return code.endsWith(VF_DEGRADED_MARKER);
|
|
22
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/http-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"http-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/http-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EAGL,KAAK,YAAY,EAKjB,wBAAwB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAElB,gBAAgB,EAEhB,KAAK,OAAO,EACb,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAA2B,MAAM,4BAA4B,CAAC;AACxF,OAAO,EACL,0BAA0B,EAO3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,sBAAsB,EAKvB,MAAM,uBAAuB,CAAC;AAe/B,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,CAAC;AAGF,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAwTlC,wEAAwE;AACxE,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,sBAAsB,CAAC,CAkCjC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAErF;AAGD,eAAO,MAAM,wBAAwB,0BAAoB,CAAC"}
|
|
@@ -23,6 +23,7 @@ import { CACHE_DIR_TOKEN, detokenizeAllCachePaths, detokenizeCachePaths, tokeniz
|
|
|
23
23
|
import { looksLikeHtmlContent as looksLikeHtmlNotJs } from "./html-content.js";
|
|
24
24
|
// Extracted modules
|
|
25
25
|
import { embedSourceUrl, extractSourceUrl } from "./source-url-embed.js";
|
|
26
|
+
import { isDegradedArtifact, markDegradedArtifact } from "./degraded-artifact.js";
|
|
26
27
|
import { buildHttpCacheIdentity, buildHttpCacheIdentityMetadata, ensureAbsoluteDir, ensurePreparedHttpCacheRequestOptions, getEffectiveHttpCacheRequest, hashHttpCacheIdentity, hasIncompatibleFilePaths, isHttpUrl, normalizeHttpUrl, prepareHttpCacheRequestOptions, } from "./http-cache-helpers.js";
|
|
27
28
|
import { extractBundleDeps, validateBundleDepsExist } from "./bundle-deps-validator.js";
|
|
28
29
|
import { __clearInFlightHttpFetches, bundleAccumulatorStorage, inFlightHttpFetches, processingStackStorage, refreshDistributedCacheAsync, trackBundleAccumulator, waitForInFlightFetch, } from "./in-flight-manager.js";
|
|
@@ -56,15 +57,32 @@ async function cacheHttpModuleInternal(url, options) {
|
|
|
56
57
|
const fs = createFileSystem();
|
|
57
58
|
if (await exists(cachePath)) {
|
|
58
59
|
const code = await fs.readTextFile(cachePath);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
if (isDegradedArtifact(code)) {
|
|
61
|
+
// The artifact on disk is the fallback a previous render wrote when a
|
|
62
|
+
// dependency could not be prefetched. Retry the prefetch instead of
|
|
63
|
+
// handing the degradation on.
|
|
64
|
+
httpCacheLog.debug("Local cache holds a degraded artifact, will re-fetch", {
|
|
65
|
+
url: normalizedUrl,
|
|
66
|
+
hash,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const deps = extractBundleDeps(code);
|
|
71
|
+
if (deps.length > 0) {
|
|
72
|
+
const depsValid = await validateBundleDepsExist(deps, cacheDir);
|
|
73
|
+
if (!depsValid) {
|
|
74
|
+
httpCacheLog.debug("Local cache has missing deps, will re-fetch", {
|
|
75
|
+
url: normalizedUrl,
|
|
76
|
+
hash,
|
|
77
|
+
missingDeps: deps.length,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
getCachedPaths().set(cacheKey, cachePath);
|
|
82
|
+
refreshDistributedCacheAsync(hash, code, cacheDir, normalizedUrl, identityMetadata, getLastDistributedRefresh);
|
|
83
|
+
trackBundleAccumulator(hash, normalizedUrl, cachePath);
|
|
84
|
+
return cachePath;
|
|
85
|
+
}
|
|
68
86
|
}
|
|
69
87
|
else {
|
|
70
88
|
getCachedPaths().set(cacheKey, cachePath);
|
|
@@ -73,12 +91,6 @@ async function cacheHttpModuleInternal(url, options) {
|
|
|
73
91
|
return cachePath;
|
|
74
92
|
}
|
|
75
93
|
}
|
|
76
|
-
else {
|
|
77
|
-
getCachedPaths().set(cacheKey, cachePath);
|
|
78
|
-
refreshDistributedCacheAsync(hash, code, cacheDir, normalizedUrl, identityMetadata, getLastDistributedRefresh);
|
|
79
|
-
trackBundleAccumulator(hash, normalizedUrl, cachePath);
|
|
80
|
-
return cachePath;
|
|
81
|
-
}
|
|
82
94
|
}
|
|
83
95
|
const processingStack = getProcessingStack();
|
|
84
96
|
if (processingStack.has(cacheIdentity)) {
|
|
@@ -198,13 +210,18 @@ async function cacheHttpModuleInternal(url, options) {
|
|
|
198
210
|
});
|
|
199
211
|
}
|
|
200
212
|
processingStack.add(cacheIdentity);
|
|
213
|
+
let degraded = [];
|
|
201
214
|
try {
|
|
202
|
-
|
|
215
|
+
const rewritten = await rewriteModuleImports(code, normalizedUrl, options, cacheHttpModule);
|
|
216
|
+
code = rewritten.code;
|
|
217
|
+
degraded = rewritten.degraded;
|
|
203
218
|
}
|
|
204
219
|
finally {
|
|
205
220
|
processingStack.delete(cacheIdentity);
|
|
206
221
|
}
|
|
207
222
|
code = embedSourceUrl(code, normalizedUrl);
|
|
223
|
+
if (degraded.length > 0)
|
|
224
|
+
code = markDegradedArtifact(code);
|
|
208
225
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
209
226
|
await fs.writeTextFile(cachePath, code);
|
|
210
227
|
if (!(await exists(cachePath))) {
|
|
@@ -212,6 +229,19 @@ async function cacheHttpModuleInternal(url, options) {
|
|
|
212
229
|
detail: `[HTTP-CACHE] INVARIANT VIOLATION: File write succeeded but file does not exist: ${cachePath}`,
|
|
213
230
|
});
|
|
214
231
|
}
|
|
232
|
+
if (degraded.length > 0) {
|
|
233
|
+
// The file on disk carries this render through, but the artifact is not
|
|
234
|
+
// the one this URL is supposed to produce. Keeping it out of the
|
|
235
|
+
// distributed cache and the in-memory path map means the next render
|
|
236
|
+
// retries the prefetch instead of inheriting one upstream blip for the
|
|
237
|
+
// lifetime of the distributed entry.
|
|
238
|
+
httpCacheLog.warn("Not caching a module with unresolved dynamic imports", {
|
|
239
|
+
url: normalizedUrl,
|
|
240
|
+
hash,
|
|
241
|
+
degraded,
|
|
242
|
+
});
|
|
243
|
+
return cachePath;
|
|
244
|
+
}
|
|
215
245
|
try {
|
|
216
246
|
await httpBundleCache.setCode(String(hash), asLocalModuleCode(code), normalizedUrl, HTTP_MODULE_DISTRIBUTED_TTL_SEC, identityMetadata);
|
|
217
247
|
}
|
|
@@ -256,7 +286,7 @@ async function cacheHttpModule(url, options) {
|
|
|
256
286
|
export function cacheHttpImportsToLocal(code, options) {
|
|
257
287
|
const requestOptions = prepareHttpCacheRequestOptions(options);
|
|
258
288
|
return bundleAccumulatorStorage.run([], async () => {
|
|
259
|
-
const replacements = await buildReplacements(code, undefined, requestOptions, cacheHttpModule);
|
|
289
|
+
const { replacements } = await buildReplacements(code, undefined, requestOptions, cacheHttpModule);
|
|
260
290
|
if (replacements.size === 0)
|
|
261
291
|
return { code };
|
|
262
292
|
httpCacheLog.debug("Cached HTTP imports", { count: replacements.size });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CA0HlC"}
|