veryfront 0.1.755 → 0.1.757
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/cli/auth/callback-server.d.ts +4 -1
- package/esm/cli/auth/callback-server.d.ts.map +1 -1
- package/esm/cli/auth/callback-server.js +52 -13
- package/esm/cli/auth/index.d.ts +1 -1
- package/esm/cli/auth/index.d.ts.map +1 -1
- package/esm/cli/auth/index.js +1 -1
- package/esm/cli/auth/login.d.ts +2 -0
- package/esm/cli/auth/login.d.ts.map +1 -1
- package/esm/cli/auth/login.js +18 -6
- package/esm/cli/commands/demo/demo.d.ts.map +1 -1
- package/esm/cli/commands/demo/demo.js +6 -7
- package/esm/deno.js +1 -1
- package/esm/src/agent/hosted/chat-preparation.d.ts +2 -0
- package/esm/src/agent/hosted/chat-preparation.d.ts.map +1 -1
- package/esm/src/agent/hosted/chat-preparation.js +4 -0
- package/esm/src/agent/hosted/chat-runtime-agent-adapter.d.ts +2 -0
- package/esm/src/agent/hosted/chat-runtime-agent-adapter.d.ts.map +1 -1
- package/esm/src/agent/hosted/chat-runtime-agent-adapter.js +2 -0
- package/esm/src/agent/hosted/chat-runtime-contract.d.ts +2 -0
- package/esm/src/agent/hosted/chat-runtime-contract.d.ts.map +1 -1
- package/esm/src/agent/hosted/default-chat-runtime.d.ts +2 -0
- package/esm/src/agent/hosted/default-chat-runtime.d.ts.map +1 -1
- package/esm/src/agent/hosted/default-chat-runtime.js +4 -0
- package/esm/src/agent/runtime/index.d.ts +3 -96
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +21 -341
- package/esm/src/agent/runtime/model-capabilities.d.ts +3 -0
- package/esm/src/agent/runtime/model-capabilities.d.ts.map +1 -0
- package/esm/src/agent/runtime/model-capabilities.js +18 -0
- package/esm/src/agent/runtime/runtime-tool-config.d.ts +14 -0
- package/esm/src/agent/runtime/runtime-tool-config.d.ts.map +1 -0
- package/esm/src/agent/runtime/runtime-tool-config.js +37 -0
- package/esm/src/agent/runtime/skill-policy-enforcement.d.ts +16 -0
- package/esm/src/agent/runtime/skill-policy-enforcement.d.ts.map +1 -0
- package/esm/src/agent/runtime/skill-policy-enforcement.js +55 -0
- package/esm/src/agent/runtime/tool-result-continuation.d.ts +34 -0
- package/esm/src/agent/runtime/tool-result-continuation.d.ts.map +1 -0
- package/esm/src/agent/runtime/tool-result-continuation.js +194 -0
- package/esm/src/agent/schemas/agent.schema.d.ts +7 -0
- package/esm/src/agent/schemas/agent.schema.d.ts.map +1 -1
- package/esm/src/agent/schemas/agent.schema.js +9 -3
- package/esm/src/agent/schemas/index.d.ts +1 -1
- package/esm/src/agent/schemas/index.d.ts.map +1 -1
- package/esm/src/agent/schemas/index.js +1 -1
- package/esm/src/modules/server/module-server.d.ts.map +1 -1
- package/esm/src/modules/server/module-server.js +31 -9
- package/esm/src/modules/server/rate-limiter.d.ts +3 -2
- package/esm/src/modules/server/rate-limiter.d.ts.map +1 -1
- package/esm/src/platform/adapters/base.d.ts +24 -3
- package/esm/src/platform/adapters/base.d.ts.map +1 -1
- package/esm/src/platform/adapters/base.js +15 -1
- package/esm/src/platform/adapters/runtime/bun/websocket-adapter.d.ts +2 -0
- package/esm/src/platform/adapters/runtime/bun/websocket-adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/runtime/bun/websocket-adapter.js +33 -1
- package/esm/src/platform/adapters/runtime/node/websocket-adapter.d.ts +1 -1
- package/esm/src/platform/adapters/runtime/node/websocket-adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/runtime/node/websocket-adapter.js +3 -10
- package/esm/src/proxy/handler.d.ts.map +1 -1
- package/esm/src/proxy/handler.js +25 -116
- package/esm/src/proxy/local-project-resolver.d.ts +18 -0
- package/esm/src/proxy/local-project-resolver.d.ts.map +1 -0
- package/esm/src/proxy/local-project-resolver.js +63 -0
- package/esm/src/proxy/proxy-token-resolution.d.ts +37 -0
- package/esm/src/proxy/proxy-token-resolution.d.ts.map +1 -0
- package/esm/src/proxy/proxy-token-resolution.js +51 -0
- package/esm/src/rendering/orchestrator/module-loader/index.d.ts +10 -0
- package/esm/src/rendering/orchestrator/module-loader/index.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-loader/index.js +23 -158
- package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts +20 -0
- package/esm/src/rendering/orchestrator/module-loader/module-persistence.d.ts.map +1 -0
- package/esm/src/rendering/orchestrator/module-loader/module-persistence.js +80 -0
- package/esm/src/rendering/orchestrator/module-loader/module-transform-cache.d.ts +59 -0
- package/esm/src/rendering/orchestrator/module-loader/module-transform-cache.d.ts.map +1 -0
- package/esm/src/rendering/orchestrator/module-loader/module-transform-cache.js +97 -0
- package/esm/src/routing/api/handler.d.ts +2 -1
- package/esm/src/routing/api/handler.d.ts.map +1 -1
- package/esm/src/routing/api/handler.js +2 -1
- package/esm/src/routing/api/route-executor.d.ts +3 -0
- package/esm/src/routing/api/route-executor.d.ts.map +1 -1
- package/esm/src/routing/api/route-executor.js +28 -0
- package/esm/src/runtime/runtime-bridge.js +2 -2
- package/esm/src/security/http/base-handler.d.ts +3 -2
- package/esm/src/security/http/base-handler.d.ts.map +1 -1
- package/esm/src/security/http/base-handler.js +1 -1
- package/esm/src/security/sandbox/project-worker.d.ts +1 -0
- package/esm/src/security/sandbox/project-worker.d.ts.map +1 -1
- package/esm/src/security/sandbox/project-worker.js +17 -1
- package/esm/src/security/sandbox/worker-egress-guard.d.ts +15 -0
- package/esm/src/security/sandbox/worker-egress-guard.d.ts.map +1 -0
- package/esm/src/security/sandbox/worker-egress-guard.js +201 -0
- package/esm/src/security/sandbox/worker-permissions.d.ts +6 -2
- package/esm/src/security/sandbox/worker-permissions.d.ts.map +1 -1
- package/esm/src/security/sandbox/worker-permissions.js +34 -3
- package/esm/src/security/sandbox/worker-pool.d.ts +1 -1
- package/esm/src/security/sandbox/worker-pool.d.ts.map +1 -1
- package/esm/src/security/sandbox/worker-pool.js +35 -9
- package/esm/src/server/handlers/preview/hmr-client-manager.d.ts +3 -2
- package/esm/src/server/handlers/preview/hmr-client-manager.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/hmr.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/hmr.handler.js +4 -1
- package/esm/src/server/handlers/request/api/api-handler-wrapper.js +1 -1
- package/esm/src/tool/remote-mcp.d.ts.map +1 -1
- package/esm/src/tool/remote-mcp.js +12 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/http-fallback.d.ts +32 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/http-fallback.d.ts.map +1 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/http-fallback.js +27 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/import-rewriter.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/import-rewriter.js +23 -3
- 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 +15 -15
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
package/esm/src/proxy/handler.js
CHANGED
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
import { TokenManager } from "./token-manager.js";
|
|
2
2
|
import { parseProjectDomain } from "../server/utils/domain-parser.js";
|
|
3
|
-
import {
|
|
4
|
-
import { cwd, getEnv } from "../platform/compat/process.js";
|
|
5
|
-
import { join } from "../platform/compat/path/index.js";
|
|
3
|
+
import { getEnv } from "../platform/compat/process.js";
|
|
6
4
|
import { injectContext, ProxySpanNames, withSpan } from "./tracing.js";
|
|
7
5
|
import { computeContentSourceId } from "../cache/keys.js";
|
|
8
6
|
import { resolve as resolveContract } from "../extensions/contracts.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
/**
|
|
12
|
-
* Bounded cache for project paths discovered dynamically at request time
|
|
13
|
-
* (slug → absolute path). Previously these were written into the per-handler
|
|
14
|
-
* `localProjects` config map, which grew without bound. Using a registered
|
|
15
|
-
* LRU keeps the working set capped and exposes it to cache monitoring, while
|
|
16
|
-
* the static `config.localProjects` map continues to reflect *configured*
|
|
17
|
-
* projects for the public `ProxyHandler.localProjects` surface.
|
|
18
|
-
*/
|
|
19
|
-
const discoveredLocalProjects = new LRUCache({ maxEntries: 100 });
|
|
20
|
-
registerLRUCache("proxy-discovered-local-projects", discoveredLocalProjects);
|
|
7
|
+
import { createLocalProjectResolver } from "./local-project-resolver.js";
|
|
8
|
+
import { isMissingCustomDomainProjectError, resolveProxyRequestToken, } from "./proxy-token-resolution.js";
|
|
21
9
|
/**
|
|
22
10
|
* Cache the resolved AuthProvider at module scope so the proxy does not pay
|
|
23
11
|
* the registry lookup on every request. The cache is cleared implicitly when
|
|
@@ -148,23 +136,11 @@ function getRequestHost(req, url) {
|
|
|
148
136
|
function getScope(environment) {
|
|
149
137
|
return environment === "preview" ? "preview" : "production";
|
|
150
138
|
}
|
|
151
|
-
function extractUserToken(cookieHeader) {
|
|
152
|
-
const match = cookieHeader.match(/(?:^|;\s*)authToken=([^;]+)/);
|
|
153
|
-
return match?.[1] ? decodeURIComponent(match[1]) : undefined;
|
|
154
|
-
}
|
|
155
139
|
function parseStatusFromError(error) {
|
|
156
140
|
const message = error instanceof Error ? error.message : String(error);
|
|
157
141
|
const match = message.match(/failed: (\d+)/);
|
|
158
142
|
return match ? Number(match[1]) : null;
|
|
159
143
|
}
|
|
160
|
-
// Brittle on purpose: we string-match the API's OAuth error body. Source of truth is
|
|
161
|
-
// veryfront-api/src/api/http/rest/auth/routes.ts — `oauthError(c, 'Project not found
|
|
162
|
-
// for domain', ...)`. If that string is renamed, this regex silently regresses to 502.
|
|
163
|
-
// Durable fix is a typed error code from the token-mint helper; tracked separately.
|
|
164
|
-
function isMissingCustomDomainProjectError(error) {
|
|
165
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
166
|
-
return /project not found for domain/i.test(message);
|
|
167
|
-
}
|
|
168
144
|
async function extractUserIdFromToken(token, apiBaseUrl, log) {
|
|
169
145
|
const auth = getAuthProvider();
|
|
170
146
|
const header = auth.decode(token);
|
|
@@ -221,52 +197,7 @@ function isProjectMember(users, userId) {
|
|
|
221
197
|
export function createProxyHandler(options) {
|
|
222
198
|
const { config, cache, logger } = options;
|
|
223
199
|
const localProjects = config.localProjects ?? {};
|
|
224
|
-
const
|
|
225
|
-
async function findLocalProject(slug) {
|
|
226
|
-
const mapped = localProjects[slug];
|
|
227
|
-
if (mapped)
|
|
228
|
-
return mapped;
|
|
229
|
-
const projectDirs = ["projects", "data/projects", "examples"];
|
|
230
|
-
const basePath = cwd();
|
|
231
|
-
// Key the discovery cache by the filesystem root as well as the slug: the
|
|
232
|
-
// cache is process-wide, so the same slug can resolve to different paths
|
|
233
|
-
// across handlers/workspaces or after a cwd change. Keying by basePath
|
|
234
|
-
// prevents a stale entry from one root being proxied for another.
|
|
235
|
-
const cacheKey = `${basePath}${slug}`;
|
|
236
|
-
const cached = discoveredLocalProjects.get(cacheKey);
|
|
237
|
-
if (cached)
|
|
238
|
-
return cached;
|
|
239
|
-
const candidatePaths = projectDirs.map((dir) => join(basePath, dir, slug));
|
|
240
|
-
const existingPaths = await Promise.all(candidatePaths.map(async (projectPath) => {
|
|
241
|
-
try {
|
|
242
|
-
return (await fs.exists(projectPath)) ? projectPath : null;
|
|
243
|
-
}
|
|
244
|
-
catch (_) {
|
|
245
|
-
/* expected: filesystem check may fail */
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
}));
|
|
249
|
-
for (const projectPath of existingPaths) {
|
|
250
|
-
if (!projectPath)
|
|
251
|
-
continue;
|
|
252
|
-
try {
|
|
253
|
-
const [hasApp, hasPages, hasComponents] = await Promise.all([
|
|
254
|
-
fs.exists(join(projectPath, "app")),
|
|
255
|
-
fs.exists(join(projectPath, "pages")),
|
|
256
|
-
fs.exists(join(projectPath, "components")),
|
|
257
|
-
]);
|
|
258
|
-
if (!hasApp && !hasPages && !hasComponents)
|
|
259
|
-
continue;
|
|
260
|
-
discoveredLocalProjects.set(cacheKey, projectPath);
|
|
261
|
-
logger?.debug("Dynamically discovered local project", { slug, projectPath });
|
|
262
|
-
return projectPath;
|
|
263
|
-
}
|
|
264
|
-
catch (_) {
|
|
265
|
-
// expected: filesystem check may fail
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return undefined;
|
|
269
|
-
}
|
|
200
|
+
const localProjectResolver = createLocalProjectResolver({ localProjects, logger });
|
|
270
201
|
const tokenManager = new TokenManager({
|
|
271
202
|
apiBaseUrl: config.apiBaseUrl,
|
|
272
203
|
apiClientId: config.apiClientId,
|
|
@@ -315,46 +246,6 @@ export function createProxyHandler(options) {
|
|
|
315
246
|
function makeProjectNotFoundContext(base, message, token) {
|
|
316
247
|
return makeErrorContext(base, 404, message, token, undefined, "project-not-found");
|
|
317
248
|
}
|
|
318
|
-
async function resolveRequestToken(req, url, scope, host, projectSlug, options) {
|
|
319
|
-
const userToken = extractUserToken(req.headers.get("cookie") ?? "");
|
|
320
|
-
const useSignedInternalControlPlaneToken = options.allowSignedInternalControlPlaneToken &&
|
|
321
|
-
isSignedInternalControlPlaneRequest(req, url);
|
|
322
|
-
let token;
|
|
323
|
-
let tokenFetchError;
|
|
324
|
-
if (useSignedInternalControlPlaneToken) {
|
|
325
|
-
token = req.headers.get("x-token") ?? undefined;
|
|
326
|
-
logger?.debug("Using signed control-plane token for internal request", {
|
|
327
|
-
pathname: url.pathname,
|
|
328
|
-
scope,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
else if (scope === "preview" && userToken) {
|
|
332
|
-
token = userToken;
|
|
333
|
-
logger?.debug("Using user auth token for preview");
|
|
334
|
-
}
|
|
335
|
-
if (!token && config.apiClientId && config.apiClientSecret) {
|
|
336
|
-
const customDomain = projectSlug ? undefined : host;
|
|
337
|
-
if (projectSlug || customDomain) {
|
|
338
|
-
try {
|
|
339
|
-
token = await tokenManager.getToken(scope, projectSlug, customDomain);
|
|
340
|
-
}
|
|
341
|
-
catch (error) {
|
|
342
|
-
tokenFetchError = error;
|
|
343
|
-
if (!(customDomain && isMissingCustomDomainProjectError(error))) {
|
|
344
|
-
logger?.error(options.tokenFetchErrorMessage, error, {
|
|
345
|
-
projectSlug,
|
|
346
|
-
customDomain,
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (!token && config.apiToken) {
|
|
353
|
-
token = config.apiToken;
|
|
354
|
-
logger?.debug("Using static API token fallback");
|
|
355
|
-
}
|
|
356
|
-
return { token, userToken, tokenFetchError };
|
|
357
|
-
}
|
|
358
249
|
async function checkProtectedAccess(req, url, matchingEnv, userToken, users, logContext) {
|
|
359
250
|
if (!matchingEnv?.protected)
|
|
360
251
|
return null;
|
|
@@ -434,7 +325,7 @@ export function createProxyHandler(options) {
|
|
|
434
325
|
isLocalProject: false,
|
|
435
326
|
};
|
|
436
327
|
}
|
|
437
|
-
const localPath = projectSlug ? await
|
|
328
|
+
const localPath = projectSlug ? await localProjectResolver.find(projectSlug) : undefined;
|
|
438
329
|
const isLocalProject = !!localPath;
|
|
439
330
|
logger?.debug("Processing request", {
|
|
440
331
|
host,
|
|
@@ -450,8 +341,17 @@ export function createProxyHandler(options) {
|
|
|
450
341
|
logger?.debug("Local project, skipping token fetch", { localPath });
|
|
451
342
|
}
|
|
452
343
|
else {
|
|
453
|
-
({ token, userToken, tokenFetchError } = await
|
|
344
|
+
({ token, userToken, tokenFetchError } = await resolveProxyRequestToken({
|
|
345
|
+
req,
|
|
346
|
+
url,
|
|
347
|
+
scope,
|
|
348
|
+
host,
|
|
349
|
+
projectSlug,
|
|
350
|
+
config,
|
|
351
|
+
tokenManager,
|
|
352
|
+
logger,
|
|
454
353
|
allowSignedInternalControlPlaneToken: true,
|
|
354
|
+
signedInternalControlPlaneRequest: isSignedInternalControlPlaneRequest(req, url),
|
|
455
355
|
tokenFetchErrorMessage: "Token fetch failed",
|
|
456
356
|
}));
|
|
457
357
|
if (projectSlug && !token) {
|
|
@@ -589,7 +489,16 @@ export function createProxyHandler(options) {
|
|
|
589
489
|
const parsedDomain = parseProjectDomain(host);
|
|
590
490
|
const scope = getScope(parsedDomain.environment);
|
|
591
491
|
const projectSlug = parsedDomain.slug ?? undefined;
|
|
592
|
-
const { token } = await
|
|
492
|
+
const { token } = await resolveProxyRequestToken({
|
|
493
|
+
req,
|
|
494
|
+
url,
|
|
495
|
+
scope,
|
|
496
|
+
host,
|
|
497
|
+
projectSlug,
|
|
498
|
+
config,
|
|
499
|
+
tokenManager,
|
|
500
|
+
logger,
|
|
501
|
+
signedInternalControlPlaneRequest: isSignedInternalControlPlaneRequest(req, url),
|
|
593
502
|
tokenFetchErrorMessage: "Token fetch failed for API",
|
|
594
503
|
});
|
|
595
504
|
return token;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface LocalProjectFileSystem {
|
|
2
|
+
exists(path: string): Promise<boolean> | boolean;
|
|
3
|
+
}
|
|
4
|
+
interface LocalProjectLogger {
|
|
5
|
+
debug(message: string, extra?: Record<string, unknown>): void;
|
|
6
|
+
}
|
|
7
|
+
export interface LocalProjectResolverOptions {
|
|
8
|
+
localProjects?: Record<string, string>;
|
|
9
|
+
fs?: LocalProjectFileSystem;
|
|
10
|
+
basePath?: () => string;
|
|
11
|
+
logger?: LocalProjectLogger;
|
|
12
|
+
}
|
|
13
|
+
export interface LocalProjectResolver {
|
|
14
|
+
find(slug: string): Promise<string | undefined>;
|
|
15
|
+
}
|
|
16
|
+
export declare function createLocalProjectResolver(options: LocalProjectResolverOptions): LocalProjectResolver;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=local-project-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-project-resolver.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/local-project-resolver.ts"],"names":[],"mappings":"AAMA,UAAU,sBAAsB;IAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAClD;AAED,UAAU,kBAAkB;IAC1B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC/D;AAED,MAAM,WAAW,2BAA2B;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,EAAE,CAAC,EAAE,sBAAsB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACjD;AAUD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,2BAA2B,GACnC,oBAAoB,CAyDtB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { registerLRUCache } from "../cache/index.js";
|
|
2
|
+
import { createFileSystem } from "../platform/compat/fs.js";
|
|
3
|
+
import { join } from "../platform/compat/path/index.js";
|
|
4
|
+
import { cwd } from "../platform/compat/process.js";
|
|
5
|
+
import { LRUCache } from "../utils/lru-wrapper.js";
|
|
6
|
+
/**
|
|
7
|
+
* Bounded cache for project paths discovered dynamically at request time
|
|
8
|
+
* (slug -> absolute path). Configured `localProjects` are never written here,
|
|
9
|
+
* so `ProxyHandler.localProjects` continues to expose only configured projects.
|
|
10
|
+
*/
|
|
11
|
+
const discoveredLocalProjects = new LRUCache({ maxEntries: 100 });
|
|
12
|
+
registerLRUCache("proxy-discovered-local-projects", discoveredLocalProjects);
|
|
13
|
+
export function createLocalProjectResolver(options) {
|
|
14
|
+
const localProjects = options.localProjects ?? {};
|
|
15
|
+
const fs = options.fs ?? createFileSystem();
|
|
16
|
+
const getBasePath = options.basePath ?? cwd;
|
|
17
|
+
const logger = options.logger;
|
|
18
|
+
async function find(slug) {
|
|
19
|
+
const mapped = localProjects[slug];
|
|
20
|
+
if (mapped)
|
|
21
|
+
return mapped;
|
|
22
|
+
const projectDirs = ["projects", "data/projects", "examples"];
|
|
23
|
+
const basePath = getBasePath();
|
|
24
|
+
// Key the discovery cache by the filesystem root as well as the slug: the
|
|
25
|
+
// cache is process-wide, so the same slug can resolve to different paths
|
|
26
|
+
// across handlers/workspaces or after a cwd change. Keying by basePath
|
|
27
|
+
// prevents a stale entry from one root being proxied for another.
|
|
28
|
+
const cacheKey = `${basePath}\0${slug}`;
|
|
29
|
+
const cached = discoveredLocalProjects.get(cacheKey);
|
|
30
|
+
if (cached)
|
|
31
|
+
return cached;
|
|
32
|
+
const candidatePaths = projectDirs.map((dir) => join(basePath, dir, slug));
|
|
33
|
+
const existingPaths = await Promise.all(candidatePaths.map(async (projectPath) => {
|
|
34
|
+
try {
|
|
35
|
+
return (await fs.exists(projectPath)) ? projectPath : null;
|
|
36
|
+
}
|
|
37
|
+
catch (_) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
for (const projectPath of existingPaths) {
|
|
42
|
+
if (!projectPath)
|
|
43
|
+
continue;
|
|
44
|
+
try {
|
|
45
|
+
const [hasApp, hasPages, hasComponents] = await Promise.all([
|
|
46
|
+
fs.exists(join(projectPath, "app")),
|
|
47
|
+
fs.exists(join(projectPath, "pages")),
|
|
48
|
+
fs.exists(join(projectPath, "components")),
|
|
49
|
+
]);
|
|
50
|
+
if (!hasApp && !hasPages && !hasComponents)
|
|
51
|
+
continue;
|
|
52
|
+
discoveredLocalProjects.set(cacheKey, projectPath);
|
|
53
|
+
logger?.debug("Dynamically discovered local project", { slug, projectPath });
|
|
54
|
+
return projectPath;
|
|
55
|
+
}
|
|
56
|
+
catch (_) {
|
|
57
|
+
// expected: filesystem check may fail
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return { find };
|
|
63
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TokenScope } from "./token-manager.js";
|
|
2
|
+
export interface ProxyTokenResolutionConfig {
|
|
3
|
+
apiClientId: string;
|
|
4
|
+
apiClientSecret: string;
|
|
5
|
+
apiToken?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ProxyTokenManager {
|
|
8
|
+
getToken(scope: TokenScope, projectSlug?: string, customDomain?: string): Promise<string>;
|
|
9
|
+
}
|
|
10
|
+
export interface ProxyTokenResolutionLogger {
|
|
11
|
+
debug: (msg: string, extra?: Record<string, unknown>) => void;
|
|
12
|
+
info: (msg: string, extra?: Record<string, unknown>) => void;
|
|
13
|
+
warn: (msg: string, extra?: Record<string, unknown>) => void;
|
|
14
|
+
error: (msg: string, error?: Error, extra?: Record<string, unknown>) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface ResolveProxyRequestTokenOptions {
|
|
17
|
+
req: Request;
|
|
18
|
+
url: URL;
|
|
19
|
+
scope: TokenScope;
|
|
20
|
+
host: string;
|
|
21
|
+
projectSlug: string | undefined;
|
|
22
|
+
config: ProxyTokenResolutionConfig;
|
|
23
|
+
tokenManager: ProxyTokenManager;
|
|
24
|
+
logger?: ProxyTokenResolutionLogger;
|
|
25
|
+
allowSignedInternalControlPlaneToken?: boolean;
|
|
26
|
+
signedInternalControlPlaneRequest?: boolean;
|
|
27
|
+
tokenFetchErrorMessage: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ResolvedProxyRequestToken {
|
|
30
|
+
token?: string;
|
|
31
|
+
userToken?: string;
|
|
32
|
+
tokenFetchError?: unknown;
|
|
33
|
+
}
|
|
34
|
+
export declare function extractUserToken(cookieHeader: string): string | undefined;
|
|
35
|
+
export declare function isMissingCustomDomainProjectError(error: unknown): boolean;
|
|
36
|
+
export declare function resolveProxyRequestToken(options: ResolveProxyRequestTokenOptions): Promise<ResolvedProxyRequestToken>;
|
|
37
|
+
//# sourceMappingURL=proxy-token-resolution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-token-resolution.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/proxy-token-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CACN,KAAK,EAAE,UAAU,EACjB,WAAW,CAAC,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC9D,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7D,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAC9E;AAED,MAAM,WAAW,+BAA+B;IAC9C,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,0BAA0B,CAAC;IACnC,YAAY,EAAE,iBAAiB,CAAC;IAChC,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,oCAAoC,CAAC,EAAE,OAAO,CAAC;IAC/C,iCAAiC,CAAC,EAAE,OAAO,CAAC;IAC5C,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGzE;AAID,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzE;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,yBAAyB,CAAC,CAqDpC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function extractUserToken(cookieHeader) {
|
|
2
|
+
const match = cookieHeader.match(/(?:^|;\s*)authToken=([^;]+)/);
|
|
3
|
+
return match?.[1] ? decodeURIComponent(match[1]) : undefined;
|
|
4
|
+
}
|
|
5
|
+
// Brittle on purpose: the API currently returns this text when token minting
|
|
6
|
+
// cannot map a custom domain. Tracked in #2217 until a typed error code exists.
|
|
7
|
+
export function isMissingCustomDomainProjectError(error) {
|
|
8
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9
|
+
return /project not found for domain/i.test(message);
|
|
10
|
+
}
|
|
11
|
+
export async function resolveProxyRequestToken(options) {
|
|
12
|
+
const { config, host, logger, projectSlug, req, scope, tokenFetchErrorMessage, tokenManager, url, } = options;
|
|
13
|
+
const userToken = extractUserToken(req.headers.get("cookie") ?? "");
|
|
14
|
+
const useSignedInternalControlPlaneToken = options.allowSignedInternalControlPlaneToken &&
|
|
15
|
+
options.signedInternalControlPlaneRequest;
|
|
16
|
+
let token;
|
|
17
|
+
let tokenFetchError;
|
|
18
|
+
if (useSignedInternalControlPlaneToken) {
|
|
19
|
+
token = req.headers.get("x-token") ?? undefined;
|
|
20
|
+
logger?.debug("Using signed control-plane token for internal request", {
|
|
21
|
+
pathname: url.pathname,
|
|
22
|
+
scope,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
else if (scope === "preview" && userToken) {
|
|
26
|
+
token = userToken;
|
|
27
|
+
logger?.debug("Using user auth token for preview");
|
|
28
|
+
}
|
|
29
|
+
if (!token && config.apiClientId && config.apiClientSecret) {
|
|
30
|
+
const customDomain = projectSlug ? undefined : host;
|
|
31
|
+
if (projectSlug || customDomain) {
|
|
32
|
+
try {
|
|
33
|
+
token = await tokenManager.getToken(scope, projectSlug, customDomain);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
tokenFetchError = error;
|
|
37
|
+
if (!(customDomain && isMissingCustomDomainProjectError(error))) {
|
|
38
|
+
logger?.error(tokenFetchErrorMessage, error, {
|
|
39
|
+
projectSlug,
|
|
40
|
+
customDomain,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!token && config.apiToken) {
|
|
47
|
+
token = config.apiToken;
|
|
48
|
+
logger?.debug("Using static API token fallback");
|
|
49
|
+
}
|
|
50
|
+
return { token, userToken, tokenFetchError };
|
|
51
|
+
}
|
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
import type { RuntimeAdapter } from "../../../platform/adapters/base.js";
|
|
10
10
|
export { createEsmCache, createModuleCache, generateHash } from "./cache.js";
|
|
11
11
|
export { fetchEsmModule, rewriteEsmPaths } from "./esm-rewriter.js";
|
|
12
|
+
/**
|
|
13
|
+
* Transform a module and all its local dependencies (@/ alias and relative imports).
|
|
14
|
+
*
|
|
15
|
+
* @param filePath - Path to the module
|
|
16
|
+
* @param tmpDir - Temp directory for caching
|
|
17
|
+
* @param localAdapter - Local file system adapter
|
|
18
|
+
* @param config - Module loader configuration
|
|
19
|
+
* @param useLocalAdapter - Whether to use local adapter for reading
|
|
20
|
+
* @returns Path to the transformed module file
|
|
21
|
+
*/
|
|
12
22
|
export declare function transformModuleWithDeps(filePath: string, tmpDir: string, localAdapter: RuntimeAdapter, config: ModuleLoaderConfig, useLocalAdapter?: boolean): Promise<string>;
|
|
13
23
|
export interface ModuleLoaderConfig {
|
|
14
24
|
projectDir: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAuBzE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAkBpE;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,cAAc,EAC5B,MAAM,EAAE,kBAAkB,EAC1B,eAAe,UAAQ,GACtB,OAAO,CAAC,MAAM,CAAC,CAgIjB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAyBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI5D;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA6DlC"}
|
|
@@ -8,41 +8,18 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { rendererLogger } from "../../../utils/index.js";
|
|
10
10
|
import { getLocalAdapter } from "../../../platform/adapters/registry.js";
|
|
11
|
-
import { transformToESM } from "../../../transforms/esm-transform.js";
|
|
12
11
|
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
13
12
|
import { getProjectTmpDir } from "../../../modules/react-loader/index.js";
|
|
14
|
-
import { generateCacheKey as generateTransformCacheKey, getOrComputeTransform, initializeTransformCache, setCachedTransformAsync, } from "../../../transforms/esm/transform-cache.js";
|
|
15
|
-
import { TRANSFORM_DISTRIBUTED_TTL_SEC } from "../../../utils/constants/cache.js";
|
|
16
|
-
import { validateCachedBundlesByManifestOrCode } from "../../../transforms/esm/cached-bundle-validation.js";
|
|
17
13
|
import { getHttpBundleCacheDir, getMdxEsmCacheDir } from "../../../utils/cache-dir.js";
|
|
18
14
|
import { join } from "../../../platform/compat/path/index.js";
|
|
19
|
-
import {
|
|
20
|
-
import { getModulePathCache, invalidateMdxEsmModule, lookupMdxEsmCache, saveModulePathCache, } from "../../../transforms/mdx/esm-module-loader/cache/index.js";
|
|
21
|
-
import { buildMdxEsmPathCacheKey } from "../../../transforms/mdx/esm-module-loader/cache-format.js";
|
|
15
|
+
import { invalidateMdxEsmModule, lookupMdxEsmCache, } from "../../../transforms/mdx/esm-module-loader/cache/index.js";
|
|
22
16
|
import { resolveModuleDependencies, rewriteResolvedDependencyImports, } from "./dependency-resolver.js";
|
|
17
|
+
import { persistTransformedModule } from "./module-persistence.js";
|
|
18
|
+
import { transformModuleCodeWithCache, UNRESOLVED_VF_MODULES_RE, } from "./module-transform-cache.js";
|
|
23
19
|
const logger = rendererLogger.component("module-loader");
|
|
24
20
|
// Re-export utilities
|
|
25
21
|
export { createEsmCache, createModuleCache, generateHash } from "./cache.js";
|
|
26
22
|
export { fetchEsmModule, rewriteEsmPaths } from "./esm-rewriter.js";
|
|
27
|
-
/** Maximum number of directories to track to prevent memory leaks */
|
|
28
|
-
const MAX_CREATED_DIRS = 5_000;
|
|
29
|
-
/** Cache for created directories to avoid repeated mkdir calls (LRU-style) */
|
|
30
|
-
const createdDirs = new Set();
|
|
31
|
-
/** TTL for cached transforms (uses centralized config) */
|
|
32
|
-
const TRANSFORM_CACHE_TTL_SECONDS = TRANSFORM_DISTRIBUTED_TTL_SEC;
|
|
33
|
-
/** Prune oldest entries when cache exceeds limit */
|
|
34
|
-
function pruneCreatedDirs() {
|
|
35
|
-
if (createdDirs.size <= MAX_CREATED_DIRS)
|
|
36
|
-
return;
|
|
37
|
-
const toDelete = createdDirs.size - MAX_CREATED_DIRS;
|
|
38
|
-
let deleted = 0;
|
|
39
|
-
for (const dir of createdDirs) {
|
|
40
|
-
if (deleted >= toDelete)
|
|
41
|
-
break;
|
|
42
|
-
createdDirs.delete(dir);
|
|
43
|
-
deleted++;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
23
|
function getModuleCacheKey(filePath, projectId, projectDir, contentSourceId) {
|
|
47
24
|
const base = projectId ?? projectDir ?? "default";
|
|
48
25
|
const source = contentSourceId ?? "default";
|
|
@@ -53,20 +30,6 @@ function decodeFileContent(fileContent) {
|
|
|
53
30
|
return fileContent;
|
|
54
31
|
return new TextDecoder().decode(fileContent);
|
|
55
32
|
}
|
|
56
|
-
async function ensureDir(adapter, dir) {
|
|
57
|
-
if (createdDirs.has(dir))
|
|
58
|
-
return;
|
|
59
|
-
try {
|
|
60
|
-
await adapter.fs.mkdir(dir, { recursive: true });
|
|
61
|
-
}
|
|
62
|
-
catch (_) {
|
|
63
|
-
/* expected: directory might already exist */
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
createdDirs.add(dir);
|
|
67
|
-
pruneCreatedDirs();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
33
|
/**
|
|
71
34
|
* Transform a module and all its local dependencies (@/ alias and relative imports).
|
|
72
35
|
*
|
|
@@ -77,8 +40,6 @@ async function ensureDir(adapter, dir) {
|
|
|
77
40
|
* @param useLocalAdapter - Whether to use local adapter for reading
|
|
78
41
|
* @returns Path to the transformed module file
|
|
79
42
|
*/
|
|
80
|
-
/** Pattern to detect unresolved /_vf_modules/ imports that will fail at runtime */
|
|
81
|
-
const UNRESOLVED_VF_MODULES_RE = /from\s*["']((?:file:\/\/)?\/?\/?_vf_modules\/[^"']+)["']/;
|
|
82
43
|
export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, config, useLocalAdapter = false) {
|
|
83
44
|
const { moduleCache, projectDir, projectId, contentSourceId, adapter, mode } = config;
|
|
84
45
|
const cacheKey = getModuleCacheKey(filePath, projectId, projectDir, contentSourceId);
|
|
@@ -158,123 +119,27 @@ export async function transformModuleWithDeps(filePath, tmpDir, localAdapter, co
|
|
|
158
119
|
projectDir,
|
|
159
120
|
});
|
|
160
121
|
}
|
|
161
|
-
const contentHash = hashCodeHex(fileContent);
|
|
162
122
|
const effectiveProjectId = projectId ?? projectDir;
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
manifestId: transformResult.bundleManifestId?.slice(0, 12),
|
|
184
|
-
failedHashes: validation.failedHashes,
|
|
185
|
-
reason: validation.reason,
|
|
186
|
-
source: validation.source,
|
|
187
|
-
});
|
|
188
|
-
bundlesValid = false;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (!bundlesValid) {
|
|
192
|
-
transformedCode = await transformToESM(fileContent, filePath, projectDir, adapter, {
|
|
193
|
-
projectId: effectiveProjectId,
|
|
194
|
-
dev: mode === "development",
|
|
195
|
-
ssr: true,
|
|
196
|
-
reactVersion: config.reactVersion,
|
|
197
|
-
});
|
|
198
|
-
setCachedTransformAsync(transformCacheKey, transformedCode, contentHash, TRANSFORM_CACHE_TTL_SECONDS).catch((error) => {
|
|
199
|
-
logger.debug("Failed to update transform cache after re-transform", {
|
|
200
|
-
filePath,
|
|
201
|
-
error,
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
// CRITICAL: Validate that no unresolved /_vf_modules/ imports remain after transform.
|
|
206
|
-
// These imports should have been resolved to file:// paths by ssrVfModulesPlugin.
|
|
207
|
-
// If they're still present, retry the transform bypassing all caches.
|
|
208
|
-
if (UNRESOLVED_VF_MODULES_RE.test(transformedCode)) {
|
|
209
|
-
const match = transformedCode.match(UNRESOLVED_VF_MODULES_RE);
|
|
210
|
-
const unresolvedImport = match?.[1] || "unknown";
|
|
211
|
-
logger.warn("[ModuleLoader] Transform has unresolved _vf_modules import, retrying without cache", {
|
|
212
|
-
filePath: filePath.slice(-60),
|
|
213
|
-
unresolvedImport: unresolvedImport.slice(0, 80),
|
|
214
|
-
cacheHit: transformResult.cacheHit,
|
|
215
|
-
});
|
|
216
|
-
// Force a fresh transform bypassing all caches
|
|
217
|
-
// Import runPipeline directly to bypass getOrComputeTransform cache
|
|
218
|
-
const { runPipeline } = await import("../../../transforms/pipeline/index.js");
|
|
219
|
-
const pipelineResult = await runPipeline(fileContent, filePath, projectDir, {
|
|
220
|
-
projectId: effectiveProjectId,
|
|
221
|
-
dev: mode === "development",
|
|
222
|
-
ssr: true,
|
|
223
|
-
reactVersion: config.reactVersion,
|
|
224
|
-
});
|
|
225
|
-
transformedCode = pipelineResult.code;
|
|
226
|
-
// Check again after retry
|
|
227
|
-
if (UNRESOLVED_VF_MODULES_RE.test(transformedCode)) {
|
|
228
|
-
const retryMatch = transformedCode.match(UNRESOLVED_VF_MODULES_RE);
|
|
229
|
-
logger.error("Transform still has unresolved _vf_modules after retry", {
|
|
230
|
-
filePath: filePath.slice(-60),
|
|
231
|
-
unresolvedImport: retryMatch?.[1]?.slice(0, 80) || "unknown",
|
|
232
|
-
hint: "Check that framework sources exist in dist/framework-src/ and ssrVfModulesPlugin is running",
|
|
233
|
-
});
|
|
234
|
-
// Continue anyway - let it fail at import time for better error context
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
// Retry succeeded - update the cache
|
|
238
|
-
setCachedTransformAsync(transformCacheKey, transformedCode, hashCodeHex(transformedCode).slice(0, 16), TRANSFORM_CACHE_TTL_SECONDS).catch((error) => {
|
|
239
|
-
logger.debug("Failed to update cache after retry", { filePath, error });
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
const transformedHash = hashCodeHex(transformedCode).slice(0, 8);
|
|
244
|
-
const relativePath = filePath.startsWith(projectDir)
|
|
245
|
-
? filePath.slice(projectDir.length).replace(/^\/+/, "")
|
|
246
|
-
: filePath.replace(/^\/+/, "");
|
|
247
|
-
const jsPath = relativePath.replace(/\.(tsx?|jsx|mdx)$/, `.${transformedHash}.js`);
|
|
248
|
-
const tempFilePath = join(tmpDir, jsPath);
|
|
249
|
-
const tempDir = tempFilePath.substring(0, tempFilePath.lastIndexOf("/"));
|
|
250
|
-
await ensureDir(localAdapter, tempDir);
|
|
251
|
-
try {
|
|
252
|
-
await localAdapter.fs.writeFile(tempFilePath, transformedCode);
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
logger.error("Failed to write module:", {
|
|
256
|
-
filePath,
|
|
257
|
-
tempFilePath,
|
|
258
|
-
error: error instanceof Error ? error.message : String(error),
|
|
259
|
-
});
|
|
260
|
-
throw error;
|
|
261
|
-
}
|
|
262
|
-
if (contentSourceId) {
|
|
263
|
-
const normalizedPath = `_vf_modules/${relativePath.replace(/\.(tsx?|jsx|mdx)$/, ".js")}`;
|
|
264
|
-
const mdxCacheKey = buildMdxEsmPathCacheKey(normalizedPath, config.reactVersion);
|
|
265
|
-
const cache = await getModulePathCache(tmpDir);
|
|
266
|
-
cache.set(mdxCacheKey, tempFilePath);
|
|
267
|
-
saveModulePathCache(tmpDir).catch((err) => {
|
|
268
|
-
logger.debug("Failed to save module cache", { error: String(err) });
|
|
269
|
-
});
|
|
270
|
-
logger.debug("Registered module in MDX-ESM cache", {
|
|
271
|
-
file: filePath.slice(-40),
|
|
272
|
-
mdxCacheKey,
|
|
273
|
-
tempFilePath: tempFilePath.slice(-60),
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
moduleCache.set(cacheKey, tempFilePath);
|
|
277
|
-
return tempFilePath;
|
|
123
|
+
const { code: transformedCode } = await transformModuleCodeWithCache({
|
|
124
|
+
fileContent,
|
|
125
|
+
filePath,
|
|
126
|
+
projectDir,
|
|
127
|
+
effectiveProjectId,
|
|
128
|
+
mode,
|
|
129
|
+
adapter,
|
|
130
|
+
reactVersion: config.reactVersion,
|
|
131
|
+
});
|
|
132
|
+
return await persistTransformedModule({
|
|
133
|
+
filePath,
|
|
134
|
+
projectDir,
|
|
135
|
+
tmpDir,
|
|
136
|
+
transformedCode,
|
|
137
|
+
localAdapter,
|
|
138
|
+
moduleCache,
|
|
139
|
+
cacheKey,
|
|
140
|
+
contentSourceId,
|
|
141
|
+
reactVersion: config.reactVersion,
|
|
142
|
+
});
|
|
278
143
|
}
|
|
279
144
|
/**
|
|
280
145
|
* Get the cache directory for module transforms.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Final module-loader persistence phase.
|
|
3
|
+
*
|
|
4
|
+
* @module rendering/orchestrator/module-loader/module-persistence
|
|
5
|
+
*/
|
|
6
|
+
import type { RuntimeAdapter } from "../../../platform/adapters/base.js";
|
|
7
|
+
export interface PersistTransformedModuleInput {
|
|
8
|
+
filePath: string;
|
|
9
|
+
projectDir: string;
|
|
10
|
+
tmpDir: string;
|
|
11
|
+
transformedCode: string;
|
|
12
|
+
localAdapter: RuntimeAdapter;
|
|
13
|
+
moduleCache: Map<string, string>;
|
|
14
|
+
cacheKey: string;
|
|
15
|
+
contentSourceId?: string;
|
|
16
|
+
reactVersion?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Write a transformed module artifact and register cache pointers. */
|
|
19
|
+
export declare function persistTransformedModule(input: PersistTransformedModuleInput): Promise<string>;
|
|
20
|
+
//# sourceMappingURL=module-persistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-persistence.d.ts","sourceRoot":"","sources":["../../../../../src/src/rendering/orchestrator/module-loader/module-persistence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AA6CzE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,cAAc,CAAC;IAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,uEAAuE;AACvE,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,6BAA6B,GACnC,OAAO,CAAC,MAAM,CAAC,CA2CjB"}
|