veryfront 0.1.53 → 0.1.54
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/html/styles-builder/tailwind-compiler-cache.d.ts +1 -1
- package/esm/src/html/styles-builder/tailwind-compiler-cache.d.ts.map +1 -1
- package/esm/src/html/styles-builder/tailwind-compiler-cache.js +8 -4
- package/esm/src/html/styles-builder/tailwind-compiler.d.ts +2 -1
- package/esm/src/html/styles-builder/tailwind-compiler.d.ts.map +1 -1
- package/esm/src/html/styles-builder/tailwind-compiler.js +7 -3
- package/esm/src/modules/react-loader/ssr-module-loader/constants.d.ts +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/constants.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/constants.js +2 -2
- package/esm/src/modules/react-loader/ssr-module-loader/ssr-circuit-breaker.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/ssr-circuit-breaker.js +3 -4
- package/esm/src/rendering/orchestrator/pipeline.d.ts +2 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +33 -7
- package/esm/src/server/dev-server/error-overlay/html-template.js +1 -1
- package/esm/src/server/handlers/dev/styles-css.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/styles-css.handler.js +3 -1
- package/esm/src/server/handlers/request/css.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/css.handler.js +3 -3
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/html/styles-builder/tailwind-compiler-cache.ts +8 -3
- package/src/src/html/styles-builder/tailwind-compiler.ts +11 -3
- package/src/src/modules/react-loader/ssr-module-loader/constants.ts +2 -2
- package/src/src/modules/react-loader/ssr-module-loader/ssr-circuit-breaker.ts +3 -4
- package/src/src/rendering/orchestrator/pipeline.ts +46 -8
- package/src/src/server/dev-server/error-overlay/html-template.ts +1 -1
- package/src/src/server/handlers/dev/styles-css.handler.ts +3 -1
- package/src/src/server/handlers/request/css.handler.ts +11 -3
package/esm/deno.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { compile } from "tailwindcss";
|
|
2
|
-
export declare function getCompiler(stylesheet: string): Promise<Awaited<ReturnType<typeof compile>>>;
|
|
2
|
+
export declare function getCompiler(stylesheet: string, projectSlug?: string): Promise<Awaited<ReturnType<typeof compile>>>;
|
|
3
3
|
export declare function invalidateCompiler(): void;
|
|
4
4
|
/**
|
|
5
5
|
* Get compiler cache statistics for monitoring.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwind-compiler-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/html/styles-builder/tailwind-compiler-cache.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA2EtC,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"tailwind-compiler-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/html/styles-builder/tailwind-compiler-cache.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA2EtC,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAkD9C;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E,CAQA;AAED,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAalD"}
|
|
@@ -62,14 +62,18 @@ function evictOldestCompiler() {
|
|
|
62
62
|
compilerCache.delete(oldestKey);
|
|
63
63
|
logger.debug("Evicted oldest compiler from cache", { hash: oldestKey });
|
|
64
64
|
}
|
|
65
|
-
export async function getCompiler(stylesheet) {
|
|
66
|
-
|
|
65
|
+
export async function getCompiler(stylesheet, projectSlug) {
|
|
66
|
+
// Tailwind v4's compile().build() is stateful — it accumulates candidates
|
|
67
|
+
// across calls. Without per-project isolation, projects sharing the same
|
|
68
|
+
// stylesheet on the shared pool contaminate each other's CSS output.
|
|
69
|
+
const stylesheetHash = hashString(stylesheet);
|
|
70
|
+
const hash = projectSlug ? `${projectSlug}:${stylesheetHash}` : stylesheetHash;
|
|
67
71
|
const cached = compilerCache.get(hash);
|
|
68
72
|
if (cached) {
|
|
69
|
-
logger.debug("Compiler cache hit", { hash });
|
|
73
|
+
logger.debug("Compiler cache hit", { hash, projectSlug });
|
|
70
74
|
return cached.compiler;
|
|
71
75
|
}
|
|
72
|
-
logger.debug("Creating new compiler", { hash });
|
|
76
|
+
logger.debug("Creating new compiler", { hash, projectSlug });
|
|
73
77
|
const tailwindBase = await getTailwindBaseCSS();
|
|
74
78
|
const pluginCache = new Map();
|
|
75
79
|
const pluginErrors = new Map();
|
|
@@ -11,6 +11,7 @@ export interface GenerateOptions {
|
|
|
11
11
|
minify?: boolean;
|
|
12
12
|
environment?: string;
|
|
13
13
|
buildMode?: "development" | "production";
|
|
14
|
+
projectSlug?: string;
|
|
14
15
|
}
|
|
15
16
|
export interface CSSErrorInfo {
|
|
16
17
|
title: string;
|
|
@@ -32,7 +33,7 @@ export declare function getProjectCSS(projectSlug: string, stylesheet: string |
|
|
|
32
33
|
* @param expectedHash - The CSS hash to regenerate
|
|
33
34
|
* @returns The regenerated CSS if inputs are cached and hash matches, undefined otherwise
|
|
34
35
|
*/
|
|
35
|
-
export declare function regenerateCSSByHash(expectedHash: string): Promise<string | undefined>;
|
|
36
|
+
export declare function regenerateCSSByHash(expectedHash: string, projectSlug: string | undefined): Promise<string | undefined>;
|
|
36
37
|
export declare function generateTailwindCSS(stylesheet: string | undefined, candidates: string[] | Set<string>, options?: GenerateOptions): Promise<TailwindResult>;
|
|
37
38
|
export declare function formatCSSError(error: Error | string): CSSErrorInfo;
|
|
38
39
|
//# sourceMappingURL=tailwind-compiler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwind-compiler.d.ts","sourceRoot":"","sources":["../../../../src/src/html/styles-builder/tailwind-compiler.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAShC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"tailwind-compiler.d.ts","sourceRoot":"","sources":["../../../../src/src/html/styles-builder/tailwind-compiler.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,yBAAyB,EACzB,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAShC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAsE5D;AAMD;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA4D7B;AAMD,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,CAgCzB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,YAAY,CAGlE"}
|
|
@@ -45,7 +45,10 @@ export async function getProjectCSS(projectSlug, stylesheet, candidates, options
|
|
|
45
45
|
}
|
|
46
46
|
const generationPromise = (async () => {
|
|
47
47
|
// Generate fresh CSS
|
|
48
|
-
const result = await generateTailwindCSS(context.stylesheet, candidates,
|
|
48
|
+
const result = await generateTailwindCSS(context.stylesheet, candidates, {
|
|
49
|
+
...options,
|
|
50
|
+
projectSlug,
|
|
51
|
+
});
|
|
49
52
|
if (result.error) {
|
|
50
53
|
const formatted = formatCSSError(result.error);
|
|
51
54
|
logger.error("Project CSS generation failed", {
|
|
@@ -88,7 +91,7 @@ export async function getProjectCSS(projectSlug, stylesheet, candidates, options
|
|
|
88
91
|
* @param expectedHash - The CSS hash to regenerate
|
|
89
92
|
* @returns The regenerated CSS if inputs are cached and hash matches, undefined otherwise
|
|
90
93
|
*/
|
|
91
|
-
export async function regenerateCSSByHash(expectedHash) {
|
|
94
|
+
export async function regenerateCSSByHash(expectedHash, projectSlug) {
|
|
92
95
|
const inFlight = inFlightRegeneration.get(expectedHash);
|
|
93
96
|
if (inFlight)
|
|
94
97
|
return await inFlight;
|
|
@@ -100,6 +103,7 @@ export async function regenerateCSSByHash(expectedHash) {
|
|
|
100
103
|
}
|
|
101
104
|
const result = await generateTailwindCSS(inputs.stylesheet, inputs.candidates, {
|
|
102
105
|
minify: true,
|
|
106
|
+
projectSlug,
|
|
103
107
|
});
|
|
104
108
|
if (result.error) {
|
|
105
109
|
logger.warn("CSS regeneration failed", {
|
|
@@ -145,7 +149,7 @@ export async function generateTailwindCSS(stylesheet, candidates, options) {
|
|
|
145
149
|
return await withSpan(SpanNames.HTML_GENERATE_TAILWIND_CSS, async () => {
|
|
146
150
|
const css = stylesheet ?? DEFAULT_STYLESHEET;
|
|
147
151
|
try {
|
|
148
|
-
const comp = await getCompiler(css);
|
|
152
|
+
const comp = await getCompiler(css, options?.projectSlug);
|
|
149
153
|
let output = comp.build(candidateArray);
|
|
150
154
|
if (options?.minify)
|
|
151
155
|
output = minifyCSS(output);
|
|
@@ -6,7 +6,7 @@ export declare const REDIS_KEY_PREFIX = "veryfront:ssr-module:";
|
|
|
6
6
|
/** Get environment-aware Redis TTL for SSR modules */
|
|
7
7
|
export declare function getSSRModuleRedisTTL(isProduction: boolean): number;
|
|
8
8
|
export { DISTRIBUTED_SSR_MODULE_TTL_PREVIEW_SEC, DISTRIBUTED_SSR_MODULE_TTL_PRODUCTION_SEC };
|
|
9
|
-
export declare const CIRCUIT_BREAKER_THRESHOLD =
|
|
9
|
+
export declare const CIRCUIT_BREAKER_THRESHOLD = 25;
|
|
10
10
|
export declare const CIRCUIT_BREAKER_RESET_MS: number;
|
|
11
11
|
export declare function getMaxConcurrentTransforms(): number;
|
|
12
12
|
export declare function getTransformPerProjectLimit(): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/src/modules/react-loader/ssr-module-loader/constants.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,sCAAsC,EACtC,yCAAyC,EAG1C,MAAM,mCAAmC,CAAC;AAE3C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,uBAAuB,QAAqB,CAAC;AAE1D,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD,sDAAsD;AACtD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAElE;AAED,OAAO,EAAE,sCAAsC,EAAE,yCAAyC,EAAE,CAAC;AAE7F,eAAO,MAAM,yBAAyB,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/src/modules/react-loader/ssr-module-loader/constants.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,sCAAsC,EACtC,yCAAyC,EAG1C,MAAM,mCAAmC,CAAC;AAE3C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,uBAAuB,QAAqB,CAAC;AAE1D,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD,sDAAsD;AACtD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAElE;AAED,OAAO,EAAE,sCAAsC,EAAE,yCAAyC,EAAE,CAAC;AAE7F,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C,eAAO,MAAM,wBAAwB,QAAW,CAAC;AAIjD,wBAAgB,0BAA0B,IAAI,MAAM,CAOnD;AASD,wBAAgB,2BAA2B,IAAI,MAAM,CAQpD;AAED,oEAAoE;AACpE,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD;AAED,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAChD,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,oBAAoB,KAAK,CAAC"}
|
|
@@ -10,8 +10,8 @@ export function getSSRModuleRedisTTL(isProduction) {
|
|
|
10
10
|
return getDistributedCacheTTL("ssr-module", isProduction);
|
|
11
11
|
}
|
|
12
12
|
export { DISTRIBUTED_SSR_MODULE_TTL_PREVIEW_SEC, DISTRIBUTED_SSR_MODULE_TTL_PRODUCTION_SEC };
|
|
13
|
-
export const CIRCUIT_BREAKER_THRESHOLD =
|
|
14
|
-
export const CIRCUIT_BREAKER_RESET_MS =
|
|
13
|
+
export const CIRCUIT_BREAKER_THRESHOLD = 25;
|
|
14
|
+
export const CIRCUIT_BREAKER_RESET_MS = 5 * 1000;
|
|
15
15
|
// Max concurrent ESM transforms (safety net, not throttle). Set to 0 to disable.
|
|
16
16
|
let _maxConcurrentTransforms;
|
|
17
17
|
export function getMaxConcurrentTransforms() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-circuit-breaker.d.ts","sourceRoot":"","sources":["../../../../../src/src/modules/react-loader/ssr-module-loader/ssr-circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAC5B;;;;OAIG;IACH,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"ssr-circuit-breaker.d.ts","sourceRoot":"","sources":["../../../../../src/src/modules/react-loader/ssr-module-loader/ssr-circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAC5B;;;;OAIG;IACH,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IA8BjD;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAOxC"}
|
|
@@ -31,12 +31,11 @@ export class SSRCircuitBreaker {
|
|
|
31
31
|
if (failureRecord.count >= CIRCUIT_BREAKER_THRESHOLD &&
|
|
32
32
|
timeSinceFailure < CIRCUIT_BREAKER_RESET_MS) {
|
|
33
33
|
throw toError(createError({
|
|
34
|
-
type: "
|
|
34
|
+
type: "render",
|
|
35
35
|
message: `Component ${filePath} is temporarily blocked due to repeated failures. Will retry in ${Math.ceil((CIRCUIT_BREAKER_RESET_MS - timeSinceFailure) / 1000)}s.`,
|
|
36
36
|
context: {
|
|
37
|
-
|
|
38
|
-
phase: "
|
|
39
|
-
failures: failureRecord.count,
|
|
37
|
+
component: filePath,
|
|
38
|
+
phase: "server",
|
|
40
39
|
},
|
|
41
40
|
}));
|
|
42
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6ChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxE,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,YAAY,EAAE,YAAY,CAAC;IAC3B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,qBAAqB,EAAE,sBAAsB,CAAC;CAC1E;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,kBAAkB,CAAqB;gBAEnC,MAAM,EAAE,oBAAoB;IAaxC;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAKxB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,sBAAsB;YAIhB,0BAA0B;IAaxC;;;;;;;;;OASG;YACW,qBAAqB;IAyDnC;;;OAGG;YACW,mBAAmB;IAqH3B,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAgP9E,+EAA+E;IACzE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA6LvF;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAetB"}
|
|
@@ -30,6 +30,7 @@ import { setupSSRGlobals } from "../ssr-globals.js";
|
|
|
30
30
|
import { LAYOUT_EXTENSIONS } from "../layouts/types.js";
|
|
31
31
|
import { withTimeout, withTimeoutThrow } from "../utils/stream-utils.js";
|
|
32
32
|
import { extractCandidates, generateTailwindCSS } from "../../html/styles-builder/index.js";
|
|
33
|
+
import { getCSSByHashAsync, regenerateCSSByHash, } from "../../html/styles-builder/tailwind-compiler.js";
|
|
33
34
|
import { createEsmCache, createModuleCache, loadModule } from "./module-loader/index.js";
|
|
34
35
|
import { getCSSImports, runWithCSSCollector, } from "../../modules/react-loader/css-import-collector.js";
|
|
35
36
|
// Extracted modules
|
|
@@ -39,6 +40,7 @@ import { collectModulesToLoad, DATA_FETCH_TIMEOUT_MS, hasDataFetchingFunction, M
|
|
|
39
40
|
const renderPageLog = logger.component("render-page");
|
|
40
41
|
const renderPipelineLog = logger.component("render-pipeline");
|
|
41
42
|
const resolvePageDataLog = logger.component("resolve-page-data");
|
|
43
|
+
const RENDERED_CSS_HASH_RE = /href="\/_vf\/css\/([a-z0-9-]{1,16})\.css"/i;
|
|
42
44
|
// Re-export test helper for backward compatibility
|
|
43
45
|
export { __injectCssCacheForTests } from "./css-cache.js";
|
|
44
46
|
export class RenderPipeline {
|
|
@@ -68,6 +70,18 @@ export class RenderPipeline {
|
|
|
68
70
|
loadModule(filePath) {
|
|
69
71
|
return loadModule(filePath, this.moduleLoaderConfig);
|
|
70
72
|
}
|
|
73
|
+
extractRenderedCssHash(html) {
|
|
74
|
+
return html.match(RENDERED_CSS_HASH_RE)?.[1];
|
|
75
|
+
}
|
|
76
|
+
async resolveCssFromRenderedHtml(html, projectSlug) {
|
|
77
|
+
const cssHash = this.extractRenderedCssHash(html);
|
|
78
|
+
if (!cssHash)
|
|
79
|
+
return undefined;
|
|
80
|
+
const cachedCss = await getCSSByHashAsync(cssHash);
|
|
81
|
+
if (cachedCss)
|
|
82
|
+
return cachedCss;
|
|
83
|
+
return await regenerateCSSByHash(cssHash, projectSlug);
|
|
84
|
+
}
|
|
71
85
|
/**
|
|
72
86
|
* Load modules in parallel and return only successfully loaded ones.
|
|
73
87
|
*
|
|
@@ -438,15 +452,27 @@ export class RenderPipeline {
|
|
|
438
452
|
skipCachePersist: true,
|
|
439
453
|
}), CSS_SSR_TIMEOUT_MS, `CSS SSR for ${slug}`);
|
|
440
454
|
if (renderResult?.html) {
|
|
441
|
-
|
|
442
|
-
|
|
455
|
+
css = await this.resolveCssFromRenderedHtml(renderResult.html, options?.projectSlug ?? options?.projectId);
|
|
456
|
+
if (css) {
|
|
457
|
+
resolvePageDataLog.debug("Reused SSR CSS for page data", {
|
|
458
|
+
slug,
|
|
459
|
+
cssLength: css.length,
|
|
460
|
+
source: "rendered-html-hash",
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
const candidates = extractCandidates(renderResult.html);
|
|
465
|
+
css = (await generateTailwindCSS(undefined, candidates, {
|
|
466
|
+
projectSlug: options?.projectSlug,
|
|
467
|
+
})).css;
|
|
468
|
+
resolvePageDataLog.debug("Fell back to HTML candidate CSS generation", {
|
|
469
|
+
slug,
|
|
470
|
+
htmlLength: renderResult.html.length,
|
|
471
|
+
cssLength: css?.length || 0,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
443
474
|
if (css)
|
|
444
475
|
cachePageCss(cssCacheKey, css);
|
|
445
|
-
resolvePageDataLog.debug("Generated and cached CSS", {
|
|
446
|
-
slug,
|
|
447
|
-
htmlLength: renderResult.html.length,
|
|
448
|
-
cssLength: css?.length || 0,
|
|
449
|
-
});
|
|
450
476
|
}
|
|
451
477
|
}
|
|
452
478
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles-css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css.handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAanG,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"styles-css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/dev/styles-css.handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAanG,qBAAa,gBAAiB,SAAQ,WAAW;IAC/C,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAkGjE,cAAc;CAiB7B"}
|
|
@@ -39,7 +39,9 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
39
39
|
});
|
|
40
40
|
candidates = new Set();
|
|
41
41
|
}
|
|
42
|
-
const result = await generateTailwindCSS(rawCss, candidates
|
|
42
|
+
const result = await generateTailwindCSS(rawCss, candidates, {
|
|
43
|
+
projectSlug: ctx.projectSlug,
|
|
44
|
+
});
|
|
43
45
|
if (result.error) {
|
|
44
46
|
const formatted = formatCSSError(result.error);
|
|
45
47
|
logger.error("Tailwind error", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/css.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"css.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/css.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AA0BnG,qBAAa,UAAW,SAAQ,WAAW;IACzC,QAAQ,EAAE,eAAe,CAOvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAkEhF"}
|
|
@@ -6,11 +6,11 @@ import { getEnv } from "../../../platform/compat/process.js";
|
|
|
6
6
|
import { runWithRequestContext } from "../../../platform/adapters/fs/veryfront/multi-project-adapter.js";
|
|
7
7
|
/** Pattern to match hashed CSS URLs: /_vf/css/[8-char-hash].css */
|
|
8
8
|
const CSS_URL_PATTERN = /^\/_vf\/css\/([a-z0-9-]{1,16})\.css$/;
|
|
9
|
-
async function getCSSWithJITFallback(cssHash) {
|
|
9
|
+
async function getCSSWithJITFallback(cssHash, projectSlug) {
|
|
10
10
|
const cached = await getCSSByHashAsync(cssHash);
|
|
11
11
|
if (cached)
|
|
12
12
|
return cached;
|
|
13
|
-
return regenerateCSSByHash(cssHash);
|
|
13
|
+
return regenerateCSSByHash(cssHash, projectSlug);
|
|
14
14
|
}
|
|
15
15
|
export class CSSHandler extends BaseHandler {
|
|
16
16
|
metadata = {
|
|
@@ -35,7 +35,7 @@ export class CSSHandler extends BaseHandler {
|
|
|
35
35
|
// null — causing cross-pod cache misses. Wrap the lookup in request context
|
|
36
36
|
// so the API backend can resolve the token and project.
|
|
37
37
|
const effectiveToken = ctx.proxyToken || getEnv("VERYFRONT_API_TOKEN") || "";
|
|
38
|
-
const lookup = () => runWithCacheKeyContext(cacheCtx, () => getCSSWithJITFallback(cssHash));
|
|
38
|
+
const lookup = () => runWithCacheKeyContext(cacheCtx, () => getCSSWithJITFallback(cssHash, ctx.projectSlug ?? ctx.projectId));
|
|
39
39
|
const css = ctx.projectSlug
|
|
40
40
|
? await runWithRequestContext({
|
|
41
41
|
projectSlug: ctx.projectSlug,
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -87,16 +87,21 @@ function evictOldestCompiler(): void {
|
|
|
87
87
|
|
|
88
88
|
export async function getCompiler(
|
|
89
89
|
stylesheet: string,
|
|
90
|
+
projectSlug?: string,
|
|
90
91
|
): Promise<Awaited<ReturnType<typeof compile>>> {
|
|
91
|
-
|
|
92
|
+
// Tailwind v4's compile().build() is stateful — it accumulates candidates
|
|
93
|
+
// across calls. Without per-project isolation, projects sharing the same
|
|
94
|
+
// stylesheet on the shared pool contaminate each other's CSS output.
|
|
95
|
+
const stylesheetHash = hashString(stylesheet);
|
|
96
|
+
const hash = projectSlug ? `${projectSlug}:${stylesheetHash}` : stylesheetHash;
|
|
92
97
|
|
|
93
98
|
const cached = compilerCache.get(hash);
|
|
94
99
|
if (cached) {
|
|
95
|
-
logger.debug("Compiler cache hit", { hash });
|
|
100
|
+
logger.debug("Compiler cache hit", { hash, projectSlug });
|
|
96
101
|
return cached.compiler;
|
|
97
102
|
}
|
|
98
103
|
|
|
99
|
-
logger.debug("Creating new compiler", { hash });
|
|
104
|
+
logger.debug("Creating new compiler", { hash, projectSlug });
|
|
100
105
|
|
|
101
106
|
const tailwindBase = await getTailwindBaseCSS();
|
|
102
107
|
const pluginCache = new Map<string, unknown>();
|
|
@@ -59,6 +59,7 @@ export interface GenerateOptions {
|
|
|
59
59
|
minify?: boolean;
|
|
60
60
|
environment?: string;
|
|
61
61
|
buildMode?: "development" | "production";
|
|
62
|
+
projectSlug?: string;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export interface CSSErrorInfo {
|
|
@@ -104,7 +105,10 @@ export async function getProjectCSS(
|
|
|
104
105
|
|
|
105
106
|
const generationPromise = (async () => {
|
|
106
107
|
// Generate fresh CSS
|
|
107
|
-
const result = await generateTailwindCSS(context.stylesheet, candidates,
|
|
108
|
+
const result = await generateTailwindCSS(context.stylesheet, candidates, {
|
|
109
|
+
...options,
|
|
110
|
+
projectSlug,
|
|
111
|
+
});
|
|
108
112
|
|
|
109
113
|
if (result.error) {
|
|
110
114
|
const formatted = formatCSSError(result.error);
|
|
@@ -159,7 +163,10 @@ export async function getProjectCSS(
|
|
|
159
163
|
* @param expectedHash - The CSS hash to regenerate
|
|
160
164
|
* @returns The regenerated CSS if inputs are cached and hash matches, undefined otherwise
|
|
161
165
|
*/
|
|
162
|
-
export async function regenerateCSSByHash(
|
|
166
|
+
export async function regenerateCSSByHash(
|
|
167
|
+
expectedHash: string,
|
|
168
|
+
projectSlug: string | undefined,
|
|
169
|
+
): Promise<string | undefined> {
|
|
163
170
|
const inFlight = inFlightRegeneration.get(expectedHash);
|
|
164
171
|
if (inFlight) return await inFlight;
|
|
165
172
|
|
|
@@ -174,6 +181,7 @@ export async function regenerateCSSByHash(expectedHash: string): Promise<string
|
|
|
174
181
|
|
|
175
182
|
const result = await generateTailwindCSS(inputs.stylesheet, inputs.candidates, {
|
|
176
183
|
minify: true,
|
|
184
|
+
projectSlug,
|
|
177
185
|
});
|
|
178
186
|
|
|
179
187
|
if (result.error) {
|
|
@@ -237,7 +245,7 @@ export async function generateTailwindCSS(
|
|
|
237
245
|
const css = stylesheet ?? DEFAULT_STYLESHEET;
|
|
238
246
|
|
|
239
247
|
try {
|
|
240
|
-
const comp = await getCompiler(css);
|
|
248
|
+
const comp = await getCompiler(css, options?.projectSlug);
|
|
241
249
|
let output = comp.build(candidateArray);
|
|
242
250
|
|
|
243
251
|
if (options?.minify) output = minifyCSS(output);
|
|
@@ -21,8 +21,8 @@ export function getSSRModuleRedisTTL(isProduction: boolean): number {
|
|
|
21
21
|
|
|
22
22
|
export { DISTRIBUTED_SSR_MODULE_TTL_PREVIEW_SEC, DISTRIBUTED_SSR_MODULE_TTL_PRODUCTION_SEC };
|
|
23
23
|
|
|
24
|
-
export const CIRCUIT_BREAKER_THRESHOLD =
|
|
25
|
-
export const CIRCUIT_BREAKER_RESET_MS =
|
|
24
|
+
export const CIRCUIT_BREAKER_THRESHOLD = 25;
|
|
25
|
+
export const CIRCUIT_BREAKER_RESET_MS = 5 * 1000;
|
|
26
26
|
|
|
27
27
|
// Max concurrent ESM transforms (safety net, not throttle). Set to 0 to disable.
|
|
28
28
|
let _maxConcurrentTransforms: number | undefined;
|
|
@@ -37,15 +37,14 @@ export class SSRCircuitBreaker {
|
|
|
37
37
|
) {
|
|
38
38
|
throw toError(
|
|
39
39
|
createError({
|
|
40
|
-
type: "
|
|
40
|
+
type: "render",
|
|
41
41
|
message:
|
|
42
42
|
`Component ${filePath} is temporarily blocked due to repeated failures. Will retry in ${
|
|
43
43
|
Math.ceil((CIRCUIT_BREAKER_RESET_MS - timeSinceFailure) / 1000)
|
|
44
44
|
}s.`,
|
|
45
45
|
context: {
|
|
46
|
-
|
|
47
|
-
phase: "
|
|
48
|
-
failures: failureRecord.count,
|
|
46
|
+
component: filePath,
|
|
47
|
+
phase: "server",
|
|
49
48
|
},
|
|
50
49
|
}),
|
|
51
50
|
);
|
|
@@ -44,6 +44,10 @@ import { LAYOUT_EXTENSIONS } from "../layouts/types.js";
|
|
|
44
44
|
import type { LayoutItem } from "../../types/index.js";
|
|
45
45
|
import { withTimeout, withTimeoutThrow } from "../utils/stream-utils.js";
|
|
46
46
|
import { extractCandidates, generateTailwindCSS } from "../../html/styles-builder/index.js";
|
|
47
|
+
import {
|
|
48
|
+
getCSSByHashAsync,
|
|
49
|
+
regenerateCSSByHash,
|
|
50
|
+
} from "../../html/styles-builder/tailwind-compiler.js";
|
|
47
51
|
import { createEsmCache, createModuleCache, loadModule } from "./module-loader/index.js";
|
|
48
52
|
import type { ModuleLoaderConfig } from "./module-loader/index.js";
|
|
49
53
|
import {
|
|
@@ -73,6 +77,7 @@ import {
|
|
|
73
77
|
const renderPageLog = logger.component("render-page");
|
|
74
78
|
const renderPipelineLog = logger.component("render-pipeline");
|
|
75
79
|
const resolvePageDataLog = logger.component("resolve-page-data");
|
|
80
|
+
const RENDERED_CSS_HASH_RE = /href="\/_vf\/css\/([a-z0-9-]{1,16})\.css"/i;
|
|
76
81
|
|
|
77
82
|
// Re-export test helper for backward compatibility
|
|
78
83
|
export { __injectCssCacheForTests } from "./css-cache.js";
|
|
@@ -137,6 +142,23 @@ export class RenderPipeline {
|
|
|
137
142
|
return loadModule(filePath, this.moduleLoaderConfig);
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
private extractRenderedCssHash(html: string): string | undefined {
|
|
146
|
+
return html.match(RENDERED_CSS_HASH_RE)?.[1];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private async resolveCssFromRenderedHtml(
|
|
150
|
+
html: string,
|
|
151
|
+
projectSlug: string | undefined,
|
|
152
|
+
): Promise<string | undefined> {
|
|
153
|
+
const cssHash = this.extractRenderedCssHash(html);
|
|
154
|
+
if (!cssHash) return undefined;
|
|
155
|
+
|
|
156
|
+
const cachedCss = await getCSSByHashAsync(cssHash);
|
|
157
|
+
if (cachedCss) return cachedCss;
|
|
158
|
+
|
|
159
|
+
return await regenerateCSSByHash(cssHash, projectSlug);
|
|
160
|
+
}
|
|
161
|
+
|
|
140
162
|
/**
|
|
141
163
|
* Load modules in parallel and return only successfully loaded ones.
|
|
142
164
|
*
|
|
@@ -687,15 +709,31 @@ export class RenderPipeline {
|
|
|
687
709
|
);
|
|
688
710
|
|
|
689
711
|
if (renderResult?.html) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
712
|
+
css = await this.resolveCssFromRenderedHtml(
|
|
713
|
+
renderResult.html,
|
|
714
|
+
options?.projectSlug ?? options?.projectId,
|
|
715
|
+
);
|
|
693
716
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
717
|
+
if (css) {
|
|
718
|
+
resolvePageDataLog.debug("Reused SSR CSS for page data", {
|
|
719
|
+
slug,
|
|
720
|
+
cssLength: css.length,
|
|
721
|
+
source: "rendered-html-hash",
|
|
722
|
+
});
|
|
723
|
+
} else {
|
|
724
|
+
const candidates = extractCandidates(renderResult.html);
|
|
725
|
+
css = (await generateTailwindCSS(undefined, candidates, {
|
|
726
|
+
projectSlug: options?.projectSlug,
|
|
727
|
+
})).css;
|
|
728
|
+
|
|
729
|
+
resolvePageDataLog.debug("Fell back to HTML candidate CSS generation", {
|
|
730
|
+
slug,
|
|
731
|
+
htmlLength: renderResult.html.length,
|
|
732
|
+
cssLength: css?.length || 0,
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (css) cachePageCss(cssCacheKey, css);
|
|
699
737
|
}
|
|
700
738
|
} catch (error) {
|
|
701
739
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -187,7 +187,7 @@ export function generateErrorHTML(errorInfo: ErrorInfo, suggestion?: string): st
|
|
|
187
187
|
<html>
|
|
188
188
|
<head>
|
|
189
189
|
<meta charset="UTF-8">
|
|
190
|
-
<title
|
|
190
|
+
<title>${errorType} Error - Veryfront</title>
|
|
191
191
|
<style>
|
|
192
192
|
body {
|
|
193
193
|
margin: 0;
|
|
@@ -54,7 +54,9 @@ export class StylesCSSHandler extends BaseHandler {
|
|
|
54
54
|
});
|
|
55
55
|
candidates = new Set<string>();
|
|
56
56
|
}
|
|
57
|
-
const result = await generateTailwindCSS(rawCss, candidates
|
|
57
|
+
const result = await generateTailwindCSS(rawCss, candidates, {
|
|
58
|
+
projectSlug: ctx.projectSlug,
|
|
59
|
+
});
|
|
58
60
|
|
|
59
61
|
if (result.error) {
|
|
60
62
|
const formatted = formatCSSError(result.error);
|
|
@@ -16,11 +16,14 @@ import { runWithRequestContext } from "../../../platform/adapters/fs/veryfront/m
|
|
|
16
16
|
/** Pattern to match hashed CSS URLs: /_vf/css/[8-char-hash].css */
|
|
17
17
|
const CSS_URL_PATTERN = /^\/_vf\/css\/([a-z0-9-]{1,16})\.css$/;
|
|
18
18
|
|
|
19
|
-
async function getCSSWithJITFallback(
|
|
19
|
+
async function getCSSWithJITFallback(
|
|
20
|
+
cssHash: string,
|
|
21
|
+
projectSlug: string | undefined,
|
|
22
|
+
): Promise<string | undefined> {
|
|
20
23
|
const cached = await getCSSByHashAsync(cssHash);
|
|
21
24
|
if (cached) return cached;
|
|
22
25
|
|
|
23
|
-
return regenerateCSSByHash(cssHash);
|
|
26
|
+
return regenerateCSSByHash(cssHash, projectSlug);
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export class CSSHandler extends BaseHandler {
|
|
@@ -48,7 +51,12 @@ export class CSSHandler extends BaseHandler {
|
|
|
48
51
|
// null — causing cross-pod cache misses. Wrap the lookup in request context
|
|
49
52
|
// so the API backend can resolve the token and project.
|
|
50
53
|
const effectiveToken = ctx.proxyToken || getEnv("VERYFRONT_API_TOKEN") || "";
|
|
51
|
-
const lookup = () =>
|
|
54
|
+
const lookup = () =>
|
|
55
|
+
runWithCacheKeyContext(cacheCtx, () =>
|
|
56
|
+
getCSSWithJITFallback(
|
|
57
|
+
cssHash,
|
|
58
|
+
ctx.projectSlug ?? ctx.projectId,
|
|
59
|
+
));
|
|
52
60
|
|
|
53
61
|
const css = ctx.projectSlug
|
|
54
62
|
? await runWithRequestContext(
|