@moonsjs/vite 0.1.16 → 0.1.18

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.
@@ -13,7 +13,7 @@ import {
13
13
  DEFAULT_SECURITY_HEADERS,
14
14
  sendNodeResponse2,
15
15
  installDevMiddleware2
16
- } from "../index-v5sbd3az.js";
16
+ } from "../index-1ksjfvnz.js";
17
17
 
18
18
  // src/server/handler.ts
19
19
  import {
@@ -26,7 +26,8 @@ import {
26
26
  isRedirectSignal,
27
27
  resolveRoute,
28
28
  routeKeyFromFile,
29
- runLoaders
29
+ runLoaders,
30
+ trailingSlashRedirect
30
31
  } from "@moonsjs/core";
31
32
  import { encodeSsrPayload, reviveError, serializeError } from "@moonsjs/core/serialize";
32
33
  import { API_METHODS, resolveApiRoute } from "@moonsjs/core";
@@ -92,9 +93,9 @@ function createRequestHandler(options) {
92
93
  return apiErrorResponse(new MoonsError({
93
94
  code: "E_API_HANDLER_INVALID",
94
95
  file: route.file,
95
- message: `modul API \`${route.path}\` tidak bisa dimuat`,
96
- details: "Peta `loadApi` tidak memuat route ini (build/dev tidak sinkron).",
97
- suggestions: ["Jalankan ulang build / restart dev server"]
96
+ message: `API module \`${route.path}\` could not be loaded`,
97
+ details: "The `loadApi` map does not contain this route (build/dev out of sync).",
98
+ suggestions: ["Rebuild / restart the dev server"]
98
99
  }), route);
99
100
  }
100
101
  let mod;
@@ -107,9 +108,9 @@ function createRequestHandler(options) {
107
108
  return apiErrorResponse(new MoonsError({
108
109
  code: "E_API_HANDLER_INVALID",
109
110
  file: route.file,
110
- message: "modul API tidak mengekspor objek handler",
111
- details: `Modul \`${route.path}\` harus mengekspor handler method.`,
112
- suggestions: ["Export `GET`/`POST`/… sebagai fungsi `(args) => Response`"]
111
+ message: "API module does not export a handler object",
112
+ details: `Module \`${route.path}\` must export method handlers.`,
113
+ suggestions: ["Export `GET`/`POST`/… as `(args) => Response` functions"]
113
114
  }), route);
114
115
  }
115
116
  const record = mod;
@@ -136,9 +137,9 @@ function createRequestHandler(options) {
136
137
  return apiErrorResponse(new MoonsError({
137
138
  code: "E_API_HANDLER_INVALID",
138
139
  file: route.file,
139
- message: `handler \`${requestMethod}\` tidak mengembalikan \`Response\``,
140
- details: "Handler API wajib mengembalikan `new Response(...)`.",
141
- suggestions: ["Kembalikan `new Response(body, { status, headers })`"]
140
+ message: `handler \`${requestMethod}\` did not return a \`Response\``,
141
+ details: "API handlers must return `new Response(...)`.",
142
+ suggestions: ["Return `new Response(body, { status, headers })`"]
142
143
  }), route);
143
144
  }
144
145
  if (stripBody) {
@@ -158,11 +159,11 @@ function createRequestHandler(options) {
158
159
  const markup = renderToMarkup({ page: key, layouts: [] }, { page: module, layouts: [] }, {}, {});
159
160
  return htmlResponse(await shell(markup, payload, request.url), 404);
160
161
  } catch (error) {
161
- console.error("[moons] gagal merender _404 kustom:", error);
162
+ console.error("[moons] failed to render custom _404:", error);
162
163
  }
163
164
  }
164
165
  const payload = encodeSsrPayload({ key: "", params: {}, data: {} });
165
- const html = await shell("<h1>404</h1><p>Halaman tidak ditemukan.</p>", payload, request.url);
166
+ const html = await shell("<h1>404</h1><p>Page not found.</p>", payload, request.url);
166
167
  return htmlResponse(html, 404);
167
168
  }
168
169
  async function routeErrorResponse(error, request, resolved) {
@@ -183,7 +184,7 @@ function createRequestHandler(options) {
183
184
  const markup = renderToMarkup({ page: key, layouts: [] }, { page: module, layouts: [] }, {}, {}, {}, serialized === undefined ? undefined : reviveError(serialized));
184
185
  return htmlResponse(await shell(markup, payload, request.url), 500);
185
186
  } catch (renderError) {
186
- console.error("[moons] gagal merender _error kustom:", renderError);
187
+ console.error("[moons] failed to render custom _error:", renderError);
187
188
  }
188
189
  }
189
190
  return internalErrorResponse(error, { log: false });
@@ -197,9 +198,9 @@ function createRequestHandler(options) {
197
198
  throw new MoonsError({
198
199
  code: "E_MIDDLEWARE_INVALID",
199
200
  file: definition.file,
200
- message: "middleware direktori tidak bisa dimuat",
201
- details: "Peta `loadRoute` tidak memuat `_middleware.ts` ini.",
202
- suggestions: ["Restart dev server / jalankan ulang build"]
201
+ message: "directory middleware could not be loaded",
202
+ details: "The `loadRoute` map does not contain this `_middleware.ts`.",
203
+ suggestions: ["Restart the dev server / rebuild"]
203
204
  });
204
205
  }
205
206
  const module = await load();
@@ -208,8 +209,8 @@ function createRequestHandler(options) {
208
209
  throw new MoonsError({
209
210
  code: "E_MIDDLEWARE_INVALID",
210
211
  file: definition.file,
211
- message: "`_middleware.ts` tidak meng-export default fungsi",
212
- details: "Middleware direktori mengikuti bentuk `(args) => Promise<Response>`.",
212
+ message: "`_middleware.ts` does not default-export a function",
213
+ details: "Directory middleware follows the `(args) => Promise<Response>` shape.",
213
214
  suggestions: ["Export default `async ({ req, ctx, next }) => await next()`"]
214
215
  });
215
216
  }
@@ -225,9 +226,9 @@ function createRequestHandler(options) {
225
226
  throw new MoonsError({
226
227
  code: "E_MIDDLEWARE_INVALID",
227
228
  file: definition.file,
228
- message: "middleware API tidak bisa dimuat",
229
- details: "Peta `loadApiMiddleware` tidak memuat `_middleware.ts` ini.",
230
- suggestions: ["Restart dev server / jalankan ulang build"]
229
+ message: "API middleware could not be loaded",
230
+ details: "The `loadApiMiddleware` map does not contain this `_middleware.ts`.",
231
+ suggestions: ["Restart the dev server / rebuild"]
231
232
  });
232
233
  }
233
234
  const module = await load();
@@ -235,8 +236,8 @@ function createRequestHandler(options) {
235
236
  throw new MoonsError({
236
237
  code: "E_MIDDLEWARE_INVALID",
237
238
  file: definition.file,
238
- message: "`_middleware.ts` tidak meng-export default fungsi",
239
- details: "Middleware direktori mengikuti bentuk `(args) => Promise<Response>`.",
239
+ message: "`_middleware.ts` does not default-export a function",
240
+ details: "Directory middleware follows the `(args) => Promise<Response>` shape.",
240
241
  suggestions: ["Export default `async ({ req, ctx, next }) => await next()`"]
241
242
  });
242
243
  }
@@ -267,7 +268,7 @@ function createRequestHandler(options) {
267
268
  return await handleApiRequest(apiMatch.route, apiMatch.params, request, ctx);
268
269
  }
269
270
  const middlewares = await loadApiDirMiddlewares(apiChain);
270
- return await composeMiddleware(middlewares, ctx, () => handleApiRequest(apiMatch.route, apiMatch.params, request, ctx), { code: "E_MIDDLEWARE_INVALID", label: "middleware API" })(request);
271
+ return await composeMiddleware(middlewares, ctx, () => handleApiRequest(apiMatch.route, apiMatch.params, request, ctx), { code: "E_MIDDLEWARE_INVALID", label: "API middleware" })(request);
271
272
  }
272
273
  }
273
274
  const resolved = resolveRoute(url.pathname, routes);
@@ -280,7 +281,7 @@ function createRequestHandler(options) {
280
281
  return await renderRoute(resolved, request, ctx);
281
282
  }
282
283
  const middlewares = await loadDirMiddlewares(dirChain);
283
- return await composeMiddleware(middlewares, ctx, () => renderRoute(resolved, request, ctx), { code: "E_MIDDLEWARE_INVALID", label: "middleware direktori" })(request);
284
+ return await composeMiddleware(middlewares, ctx, () => renderRoute(resolved, request, ctx), { code: "E_MIDDLEWARE_INVALID", label: "directory middleware" })(request);
284
285
  } catch (error) {
285
286
  if (isRedirectSignal(error)) {
286
287
  return new Response(null, {
@@ -323,6 +324,14 @@ function createRequestHandler(options) {
323
324
  return response;
324
325
  };
325
326
  try {
327
+ if (!isPrerender && config.trailingSlash !== undefined) {
328
+ const policyUrl = new URL(request.url);
329
+ const canonical = trailingSlashRedirect(policyUrl.pathname, config.trailingSlash);
330
+ if (canonical !== null) {
331
+ policyUrl.pathname = canonical;
332
+ return applyHeaders(new Response(null, { status: 308, headers: { location: policyUrl.toString() } }));
333
+ }
334
+ }
326
335
  if (!isPrerender && redirects.length > 0) {
327
336
  const url = new URL(request.url);
328
337
  const path = url.pathname.replace(/\/$/, "");
@@ -377,9 +386,9 @@ async function loadModules(keys, loadRoute) {
377
386
  if (load === undefined) {
378
387
  throw new MoonsError({
379
388
  code: "E_INTERNAL",
380
- message: `modul route \`${key}\` tidak terdaftar`,
381
- details: "Registry route tidak sinkron dengan hasil scan.",
382
- suggestions: ["Jalankan ulang `bun run build`, atau restart dev server"]
389
+ message: `route module \`${key}\` is not registered`,
390
+ details: "The route registry is out of sync with the scan result.",
391
+ suggestions: ["Run `bun run build` again, or restart the dev server"]
383
392
  });
384
393
  }
385
394
  return await load();
@@ -394,11 +403,11 @@ async function runRouteMeta(pageModule, pageData, file) {
394
403
  throw new MoonsError({
395
404
  code: "E_INTERNAL",
396
405
  file,
397
- message: "export `meta` harus berupa fungsi `({ data }) => RouteMeta`",
398
- details: "Bentuk `meta` mengikuti ",
406
+ message: "export `meta` must be a function `({ data }) => RouteMeta`",
407
+ details: "The `meta` shape follows `RouteMeta`.",
399
408
  suggestions: [
400
- 'Ubah menjadi `export const meta = ({ data }) => ({ title: "..." })`',
401
- "Atau hapus export `meta`"
409
+ 'Change it to `export const meta = ({ data }) => ({ title: "..." })`',
410
+ "Or remove the `meta` export"
402
411
  ]
403
412
  });
404
413
  }
@@ -407,9 +416,9 @@ async function runRouteMeta(pageModule, pageData, file) {
407
416
  throw new MoonsError({
408
417
  code: "E_INTERNAL",
409
418
  file,
410
- message: "`meta()` harus mengembalikan objek RouteMeta",
411
- details: "Nilai yang dikembalikan dipakai untuk <title>/description/og.",
412
- suggestions: ['Kembalikan objek, mis. `({ title: "Halaman" })`']
419
+ message: "`meta()` must return a RouteMeta object",
420
+ details: "The returned value is used for <title>/description/og.",
421
+ suggestions: ['Return an object, e.g. `({ title: "Home" })`']
413
422
  });
414
423
  }
415
424
  return result;
@@ -418,13 +427,13 @@ function apiErrorResponse(error, route) {
418
427
  const isProduction = process.env["NODE_ENV"] === "production";
419
428
  if (error instanceof MoonsError) {
420
429
  console.error(`[moons] API ${route.path}: ${error.format()}`);
421
- return new Response(isProduction ? "Terjadi kesalahan internal." : error.format(), {
430
+ return new Response(isProduction ? "Internal server error." : error.format(), {
422
431
  status: 500,
423
432
  headers: { "content-type": "text/plain; charset=utf-8" }
424
433
  });
425
434
  }
426
435
  console.error(`[moons] API ${route.path}:`, error);
427
- return new Response(isProduction ? "Terjadi kesalahan internal." : String(error), {
436
+ return new Response(isProduction ? "Internal server error." : String(error), {
428
437
  status: 500,
429
438
  headers: { "content-type": "text/plain; charset=utf-8" }
430
439
  });
@@ -433,8 +442,8 @@ function internalErrorResponse(error, options = {}) {
433
442
  if (options.log !== false) {
434
443
  console.error("[moons] request error:", error);
435
444
  }
436
- const detail = process.env["NODE_ENV"] !== "production" && error instanceof MoonsError ? `<pre>${escapeHtml(error.format())}</pre>` : "<p>Terjadi kesalahan internal.</p>";
437
- return htmlResponse(`<!doctype html><html lang="id"><head><meta charset="utf-8"><title>500</title></head><body><h1>500</h1>${detail}</body></html>`, 500);
445
+ const detail = process.env["NODE_ENV"] !== "production" && error instanceof MoonsError ? `<pre>${escapeHtml(error.format())}</pre>` : "<p>Internal server error.</p>";
446
+ return htmlResponse(`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>500</title></head><body><h1>500</h1>${detail}</body></html>`, 500);
438
447
  }
439
448
  function htmlResponse(html, status) {
440
449
  return new Response(html, {
@@ -468,7 +477,7 @@ function warnPayloadFindings(payload, scan, warned) {
468
477
  continue;
469
478
  }
470
479
  warned.add(label);
471
- console.warn(`[moons] peringatan dev: payload SSR memuat ${label} — jangan mengembalikan rahasia dari loader.`);
480
+ console.warn(`[moons] dev warning: SSR payload contains ${label} — do not return secrets from loaders.`);
472
481
  }
473
482
  }
474
483
  // src/server/node-server.ts
@@ -476,7 +485,11 @@ import { existsSync, readFileSync as readFileSync2, statSync as statSync2 } from
476
485
  import { gzipSync } from "node:zlib";
477
486
  import { createServer } from "node:http";
478
487
  import { extname as extname2, join, resolve, sep } from "node:path";
479
- import { DEFAULT_BODY_LIMIT_BYTES, MoonsError as MoonsError2 } from "@moonsjs/core";
488
+ import {
489
+ DEFAULT_BODY_LIMIT_BYTES,
490
+ MoonsError as MoonsError2,
491
+ trailingSlashRedirect as trailingSlashRedirect2
492
+ } from "@moonsjs/core";
480
493
 
481
494
  // src/server/isr.ts
482
495
  import { renameSync, writeFileSync } from "node:fs";
@@ -504,7 +517,7 @@ async function runRevalidate(options) {
504
517
  headers: { "x-moons-prerender": "1" }
505
518
  }));
506
519
  if (response.status !== 200) {
507
- log(`[moons] isr: lewati ${options.pathname} (status ${response.status})`);
520
+ log(`[moons] isr: skipped ${options.pathname} (status ${response.status})`);
508
521
  return;
509
522
  }
510
523
  const html = await response.text();
@@ -514,7 +527,7 @@ async function runRevalidate(options) {
514
527
  });
515
528
  if (findings.length > 0) {
516
529
  const labels = findings.map((finding) => finding.kind === "canary" ? "canary" : `env(${finding.label})`).join(", ");
517
- log(`[moons] isr: TIDAK menulis ${options.pathname} — memuat ${labels}`);
530
+ log(`[moons] isr: NOT writing ${options.pathname} — contains ${labels}`);
518
531
  return;
519
532
  }
520
533
  if (options.storage !== undefined) {
@@ -522,14 +535,14 @@ async function runRevalidate(options) {
522
535
  return;
523
536
  }
524
537
  if (options.file === undefined) {
525
- log(`[moons] isr: lewati ${options.pathname} — tanpa file/storage tujuan`);
538
+ log(`[moons] isr: skipped ${options.pathname} — no destination file/storage`);
526
539
  return;
527
540
  }
528
541
  const tmp = `${options.file}.tmp-${process.pid}`;
529
542
  writeFileSync(tmp, html);
530
543
  renameSync(tmp, options.file);
531
544
  } catch (error) {
532
- log(`[moons] isr: gagal regenerasi ${options.pathname}: ${String(error)}`);
545
+ log(`[moons] isr: failed to regenerate ${options.pathname}: ${String(error)}`);
533
546
  }
534
547
  }
535
548
 
@@ -549,7 +562,7 @@ function createFileStorage(clientDir) {
549
562
  async write(pathname, body) {
550
563
  const target = staticFileTarget(clientDir, pathname);
551
564
  if (target === null) {
552
- throw new Error(`[moons] isr: path tidak valid untuk ditulis: ${pathname}`);
565
+ throw new Error(`[moons] isr: invalid path to write: ${pathname}`);
553
566
  }
554
567
  mkdirSync(dirname(target), { recursive: true });
555
568
  const tmp = `${target}.tmp-${process.pid}`;
@@ -632,9 +645,9 @@ function installGracefulShutdown(server, options = {}) {
632
645
  return;
633
646
  }
634
647
  shuttingDown = true;
635
- log(`[moons] menerima ${signal} — berhenti menerima koneksi, drain...`);
648
+ log(`[moons] received ${signal} — no longer accepting connections, draining...`);
636
649
  const timer = setTimeout(() => {
637
- log(`[moons] drain melewati ${timeoutMs}ms — keluar paksa`);
650
+ log(`[moons] drain exceeded ${timeoutMs}ms — forcing exit`);
638
651
  const closeAll = server.closeAllConnections;
639
652
  try {
640
653
  closeAll?.call(server);
@@ -648,7 +661,7 @@ function installGracefulShutdown(server, options = {}) {
648
661
  server.close(async (error) => {
649
662
  if (error !== undefined) {
650
663
  clearTimeout(timer);
651
- log(`[moons] penutupan server gagal: ${error.message}`);
664
+ log(`[moons] server close failed: ${error.message}`);
652
665
  exit(1);
653
666
  return;
654
667
  }
@@ -657,11 +670,11 @@ function installGracefulShutdown(server, options = {}) {
657
670
  try {
658
671
  await options.onShutdown();
659
672
  } catch (hookError) {
660
- log(`[moons] onShutdown gagal: ${String(hookError)}`);
673
+ log(`[moons] onShutdown failed: ${String(hookError)}`);
661
674
  }
662
675
  }
663
676
  clearTimeout(timer);
664
- log("[moons] shutdown selesai (drain penuh)");
677
+ log("[moons] shutdown complete (full drain)");
665
678
  exit(0);
666
679
  });
667
680
  };
@@ -724,6 +737,16 @@ function startNodeServer(handler, options = {}) {
724
737
  throw error;
725
738
  }
726
739
  try {
740
+ if (options.trailingSlash !== undefined) {
741
+ const canonical = trailingSlashRedirect2(url.pathname, options.trailingSlash);
742
+ if (canonical !== null) {
743
+ url.pathname = canonical;
744
+ res.statusCode = 308;
745
+ res.setHeader("location", url.toString());
746
+ res.end();
747
+ return;
748
+ }
749
+ }
727
750
  if (req.method === "GET" || req.method === "HEAD") {
728
751
  if (options.storage !== undefined) {
729
752
  const entry = await options.storage.read(url.pathname);
@@ -898,11 +921,11 @@ function rpcBodyTooLargeText(limit, size) {
898
921
  const megabytes = (bytes) => (bytes / 1e6).toFixed(1);
899
922
  return new MoonsError2({
900
923
  code: "E_RPC_BODY_TOO_LARGE",
901
- message: size === null ? `body melebihi limit ${megabytes(limit)} MB` : `body ${megabytes(size)} MB melebihi limit ${megabytes(limit)} MB`,
902
- details: "Body melebihi `security.bodyLimitBytes`; pembacaan dihentikan di transport sebelum penuh.",
924
+ message: size === null ? `body exceeds the ${megabytes(limit)} MB limit` : `body ${megabytes(size)} MB exceeds the ${megabytes(limit)} MB limit`,
925
+ details: "Body exceeds `security.bodyLimitBytes`; reading stops at the transport before the body is fully buffered.",
903
926
  suggestions: [
904
- "Kurangi ukuran payload, atau",
905
- "Naikkan security.bodyLimitBytes jika memang perlu"
927
+ "Reduce the payload size, or",
928
+ "Increase security.bodyLimitBytes if you really need to"
906
929
  ]
907
930
  }).format();
908
931
  }
@@ -1007,7 +1030,7 @@ function scheduleRevalidation(input) {
1007
1030
  if (logger !== null) {
1008
1031
  logger({ msg: "isr:revalidate-failed", level: "warn", path: normalized, error: message });
1009
1032
  } else {
1010
- console.error(`[moons] isr regenerasi gagal (${normalized}): ${message}`);
1033
+ console.error(`[moons] isr regeneration failed (${normalized}): ${message}`);
1011
1034
  }
1012
1035
  });
1013
1036
  }
@@ -1068,7 +1091,7 @@ function createPluginRuntime(options) {
1068
1091
  try {
1069
1092
  await plugin.onShutdown?.(app);
1070
1093
  } catch (error) {
1071
- log(`[moons] plugin \`${plugin.name}\` gagal saat onShutdown: ${String(error)}`);
1094
+ log(`[moons] plugin \`${plugin.name}\` failed during onShutdown: ${String(error)}`);
1072
1095
  }
1073
1096
  }
1074
1097
  },
@@ -1110,18 +1133,18 @@ async function runPrerender(options) {
1110
1133
  throw new MoonsError3({
1111
1134
  code: "E_ISR_INVALID",
1112
1135
  file: route.file,
1113
- message: "`revalidate` tidak valid atau dipakai tanpa `prerender = true`",
1114
- details: "ISR berbasis file: `revalidate` (detik, number >= 0) + `prerender`.",
1136
+ message: "`revalidate` is invalid or used without `prerender = true`",
1137
+ details: "File-based ISR: `revalidate` (seconds, number >= 0) + `prerender`.",
1115
1138
  suggestions: [
1116
- "Gunakan angka detik, mis. `export const revalidate = 60`",
1117
- "Tambahkan `export const prerender = true` pada page yang sama"
1139
+ "Use a number of seconds, e.g. `export const revalidate = 60`",
1140
+ "Add `export const prerender = true` to the same page"
1118
1141
  ]
1119
1142
  });
1120
1143
  }
1121
1144
  }
1122
1145
  if (prerender !== true) {
1123
1146
  if (prerender !== undefined && prerender !== false) {
1124
- throw prerenderError(route, "`prerender` harus boolean (`true`/`false`)");
1147
+ throw prerenderError(route, "`prerender` must be a boolean (`true`/`false`)");
1125
1148
  }
1126
1149
  continue;
1127
1150
  }
@@ -1132,15 +1155,15 @@ async function runPrerender(options) {
1132
1155
  headers: { "x-moons-prerender": "1" }
1133
1156
  }));
1134
1157
  if (response.status === 404) {
1135
- log(`[moons] prerender: lewati ${pathname} (notFound)`);
1158
+ log(`[moons] prerender: skipped ${pathname} (notFound)`);
1136
1159
  skipped += 1;
1137
1160
  continue;
1138
1161
  }
1139
1162
  if (response.status >= 300 && response.status < 400) {
1140
- throw prerenderError(route, `\`redirect()\` tidak didukung saat prerender (${pathname})`);
1163
+ throw prerenderError(route, `\`redirect()\` is not supported during prerender (${pathname})`);
1141
1164
  }
1142
1165
  if (response.status !== 200) {
1143
- throw prerenderError(route, `status ${response.status} saat merender ${pathname}`);
1166
+ throw prerenderError(route, `status ${response.status} while rendering ${pathname}`);
1144
1167
  }
1145
1168
  const html = await response.text();
1146
1169
  assertNoLeak(html, pathname, route.file, options, env);
@@ -1161,7 +1184,7 @@ async function runPrerender(options) {
1161
1184
  `);
1162
1185
  const emitted = emitStaticFiles(options.clientDir, options.staticConfig, entries);
1163
1186
  const extras = [...wrote404 ? [NOT_FOUND_FILE] : [], ...emitted];
1164
- log(`[moons] prerender selesai: ${written} file${extras.length > 0 ? ` + ${extras.join(" + ")}` : ""} (${skipped} dilewati)`);
1187
+ log(`[moons] prerender done: ${written} file${extras.length > 0 ? ` + ${extras.join(" + ")}` : ""} (${skipped} skipped)`);
1165
1188
  return { written, skipped };
1166
1189
  }
1167
1190
  function emitStaticFiles(clientDir, staticConfig, entries) {
@@ -1226,7 +1249,7 @@ async function renderStaticNotFound(options, env, log) {
1226
1249
  headers: { "x-moons-prerender": "1" }
1227
1250
  }));
1228
1251
  if (response.status !== 404) {
1229
- log(`[moons] prerender: ${NOT_FOUND_FILE} dilewati (sentinel \`${NOT_FOUND_SENTINEL}\` → ${response.status})`);
1252
+ log(`[moons] prerender: ${NOT_FOUND_FILE} skipped (sentinel \`${NOT_FOUND_SENTINEL}\` → ${response.status})`);
1230
1253
  return null;
1231
1254
  }
1232
1255
  const html = await response.text();
@@ -1245,11 +1268,11 @@ function assertNoLeak(html, pathname, file, options, env) {
1245
1268
  throw new MoonsError3({
1246
1269
  code: "E_PRERENDER_LEAK",
1247
1270
  file,
1248
- message: `hasil prerender \`${pathname}\` memuat ${labels}`,
1249
- details: "HTML statis permanen — rahasia yang ter-prerender tidak bisa ditarik kembali.",
1271
+ message: `prerender output \`${pathname}\` contains ${labels}`,
1272
+ details: "Static HTML is permanent — a prerendered secret cannot be retracted.",
1250
1273
  suggestions: [
1251
- "Jangan mengembalikan nilai env rahasia dari loader yang di-prerender",
1252
- "Matikan `prerender` untuk route ini atau pindahkan datanya ke serverFn request-time"
1274
+ "Do not return secret env values from a loader that runs at prerender time",
1275
+ "Turn off `prerender` for this route or move the data to a request-time serverFn"
1253
1276
  ]
1254
1277
  });
1255
1278
  }
@@ -1267,35 +1290,35 @@ async function resolvePathnames(route, module) {
1267
1290
  throw new MoonsError3({
1268
1291
  code: "E_PRERENDER_NO_PATHS",
1269
1292
  file: route.file,
1270
- message: "route dynamic ber-`prerender = true` tanpa `export const paths`",
1271
- details: "Prerender butuh daftar param untuk merender tiap path.",
1293
+ message: "dynamic route with `prerender = true` without `export const paths`",
1294
+ details: "Prerendering needs a param list to render each path.",
1272
1295
  suggestions: [
1273
- 'Tambahkan `export const paths = () => [{ id: "…" }]`',
1274
- "Atau hapus `prerender` agar dirender SSR saat request"
1296
+ 'Add `export const paths = () => [{ id: "…" }]`',
1297
+ "Or remove `prerender` so the route renders at request time (SSR)"
1275
1298
  ]
1276
1299
  });
1277
1300
  }
1278
1301
  const raw = await paths();
1279
1302
  if (!Array.isArray(raw)) {
1280
- throw prerenderError(route, "`paths()` harus mengembalikan array");
1303
+ throw prerenderError(route, "`paths()` must return an array");
1281
1304
  }
1282
1305
  const expected = new Set(route.params);
1283
1306
  const result = [];
1284
1307
  for (const item of raw) {
1285
1308
  if (item === null || typeof item !== "object" || Array.isArray(item)) {
1286
- throw prerenderError(route, "item `paths()` harus objek param string");
1309
+ throw prerenderError(route, "`paths()` items must be objects with string params");
1287
1310
  }
1288
1311
  const record = item;
1289
1312
  const keys = Object.keys(record);
1290
1313
  for (const name of expected) {
1291
1314
  const value = record[name];
1292
1315
  if (typeof value !== "string" || value.length === 0) {
1293
- throw prerenderError(route, `param \`${name}\` wajib string non-kosong`);
1316
+ throw prerenderError(route, `param \`${name}\` must be a non-empty string`);
1294
1317
  }
1295
1318
  }
1296
1319
  for (const key of keys) {
1297
1320
  if (!expected.has(key)) {
1298
- throw prerenderError(route, `param \`${key}\` tidak ada di pola route`);
1321
+ throw prerenderError(route, `param \`${key}\` is not in the route pattern`);
1299
1322
  }
1300
1323
  }
1301
1324
  result.push(buildPathname(route, record));
@@ -1328,7 +1351,7 @@ function buildPathname(route, params) {
1328
1351
  }
1329
1352
  function encodeSegment(route, value) {
1330
1353
  if (value.length === 0 || /^\.+$/.test(value)) {
1331
- throw prerenderError(route, `nilai param \`${value}\` tidak valid untuk path file`);
1354
+ throw prerenderError(route, `param value \`${value}\` is not valid for a file path`);
1332
1355
  }
1333
1356
  return encodeURIComponent(value);
1334
1357
  }
@@ -1339,9 +1362,9 @@ function outputFile(clientDir, pathname) {
1339
1362
  if (file !== base && !file.startsWith(base + sep2)) {
1340
1363
  throw new MoonsError3({
1341
1364
  code: "E_PRERENDER_INVALID",
1342
- message: `path prerender keluar dari direktori client (\`${pathname}\`)`,
1343
- details: "Nama file harus berada di dalam `dist/client`.",
1344
- suggestions: ["Periksa nilai param `paths` — jangan memakai `..`"]
1365
+ message: `prerender path escapes the client directory (\`${pathname}\`)`,
1366
+ details: "The file name must stay inside `dist/client`.",
1367
+ suggestions: ["Check the `paths` param values — do not use `..`"]
1345
1368
  });
1346
1369
  }
1347
1370
  return file;
@@ -1351,10 +1374,10 @@ function prerenderError(route, message) {
1351
1374
  code: "E_PRERENDER_INVALID",
1352
1375
  file: route.file,
1353
1376
  message,
1354
- details: "Kontrak prerender mengikuti.",
1377
+ details: "The prerender contract is enforced.",
1355
1378
  suggestions: [
1356
- "Perbaiki bentuk `paths`/param agar cocok dengan pola route",
1357
- "Jangan memakai `redirect()` pada route yang di-prerender"
1379
+ "Fix the `paths`/params shape so it matches the route pattern",
1380
+ "Do not use `redirect()` on a prerendered route"
1358
1381
  ]
1359
1382
  });
1360
1383
  }
@@ -1378,4 +1401,4 @@ export {
1378
1401
  toWebRequest2 as toWebRequest
1379
1402
  };
1380
1403
 
1381
- //# debugId=3FA2D69BB84FEC9664756E2164756E21
1404
+ //# debugId=01666BCB63B6268E64756E2164756E21