veryfront 0.1.71 → 0.1.73
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/commands/files/command-help.d.ts +3 -0
- package/esm/cli/commands/files/command-help.d.ts.map +1 -0
- package/esm/cli/commands/files/command-help.js +38 -0
- package/esm/cli/commands/files/command.d.ts +105 -0
- package/esm/cli/commands/files/command.d.ts.map +1 -0
- package/esm/cli/commands/files/command.js +250 -0
- package/esm/cli/commands/files/handler.d.ts +3 -0
- package/esm/cli/commands/files/handler.d.ts.map +1 -0
- package/esm/cli/commands/files/handler.js +4 -0
- package/esm/cli/commands/files/index.d.ts +4 -0
- package/esm/cli/commands/files/index.d.ts.map +1 -0
- package/esm/cli/commands/files/index.js +2 -0
- package/esm/cli/commands/knowledge/command-help.d.ts +3 -0
- package/esm/cli/commands/knowledge/command-help.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/command-help.js +38 -0
- package/esm/cli/commands/knowledge/command.d.ts +122 -0
- package/esm/cli/commands/knowledge/command.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/command.js +382 -0
- package/esm/cli/commands/knowledge/handler.d.ts +3 -0
- package/esm/cli/commands/knowledge/handler.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/handler.js +4 -0
- package/esm/cli/commands/knowledge/index.d.ts +3 -0
- package/esm/cli/commands/knowledge/index.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/index.js +2 -0
- package/esm/cli/commands/knowledge/parser-source.d.ts +2 -0
- package/esm/cli/commands/knowledge/parser-source.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/parser-source.js +415 -0
- package/esm/cli/commands/uploads/command-help.d.ts +3 -0
- package/esm/cli/commands/uploads/command-help.d.ts.map +1 -0
- package/esm/cli/commands/uploads/command-help.js +43 -0
- package/esm/cli/commands/uploads/command.d.ts +140 -0
- package/esm/cli/commands/uploads/command.d.ts.map +1 -0
- package/esm/cli/commands/uploads/command.js +323 -0
- package/esm/cli/commands/uploads/handler.d.ts +3 -0
- package/esm/cli/commands/uploads/handler.d.ts.map +1 -0
- package/esm/cli/commands/uploads/handler.js +4 -0
- package/esm/cli/commands/uploads/index.d.ts +4 -0
- package/esm/cli/commands/uploads/index.d.ts.map +1 -0
- package/esm/cli/commands/uploads/index.js +2 -0
- package/esm/cli/help/command-definitions.d.ts.map +1 -1
- package/esm/cli/help/command-definitions.js +6 -0
- package/esm/cli/router.d.ts.map +1 -1
- package/esm/cli/router.js +6 -0
- package/esm/deno.js +1 -1
- package/esm/src/errors/error-registry.d.ts +2 -0
- package/esm/src/errors/error-registry.d.ts.map +1 -1
- package/esm/src/errors/error-registry.js +8 -0
- package/esm/src/errors/index.d.ts +1 -1
- package/esm/src/errors/index.d.ts.map +1 -1
- package/esm/src/errors/index.js +1 -1
- package/esm/src/html/html-shell-generator.d.ts.map +1 -1
- package/esm/src/html/html-shell-generator.js +6 -0
- package/esm/src/platform/compat/media-types.d.ts +5 -0
- package/esm/src/platform/compat/media-types.d.ts.map +1 -0
- package/esm/src/platform/compat/media-types.js +19 -0
- package/esm/src/platform/index.d.ts +1 -0
- package/esm/src/platform/index.d.ts.map +1 -1
- package/esm/src/platform/index.js +2 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +116 -105
- package/esm/src/server/bootstrap.js +5 -1
- package/esm/src/server/dev-server/error-overlay/error-formatter.d.ts +4 -0
- package/esm/src/server/dev-server/error-overlay/error-formatter.d.ts.map +1 -1
- package/esm/src/server/dev-server/error-overlay/error-formatter.js +15 -0
- package/esm/src/server/dev-server/error-overlay/html-template.d.ts +1 -1
- package/esm/src/server/dev-server/error-overlay/html-template.d.ts.map +1 -1
- package/esm/src/server/dev-server/error-overlay/html-template.js +131 -8
- package/esm/src/server/dev-server/error-overlay/index.d.ts +1 -1
- package/esm/src/server/dev-server/error-overlay/index.d.ts.map +1 -1
- package/esm/src/server/dev-server/error-overlay/index.js +1 -1
- package/esm/src/server/dev-server/error-overlay/overlay-renderer.d.ts +1 -1
- package/esm/src/server/dev-server/error-overlay/overlay-renderer.d.ts.map +1 -1
- package/esm/src/server/dev-server/error-overlay/overlay-renderer.js +2 -2
- package/esm/src/server/dev-server/request-handler.d.ts.map +1 -1
- package/esm/src/server/dev-server/request-handler.js +6 -2
- package/esm/src/server/services/rendering/ssr.service.d.ts.map +1 -1
- package/esm/src/server/services/rendering/ssr.service.js +9 -2
- package/esm/src/server/utils/error-html.d.ts.map +1 -1
- package/esm/src/server/utils/error-html.js +26 -6
- package/package.json +2 -1
- package/src/cli/commands/files/command-help.ts +40 -0
- package/src/cli/commands/files/command.ts +328 -0
- package/src/cli/commands/files/handler.ts +6 -0
- package/src/cli/commands/files/index.ts +19 -0
- package/src/cli/commands/knowledge/command-help.ts +40 -0
- package/src/cli/commands/knowledge/command.ts +513 -0
- package/src/cli/commands/knowledge/handler.ts +6 -0
- package/src/cli/commands/knowledge/index.ts +2 -0
- package/src/cli/commands/knowledge/parser-source.ts +415 -0
- package/src/cli/commands/uploads/command-help.ts +45 -0
- package/src/cli/commands/uploads/command.ts +465 -0
- package/src/cli/commands/uploads/handler.ts +6 -0
- package/src/cli/commands/uploads/index.ts +23 -0
- package/src/cli/help/command-definitions.ts +6 -0
- package/src/cli/router.ts +6 -0
- package/src/deno.js +1 -1
- package/src/src/errors/error-registry.ts +9 -0
- package/src/src/errors/index.ts +1 -0
- package/src/src/html/html-shell-generator.ts +9 -0
- package/src/src/platform/compat/media-types.ts +23 -0
- package/src/src/platform/index.ts +3 -0
- package/src/src/rendering/orchestrator/pipeline.ts +186 -172
- package/src/src/server/bootstrap.ts +6 -1
- package/src/src/server/dev-server/error-overlay/error-formatter.ts +21 -0
- package/src/src/server/dev-server/error-overlay/html-template.ts +139 -8
- package/src/src/server/dev-server/error-overlay/index.ts +1 -0
- package/src/src/server/dev-server/error-overlay/overlay-renderer.ts +2 -1
- package/src/src/server/dev-server/request-handler.ts +6 -2
- package/src/src/server/services/rendering/ssr.service.ts +9 -2
- package/src/src/server/utils/error-html.ts +29 -6
|
@@ -391,191 +391,205 @@ export class RenderPipeline {
|
|
|
391
391
|
);
|
|
392
392
|
timing.pageResolve = Math.round(performance.now() - pageResolveStart);
|
|
393
393
|
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const layoutResult = skipLayouts ? EMPTY_LAYOUT_RESULT : await withSpan(
|
|
398
|
-
"render.collect_layouts",
|
|
399
|
-
() => this.config.layoutOrchestrator.collectLayouts(pageInfo),
|
|
400
|
-
{ "render.slug": slug },
|
|
394
|
+
const sourceFile = extractRelativePathShared(
|
|
395
|
+
pageInfo.entity.path,
|
|
396
|
+
this.config.projectDir,
|
|
401
397
|
);
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
? { ...options.params }
|
|
411
|
-
: {};
|
|
412
|
-
let layoutDataMap = new Map<string, Record<string, unknown>>();
|
|
413
|
-
|
|
414
|
-
const dataFetchStart = performance.now();
|
|
415
|
-
if (options?.request && options?.url) {
|
|
416
|
-
await withSpan(
|
|
417
|
-
"render.data_fetching",
|
|
418
|
-
async () => {
|
|
419
|
-
try {
|
|
420
|
-
const dataResolution = await this.resolveDataFetching(
|
|
421
|
-
slug,
|
|
422
|
-
pageInfo.entity.path,
|
|
423
|
-
layoutResult.nestedLayouts,
|
|
424
|
-
options,
|
|
425
|
-
);
|
|
426
|
-
resolvedParams = dataResolution.params;
|
|
427
|
-
dataFetchingProps = Object.keys(dataResolution.pageProps).length > 0
|
|
428
|
-
? dataResolution.pageProps
|
|
429
|
-
: undefined;
|
|
430
|
-
layoutDataMap = dataResolution.layoutProps;
|
|
431
|
-
} catch (error) {
|
|
432
|
-
if (error instanceof VeryfrontError) throw error;
|
|
433
|
-
|
|
434
|
-
renderPageLog.error("Data fetching error", {
|
|
435
|
-
slug,
|
|
436
|
-
error: error instanceof Error ? error.message : String(error),
|
|
437
|
-
});
|
|
438
|
-
throw error;
|
|
439
|
-
}
|
|
440
|
-
},
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
const skipLayouts = isDotPath(slug, pageInfo.entity.path);
|
|
401
|
+
|
|
402
|
+
const layoutCollectStart = performance.now();
|
|
403
|
+
const layoutResult = skipLayouts ? EMPTY_LAYOUT_RESULT : await withSpan(
|
|
404
|
+
"render.collect_layouts",
|
|
405
|
+
() => this.config.layoutOrchestrator.collectLayouts(pageInfo),
|
|
441
406
|
{ "render.slug": slug },
|
|
442
407
|
);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
408
|
+
timing.layoutCollect = Math.round(performance.now() - layoutCollectStart);
|
|
409
|
+
|
|
410
|
+
const layoutPreloadPromise = !skipLayouts && layoutResult.nestedLayouts.length > 0
|
|
411
|
+
? this.config.layoutOrchestrator.preloadLayoutModules(layoutResult.nestedLayouts)
|
|
412
|
+
: Promise.resolve();
|
|
413
|
+
|
|
414
|
+
let dataFetchingProps: Record<string, unknown> | undefined;
|
|
415
|
+
let resolvedParams: Record<string, string | string[]> = options?.params
|
|
416
|
+
? { ...options.params }
|
|
417
|
+
: {};
|
|
418
|
+
let layoutDataMap = new Map<string, Record<string, unknown>>();
|
|
419
|
+
|
|
420
|
+
const dataFetchStart = performance.now();
|
|
421
|
+
if (options?.request && options?.url) {
|
|
422
|
+
await withSpan(
|
|
423
|
+
"render.data_fetching",
|
|
424
|
+
async () => {
|
|
425
|
+
try {
|
|
426
|
+
const dataResolution = await this.resolveDataFetching(
|
|
427
|
+
slug,
|
|
428
|
+
pageInfo.entity.path,
|
|
429
|
+
layoutResult.nestedLayouts,
|
|
430
|
+
options,
|
|
431
|
+
);
|
|
432
|
+
resolvedParams = dataResolution.params;
|
|
433
|
+
dataFetchingProps = Object.keys(dataResolution.pageProps).length > 0
|
|
434
|
+
? dataResolution.pageProps
|
|
435
|
+
: undefined;
|
|
436
|
+
layoutDataMap = dataResolution.layoutProps;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
if (error instanceof VeryfrontError) throw error;
|
|
439
|
+
|
|
440
|
+
renderPageLog.error("Data fetching error", {
|
|
441
|
+
slug,
|
|
442
|
+
error: error instanceof Error ? error.message : String(error),
|
|
443
|
+
});
|
|
444
|
+
throw error;
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
{ "render.slug": slug },
|
|
448
|
+
);
|
|
452
449
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
450
|
+
timing.dataFetch = Math.round(performance.now() - dataFetchStart);
|
|
451
|
+
|
|
452
|
+
const hasResolvedParams = Object.keys(resolvedParams).length > 0;
|
|
453
|
+
const mergedOptions = (dataFetchingProps || hasResolvedParams)
|
|
454
|
+
? {
|
|
455
|
+
...options,
|
|
456
|
+
...(hasResolvedParams ? { params: resolvedParams } : {}),
|
|
457
|
+
...(dataFetchingProps
|
|
458
|
+
? { props: { ...options?.props, ...dataFetchingProps } }
|
|
459
|
+
: {}),
|
|
460
|
+
}
|
|
461
|
+
: options;
|
|
462
|
+
|
|
463
|
+
const bundlePrepStart = performance.now();
|
|
464
|
+
const pageBundleResult = await withSpan(
|
|
465
|
+
"render.prepare_bundles",
|
|
466
|
+
() =>
|
|
467
|
+
this.config.pageRenderer.preparePageBundles(
|
|
468
|
+
pageInfo,
|
|
469
|
+
slug,
|
|
470
|
+
cacheResult?.cachedModule,
|
|
471
|
+
mergedOptions,
|
|
472
|
+
),
|
|
473
|
+
{ "render.slug": slug },
|
|
474
|
+
);
|
|
475
|
+
timing.bundlePrep = Math.round(performance.now() - bundlePrepStart);
|
|
468
476
|
|
|
469
|
-
|
|
477
|
+
if (pageBundleResult.scriptResult) return pageBundleResult.scriptResult;
|
|
470
478
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
479
|
+
if (!pageBundleResult.pageElement || !pageBundleResult.pageBundle) {
|
|
480
|
+
throw RENDER_ERROR.create({
|
|
481
|
+
detail: "Failed to prepare page bundle",
|
|
482
|
+
context: { slug },
|
|
483
|
+
});
|
|
484
|
+
}
|
|
477
485
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
),
|
|
504
|
-
{ "render.slug": slug, "render.layout_count": layoutResult.nestedLayouts.length },
|
|
505
|
-
);
|
|
506
|
-
timing.layoutApply = Math.round(performance.now() - layoutApplyStart);
|
|
507
|
-
|
|
508
|
-
// Snapshot CSS imports collected during module loading (before SSR rendering).
|
|
509
|
-
// These are passed to the HTML generator to be included in the output.
|
|
510
|
-
const collectedCSSImports = getCSSImports();
|
|
511
|
-
|
|
512
|
-
const ssrStart = performance.now();
|
|
513
|
-
const ssrResult = await withSpan(
|
|
514
|
-
"render.ssr",
|
|
515
|
-
() =>
|
|
516
|
-
withTimeoutThrow(
|
|
517
|
-
this.config.ssrOrchestrator.performSSRRendering(
|
|
518
|
-
wrappedElement,
|
|
519
|
-
{
|
|
520
|
-
pageInfo,
|
|
521
|
-
pageBundle,
|
|
522
|
-
layoutBundle: layoutResult.layoutBundle,
|
|
523
|
-
nestedLayouts: layoutResult.nestedLayouts,
|
|
524
|
-
collectedMetadata: pageBundleResult.collectedMetadata,
|
|
525
|
-
slug,
|
|
526
|
-
cssImports: collectedCSSImports,
|
|
527
|
-
},
|
|
528
|
-
mergedOptions,
|
|
486
|
+
const { pageElement, pageBundle } = pageBundleResult;
|
|
487
|
+
|
|
488
|
+
const mergedFrontmatter = {
|
|
489
|
+
...pageInfo.entity.frontmatter,
|
|
490
|
+
...(pageBundle as MdxBundle).frontmatter,
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
const headings = (pageBundle as PageBundle).headings || [];
|
|
494
|
+
|
|
495
|
+
await layoutPreloadPromise;
|
|
496
|
+
|
|
497
|
+
const layoutApplyStart = performance.now();
|
|
498
|
+
const wrappedElement = await withSpan(
|
|
499
|
+
"render.apply_layouts",
|
|
500
|
+
() =>
|
|
501
|
+
this.config.layoutOrchestrator.applyLayoutsAndWrappers(
|
|
502
|
+
pageElement,
|
|
503
|
+
pageInfo,
|
|
504
|
+
layoutResult.layoutBundle,
|
|
505
|
+
layoutResult.nestedLayouts,
|
|
506
|
+
layoutDataMap,
|
|
507
|
+
options?.url,
|
|
508
|
+
mergedFrontmatter,
|
|
509
|
+
headings,
|
|
510
|
+
options?.projectSlug,
|
|
529
511
|
),
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
512
|
+
{ "render.slug": slug, "render.layout_count": layoutResult.nestedLayouts.length },
|
|
513
|
+
);
|
|
514
|
+
timing.layoutApply = Math.round(performance.now() - layoutApplyStart);
|
|
515
|
+
|
|
516
|
+
// Snapshot CSS imports collected during module loading (before SSR rendering).
|
|
517
|
+
// These are passed to the HTML generator to be included in the output.
|
|
518
|
+
const collectedCSSImports = getCSSImports();
|
|
519
|
+
|
|
520
|
+
const ssrStart = performance.now();
|
|
521
|
+
const ssrResult = await withSpan(
|
|
522
|
+
"render.ssr",
|
|
523
|
+
() =>
|
|
524
|
+
withTimeoutThrow(
|
|
525
|
+
this.config.ssrOrchestrator.performSSRRendering(
|
|
526
|
+
wrappedElement,
|
|
527
|
+
{
|
|
528
|
+
pageInfo,
|
|
529
|
+
pageBundle,
|
|
530
|
+
layoutBundle: layoutResult.layoutBundle,
|
|
531
|
+
nestedLayouts: layoutResult.nestedLayouts,
|
|
532
|
+
collectedMetadata: pageBundleResult.collectedMetadata,
|
|
533
|
+
slug,
|
|
534
|
+
cssImports: collectedCSSImports,
|
|
535
|
+
},
|
|
536
|
+
mergedOptions,
|
|
537
|
+
),
|
|
538
|
+
SSR_RENDER_TIMEOUT_MS,
|
|
539
|
+
`SSR rendering for ${slug}`,
|
|
540
|
+
),
|
|
541
|
+
{ "render.slug": slug, "render.delivery": mergedOptions?.delivery || "full" },
|
|
542
|
+
);
|
|
543
|
+
timing.ssr = Math.round(performance.now() - ssrStart);
|
|
536
544
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
545
|
+
if (collectedCSSImports.length > 0) {
|
|
546
|
+
renderPipelineLog.debug("CSS imports collected for HTML generation", {
|
|
547
|
+
slug,
|
|
548
|
+
count: collectedCSSImports.length,
|
|
549
|
+
paths: collectedCSSImports.map((p) => p.split("/").pop()),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
544
552
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
553
|
+
const pageModule = pageBundleResult.clientModuleCode && pageBundleResult.pageModuleType
|
|
554
|
+
? {
|
|
555
|
+
slug,
|
|
556
|
+
code: pageBundleResult.clientModuleCode,
|
|
557
|
+
type: pageBundleResult.pageModuleType,
|
|
558
|
+
}
|
|
559
|
+
: undefined;
|
|
560
|
+
|
|
561
|
+
const result: RenderResult = {
|
|
562
|
+
html: ssrResult.fullHtml,
|
|
563
|
+
frontmatter: (pageBundleResult.pageBundle as MdxBundle).frontmatter || {},
|
|
564
|
+
headings: pageBundleResult.pageBundle.headings || [],
|
|
565
|
+
nodeMap: pageBundleResult.pageBundle.nodeMap,
|
|
566
|
+
stream: ssrResult.finalStream,
|
|
567
|
+
ssrHash: ssrResult.ssrHash,
|
|
568
|
+
...(pageModule ? { pageModule } : {}),
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
if (shouldCache && !options?.skipCachePersist) {
|
|
572
|
+
void this.config.cacheCoordinator.persistResult(result, slug, cacheKey).catch(
|
|
573
|
+
(error) => {
|
|
574
|
+
renderPipelineLog.error("Cache persist failed", {
|
|
575
|
+
slug,
|
|
576
|
+
error: error instanceof Error ? error.message : String(error),
|
|
577
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
);
|
|
550
581
|
}
|
|
551
|
-
: undefined;
|
|
552
|
-
|
|
553
|
-
const result: RenderResult = {
|
|
554
|
-
html: ssrResult.fullHtml,
|
|
555
|
-
frontmatter: (pageBundleResult.pageBundle as MdxBundle).frontmatter || {},
|
|
556
|
-
headings: pageBundleResult.pageBundle.headings || [],
|
|
557
|
-
nodeMap: pageBundleResult.pageBundle.nodeMap,
|
|
558
|
-
stream: ssrResult.finalStream,
|
|
559
|
-
ssrHash: ssrResult.ssrHash,
|
|
560
|
-
...(pageModule ? { pageModule } : {}),
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
if (shouldCache && !options?.skipCachePersist) {
|
|
564
|
-
void this.config.cacheCoordinator.persistResult(result, slug, cacheKey).catch(
|
|
565
|
-
(error) => {
|
|
566
|
-
renderPipelineLog.error("Cache persist failed", {
|
|
567
|
-
slug,
|
|
568
|
-
error: error instanceof Error ? error.message : String(error),
|
|
569
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
570
|
-
});
|
|
571
|
-
},
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
582
|
|
|
575
|
-
|
|
576
|
-
|
|
583
|
+
timing.total = Math.round(performance.now() - pipelineStartTime);
|
|
584
|
+
renderPipelineLog.debug("Complete", { slug, timing });
|
|
577
585
|
|
|
578
|
-
|
|
586
|
+
return result;
|
|
587
|
+
} catch (error) {
|
|
588
|
+
if (error instanceof Error) {
|
|
589
|
+
(error as Error & { sourceFile?: string }).sourceFile = sourceFile;
|
|
590
|
+
}
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
579
593
|
});
|
|
580
594
|
return result;
|
|
581
595
|
},
|
|
@@ -278,7 +278,12 @@ function validateProductionEnvironment(_adapter: RuntimeAdapter): void {
|
|
|
278
278
|
);
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
if (!controlPlanePublicKey) {
|
|
281
|
+
if (!controlPlanePublicKey && nodeEnv === "development") {
|
|
282
|
+
logger.warn(
|
|
283
|
+
"[Bootstrap:Prod] CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY is not set. " +
|
|
284
|
+
"Channel dispatch verification will be unavailable (local dev mode).",
|
|
285
|
+
);
|
|
286
|
+
} else if (!controlPlanePublicKey) {
|
|
282
287
|
logger.error(
|
|
283
288
|
"[Bootstrap:Prod] CRITICAL: CHANNEL_DISPATCH_SIGNING_PUBLIC_KEY is not set in proxy mode. " +
|
|
284
289
|
"Hosted runtimes cannot verify control-plane requests without it.",
|
|
@@ -53,6 +53,27 @@ export function getSuggestion(error: Error): string | undefined {
|
|
|
53
53
|
return undefined;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export function parseErrorLocation(
|
|
57
|
+
error: Error,
|
|
58
|
+
file: string,
|
|
59
|
+
): { line?: number; column?: number } {
|
|
60
|
+
if (!error.stack || !file) return {};
|
|
61
|
+
|
|
62
|
+
const lines = error.stack.split("\n");
|
|
63
|
+
|
|
64
|
+
// First try to match the known source file
|
|
65
|
+
for (const stackLine of lines) {
|
|
66
|
+
if (!stackLine.includes(file)) continue;
|
|
67
|
+
|
|
68
|
+
const match = stackLine.match(/:(\d+):(\d+)\)?$/);
|
|
69
|
+
if (match) {
|
|
70
|
+
return { line: Number(match[1]), column: Number(match[2]) };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
|
|
56
77
|
export function formatErrorType(type: ErrorType): string {
|
|
57
78
|
const firstChar = type[0];
|
|
58
79
|
if (!firstChar) return "";
|
|
@@ -10,6 +10,13 @@ const WS_RECONNECT_MAX_DELAY_MS = 5_000;
|
|
|
10
10
|
/** Maximum number of WebSocket reconnection attempts before giving up */
|
|
11
11
|
const WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
12
12
|
|
|
13
|
+
/** JSON.stringify that escapes `<` to prevent `</script>` breaking inline scripts */
|
|
14
|
+
function jsonForScript(value: unknown): string {
|
|
15
|
+
const json = JSON.stringify(value);
|
|
16
|
+
// JSON.stringify(undefined) returns undefined (not a string)
|
|
17
|
+
return json === undefined ? "undefined" : json.replace(/</g, "\\u003c");
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
export function generateRuntimeScript(): string {
|
|
14
21
|
return `
|
|
15
22
|
// Veryfront Error Overlay Runtime
|
|
@@ -110,21 +117,76 @@ export function generateRuntimeScript(): string {
|
|
|
110
117
|
</div>
|
|
111
118
|
|
|
112
119
|
<button type="button" onclick="document.getElementById('veryfront-error-overlay').remove()" style="
|
|
113
|
-
background: #
|
|
114
|
-
border:
|
|
115
|
-
color: #
|
|
116
|
-
padding:
|
|
117
|
-
border-radius:
|
|
120
|
+
background: #fff;
|
|
121
|
+
border: none;
|
|
122
|
+
color: #000;
|
|
123
|
+
padding: 10px 20px;
|
|
124
|
+
border-radius: 9999px;
|
|
118
125
|
cursor: pointer;
|
|
119
126
|
font-family: inherit;
|
|
120
127
|
">
|
|
121
128
|
Dismiss
|
|
122
129
|
</button>
|
|
130
|
+
\${window.__VF_PROJECT_SLUG__ ? \`
|
|
131
|
+
<button type="button" id="vf-fix-btn-runtime" style="
|
|
132
|
+
background: transparent;
|
|
133
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
134
|
+
color: rgba(255, 255, 255, 0.7);
|
|
135
|
+
padding: 10px 20px;
|
|
136
|
+
border-radius: 9999px;
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
font-family: inherit;
|
|
139
|
+
margin-left: 8px;
|
|
140
|
+
">
|
|
141
|
+
Fix in Veryfront
|
|
142
|
+
</button>
|
|
143
|
+
\` : ''}
|
|
123
144
|
</div>
|
|
124
145
|
</div>
|
|
125
146
|
\`;
|
|
126
147
|
|
|
127
148
|
document.body.appendChild(overlay);
|
|
149
|
+
|
|
150
|
+
// Notify Studio of runtime error
|
|
151
|
+
if (window.parent !== window) {
|
|
152
|
+
try {
|
|
153
|
+
window.parent.postMessage({
|
|
154
|
+
action: 'runtimeError',
|
|
155
|
+
url: window.location.href,
|
|
156
|
+
hasError: true,
|
|
157
|
+
errors: [{
|
|
158
|
+
type: 'error',
|
|
159
|
+
message: (errorInfo.error && errorInfo.error.message) || 'Unknown error',
|
|
160
|
+
file: errorInfo.file ? String(errorInfo.file) : undefined,
|
|
161
|
+
line: errorInfo.line ? Number(errorInfo.line) : undefined,
|
|
162
|
+
column: errorInfo.column ? Number(errorInfo.column) : undefined
|
|
163
|
+
}]
|
|
164
|
+
}, '*');
|
|
165
|
+
} catch (e) { /* postMessage may fail */ }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (window.__VF_PROJECT_SLUG__) {
|
|
169
|
+
var fixBtn = document.getElementById('vf-fix-btn-runtime');
|
|
170
|
+
if (fixBtn) {
|
|
171
|
+
fixBtn.addEventListener('click', function() {
|
|
172
|
+
var rawName = (errorInfo.error && errorInfo.error.name) || 'Error';
|
|
173
|
+
var rawMessage = (errorInfo.error && errorInfo.error.message) || 'Unknown error';
|
|
174
|
+
var rawFile = errorInfo.file ? String(errorInfo.file) : null;
|
|
175
|
+
var rawLine = errorInfo.line ? String(errorInfo.line) : null;
|
|
176
|
+
var rawColumn = errorInfo.column ? String(errorInfo.column) : null;
|
|
177
|
+
var loc = rawFile ? rawFile + (rawLine ? ':' + rawLine : '') + (rawColumn ? ':' + rawColumn : '') : null;
|
|
178
|
+
var bt = String.fromCharCode(96);
|
|
179
|
+
var prompt = 'Find and fix the following error' +
|
|
180
|
+
(loc ? ' in ' + bt + loc + bt : '') +
|
|
181
|
+
'\\n\\n' + bt + rawMessage + bt;
|
|
182
|
+
if (window.parent !== window) {
|
|
183
|
+
window.parent.postMessage({ action: 'chatMessage', prompt: prompt }, '*');
|
|
184
|
+
} else {
|
|
185
|
+
window.open('https://veryfront.com/projects/' + window.__VF_PROJECT_SLUG__ + '?prompt=' + encodeURIComponent(prompt));
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
128
190
|
};
|
|
129
191
|
|
|
130
192
|
window.addEventListener('error', (event) => {
|
|
@@ -146,7 +208,11 @@ export function generateRuntimeScript(): string {
|
|
|
146
208
|
`;
|
|
147
209
|
}
|
|
148
210
|
|
|
149
|
-
export function generateErrorHTML(
|
|
211
|
+
export function generateErrorHTML(
|
|
212
|
+
errorInfo: ErrorInfo,
|
|
213
|
+
suggestion?: string,
|
|
214
|
+
projectSlug?: string,
|
|
215
|
+
): string {
|
|
150
216
|
const errorType = escapeHtml(formatErrorType(errorInfo.type));
|
|
151
217
|
const errorName = escapeHtml(errorInfo.error.name);
|
|
152
218
|
const errorMessage = escapeHtml(errorInfo.error.message);
|
|
@@ -182,6 +248,10 @@ export function generateErrorHTML(errorInfo: ErrorInfo, suggestion?: string): st
|
|
|
182
248
|
`
|
|
183
249
|
: "";
|
|
184
250
|
|
|
251
|
+
const fixButtonHtml = projectSlug
|
|
252
|
+
? `<button type="button" id="vf-fix-btn" class="btn btn-fix">Fix in Veryfront</button>`
|
|
253
|
+
: "";
|
|
254
|
+
|
|
185
255
|
return `
|
|
186
256
|
<!DOCTYPE html>
|
|
187
257
|
<html>
|
|
@@ -253,6 +323,32 @@ export function generateErrorHTML(errorInfo: ErrorInfo, suggestion?: string): st
|
|
|
253
323
|
overflow-x: auto;
|
|
254
324
|
font-size: 12px;
|
|
255
325
|
}
|
|
326
|
+
.btn {
|
|
327
|
+
padding: 10px 20px;
|
|
328
|
+
border-radius: 9999px;
|
|
329
|
+
cursor: pointer;
|
|
330
|
+
font-family: inherit;
|
|
331
|
+
font-size: 14px;
|
|
332
|
+
border: none;
|
|
333
|
+
}
|
|
334
|
+
.btn-dismiss {
|
|
335
|
+
background: #fff;
|
|
336
|
+
color: #000;
|
|
337
|
+
}
|
|
338
|
+
.btn-dismiss:hover {
|
|
339
|
+
background: #e5e5e5;
|
|
340
|
+
}
|
|
341
|
+
.btn-fix {
|
|
342
|
+
background: transparent;
|
|
343
|
+
color: rgba(255, 255, 255, 0.7);
|
|
344
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
345
|
+
margin-left: 8px;
|
|
346
|
+
}
|
|
347
|
+
.btn-fix:hover {
|
|
348
|
+
background: rgba(255, 255, 255, 0.1);
|
|
349
|
+
color: #fff;
|
|
350
|
+
border-color: rgba(255, 255, 255, 0.4);
|
|
351
|
+
}
|
|
256
352
|
</style>
|
|
257
353
|
</head>
|
|
258
354
|
<body>
|
|
@@ -267,8 +363,36 @@ export function generateErrorHTML(errorInfo: ErrorInfo, suggestion?: string): st
|
|
|
267
363
|
${suggestionSection}
|
|
268
364
|
${stackSection}
|
|
269
365
|
</div>
|
|
366
|
+
${fixButtonHtml}
|
|
270
367
|
</div>
|
|
271
|
-
<script
|
|
368
|
+
<script>${
|
|
369
|
+
projectSlug
|
|
370
|
+
? `
|
|
371
|
+
(function() {
|
|
372
|
+
var slug = ${jsonForScript(projectSlug)};
|
|
373
|
+
var errorName = ${jsonForScript(errorInfo.error.name)};
|
|
374
|
+
var errorMessage = ${jsonForScript(errorInfo.error.message)};
|
|
375
|
+
var errorFile = ${jsonForScript(errorInfo.file ?? null)};
|
|
376
|
+
var errorLine = ${jsonForScript(errorInfo.line ?? null)};
|
|
377
|
+
var errorColumn = ${jsonForScript(errorInfo.column ?? null)};
|
|
378
|
+
var btn = document.getElementById('vf-fix-btn');
|
|
379
|
+
if (btn) {
|
|
380
|
+
btn.addEventListener('click', function() {
|
|
381
|
+
var loc = errorFile ? errorFile + (errorLine ? ':' + errorLine : '') + (errorColumn ? ':' + errorColumn : '') : null;
|
|
382
|
+
var bt = String.fromCharCode(96);
|
|
383
|
+
var prompt = 'Find and fix the following error' +
|
|
384
|
+
(loc ? ' in ' + bt + loc + bt : '') +
|
|
385
|
+
'\\n\\n' + bt + errorMessage + bt;
|
|
386
|
+
if (window.parent !== window) {
|
|
387
|
+
window.parent.postMessage({ action: 'chatMessage', prompt: prompt }, '*');
|
|
388
|
+
} else {
|
|
389
|
+
window.open('https://veryfront.com/projects/' + slug + '?prompt=' + encodeURIComponent(prompt));
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
})();`
|
|
394
|
+
: ""
|
|
395
|
+
}
|
|
272
396
|
// Notify Studio (parent) that page has loaded with an error
|
|
273
397
|
// This hides the loading spinner in Studio's preview iframe
|
|
274
398
|
if (window.parent !== window) {
|
|
@@ -277,7 +401,14 @@ export function generateErrorHTML(errorInfo: ErrorInfo, suggestion?: string): st
|
|
|
277
401
|
action: 'appUpdated',
|
|
278
402
|
isInitialLoad: true,
|
|
279
403
|
hasError: true,
|
|
280
|
-
url: window.location.href
|
|
404
|
+
url: window.location.href,
|
|
405
|
+
errors: [{
|
|
406
|
+
type: 'error',
|
|
407
|
+
message: ${jsonForScript(errorInfo.error.message)},
|
|
408
|
+
file: ${jsonForScript(errorInfo.file || undefined)},
|
|
409
|
+
line: ${errorInfo.line ? String(errorInfo.line) : "undefined"},
|
|
410
|
+
column: ${errorInfo.column ? String(errorInfo.column) : "undefined"}
|
|
411
|
+
}]
|
|
281
412
|
}, '*');
|
|
282
413
|
} catch (e) { /* postMessage may fail in cross-origin iframes */ }
|
|
283
414
|
}
|
|
@@ -4,10 +4,11 @@ import { generateErrorHTML, generateRuntimeScript } from "./html-template.js";
|
|
|
4
4
|
export const ErrorOverlay = {
|
|
5
5
|
getRuntime: generateRuntimeScript,
|
|
6
6
|
getSuggestion,
|
|
7
|
-
createHTML(errorInfo: ErrorInfo): string {
|
|
7
|
+
createHTML(errorInfo: ErrorInfo, projectSlug?: string): string {
|
|
8
8
|
return generateErrorHTML(
|
|
9
9
|
errorInfo,
|
|
10
10
|
errorInfo.suggestion ?? getSuggestion(errorInfo.error),
|
|
11
|
+
projectSlug,
|
|
11
12
|
);
|
|
12
13
|
},
|
|
13
14
|
};
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
11
11
|
import type { VeryfrontConfig } from "../../config/index.js";
|
|
12
12
|
import { clearConfigCache } from "../../config/index.js";
|
|
13
|
-
import { ErrorOverlay } from "./error-overlay/index.js";
|
|
13
|
+
import { ErrorOverlay, parseErrorLocation } from "./error-overlay/index.js";
|
|
14
14
|
import { createResponseBuilder } from "../../security/index.js";
|
|
15
15
|
import { resetApiHandler } from "../handlers/request/api/pages-api-handler.js";
|
|
16
16
|
import { clearLayoutDiscoveryCache } from "../../rendering/layouts/index.js";
|
|
@@ -169,11 +169,15 @@ export class RequestHandler {
|
|
|
169
169
|
const err = error as Error;
|
|
170
170
|
getErrorCollector().addRuntimeError(err.message, err.stack, { source: "request-handler" });
|
|
171
171
|
|
|
172
|
+
const sourceFile = (err as Error & { sourceFile?: string }).sourceFile;
|
|
173
|
+
const location = sourceFile ? parseErrorLocation(err, sourceFile) : {};
|
|
172
174
|
return new dntShim.Response(
|
|
173
175
|
ErrorOverlay.createHTML({
|
|
174
176
|
type: "runtime",
|
|
175
177
|
error: err,
|
|
176
|
-
|
|
178
|
+
...(sourceFile ? { file: sourceFile } : {}),
|
|
179
|
+
...location,
|
|
180
|
+
}, this.defaultProjectSlug),
|
|
177
181
|
{
|
|
178
182
|
status: HTTP_SERVER_ERROR,
|
|
179
183
|
headers: { "content-type": "text/html; charset=utf-8" },
|