@decocms/start 0.37.1 → 0.37.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/start",
3
- "version": "0.37.1",
3
+ "version": "0.37.3",
4
4
  "type": "module",
5
5
  "description": "Deco framework for TanStack Start - CMS bridge, admin protocol, hooks, schema generation",
6
6
  "main": "./src/index.ts",
@@ -544,66 +544,19 @@ export function cmsRouteConfig(options: CmsRouteOptions) {
544
544
  await preloadSectionComponents(keys);
545
545
  }
546
546
 
547
- // SSR: create unawaited promises for TanStack native streaming.
548
- // Each deferred section becomes a promise that TanStack streams
549
- // via SSR chunked transfer all resolved in the SAME request.
547
+ // Deferred sections use client-side IntersectionObserver loading.
548
+ // DecoPageRenderer renders skeletons (LoadingFallback) during SSR and
549
+ // loads full section content via _serverFn when scrolled into view.
550
550
  //
551
- // IMPORTANT: We call resolveDeferredSectionFull directly instead of
552
- // loadDeferredSection (server function). Server functions serialize
553
- // their return via JSON-RPC TanStack only streams promises that
554
- // are directly in the loader return, not serialized server fn results.
555
- if (isServer && page.deferredSections?.length) {
556
- const originRequest = getRequest();
557
- const serverUrl = getRequestUrl();
558
- const matcherCtx: MatcherContext = {
559
- userAgent: getRequestHeader("user-agent") ?? "",
560
- url: page.pageUrl ?? serverUrl.toString(),
561
- path: page.pagePath ?? basePath,
562
- cookies: getCookies(),
563
- request: originRequest,
564
- };
565
- const deferredRequest = new Request(page.pageUrl ?? serverUrl.toString(), {
566
- headers: originRequest.headers,
567
- });
568
-
569
- const deferredPromises: Record<string, Promise<ResolvedSection | null>> = {};
570
- for (const ds of page.deferredSections) {
571
- deferredPromises[`d_${ds.index}`] = resolveDeferredSectionFull(
572
- ds,
573
- page.pagePath ?? basePath,
574
- deferredRequest,
575
- matcherCtx,
576
- ).catch((e) => {
577
- console.error(`[CMS] Deferred section "${ds.component}" failed:`, e);
578
- return null;
579
- });
580
- }
581
- return { ...page, deferredPromises };
582
- }
583
-
584
- // Client SPA navigation: resolve all deferred sections via server
585
- // function batch and merge into resolvedSections for immediate render.
586
- if (!isServer && page.deferredSections?.length) {
587
- const resolved = await Promise.all(
588
- page.deferredSections.map((ds: DeferredSection) =>
589
- loadDeferredSection({
590
- data: {
591
- component: ds.component,
592
- rawProps: ds.rawProps,
593
- pagePath: page.pagePath ?? basePath,
594
- pageUrl: page.pageUrl,
595
- index: ds.index,
596
- },
597
- }).catch(() => null),
598
- ),
599
- );
600
- const all = [
601
- ...page.resolvedSections,
602
- ...resolved.filter((s): s is ResolvedSection => s != null),
603
- ].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
604
- return { ...page, resolvedSections: all, deferredSections: [] };
605
- }
606
-
551
+ // Previously, SSR streaming via TanStack <Await> resolved ALL deferred
552
+ // sections server-side and streamed them in the initial HTML. This caused
553
+ // the browser to load ALL product shelf images at once (~3x more image
554
+ // requests than the IntersectionObserver path). Client SPA navigation
555
+ // also blocked on await Promise.all() for all deferred sections.
556
+ //
557
+ // The IO path (DeferredSectionWrapper) only fetches section data when
558
+ // the skeleton scrolls into view, matching Fresh/Deno partial behavior
559
+ // and significantly reducing initial image load and TBT.
607
560
  return page;
608
561
  },
609
562
 
@@ -653,55 +606,8 @@ export function cmsHomeRouteConfig(options: {
653
606
  await preloadSectionComponents(keys);
654
607
  }
655
608
 
656
- // SSR: create unawaited promises for TanStack native streaming
657
- if (isServer && page.deferredSections?.length) {
658
- const originRequest = getRequest();
659
- const serverUrl = getRequestUrl();
660
- const matcherCtx: MatcherContext = {
661
- userAgent: getRequestHeader("user-agent") ?? "",
662
- url: page.pageUrl ?? serverUrl.toString(),
663
- path: "/",
664
- cookies: getCookies(),
665
- request: originRequest,
666
- };
667
- const deferredRequest = new Request(page.pageUrl ?? serverUrl.toString(), {
668
- headers: originRequest.headers,
669
- });
670
-
671
- const deferredPromises: Record<string, Promise<ResolvedSection | null>> = {};
672
- for (const ds of page.deferredSections) {
673
- deferredPromises[`d_${ds.index}`] = resolveDeferredSectionFull(
674
- ds, "/", deferredRequest, matcherCtx,
675
- ).catch((e) => {
676
- console.error(`[CMS] Deferred section "${ds.component}" failed:`, e);
677
- return null;
678
- });
679
- }
680
- return { ...page, deferredPromises };
681
- }
682
-
683
- // Client SPA navigation: resolve all deferred via server function batch
684
- if (!isServer && page.deferredSections?.length) {
685
- const resolved = await Promise.all(
686
- page.deferredSections.map((ds: DeferredSection) =>
687
- loadDeferredSection({
688
- data: {
689
- component: ds.component,
690
- rawProps: ds.rawProps,
691
- pagePath: "/",
692
- pageUrl: page.pageUrl,
693
- index: ds.index,
694
- },
695
- }).catch(() => null),
696
- ),
697
- );
698
- const all = [
699
- ...page.resolvedSections,
700
- ...resolved.filter((s): s is ResolvedSection => s != null),
701
- ].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
702
- return { ...page, resolvedSections: all, deferredSections: [] };
703
- }
704
-
609
+ // Deferred sections use client-side IntersectionObserver loading.
610
+ // See cmsRouteConfig loader for rationale.
705
611
  return page;
706
612
  },
707
613
  ...(options.pendingComponent ? { pendingComponent: options.pendingComponent } : {}),
@@ -873,10 +873,12 @@ export function createDecoWorkerEntry(
873
873
  return resp;
874
874
  }
875
875
 
876
- // Store in Cache API and return
877
- const toReturn = dressResponse(origin, "MISS");
876
+ // Clone for cache BEFORE dressResponse consumes the body stream.
877
+ // dressResponse() calls new Response(resp.body, resp) which locks
878
+ // the ReadableStream. Calling clone() on a locked body corrupts
879
+ // the stream in Workers runtime, causing Error 1101.
878
880
  storeInCache(origin);
879
- return toReturn;
881
+ return dressResponse(origin, "MISS");
880
882
  },
881
883
  };
882
884
  }