veryfront 0.1.177 → 0.1.179
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/rendering/orchestrator/pipeline.d.ts +3 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +89 -77
- 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/src/deno.js +1 -1
- package/src/src/rendering/orchestrator/pipeline.ts +147 -98
- package/src/src/utils/version-constant.ts +1 -1
package/esm/deno.js
CHANGED
|
@@ -75,6 +75,9 @@ export declare class RenderPipeline {
|
|
|
75
75
|
renderPage(slug: string, options?: RenderOptions): Promise<RenderResult>;
|
|
76
76
|
/** Resolve page data for SPA client-side navigation without rendering HTML. */
|
|
77
77
|
resolvePageData(slug: string, options?: RenderOptions): Promise<PageDataResponse>;
|
|
78
|
+
private extractMdxMetadata;
|
|
79
|
+
private resolvePageDataCss;
|
|
80
|
+
private generatePageCssFromHtml;
|
|
78
81
|
/**
|
|
79
82
|
* Build a cache key that is safe for multi-tenant + query-param aware caching.
|
|
80
83
|
* Returns null when request contains sensitive headers (Authorization/Cookie) and
|
|
@@ -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;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;
|
|
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;AAyBD,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;IAiGjC,OAAO,CAAC,uBAAuB;IAkCzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IA+P9E,+EAA+E;IACzE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAsGzE,kBAAkB;YA2ClB,kBAAkB;YAmElB,uBAAuB;IAmBrC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAetB"}
|
|
@@ -411,31 +411,7 @@ export class RenderPipeline {
|
|
|
411
411
|
for (const [layoutId, props] of dataResolution.layoutProps.entries()) {
|
|
412
412
|
layoutProps[layoutId] = props;
|
|
413
413
|
}
|
|
414
|
-
|
|
415
|
-
let headings = [];
|
|
416
|
-
if (pageType === "mdx") {
|
|
417
|
-
try {
|
|
418
|
-
const bundleResult = await this.config.pageRenderer.preparePageBundles(pageInfo, slug, undefined, {
|
|
419
|
-
...options,
|
|
420
|
-
...(Object.keys(params).length > 0 ? { params } : {}),
|
|
421
|
-
});
|
|
422
|
-
if (bundleResult.pageBundle && "frontmatter" in bundleResult.pageBundle) {
|
|
423
|
-
frontmatter =
|
|
424
|
-
bundleResult.pageBundle.frontmatter ||
|
|
425
|
-
{};
|
|
426
|
-
}
|
|
427
|
-
if (bundleResult.pageBundle && "headings" in bundleResult.pageBundle) {
|
|
428
|
-
headings = bundleResult.pageBundle.headings || [];
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
catch (error) {
|
|
432
|
-
renderPipelineLog.error("Frontmatter/headings extraction failed", {
|
|
433
|
-
slug,
|
|
434
|
-
error: error instanceof Error ? error.message : String(error),
|
|
435
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
}
|
|
414
|
+
const { frontmatter, headings } = await this.extractMdxMetadata(pageType, pageInfo, slug, options, params);
|
|
439
415
|
const layouts = layoutResult.nestedLayouts
|
|
440
416
|
.filter((l) => l.componentPath || l.path)
|
|
441
417
|
.map((l) => ({
|
|
@@ -457,58 +433,7 @@ export class RenderPipeline {
|
|
|
457
433
|
break;
|
|
458
434
|
}
|
|
459
435
|
}
|
|
460
|
-
|
|
461
|
-
let cssError;
|
|
462
|
-
const cssCacheKey = getPageCssCacheKey(options?.projectId, options?.environment, slug, projectUpdatedAt);
|
|
463
|
-
const cachedCss = getCachedPageCss(cssCacheKey);
|
|
464
|
-
if (cachedCss) {
|
|
465
|
-
css = cachedCss;
|
|
466
|
-
resolvePageDataLog.debug("CSS cache hit", { slug, cssLength: css.length });
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
try {
|
|
470
|
-
const renderResult = await withTimeout(this.renderPage(slug, {
|
|
471
|
-
...options,
|
|
472
|
-
delivery: "string",
|
|
473
|
-
skipCacheCheck: true,
|
|
474
|
-
skipCachePersist: true,
|
|
475
|
-
}), CSS_SSR_TIMEOUT_MS, `CSS SSR for ${slug}`);
|
|
476
|
-
if (renderResult?.html) {
|
|
477
|
-
css = await this.resolveCssFromRenderedHtml(renderResult.html, options?.projectSlug ?? options?.projectId);
|
|
478
|
-
if (css) {
|
|
479
|
-
resolvePageDataLog.debug("Reused SSR CSS for page data", {
|
|
480
|
-
slug,
|
|
481
|
-
cssLength: css.length,
|
|
482
|
-
source: "rendered-html-hash",
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
else {
|
|
486
|
-
const candidates = extractCandidates(renderResult.html);
|
|
487
|
-
css = (await generateTailwindCSS(undefined, candidates, {
|
|
488
|
-
projectSlug: options?.projectSlug,
|
|
489
|
-
})).css;
|
|
490
|
-
resolvePageDataLog.debug("Fell back to HTML candidate CSS generation", {
|
|
491
|
-
slug,
|
|
492
|
-
htmlLength: renderResult.html.length,
|
|
493
|
-
cssLength: css?.length || 0,
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
if (css)
|
|
497
|
-
cachePageCss(cssCacheKey, css);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
catch (error) {
|
|
501
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
502
|
-
// Surface CSS generation failures instead of silently swallowing them.
|
|
503
|
-
// This allows clients to show a warning or fall back gracefully.
|
|
504
|
-
cssError = `CSS generation failed: ${errorMessage}`;
|
|
505
|
-
resolvePageDataLog.error("CSS generation failed", {
|
|
506
|
-
slug,
|
|
507
|
-
error: errorMessage,
|
|
508
|
-
projectId: options?.projectId,
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
}
|
|
436
|
+
const { css, cssError } = await this.resolvePageDataCss(slug, options, projectUpdatedAt);
|
|
512
437
|
resolvePageDataLog.debug("Resolved page data", {
|
|
513
438
|
slug,
|
|
514
439
|
pagePath,
|
|
@@ -536,6 +461,93 @@ export class RenderPipeline {
|
|
|
536
461
|
cssError,
|
|
537
462
|
};
|
|
538
463
|
}
|
|
464
|
+
async extractMdxMetadata(pageType, pageInfo, slug, options, params) {
|
|
465
|
+
if (pageType !== "mdx") {
|
|
466
|
+
return { frontmatter: {}, headings: [] };
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const bundleResult = await this.config.pageRenderer.preparePageBundles(pageInfo, slug, undefined, {
|
|
470
|
+
...options,
|
|
471
|
+
...(Object.keys(params).length > 0 ? { params } : {}),
|
|
472
|
+
});
|
|
473
|
+
const pageBundle = bundleResult.pageBundle;
|
|
474
|
+
return {
|
|
475
|
+
frontmatter: pageBundle && "frontmatter" in pageBundle
|
|
476
|
+
? pageBundle.frontmatter || {}
|
|
477
|
+
: {},
|
|
478
|
+
headings: pageBundle && "headings" in pageBundle
|
|
479
|
+
? pageBundle.headings || []
|
|
480
|
+
: [],
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
renderPipelineLog.error("Frontmatter/headings extraction failed", {
|
|
485
|
+
slug,
|
|
486
|
+
error: error instanceof Error ? error.message : String(error),
|
|
487
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
488
|
+
});
|
|
489
|
+
return { frontmatter: {}, headings: [] };
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async resolvePageDataCss(slug, options, projectUpdatedAt) {
|
|
493
|
+
const cssCacheKey = getPageCssCacheKey(options?.projectId, options?.environment, slug, projectUpdatedAt);
|
|
494
|
+
const cachedCss = getCachedPageCss(cssCacheKey);
|
|
495
|
+
if (cachedCss) {
|
|
496
|
+
resolvePageDataLog.debug("CSS cache hit", { slug, cssLength: cachedCss.length });
|
|
497
|
+
return { css: cachedCss, cssError: undefined };
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
const renderResult = await withTimeout(this.renderPage(slug, {
|
|
501
|
+
...options,
|
|
502
|
+
delivery: "string",
|
|
503
|
+
skipCacheCheck: true,
|
|
504
|
+
skipCachePersist: true,
|
|
505
|
+
}), CSS_SSR_TIMEOUT_MS, `CSS SSR for ${slug}`);
|
|
506
|
+
if (!renderResult?.html) {
|
|
507
|
+
return { css: undefined, cssError: undefined };
|
|
508
|
+
}
|
|
509
|
+
let css = await this.resolveCssFromRenderedHtml(renderResult.html, options?.projectSlug ?? options?.projectId);
|
|
510
|
+
if (css) {
|
|
511
|
+
resolvePageDataLog.debug("Reused SSR CSS for page data", {
|
|
512
|
+
slug,
|
|
513
|
+
cssLength: css.length,
|
|
514
|
+
source: "rendered-html-hash",
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
css = await this.generatePageCssFromHtml(slug, renderResult.html, options);
|
|
519
|
+
}
|
|
520
|
+
if (css)
|
|
521
|
+
cachePageCss(cssCacheKey, css);
|
|
522
|
+
return { css, cssError: undefined };
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
526
|
+
// Surface CSS generation failures instead of silently swallowing them.
|
|
527
|
+
// This allows clients to show a warning or fall back gracefully.
|
|
528
|
+
resolvePageDataLog.error("CSS generation failed", {
|
|
529
|
+
slug,
|
|
530
|
+
error: errorMessage,
|
|
531
|
+
projectId: options?.projectId,
|
|
532
|
+
});
|
|
533
|
+
return {
|
|
534
|
+
css: undefined,
|
|
535
|
+
cssError: `CSS generation failed: ${errorMessage}`,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async generatePageCssFromHtml(slug, html, options) {
|
|
540
|
+
const candidates = extractCandidates(html);
|
|
541
|
+
const generatedCss = (await generateTailwindCSS(undefined, candidates, {
|
|
542
|
+
projectSlug: options?.projectSlug,
|
|
543
|
+
})).css;
|
|
544
|
+
resolvePageDataLog.debug("Fell back to HTML candidate CSS generation", {
|
|
545
|
+
slug,
|
|
546
|
+
htmlLength: html.length,
|
|
547
|
+
cssLength: generatedCss?.length || 0,
|
|
548
|
+
});
|
|
549
|
+
return generatedCss;
|
|
550
|
+
}
|
|
539
551
|
/**
|
|
540
552
|
* Build a cache key that is safe for multi-tenant + query-param aware caching.
|
|
541
553
|
* Returns null when request contains sensitive headers (Authorization/Cookie) and
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.179";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -111,6 +111,16 @@ interface DataResolutionResult {
|
|
|
111
111
|
layoutProps: Map<string, Record<string, unknown>>;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
interface MdxMetadataResult {
|
|
115
|
+
frontmatter: Record<string, unknown>;
|
|
116
|
+
headings: Array<{ id: string; text: string; level: number }>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface PageCssResult {
|
|
120
|
+
css: string | undefined;
|
|
121
|
+
cssError: string | undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
114
124
|
interface FetchedDataResult {
|
|
115
125
|
type: "page" | "layout";
|
|
116
126
|
id: string;
|
|
@@ -660,39 +670,13 @@ export class RenderPipeline {
|
|
|
660
670
|
layoutProps[layoutId] = props;
|
|
661
671
|
}
|
|
662
672
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
undefined,
|
|
671
|
-
{
|
|
672
|
-
...options,
|
|
673
|
-
...(Object.keys(params).length > 0 ? { params } : {}),
|
|
674
|
-
},
|
|
675
|
-
);
|
|
676
|
-
|
|
677
|
-
if (bundleResult.pageBundle && "frontmatter" in bundleResult.pageBundle) {
|
|
678
|
-
frontmatter =
|
|
679
|
-
(bundleResult.pageBundle as { frontmatter?: Record<string, unknown> }).frontmatter ||
|
|
680
|
-
{};
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
if (bundleResult.pageBundle && "headings" in bundleResult.pageBundle) {
|
|
684
|
-
headings = (bundleResult.pageBundle as {
|
|
685
|
-
headings?: Array<{ id: string; text: string; level: number }>;
|
|
686
|
-
}).headings || [];
|
|
687
|
-
}
|
|
688
|
-
} catch (error) {
|
|
689
|
-
renderPipelineLog.error("Frontmatter/headings extraction failed", {
|
|
690
|
-
slug,
|
|
691
|
-
error: error instanceof Error ? error.message : String(error),
|
|
692
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
}
|
|
673
|
+
const { frontmatter, headings } = await this.extractMdxMetadata(
|
|
674
|
+
pageType,
|
|
675
|
+
pageInfo,
|
|
676
|
+
slug,
|
|
677
|
+
options,
|
|
678
|
+
params,
|
|
679
|
+
);
|
|
696
680
|
|
|
697
681
|
const layouts = layoutResult.nestedLayouts
|
|
698
682
|
.filter((l: LayoutItem) => l.componentPath || l.path)
|
|
@@ -721,71 +705,7 @@ export class RenderPipeline {
|
|
|
721
705
|
}
|
|
722
706
|
}
|
|
723
707
|
|
|
724
|
-
|
|
725
|
-
let cssError: string | undefined;
|
|
726
|
-
const cssCacheKey = getPageCssCacheKey(
|
|
727
|
-
options?.projectId,
|
|
728
|
-
options?.environment,
|
|
729
|
-
slug,
|
|
730
|
-
projectUpdatedAt,
|
|
731
|
-
);
|
|
732
|
-
|
|
733
|
-
const cachedCss = getCachedPageCss(cssCacheKey);
|
|
734
|
-
if (cachedCss) {
|
|
735
|
-
css = cachedCss;
|
|
736
|
-
resolvePageDataLog.debug("CSS cache hit", { slug, cssLength: css.length });
|
|
737
|
-
} else {
|
|
738
|
-
try {
|
|
739
|
-
const renderResult = await withTimeout(
|
|
740
|
-
this.renderPage(slug, {
|
|
741
|
-
...options,
|
|
742
|
-
delivery: "string",
|
|
743
|
-
skipCacheCheck: true,
|
|
744
|
-
skipCachePersist: true,
|
|
745
|
-
}),
|
|
746
|
-
CSS_SSR_TIMEOUT_MS,
|
|
747
|
-
`CSS SSR for ${slug}`,
|
|
748
|
-
);
|
|
749
|
-
|
|
750
|
-
if (renderResult?.html) {
|
|
751
|
-
css = await this.resolveCssFromRenderedHtml(
|
|
752
|
-
renderResult.html,
|
|
753
|
-
options?.projectSlug ?? options?.projectId,
|
|
754
|
-
);
|
|
755
|
-
|
|
756
|
-
if (css) {
|
|
757
|
-
resolvePageDataLog.debug("Reused SSR CSS for page data", {
|
|
758
|
-
slug,
|
|
759
|
-
cssLength: css.length,
|
|
760
|
-
source: "rendered-html-hash",
|
|
761
|
-
});
|
|
762
|
-
} else {
|
|
763
|
-
const candidates = extractCandidates(renderResult.html);
|
|
764
|
-
css = (await generateTailwindCSS(undefined, candidates, {
|
|
765
|
-
projectSlug: options?.projectSlug,
|
|
766
|
-
})).css;
|
|
767
|
-
|
|
768
|
-
resolvePageDataLog.debug("Fell back to HTML candidate CSS generation", {
|
|
769
|
-
slug,
|
|
770
|
-
htmlLength: renderResult.html.length,
|
|
771
|
-
cssLength: css?.length || 0,
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
if (css) cachePageCss(cssCacheKey, css);
|
|
776
|
-
}
|
|
777
|
-
} catch (error) {
|
|
778
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
779
|
-
// Surface CSS generation failures instead of silently swallowing them.
|
|
780
|
-
// This allows clients to show a warning or fall back gracefully.
|
|
781
|
-
cssError = `CSS generation failed: ${errorMessage}`;
|
|
782
|
-
resolvePageDataLog.error("CSS generation failed", {
|
|
783
|
-
slug,
|
|
784
|
-
error: errorMessage,
|
|
785
|
-
projectId: options?.projectId,
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
}
|
|
708
|
+
const { css, cssError } = await this.resolvePageDataCss(slug, options, projectUpdatedAt);
|
|
789
709
|
|
|
790
710
|
resolvePageDataLog.debug("Resolved page data", {
|
|
791
711
|
slug,
|
|
@@ -816,6 +736,135 @@ export class RenderPipeline {
|
|
|
816
736
|
};
|
|
817
737
|
}
|
|
818
738
|
|
|
739
|
+
private async extractMdxMetadata(
|
|
740
|
+
pageType: PageDataResponse["pageType"],
|
|
741
|
+
pageInfo: Awaited<ReturnType<PageResolver["resolvePage"]>>,
|
|
742
|
+
slug: string,
|
|
743
|
+
options: RenderOptions | undefined,
|
|
744
|
+
params: Record<string, string | string[]>,
|
|
745
|
+
): Promise<MdxMetadataResult> {
|
|
746
|
+
if (pageType !== "mdx") {
|
|
747
|
+
return { frontmatter: {}, headings: [] };
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
try {
|
|
751
|
+
const bundleResult = await this.config.pageRenderer.preparePageBundles(
|
|
752
|
+
pageInfo,
|
|
753
|
+
slug,
|
|
754
|
+
undefined,
|
|
755
|
+
{
|
|
756
|
+
...options,
|
|
757
|
+
...(Object.keys(params).length > 0 ? { params } : {}),
|
|
758
|
+
},
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
const pageBundle = bundleResult.pageBundle;
|
|
762
|
+
return {
|
|
763
|
+
frontmatter: pageBundle && "frontmatter" in pageBundle
|
|
764
|
+
? (pageBundle as { frontmatter?: Record<string, unknown> }).frontmatter || {}
|
|
765
|
+
: {},
|
|
766
|
+
headings: pageBundle && "headings" in pageBundle
|
|
767
|
+
? (pageBundle as {
|
|
768
|
+
headings?: Array<{ id: string; text: string; level: number }>;
|
|
769
|
+
}).headings || []
|
|
770
|
+
: [],
|
|
771
|
+
};
|
|
772
|
+
} catch (error) {
|
|
773
|
+
renderPipelineLog.error("Frontmatter/headings extraction failed", {
|
|
774
|
+
slug,
|
|
775
|
+
error: error instanceof Error ? error.message : String(error),
|
|
776
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
777
|
+
});
|
|
778
|
+
return { frontmatter: {}, headings: [] };
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
private async resolvePageDataCss(
|
|
783
|
+
slug: string,
|
|
784
|
+
options: RenderOptions | undefined,
|
|
785
|
+
projectUpdatedAt: string | undefined,
|
|
786
|
+
): Promise<PageCssResult> {
|
|
787
|
+
const cssCacheKey = getPageCssCacheKey(
|
|
788
|
+
options?.projectId,
|
|
789
|
+
options?.environment,
|
|
790
|
+
slug,
|
|
791
|
+
projectUpdatedAt,
|
|
792
|
+
);
|
|
793
|
+
|
|
794
|
+
const cachedCss = getCachedPageCss(cssCacheKey);
|
|
795
|
+
if (cachedCss) {
|
|
796
|
+
resolvePageDataLog.debug("CSS cache hit", { slug, cssLength: cachedCss.length });
|
|
797
|
+
return { css: cachedCss, cssError: undefined };
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
try {
|
|
801
|
+
const renderResult = await withTimeout(
|
|
802
|
+
this.renderPage(slug, {
|
|
803
|
+
...options,
|
|
804
|
+
delivery: "string",
|
|
805
|
+
skipCacheCheck: true,
|
|
806
|
+
skipCachePersist: true,
|
|
807
|
+
}),
|
|
808
|
+
CSS_SSR_TIMEOUT_MS,
|
|
809
|
+
`CSS SSR for ${slug}`,
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
if (!renderResult?.html) {
|
|
813
|
+
return { css: undefined, cssError: undefined };
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
let css = await this.resolveCssFromRenderedHtml(
|
|
817
|
+
renderResult.html,
|
|
818
|
+
options?.projectSlug ?? options?.projectId,
|
|
819
|
+
);
|
|
820
|
+
|
|
821
|
+
if (css) {
|
|
822
|
+
resolvePageDataLog.debug("Reused SSR CSS for page data", {
|
|
823
|
+
slug,
|
|
824
|
+
cssLength: css.length,
|
|
825
|
+
source: "rendered-html-hash",
|
|
826
|
+
});
|
|
827
|
+
} else {
|
|
828
|
+
css = await this.generatePageCssFromHtml(slug, renderResult.html, options);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (css) cachePageCss(cssCacheKey, css);
|
|
832
|
+
return { css, cssError: undefined };
|
|
833
|
+
} catch (error) {
|
|
834
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
835
|
+
// Surface CSS generation failures instead of silently swallowing them.
|
|
836
|
+
// This allows clients to show a warning or fall back gracefully.
|
|
837
|
+
resolvePageDataLog.error("CSS generation failed", {
|
|
838
|
+
slug,
|
|
839
|
+
error: errorMessage,
|
|
840
|
+
projectId: options?.projectId,
|
|
841
|
+
});
|
|
842
|
+
return {
|
|
843
|
+
css: undefined,
|
|
844
|
+
cssError: `CSS generation failed: ${errorMessage}`,
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
private async generatePageCssFromHtml(
|
|
850
|
+
slug: string,
|
|
851
|
+
html: string,
|
|
852
|
+
options: RenderOptions | undefined,
|
|
853
|
+
): Promise<string | undefined> {
|
|
854
|
+
const candidates = extractCandidates(html);
|
|
855
|
+
const generatedCss = (await generateTailwindCSS(undefined, candidates, {
|
|
856
|
+
projectSlug: options?.projectSlug,
|
|
857
|
+
})).css;
|
|
858
|
+
|
|
859
|
+
resolvePageDataLog.debug("Fell back to HTML candidate CSS generation", {
|
|
860
|
+
slug,
|
|
861
|
+
htmlLength: html.length,
|
|
862
|
+
cssLength: generatedCss?.length || 0,
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
return generatedCss;
|
|
866
|
+
}
|
|
867
|
+
|
|
819
868
|
/**
|
|
820
869
|
* Build a cache key that is safe for multi-tenant + query-param aware caching.
|
|
821
870
|
* Returns null when request contains sensitive headers (Authorization/Cookie) and
|