qlara 0.1.11 → 0.1.12

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/dist/aws.cjs CHANGED
@@ -462,8 +462,25 @@ function buildTemplate(config) {
462
462
  }
463
463
  }
464
464
  ]
465
- }
466
- // No CustomErrorResponses the edge handler manages 403/404
465
+ },
466
+ // Serve the framework's 404 page (e.g., Next.js not-found.ts → 404.html)
467
+ // when S3 returns 403 (missing file) or 404. This covers:
468
+ // - Unknown routes not in the manifest
469
+ // - Validation failures (renderer abandons, no file uploaded)
470
+ CustomErrorResponses: [
471
+ {
472
+ ErrorCode: 403,
473
+ ResponseCode: 404,
474
+ ResponsePagePath: "/404.html",
475
+ ErrorCachingMinTTL: 10
476
+ },
477
+ {
478
+ ErrorCode: 404,
479
+ ResponseCode: 404,
480
+ ResponsePagePath: "/404.html",
481
+ ErrorCachingMinTTL: 10
482
+ }
483
+ ]
467
484
  }
468
485
  }
469
486
  }
@@ -665,33 +682,55 @@ function generateFallbackFromTemplate(templateHtml, routePattern) {
665
682
  );
666
683
  return fallback;
667
684
  }
685
+ function findTemplateForRoute(buildDir, routePattern) {
686
+ const segments = routePattern.replace(/^\//, "").split("/");
687
+ function walk(currentDir, segmentIndex) {
688
+ if (segmentIndex >= segments.length) return null;
689
+ if (!(0, import_node_fs3.existsSync)(currentDir)) return null;
690
+ const segment = segments[segmentIndex];
691
+ const isLast = segmentIndex === segments.length - 1;
692
+ const isDynamic = segment.startsWith(":");
693
+ if (isLast) {
694
+ const files = (0, import_node_fs3.readdirSync)(currentDir).filter(
695
+ (f) => f.endsWith(".html") && f !== FALLBACK_FILENAME
696
+ );
697
+ return files.length > 0 ? (0, import_node_path3.join)(currentDir, files[0]) : null;
698
+ }
699
+ if (isDynamic) {
700
+ const entries = (0, import_node_fs3.readdirSync)(currentDir, { withFileTypes: true });
701
+ for (const entry of entries) {
702
+ if (entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".")) {
703
+ const result = walk((0, import_node_path3.join)(currentDir, entry.name), segmentIndex + 1);
704
+ if (result) return result;
705
+ }
706
+ }
707
+ return null;
708
+ }
709
+ return walk((0, import_node_path3.join)(currentDir, segment), segmentIndex + 1);
710
+ }
711
+ return walk(buildDir, 0);
712
+ }
668
713
  function generateFallbacks(buildDir, routes) {
669
714
  const generated = [];
670
715
  for (const route of routes) {
671
- const parts = route.pattern.replace(/^\//, "").split("/");
672
- const dirParts = parts.filter((p) => !p.startsWith(":"));
673
- const routeDir = (0, import_node_path3.join)(buildDir, ...dirParts);
674
- if (!(0, import_node_fs3.existsSync)(routeDir)) {
675
- console.warn(
676
- `[qlara] Warning: No output directory for route ${route.pattern} at ${routeDir}`
677
- );
678
- continue;
679
- }
680
- const files = (0, import_node_fs3.readdirSync)(routeDir).filter(
681
- (f) => f.endsWith(".html") && f !== FALLBACK_FILENAME
682
- );
683
- if (files.length === 0) {
716
+ const templatePath = findTemplateForRoute(buildDir, route.pattern);
717
+ if (!templatePath) {
684
718
  console.warn(
685
- `[qlara] Warning: No HTML files in ${routeDir} to create fallback template`
719
+ `[qlara] Warning: No HTML template found for route ${route.pattern}`
686
720
  );
687
721
  continue;
688
722
  }
689
- const templatePath = (0, import_node_path3.join)(routeDir, files[0]);
690
723
  const templateHtml = (0, import_node_fs3.readFileSync)(templatePath, "utf-8");
691
724
  const fallbackHtml = generateFallbackFromTemplate(templateHtml, route.pattern);
692
- const fallbackPath = (0, import_node_path3.join)(routeDir, FALLBACK_FILENAME);
725
+ const parts = route.pattern.replace(/^\//, "").split("/");
726
+ const dirParts = parts.filter((p) => !p.startsWith(":"));
727
+ const fallbackDir = dirParts.length > 0 ? (0, import_node_path3.join)(buildDir, ...dirParts) : buildDir;
728
+ if (!(0, import_node_fs3.existsSync)(fallbackDir)) {
729
+ (0, import_node_fs3.mkdirSync)(fallbackDir, { recursive: true });
730
+ }
731
+ const fallbackPath = (0, import_node_path3.join)(fallbackDir, FALLBACK_FILENAME);
693
732
  (0, import_node_fs3.writeFileSync)(fallbackPath, fallbackHtml);
694
- const relativePath = (0, import_node_path3.join)(...dirParts, FALLBACK_FILENAME);
733
+ const relativePath = dirParts.length > 0 ? (0, import_node_path3.join)(...dirParts, FALLBACK_FILENAME) : FALLBACK_FILENAME;
695
734
  generated.push(relativePath);
696
735
  console.log(`[qlara] Generated fallback: ${relativePath}`);
697
736
  }
@@ -789,6 +828,18 @@ async function updateCloudFrontEdgeVersion(cf, distributionId, newVersionArn) {
789
828
  const qlaraPolicyId = await ensureCachePolicy(cf);
790
829
  config.DefaultCacheBehavior.CachePolicyId = qlaraPolicyId;
791
830
  }
831
+ const hasErrorResponses = config.CustomErrorResponses?.Items?.some(
832
+ (r) => r.ErrorCode === 403
833
+ );
834
+ if (!hasErrorResponses) {
835
+ config.CustomErrorResponses = {
836
+ Quantity: 2,
837
+ Items: [
838
+ { ErrorCode: 403, ResponseCode: "404", ResponsePagePath: "/404.html", ErrorCachingMinTTL: 10 },
839
+ { ErrorCode: 404, ResponseCode: "404", ResponsePagePath: "/404.html", ErrorCachingMinTTL: 10 }
840
+ ]
841
+ };
842
+ }
792
843
  await cf.send(
793
844
  new import_client_cloudfront.UpdateDistributionCommand({
794
845
  Id: distributionId,
package/dist/aws.js CHANGED
@@ -459,8 +459,25 @@ function buildTemplate(config) {
459
459
  }
460
460
  }
461
461
  ]
462
- }
463
- // No CustomErrorResponses the edge handler manages 403/404
462
+ },
463
+ // Serve the framework's 404 page (e.g., Next.js not-found.ts → 404.html)
464
+ // when S3 returns 403 (missing file) or 404. This covers:
465
+ // - Unknown routes not in the manifest
466
+ // - Validation failures (renderer abandons, no file uploaded)
467
+ CustomErrorResponses: [
468
+ {
469
+ ErrorCode: 403,
470
+ ResponseCode: 404,
471
+ ResponsePagePath: "/404.html",
472
+ ErrorCachingMinTTL: 10
473
+ },
474
+ {
475
+ ErrorCode: 404,
476
+ ResponseCode: 404,
477
+ ResponsePagePath: "/404.html",
478
+ ErrorCachingMinTTL: 10
479
+ }
480
+ ]
464
481
  }
465
482
  }
466
483
  }
@@ -592,7 +609,7 @@ async function bundleRenderer(routeFile, cacheTtl = 3600, framework) {
592
609
  var STACK_NAME_PREFIX = "qlara";
593
610
 
594
611
  // src/fallback.ts
595
- import { readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
612
+ import { readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
596
613
  import { join as join3 } from "path";
597
614
  var FALLBACK_FILENAME = "_fallback.html";
598
615
  function paramPlaceholder(paramName) {
@@ -661,33 +678,55 @@ function generateFallbackFromTemplate(templateHtml, routePattern) {
661
678
  );
662
679
  return fallback;
663
680
  }
681
+ function findTemplateForRoute(buildDir, routePattern) {
682
+ const segments = routePattern.replace(/^\//, "").split("/");
683
+ function walk(currentDir, segmentIndex) {
684
+ if (segmentIndex >= segments.length) return null;
685
+ if (!existsSync2(currentDir)) return null;
686
+ const segment = segments[segmentIndex];
687
+ const isLast = segmentIndex === segments.length - 1;
688
+ const isDynamic = segment.startsWith(":");
689
+ if (isLast) {
690
+ const files = readdirSync2(currentDir).filter(
691
+ (f) => f.endsWith(".html") && f !== FALLBACK_FILENAME
692
+ );
693
+ return files.length > 0 ? join3(currentDir, files[0]) : null;
694
+ }
695
+ if (isDynamic) {
696
+ const entries = readdirSync2(currentDir, { withFileTypes: true });
697
+ for (const entry of entries) {
698
+ if (entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".")) {
699
+ const result = walk(join3(currentDir, entry.name), segmentIndex + 1);
700
+ if (result) return result;
701
+ }
702
+ }
703
+ return null;
704
+ }
705
+ return walk(join3(currentDir, segment), segmentIndex + 1);
706
+ }
707
+ return walk(buildDir, 0);
708
+ }
664
709
  function generateFallbacks(buildDir, routes) {
665
710
  const generated = [];
666
711
  for (const route of routes) {
667
- const parts = route.pattern.replace(/^\//, "").split("/");
668
- const dirParts = parts.filter((p) => !p.startsWith(":"));
669
- const routeDir = join3(buildDir, ...dirParts);
670
- if (!existsSync2(routeDir)) {
671
- console.warn(
672
- `[qlara] Warning: No output directory for route ${route.pattern} at ${routeDir}`
673
- );
674
- continue;
675
- }
676
- const files = readdirSync2(routeDir).filter(
677
- (f) => f.endsWith(".html") && f !== FALLBACK_FILENAME
678
- );
679
- if (files.length === 0) {
712
+ const templatePath = findTemplateForRoute(buildDir, route.pattern);
713
+ if (!templatePath) {
680
714
  console.warn(
681
- `[qlara] Warning: No HTML files in ${routeDir} to create fallback template`
715
+ `[qlara] Warning: No HTML template found for route ${route.pattern}`
682
716
  );
683
717
  continue;
684
718
  }
685
- const templatePath = join3(routeDir, files[0]);
686
719
  const templateHtml = readFileSync3(templatePath, "utf-8");
687
720
  const fallbackHtml = generateFallbackFromTemplate(templateHtml, route.pattern);
688
- const fallbackPath = join3(routeDir, FALLBACK_FILENAME);
721
+ const parts = route.pattern.replace(/^\//, "").split("/");
722
+ const dirParts = parts.filter((p) => !p.startsWith(":"));
723
+ const fallbackDir = dirParts.length > 0 ? join3(buildDir, ...dirParts) : buildDir;
724
+ if (!existsSync2(fallbackDir)) {
725
+ mkdirSync2(fallbackDir, { recursive: true });
726
+ }
727
+ const fallbackPath = join3(fallbackDir, FALLBACK_FILENAME);
689
728
  writeFileSync(fallbackPath, fallbackHtml);
690
- const relativePath = join3(...dirParts, FALLBACK_FILENAME);
729
+ const relativePath = dirParts.length > 0 ? join3(...dirParts, FALLBACK_FILENAME) : FALLBACK_FILENAME;
691
730
  generated.push(relativePath);
692
731
  console.log(`[qlara] Generated fallback: ${relativePath}`);
693
732
  }
@@ -785,6 +824,18 @@ async function updateCloudFrontEdgeVersion(cf, distributionId, newVersionArn) {
785
824
  const qlaraPolicyId = await ensureCachePolicy(cf);
786
825
  config.DefaultCacheBehavior.CachePolicyId = qlaraPolicyId;
787
826
  }
827
+ const hasErrorResponses = config.CustomErrorResponses?.Items?.some(
828
+ (r) => r.ErrorCode === 403
829
+ );
830
+ if (!hasErrorResponses) {
831
+ config.CustomErrorResponses = {
832
+ Quantity: 2,
833
+ Items: [
834
+ { ErrorCode: 403, ResponseCode: "404", ResponsePagePath: "/404.html", ErrorCachingMinTTL: 10 },
835
+ { ErrorCode: 404, ResponseCode: "404", ResponsePagePath: "/404.html", ErrorCachingMinTTL: 10 }
836
+ ]
837
+ };
838
+ }
788
839
  await cf.send(
789
840
  new UpdateDistributionCommand({
790
841
  Id: distributionId,
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
4
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
5
5
  import { join as join4 } from "path";
6
6
 
7
7
  // src/provider/aws/constants.ts
@@ -470,8 +470,25 @@ function buildTemplate(config) {
470
470
  }
471
471
  }
472
472
  ]
473
- }
474
- // No CustomErrorResponses the edge handler manages 403/404
473
+ },
474
+ // Serve the framework's 404 page (e.g., Next.js not-found.ts → 404.html)
475
+ // when S3 returns 403 (missing file) or 404. This covers:
476
+ // - Unknown routes not in the manifest
477
+ // - Validation failures (renderer abandons, no file uploaded)
478
+ CustomErrorResponses: [
479
+ {
480
+ ErrorCode: 403,
481
+ ResponseCode: 404,
482
+ ResponsePagePath: "/404.html",
483
+ ErrorCachingMinTTL: 10
484
+ },
485
+ {
486
+ ErrorCode: 404,
487
+ ResponseCode: 404,
488
+ ResponsePagePath: "/404.html",
489
+ ErrorCachingMinTTL: 10
490
+ }
491
+ ]
475
492
  }
476
493
  }
477
494
  }
@@ -600,7 +617,7 @@ async function bundleRenderer(routeFile, cacheTtl = 3600, framework) {
600
617
  }
601
618
 
602
619
  // src/fallback.ts
603
- import { readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
620
+ import { readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
604
621
  import { join as join3 } from "path";
605
622
  var FALLBACK_FILENAME = "_fallback.html";
606
623
  function paramPlaceholder(paramName) {
@@ -669,33 +686,55 @@ function generateFallbackFromTemplate(templateHtml, routePattern) {
669
686
  );
670
687
  return fallback;
671
688
  }
689
+ function findTemplateForRoute(buildDir, routePattern) {
690
+ const segments = routePattern.replace(/^\//, "").split("/");
691
+ function walk(currentDir, segmentIndex) {
692
+ if (segmentIndex >= segments.length) return null;
693
+ if (!existsSync2(currentDir)) return null;
694
+ const segment = segments[segmentIndex];
695
+ const isLast = segmentIndex === segments.length - 1;
696
+ const isDynamic = segment.startsWith(":");
697
+ if (isLast) {
698
+ const files = readdirSync2(currentDir).filter(
699
+ (f) => f.endsWith(".html") && f !== FALLBACK_FILENAME
700
+ );
701
+ return files.length > 0 ? join3(currentDir, files[0]) : null;
702
+ }
703
+ if (isDynamic) {
704
+ const entries = readdirSync2(currentDir, { withFileTypes: true });
705
+ for (const entry of entries) {
706
+ if (entry.isDirectory() && !entry.name.startsWith("_") && !entry.name.startsWith(".")) {
707
+ const result = walk(join3(currentDir, entry.name), segmentIndex + 1);
708
+ if (result) return result;
709
+ }
710
+ }
711
+ return null;
712
+ }
713
+ return walk(join3(currentDir, segment), segmentIndex + 1);
714
+ }
715
+ return walk(buildDir, 0);
716
+ }
672
717
  function generateFallbacks(buildDir, routes) {
673
718
  const generated = [];
674
719
  for (const route of routes) {
675
- const parts = route.pattern.replace(/^\//, "").split("/");
676
- const dirParts = parts.filter((p) => !p.startsWith(":"));
677
- const routeDir = join3(buildDir, ...dirParts);
678
- if (!existsSync2(routeDir)) {
679
- console.warn(
680
- `[qlara] Warning: No output directory for route ${route.pattern} at ${routeDir}`
681
- );
682
- continue;
683
- }
684
- const files = readdirSync2(routeDir).filter(
685
- (f) => f.endsWith(".html") && f !== FALLBACK_FILENAME
686
- );
687
- if (files.length === 0) {
720
+ const templatePath = findTemplateForRoute(buildDir, route.pattern);
721
+ if (!templatePath) {
688
722
  console.warn(
689
- `[qlara] Warning: No HTML files in ${routeDir} to create fallback template`
723
+ `[qlara] Warning: No HTML template found for route ${route.pattern}`
690
724
  );
691
725
  continue;
692
726
  }
693
- const templatePath = join3(routeDir, files[0]);
694
727
  const templateHtml = readFileSync3(templatePath, "utf-8");
695
728
  const fallbackHtml = generateFallbackFromTemplate(templateHtml, route.pattern);
696
- const fallbackPath = join3(routeDir, FALLBACK_FILENAME);
729
+ const parts = route.pattern.replace(/^\//, "").split("/");
730
+ const dirParts = parts.filter((p) => !p.startsWith(":"));
731
+ const fallbackDir = dirParts.length > 0 ? join3(buildDir, ...dirParts) : buildDir;
732
+ if (!existsSync2(fallbackDir)) {
733
+ mkdirSync2(fallbackDir, { recursive: true });
734
+ }
735
+ const fallbackPath = join3(fallbackDir, FALLBACK_FILENAME);
697
736
  writeFileSync(fallbackPath, fallbackHtml);
698
- const relativePath = join3(...dirParts, FALLBACK_FILENAME);
737
+ const relativePath = dirParts.length > 0 ? join3(...dirParts, FALLBACK_FILENAME) : FALLBACK_FILENAME;
699
738
  generated.push(relativePath);
700
739
  console.log(`[qlara] Generated fallback: ${relativePath}`);
701
740
  }
@@ -793,6 +832,18 @@ async function updateCloudFrontEdgeVersion(cf, distributionId, newVersionArn) {
793
832
  const qlaraPolicyId = await ensureCachePolicy(cf);
794
833
  config.DefaultCacheBehavior.CachePolicyId = qlaraPolicyId;
795
834
  }
835
+ const hasErrorResponses = config.CustomErrorResponses?.Items?.some(
836
+ (r) => r.ErrorCode === 403
837
+ );
838
+ if (!hasErrorResponses) {
839
+ config.CustomErrorResponses = {
840
+ Quantity: 2,
841
+ Items: [
842
+ { ErrorCode: 403, ResponseCode: "404", ResponsePagePath: "/404.html", ErrorCachingMinTTL: 10 },
843
+ { ErrorCode: 404, ResponseCode: "404", ResponsePagePath: "/404.html", ErrorCachingMinTTL: 10 }
844
+ ]
845
+ };
846
+ }
796
847
  await cf.send(
797
848
  new UpdateDistributionCommand({
798
849
  Id: distributionId,
@@ -1161,7 +1212,7 @@ function loadResources() {
1161
1212
  return JSON.parse(readFileSync4(RESOURCES_PATH, "utf-8"));
1162
1213
  }
1163
1214
  function saveResources(resources) {
1164
- mkdirSync2(QLARA_DIR, { recursive: true });
1215
+ mkdirSync3(QLARA_DIR, { recursive: true });
1165
1216
  writeFileSync2(RESOURCES_PATH, JSON.stringify(resources, null, 2));
1166
1217
  }
1167
1218
  async function deploy() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qlara",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Runtime ISR for static React apps — dynamic routing and SEO metadata for statically exported Next.js apps on AWS",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -834,6 +834,58 @@ async function findReferenceSegmentDir(bucket: string, routePrefix: string): Pro
834
834
  return null;
835
835
  }
836
836
 
837
+ /**
838
+ * Find a reference segment directory using the route pattern.
839
+ * For multi-param routes like /:lang/products/:id, the direct URI-based prefix
840
+ * (e.g., 'da/products') may not contain any build-time pages. This function
841
+ * walks the S3 key hierarchy following the route pattern: static segments
842
+ * descend directly, dynamic segments list subdirectories and try any of them.
843
+ */
844
+ async function findReferenceSegmentDirByPattern(
845
+ bucket: string,
846
+ routePattern: string,
847
+ ): Promise<string | null> {
848
+ // Remove the last segment (the page-level param) to get the parent segments
849
+ const segments = routePattern.replace(/^\//, '').split('/');
850
+ const parentSegments = segments.slice(0, -1);
851
+
852
+ async function searchPrefix(prefix: string, segmentIndex: number): Promise<string | null> {
853
+ if (segmentIndex >= parentSegments.length) {
854
+ // We've traversed all parent segments — check for segment files here
855
+ return findReferenceSegmentDir(bucket, prefix.replace(/\/$/, ''));
856
+ }
857
+
858
+ const segment = parentSegments[segmentIndex];
859
+
860
+ if (!segment.startsWith(':')) {
861
+ // Static segment — descend directly
862
+ const nextPrefix = prefix ? `${prefix}${segment}/` : `${segment}/`;
863
+ return searchPrefix(nextPrefix, segmentIndex + 1);
864
+ }
865
+
866
+ // Dynamic segment — list subdirectories and try each
867
+ try {
868
+ const response = await s3.send(new ListObjectsV2Command({
869
+ Bucket: bucket,
870
+ Prefix: prefix,
871
+ Delimiter: '/',
872
+ MaxKeys: 10,
873
+ }));
874
+
875
+ for (const commonPrefix of response.CommonPrefixes || []) {
876
+ const subPrefix = commonPrefix.Prefix || '';
877
+ const result = await searchPrefix(subPrefix, segmentIndex + 1);
878
+ if (result) return result;
879
+ }
880
+ } catch {
881
+ // S3 error — skip
882
+ }
883
+ return null;
884
+ }
885
+
886
+ return searchPrefix('', 0);
887
+ }
888
+
837
889
  type SegmentFileType = 'shared' | 'tree' | 'head' | 'full' | 'page';
838
890
 
839
891
  /**
@@ -1004,14 +1056,19 @@ async function generateSegmentFiles(
1004
1056
  params: Record<string, string>,
1005
1057
  rscData: string | null,
1006
1058
  metadata: QlaraMetadata | null,
1059
+ routePattern: string,
1007
1060
  ): Promise<void> {
1008
1061
  const cleanUri = uri.replace(/^\//, '').replace(/\/$/, '');
1009
1062
  const parts = cleanUri.split('/');
1010
- const routePrefix = parts.slice(0, -1).join('/'); // 'product'
1011
- const segmentDir = `${cleanUri}/`; // 'product/42/'
1012
-
1013
- // Find a build-time page with segment files to use as reference
1014
- const refDir = await findReferenceSegmentDir(bucket, routePrefix);
1063
+ const routePrefix = parts.slice(0, -1).join('/'); // 'da/products'
1064
+ const segmentDir = `${cleanUri}/`; // 'da/products/42/'
1065
+
1066
+ // Find a build-time page with segment files to use as reference.
1067
+ // Try the direct URI prefix first; fall back to pattern-based search for multi-param routes.
1068
+ let refDir = await findReferenceSegmentDir(bucket, routePrefix);
1069
+ if (!refDir) {
1070
+ refDir = await findReferenceSegmentDirByPattern(bucket, routePattern);
1071
+ }
1015
1072
  if (!refDir) return; // No segment files on S3 — Next.js 15 or no build-time pages
1016
1073
 
1017
1074
  // List all segment files in the reference directory
@@ -1217,7 +1274,7 @@ export async function handler(event: RendererEvent & { warmup?: boolean }): Prom
1217
1274
  // 7. Generate per-segment prefetch files (Next.js 16+ Segment Cache)
1218
1275
  // Reads templates from a build-time reference page, patches page-specific data.
1219
1276
  // Skips automatically for Next.js 15 (no segment files on S3).
1220
- await generateSegmentFiles(bucket, uri, params, rscData, metadata);
1277
+ await generateSegmentFiles(bucket, uri, params, rscData, metadata, routePattern);
1221
1278
  }
1222
1279
 
1223
1280
  return {