next-bun-compile 0.11.0 → 0.11.1

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/adapter.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ADAPTER_OUTPUTS_FILE,
3
3
  adapter_default
4
- } from "./index-ajd4bx9z.js";
4
+ } from "./index-k3fkzeev.js";
5
5
  export {
6
6
  adapter_default as default,
7
7
  ADAPTER_OUTPUTS_FILE
@@ -567,6 +567,47 @@ function computeStaticTiersFromSnapshot(snapshot, args) {
567
567
  tags
568
568
  });
569
569
  }
570
+ const claimed = new Set(staticPages.map((s) => s.path));
571
+ for (const f of snapshot.staticFiles ?? []) {
572
+ if (f.pathname.startsWith("/_"))
573
+ continue;
574
+ if (f.pathname === "/404" || f.pathname === "/500")
575
+ continue;
576
+ if (claimed.has(f.pathname) || covered(f.pathname))
577
+ continue;
578
+ const file = f.file.replace(/\\/g, "/");
579
+ let meta = {};
580
+ const suffix = [".html", ".body"].find((s) => file.endsWith(s));
581
+ if (suffix) {
582
+ try {
583
+ meta = JSON.parse(readFileSync(join(args.distDir, file.slice(0, -suffix.length) + ".meta"), "utf-8"));
584
+ } catch {}
585
+ }
586
+ const isHtml = file.endsWith(".html");
587
+ const contentType = meta.headers?.["content-type"];
588
+ if (!isHtml && !contentType)
589
+ continue;
590
+ const headers = {};
591
+ let tags = [];
592
+ for (const [k, v] of Object.entries(meta.headers ?? {})) {
593
+ const lc = k.toLowerCase();
594
+ if (lc === "x-next-cache-tags") {
595
+ tags = v.split(",").map((t) => t.trim()).filter(Boolean);
596
+ } else if (lc !== "vary" && lc !== "content-type") {
597
+ headers[k] = v;
598
+ }
599
+ }
600
+ claimed.add(f.pathname);
601
+ staticPages.push({
602
+ path: f.pathname,
603
+ htmlKey: `__runtime/.next/${file}`,
604
+ rscKey: null,
605
+ headers,
606
+ status: meta.status ?? 200,
607
+ tags,
608
+ ...isHtml ? {} : { contentType }
609
+ });
610
+ }
570
611
  return {
571
612
  tier1,
572
613
  staticPages,
@@ -581,6 +622,7 @@ function computeStaticTiers(args) {
581
622
  throw new Error('next-bun-compile: adapter outputs not found for this build. Build through the adapter: set `adapterPath: "next-bun-compile/adapter"` in next.config (or NEXT_ADAPTER_PATH=next-bun-compile/adapter) and run `next build`.');
582
623
  }
583
624
  return computeStaticTiersFromSnapshot(snapshot, {
625
+ distDir,
584
626
  staticFiles,
585
627
  publicFiles,
586
628
  assetPrefix
@@ -1116,15 +1158,35 @@ async function assembleStandalone(ctx) {
1116
1158
  }
1117
1159
  } catch {}
1118
1160
  }
1119
- for (const p of outputs.prerenders ?? []) {
1120
- const file = p.fallback?.filePath;
1121
- if (!file)
1122
- continue;
1161
+ const copySeed = (file) => {
1123
1162
  copyTo(relative2(ctx.repoRoot, file), file);
1124
- if (file.endsWith(".html")) {
1125
- const meta = file.slice(0, -".html".length) + ".meta";
1163
+ const suffix = [".html", ".body"].find((s) => file.endsWith(s));
1164
+ if (suffix) {
1165
+ const meta = file.slice(0, -suffix.length) + ".meta";
1126
1166
  copyTo(relative2(ctx.repoRoot, meta), meta);
1127
1167
  }
1168
+ };
1169
+ for (const p of outputs.prerenders ?? []) {
1170
+ if (p.fallback?.filePath)
1171
+ copySeed(p.fallback.filePath);
1172
+ }
1173
+ for (const f of outputs.staticFiles ?? []) {
1174
+ if (!f.filePath || f.pathname?.startsWith("/_next/"))
1175
+ continue;
1176
+ copySeed(f.filePath);
1177
+ if (f.filePath.endsWith(".body")) {
1178
+ const routeJs = join4(f.filePath.slice(0, -".body".length), "route.js");
1179
+ if (existsSync3(routeJs)) {
1180
+ copyTo(relative2(ctx.repoRoot, routeJs), routeJs);
1181
+ try {
1182
+ const { files } = JSON.parse(readFileSync2(routeJs + ".nft.json", "utf-8"));
1183
+ for (const dep of files) {
1184
+ const src = resolve(dirname2(routeJs), dep);
1185
+ copyTo(relative2(ctx.repoRoot, src), src);
1186
+ }
1187
+ } catch {}
1188
+ }
1189
+ }
1128
1190
  }
1129
1191
  for (const extra of ["BUILD_ID", "required-server-files.json"]) {
1130
1192
  const src = join4(ctx.distDir, extra);
@@ -1153,7 +1215,9 @@ var adapter = {
1153
1215
  if (!Array.isArray(list))
1154
1216
  continue;
1155
1217
  for (const route of list) {
1156
- if (route.sourceRegex && typeof route.source === "string" && !route.priority && (route.headers || route.destination || route.status)) {
1218
+ const headerKeys = Object.keys(route.headers ?? {});
1219
+ const isDeploymentIdRule = headerKeys.length === 1 && headerKeys[0] === "x-nextjs-deployment-id";
1220
+ if (route.sourceRegex && typeof route.source === "string" && !route.priority && !isDeploymentIdRule && (headerKeys.length > 0 || route.destination || route.status)) {
1157
1221
  routingRules.push(route.sourceRegex);
1158
1222
  }
1159
1223
  }
@@ -1172,6 +1236,18 @@ var adapter = {
1172
1236
  }
1173
1237
  ];
1174
1238
  });
1239
+ const staticFiles = (ctx.outputs?.staticFiles ?? []).flatMap((f) => {
1240
+ if (!f.pathname || !f.filePath)
1241
+ return [];
1242
+ if (f.pathname.startsWith("/_next/"))
1243
+ return [];
1244
+ return [
1245
+ {
1246
+ pathname: f.pathname,
1247
+ file: relative2(ctx.distDir, f.filePath)
1248
+ }
1249
+ ];
1250
+ });
1175
1251
  const snapshot = {
1176
1252
  version: 1,
1177
1253
  buildId: ctx.buildId,
@@ -1181,7 +1257,8 @@ var adapter = {
1181
1257
  hasCustomCacheHandler: !!ctx.config.cacheHandler,
1182
1258
  middlewareMatchers: (ctx.outputs?.middleware?.config?.matchers ?? []).map((m) => m.sourceRegex).filter((r) => !!r),
1183
1259
  routingRules,
1184
- prerenders
1260
+ prerenders,
1261
+ staticFiles
1185
1262
  };
1186
1263
  writeFileSync2(join4(ctx.distDir, ADAPTER_OUTPUTS_FILE), JSON.stringify(snapshot, null, 2));
1187
1264
  console.log(`next-bun-compile: adapter outputs written (${prerenders.length} prerender entries)`);
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  compile,
4
4
  generateEntryPoint,
5
5
  runBuild
6
- } from "./index-ajd4bx9z.js";
6
+ } from "./index-k3fkzeev.js";
7
7
  export {
8
8
  runBuild,
9
9
  generateEntryPoint,
@@ -355,7 +355,8 @@ async function buildTier1Routes(tier1, assetMap, bridge) {
355
355
 
356
356
  /** Tier 2: prerendered page with RSC negotiation + ETag/304. */
357
357
  function makePageHandler(page, bridge) {
358
- const { html, rsc, headers: metaHeaders, status } = page;
358
+ const { html, rsc, headers: metaHeaders, status, contentType, deploymentId } =
359
+ page;
359
360
  const htmlEtag = `"${Bun.hash(html).toString(36)}"`;
360
361
  const rscEtag = rsc ? `"${Bun.hash(rsc).toString(36)}"` : null;
361
362
  const htmlGz = html.byteLength >= GZIP_MIN_BYTES ? Bun.gzipSync(html) : null;
@@ -363,9 +364,16 @@ function makePageHandler(page, bridge) {
363
364
  rsc && rsc.byteLength >= GZIP_MIN_BYTES ? Bun.gzipSync(rsc) : null;
364
365
 
365
366
  const base = {};
366
- for (const [k, v] of Object.entries(metaHeaders || {})) base[k] = v;
367
+ let hasCacheControl = false;
368
+ for (const [k, v] of Object.entries(metaHeaders || {})) {
369
+ base[k] = v;
370
+ if (k.toLowerCase() === "cache-control") hasCacheControl = true;
371
+ }
367
372
  base["Vary"] = PAGE_VARY;
368
- base["Cache-Control"] = "s-maxage=31536000";
373
+ // Seeds that recorded an explicit cache-control (static metadata routes:
374
+ // public, max-age=0, must-revalidate) keep it — pages get the frozen-
375
+ // prerender policy.
376
+ if (!hasCacheControl) base["Cache-Control"] = "s-maxage=31536000";
369
377
  base["x-nextjs-cache"] = "HIT";
370
378
 
371
379
  return (req, server) => {
@@ -391,12 +399,16 @@ function makePageHandler(page, bridge) {
391
399
  ...base,
392
400
  "Content-Type": wantsRsc
393
401
  ? "text/x-component"
394
- : "text/html; charset=utf-8",
402
+ : contentType || "text/html; charset=utf-8",
395
403
  ...(useGzip && { "Content-Encoding": "gzip" }),
396
404
  "Content-Length": String(payload.byteLength),
397
405
  ETag: etag,
398
406
  // Baseline sends X-Powered-By on documents but not RSC payloads.
399
407
  ...(!wantsRsc && { "X-Powered-By": "Next.js" }),
408
+ // With a deploymentId configured, Next stamps RSC responses so the
409
+ // client router can detect deployment skew — replicate it.
410
+ ...(wantsRsc &&
411
+ deploymentId && { "x-nextjs-deployment-id": deploymentId }),
400
412
  };
401
413
  if (req.headers.get("if-none-match") === etag) {
402
414
  return new Response(null, { status: 304, headers });
@@ -408,7 +420,7 @@ function makePageHandler(page, bridge) {
408
420
  };
409
421
  }
410
422
 
411
- async function buildTier2Routes(staticPages, assetMap, bridge) {
423
+ async function buildTier2Routes(staticPages, assetMap, bridge, deploymentId) {
412
424
  const routes = {};
413
425
  await Promise.all(
414
426
  staticPages.map(async (spec) => {
@@ -416,7 +428,14 @@ async function buildTier2Routes(staticPages, assetMap, bridge) {
416
428
  if (html == null) return;
417
429
  const rsc = spec.rscKey ? await loadBytes(assetMap, spec.rscKey) : null;
418
430
  const handler = makePageHandler(
419
- { html, rsc, headers: spec.headers, status: spec.status },
431
+ {
432
+ html,
433
+ rsc,
434
+ headers: spec.headers,
435
+ status: spec.status,
436
+ contentType: spec.contentType,
437
+ deploymentId,
438
+ },
420
439
  bridge
421
440
  );
422
441
  // Plain function route: GET/HEAD from memory, everything else
@@ -577,7 +596,12 @@ async function start(opts) {
577
596
 
578
597
  const [tier1Routes, tier2Routes] = await Promise.all([
579
598
  buildTier1Routes(tier1, assetMap, bridgeLazy),
580
- buildTier2Routes(staticPages, assetMap, bridgeLazy),
599
+ buildTier2Routes(
600
+ staticPages,
601
+ assetMap,
602
+ bridgeLazy,
603
+ nextConfig?.deploymentId
604
+ ),
581
605
  ]);
582
606
 
583
607
  const routes = { ...tier1Routes, ...tier2Routes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-bun-compile",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Compile your Next.js app into a Bun single-file executable",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",