@moonsjs/vite 0.1.17 → 0.2.0

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.
Files changed (43) hide show
  1. package/dist/{index-v5sbd3az.js → index-0agjty3z.js} +21 -18
  2. package/dist/index-0agjty3z.js.map +15 -0
  3. package/dist/index-f8w073cm.js +30 -0
  4. package/dist/index-f8w073cm.js.map +10 -0
  5. package/dist/{index-snbxks72.js → index-tbph0859.js} +80 -80
  6. package/dist/{index-snbxks72.js.map → index-tbph0859.js.map} +4 -4
  7. package/dist/index.js +33 -33
  8. package/dist/index.js.map +4 -4
  9. package/dist/server/handler.d.ts.map +1 -1
  10. package/dist/server/index.js +104 -93
  11. package/dist/server/index.js.map +10 -10
  12. package/dist/server/test-server.d.ts +6 -0
  13. package/dist/server/test-server.d.ts.map +1 -1
  14. package/dist/testing-preload.d.ts +2 -0
  15. package/dist/testing-preload.d.ts.map +1 -0
  16. package/dist/testing-preload.js +10 -0
  17. package/dist/testing-preload.js.map +10 -0
  18. package/dist/testing.d.ts +11 -0
  19. package/dist/testing.d.ts.map +1 -0
  20. package/dist/testing.js +13 -0
  21. package/dist/testing.js.map +9 -0
  22. package/dist/transform.js +1 -1
  23. package/package.json +15 -5
  24. package/src/api.ts +14 -14
  25. package/src/devtools.ts +5 -5
  26. package/src/manifest.ts +2 -2
  27. package/src/plugin.ts +21 -21
  28. package/src/server/dev.ts +6 -1
  29. package/src/server/handler.ts +69 -47
  30. package/src/server/isr.ts +5 -5
  31. package/src/server/node-server.ts +6 -6
  32. package/src/server/node.ts +2 -2
  33. package/src/server/plugins.ts +1 -1
  34. package/src/server/prerender.ts +29 -29
  35. package/src/server/shell.ts +8 -8
  36. package/src/server/shutdown.ts +5 -5
  37. package/src/server/storage.ts +1 -1
  38. package/src/server/test-server.ts +12 -1
  39. package/src/server-only.ts +7 -7
  40. package/src/testing-preload.ts +5 -0
  41. package/src/testing.ts +34 -0
  42. package/src/transform.ts +76 -76
  43. package/dist/index-v5sbd3az.js.map +0 -15
@@ -13,7 +13,7 @@ import {
13
13
  DEFAULT_SECURITY_HEADERS,
14
14
  sendNodeResponse2,
15
15
  installDevMiddleware2
16
- } from "../index-v5sbd3az.js";
16
+ } from "../index-0agjty3z.js";
17
17
 
18
18
  // src/server/handler.ts
19
19
  import {
@@ -76,7 +76,7 @@ function createRequestHandler(options) {
76
76
  const entry = { page: pageKey, layouts: keys.slice(0, -1) };
77
77
  const searchParams = Object.fromEntries(new URL(request.url).searchParams);
78
78
  const meta = await runRouteMeta(pageModule, data[pageKey], resolved.route.file);
79
- const markup = renderToMarkup(entry, { page: pageModule, layouts: modules.slice(0, -1) }, resolved.params, data, searchParams);
79
+ const markup = renderToMarkup(entry, { page: pageModule, layouts: modules.slice(0, -1) }, resolved.params, data, searchParams, undefined, config.trailingSlash);
80
80
  const payload = encodeSsrPayload({
81
81
  key: pageKey,
82
82
  params: resolved.params,
@@ -85,7 +85,7 @@ function createRequestHandler(options) {
85
85
  ...meta === undefined ? {} : { meta }
86
86
  });
87
87
  warnPayloadFindings(payload, options.payloadScan, warnedPayloadLabels);
88
- return htmlResponse(await shell(markup, payload, request.url, meta), 200);
88
+ return htmlResponse(withTrailingSlashMeta(await shell(markup, payload, request.url, meta), config.trailingSlash), 200);
89
89
  }
90
90
  async function handleApiRequest(route, params, request, ctx) {
91
91
  const load = options.loadApi?.[route.path];
@@ -93,9 +93,9 @@ function createRequestHandler(options) {
93
93
  return apiErrorResponse(new MoonsError({
94
94
  code: "E_API_HANDLER_INVALID",
95
95
  file: route.file,
96
- message: `modul API \`${route.path}\` tidak bisa dimuat`,
97
- details: "Peta `loadApi` tidak memuat route ini (build/dev tidak sinkron).",
98
- 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"]
99
99
  }), route);
100
100
  }
101
101
  let mod;
@@ -108,9 +108,9 @@ function createRequestHandler(options) {
108
108
  return apiErrorResponse(new MoonsError({
109
109
  code: "E_API_HANDLER_INVALID",
110
110
  file: route.file,
111
- message: "modul API tidak mengekspor objek handler",
112
- details: `Modul \`${route.path}\` harus mengekspor handler method.`,
113
- 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"]
114
114
  }), route);
115
115
  }
116
116
  const record = mod;
@@ -137,9 +137,9 @@ function createRequestHandler(options) {
137
137
  return apiErrorResponse(new MoonsError({
138
138
  code: "E_API_HANDLER_INVALID",
139
139
  file: route.file,
140
- message: `handler \`${requestMethod}\` tidak mengembalikan \`Response\``,
141
- details: "Handler API wajib mengembalikan `new Response(...)`.",
142
- 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 })`"]
143
143
  }), route);
144
144
  }
145
145
  if (stripBody) {
@@ -156,15 +156,15 @@ function createRequestHandler(options) {
156
156
  const key = routeKeyFromFile(routesDir, notFoundRoute.file);
157
157
  const [module] = await loadModules([key], loadRoute);
158
158
  const payload = encodeSsrPayload({ key: NOT_FOUND_ROUTE_KEY, params: {}, data: {} });
159
- const markup = renderToMarkup({ page: key, layouts: [] }, { page: module, layouts: [] }, {}, {});
160
- return htmlResponse(await shell(markup, payload, request.url), 404);
159
+ const markup = renderToMarkup({ page: key, layouts: [] }, { page: module, layouts: [] }, {}, {}, {}, undefined, config.trailingSlash);
160
+ return htmlResponse(withTrailingSlashMeta(await shell(markup, payload, request.url), config.trailingSlash), 404);
161
161
  } catch (error) {
162
- console.error("[moons] gagal merender _404 kustom:", error);
162
+ console.error("[moons] failed to render custom _404:", error);
163
163
  }
164
164
  }
165
165
  const payload = encodeSsrPayload({ key: "", params: {}, data: {} });
166
- const html = await shell("<h1>404</h1><p>Halaman tidak ditemukan.</p>", payload, request.url);
167
- return htmlResponse(html, 404);
166
+ const html = await shell("<h1>404</h1><p>Page not found.</p>", payload, request.url);
167
+ return htmlResponse(withTrailingSlashMeta(html, config.trailingSlash), 404);
168
168
  }
169
169
  async function routeErrorResponse(error, request, resolved) {
170
170
  console.error("[moons] route error:", error);
@@ -181,10 +181,10 @@ function createRequestHandler(options) {
181
181
  data: {},
182
182
  ...serialized === undefined ? {} : { error: serialized }
183
183
  });
184
- const markup = renderToMarkup({ page: key, layouts: [] }, { page: module, layouts: [] }, {}, {}, {}, serialized === undefined ? undefined : reviveError(serialized));
185
- return htmlResponse(await shell(markup, payload, request.url), 500);
184
+ const markup = renderToMarkup({ page: key, layouts: [] }, { page: module, layouts: [] }, {}, {}, {}, serialized === undefined ? undefined : reviveError(serialized), config.trailingSlash);
185
+ return htmlResponse(withTrailingSlashMeta(await shell(markup, payload, request.url), config.trailingSlash), 500);
186
186
  } catch (renderError) {
187
- console.error("[moons] gagal merender _error kustom:", renderError);
187
+ console.error("[moons] failed to render custom _error:", renderError);
188
188
  }
189
189
  }
190
190
  return internalErrorResponse(error, { log: false });
@@ -198,9 +198,9 @@ function createRequestHandler(options) {
198
198
  throw new MoonsError({
199
199
  code: "E_MIDDLEWARE_INVALID",
200
200
  file: definition.file,
201
- message: "middleware direktori tidak bisa dimuat",
202
- details: "Peta `loadRoute` tidak memuat `_middleware.ts` ini.",
203
- 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"]
204
204
  });
205
205
  }
206
206
  const module = await load();
@@ -209,8 +209,8 @@ function createRequestHandler(options) {
209
209
  throw new MoonsError({
210
210
  code: "E_MIDDLEWARE_INVALID",
211
211
  file: definition.file,
212
- message: "`_middleware.ts` tidak meng-export default fungsi",
213
- 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.",
214
214
  suggestions: ["Export default `async ({ req, ctx, next }) => await next()`"]
215
215
  });
216
216
  }
@@ -226,9 +226,9 @@ function createRequestHandler(options) {
226
226
  throw new MoonsError({
227
227
  code: "E_MIDDLEWARE_INVALID",
228
228
  file: definition.file,
229
- message: "middleware API tidak bisa dimuat",
230
- details: "Peta `loadApiMiddleware` tidak memuat `_middleware.ts` ini.",
231
- 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"]
232
232
  });
233
233
  }
234
234
  const module = await load();
@@ -236,8 +236,8 @@ function createRequestHandler(options) {
236
236
  throw new MoonsError({
237
237
  code: "E_MIDDLEWARE_INVALID",
238
238
  file: definition.file,
239
- message: "`_middleware.ts` tidak meng-export default fungsi",
240
- 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.",
241
241
  suggestions: ["Export default `async ({ req, ctx, next }) => await next()`"]
242
242
  });
243
243
  }
@@ -268,7 +268,7 @@ function createRequestHandler(options) {
268
268
  return await handleApiRequest(apiMatch.route, apiMatch.params, request, ctx);
269
269
  }
270
270
  const middlewares = await loadApiDirMiddlewares(apiChain);
271
- 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);
272
272
  }
273
273
  }
274
274
  const resolved = resolveRoute(url.pathname, routes);
@@ -281,7 +281,7 @@ function createRequestHandler(options) {
281
281
  return await renderRoute(resolved, request, ctx);
282
282
  }
283
283
  const middlewares = await loadDirMiddlewares(dirChain);
284
- 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);
285
285
  } catch (error) {
286
286
  if (isRedirectSignal(error)) {
287
287
  return new Response(null, {
@@ -386,9 +386,9 @@ async function loadModules(keys, loadRoute) {
386
386
  if (load === undefined) {
387
387
  throw new MoonsError({
388
388
  code: "E_INTERNAL",
389
- message: `modul route \`${key}\` tidak terdaftar`,
390
- details: "Registry route tidak sinkron dengan hasil scan.",
391
- 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"]
392
392
  });
393
393
  }
394
394
  return await load();
@@ -403,11 +403,11 @@ async function runRouteMeta(pageModule, pageData, file) {
403
403
  throw new MoonsError({
404
404
  code: "E_INTERNAL",
405
405
  file,
406
- message: "export `meta` harus berupa fungsi `({ data }) => RouteMeta`",
407
- details: "Bentuk `meta` mengikuti ",
406
+ message: "export `meta` must be a function `({ data }) => RouteMeta`",
407
+ details: "The `meta` shape follows `RouteMeta`.",
408
408
  suggestions: [
409
- 'Ubah menjadi `export const meta = ({ data }) => ({ title: "..." })`',
410
- "Atau hapus export `meta`"
409
+ 'Change it to `export const meta = ({ data }) => ({ title: "..." })`',
410
+ "Or remove the `meta` export"
411
411
  ]
412
412
  });
413
413
  }
@@ -416,9 +416,9 @@ async function runRouteMeta(pageModule, pageData, file) {
416
416
  throw new MoonsError({
417
417
  code: "E_INTERNAL",
418
418
  file,
419
- message: "`meta()` harus mengembalikan objek RouteMeta",
420
- details: "Nilai yang dikembalikan dipakai untuk <title>/description/og.",
421
- 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" })`']
422
422
  });
423
423
  }
424
424
  return result;
@@ -427,13 +427,13 @@ function apiErrorResponse(error, route) {
427
427
  const isProduction = process.env["NODE_ENV"] === "production";
428
428
  if (error instanceof MoonsError) {
429
429
  console.error(`[moons] API ${route.path}: ${error.format()}`);
430
- return new Response(isProduction ? "Terjadi kesalahan internal." : error.format(), {
430
+ return new Response(isProduction ? "Internal server error." : error.format(), {
431
431
  status: 500,
432
432
  headers: { "content-type": "text/plain; charset=utf-8" }
433
433
  });
434
434
  }
435
435
  console.error(`[moons] API ${route.path}:`, error);
436
- return new Response(isProduction ? "Terjadi kesalahan internal." : String(error), {
436
+ return new Response(isProduction ? "Internal server error." : String(error), {
437
437
  status: 500,
438
438
  headers: { "content-type": "text/plain; charset=utf-8" }
439
439
  });
@@ -442,8 +442,15 @@ function internalErrorResponse(error, options = {}) {
442
442
  if (options.log !== false) {
443
443
  console.error("[moons] request error:", error);
444
444
  }
445
- const detail = process.env["NODE_ENV"] !== "production" && error instanceof MoonsError ? `<pre>${escapeHtml(error.format())}</pre>` : "<p>Terjadi kesalahan internal.</p>";
446
- 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);
447
+ }
448
+ function withTrailingSlashMeta(html, policy) {
449
+ if (policy === undefined) {
450
+ return html;
451
+ }
452
+ return html.replace("</head>", ` <meta name="moons:trailing-slash" content="${policy}" />
453
+ </head>`);
447
454
  }
448
455
  function htmlResponse(html, status) {
449
456
  return new Response(html, {
@@ -477,7 +484,7 @@ function warnPayloadFindings(payload, scan, warned) {
477
484
  continue;
478
485
  }
479
486
  warned.add(label);
480
- console.warn(`[moons] peringatan dev: payload SSR memuat ${label} — jangan mengembalikan rahasia dari loader.`);
487
+ console.warn(`[moons] dev warning: SSR payload contains ${label} — do not return secrets from loaders.`);
481
488
  }
482
489
  }
483
490
  // src/server/node-server.ts
@@ -517,7 +524,7 @@ async function runRevalidate(options) {
517
524
  headers: { "x-moons-prerender": "1" }
518
525
  }));
519
526
  if (response.status !== 200) {
520
- log(`[moons] isr: lewati ${options.pathname} (status ${response.status})`);
527
+ log(`[moons] isr: skipped ${options.pathname} (status ${response.status})`);
521
528
  return;
522
529
  }
523
530
  const html = await response.text();
@@ -527,7 +534,7 @@ async function runRevalidate(options) {
527
534
  });
528
535
  if (findings.length > 0) {
529
536
  const labels = findings.map((finding) => finding.kind === "canary" ? "canary" : `env(${finding.label})`).join(", ");
530
- log(`[moons] isr: TIDAK menulis ${options.pathname} — memuat ${labels}`);
537
+ log(`[moons] isr: NOT writing ${options.pathname} — contains ${labels}`);
531
538
  return;
532
539
  }
533
540
  if (options.storage !== undefined) {
@@ -535,14 +542,14 @@ async function runRevalidate(options) {
535
542
  return;
536
543
  }
537
544
  if (options.file === undefined) {
538
- log(`[moons] isr: lewati ${options.pathname} — tanpa file/storage tujuan`);
545
+ log(`[moons] isr: skipped ${options.pathname} — no destination file/storage`);
539
546
  return;
540
547
  }
541
548
  const tmp = `${options.file}.tmp-${process.pid}`;
542
549
  writeFileSync(tmp, html);
543
550
  renameSync(tmp, options.file);
544
551
  } catch (error) {
545
- log(`[moons] isr: gagal regenerasi ${options.pathname}: ${String(error)}`);
552
+ log(`[moons] isr: failed to regenerate ${options.pathname}: ${String(error)}`);
546
553
  }
547
554
  }
548
555
 
@@ -562,7 +569,7 @@ function createFileStorage(clientDir) {
562
569
  async write(pathname, body) {
563
570
  const target = staticFileTarget(clientDir, pathname);
564
571
  if (target === null) {
565
- throw new Error(`[moons] isr: path tidak valid untuk ditulis: ${pathname}`);
572
+ throw new Error(`[moons] isr: invalid path to write: ${pathname}`);
566
573
  }
567
574
  mkdirSync(dirname(target), { recursive: true });
568
575
  const tmp = `${target}.tmp-${process.pid}`;
@@ -645,9 +652,9 @@ function installGracefulShutdown(server, options = {}) {
645
652
  return;
646
653
  }
647
654
  shuttingDown = true;
648
- log(`[moons] menerima ${signal} — berhenti menerima koneksi, drain...`);
655
+ log(`[moons] received ${signal} — no longer accepting connections, draining...`);
649
656
  const timer = setTimeout(() => {
650
- log(`[moons] drain melewati ${timeoutMs}ms — keluar paksa`);
657
+ log(`[moons] drain exceeded ${timeoutMs}ms — forcing exit`);
651
658
  const closeAll = server.closeAllConnections;
652
659
  try {
653
660
  closeAll?.call(server);
@@ -661,7 +668,7 @@ function installGracefulShutdown(server, options = {}) {
661
668
  server.close(async (error) => {
662
669
  if (error !== undefined) {
663
670
  clearTimeout(timer);
664
- log(`[moons] penutupan server gagal: ${error.message}`);
671
+ log(`[moons] server close failed: ${error.message}`);
665
672
  exit(1);
666
673
  return;
667
674
  }
@@ -670,11 +677,11 @@ function installGracefulShutdown(server, options = {}) {
670
677
  try {
671
678
  await options.onShutdown();
672
679
  } catch (hookError) {
673
- log(`[moons] onShutdown gagal: ${String(hookError)}`);
680
+ log(`[moons] onShutdown failed: ${String(hookError)}`);
674
681
  }
675
682
  }
676
683
  clearTimeout(timer);
677
- log("[moons] shutdown selesai (drain penuh)");
684
+ log("[moons] shutdown complete (full drain)");
678
685
  exit(0);
679
686
  });
680
687
  };
@@ -921,11 +928,11 @@ function rpcBodyTooLargeText(limit, size) {
921
928
  const megabytes = (bytes) => (bytes / 1e6).toFixed(1);
922
929
  return new MoonsError2({
923
930
  code: "E_RPC_BODY_TOO_LARGE",
924
- message: size === null ? `body melebihi limit ${megabytes(limit)} MB` : `body ${megabytes(size)} MB melebihi limit ${megabytes(limit)} MB`,
925
- details: "Body melebihi `security.bodyLimitBytes`; pembacaan dihentikan di transport sebelum penuh.",
931
+ message: size === null ? `body exceeds the ${megabytes(limit)} MB limit` : `body ${megabytes(size)} MB exceeds the ${megabytes(limit)} MB limit`,
932
+ details: "Body exceeds `security.bodyLimitBytes`; reading stops at the transport before the body is fully buffered.",
926
933
  suggestions: [
927
- "Kurangi ukuran payload, atau",
928
- "Naikkan security.bodyLimitBytes jika memang perlu"
934
+ "Reduce the payload size, or",
935
+ "Increase security.bodyLimitBytes if you really need to"
929
936
  ]
930
937
  }).format();
931
938
  }
@@ -1030,7 +1037,7 @@ function scheduleRevalidation(input) {
1030
1037
  if (logger !== null) {
1031
1038
  logger({ msg: "isr:revalidate-failed", level: "warn", path: normalized, error: message });
1032
1039
  } else {
1033
- console.error(`[moons] isr regenerasi gagal (${normalized}): ${message}`);
1040
+ console.error(`[moons] isr regeneration failed (${normalized}): ${message}`);
1034
1041
  }
1035
1042
  });
1036
1043
  }
@@ -1042,6 +1049,10 @@ function createTestServer(options, baseUrl = "http://localhost") {
1042
1049
  config: options.config,
1043
1050
  routesDir: options.routesDir,
1044
1051
  loadRoute: options.loadRoute,
1052
+ ...options.apiRoutes === undefined ? {} : { apiRoutes: options.apiRoutes },
1053
+ ...options.loadApi === undefined ? {} : { loadApi: options.loadApi },
1054
+ ...options.apiMiddlewares === undefined ? {} : { apiMiddlewares: options.apiMiddlewares },
1055
+ ...options.loadApiMiddleware === undefined ? {} : { loadApiMiddleware: options.loadApiMiddleware },
1045
1056
  shell
1046
1057
  });
1047
1058
  return {
@@ -1091,7 +1102,7 @@ function createPluginRuntime(options) {
1091
1102
  try {
1092
1103
  await plugin.onShutdown?.(app);
1093
1104
  } catch (error) {
1094
- log(`[moons] plugin \`${plugin.name}\` gagal saat onShutdown: ${String(error)}`);
1105
+ log(`[moons] plugin \`${plugin.name}\` failed during onShutdown: ${String(error)}`);
1095
1106
  }
1096
1107
  }
1097
1108
  },
@@ -1133,18 +1144,18 @@ async function runPrerender(options) {
1133
1144
  throw new MoonsError3({
1134
1145
  code: "E_ISR_INVALID",
1135
1146
  file: route.file,
1136
- message: "`revalidate` tidak valid atau dipakai tanpa `prerender = true`",
1137
- details: "ISR berbasis file: `revalidate` (detik, number >= 0) + `prerender`.",
1147
+ message: "`revalidate` is invalid or used without `prerender = true`",
1148
+ details: "File-based ISR: `revalidate` (seconds, number >= 0) + `prerender`.",
1138
1149
  suggestions: [
1139
- "Gunakan angka detik, mis. `export const revalidate = 60`",
1140
- "Tambahkan `export const prerender = true` pada page yang sama"
1150
+ "Use a number of seconds, e.g. `export const revalidate = 60`",
1151
+ "Add `export const prerender = true` to the same page"
1141
1152
  ]
1142
1153
  });
1143
1154
  }
1144
1155
  }
1145
1156
  if (prerender !== true) {
1146
1157
  if (prerender !== undefined && prerender !== false) {
1147
- throw prerenderError(route, "`prerender` harus boolean (`true`/`false`)");
1158
+ throw prerenderError(route, "`prerender` must be a boolean (`true`/`false`)");
1148
1159
  }
1149
1160
  continue;
1150
1161
  }
@@ -1155,15 +1166,15 @@ async function runPrerender(options) {
1155
1166
  headers: { "x-moons-prerender": "1" }
1156
1167
  }));
1157
1168
  if (response.status === 404) {
1158
- log(`[moons] prerender: lewati ${pathname} (notFound)`);
1169
+ log(`[moons] prerender: skipped ${pathname} (notFound)`);
1159
1170
  skipped += 1;
1160
1171
  continue;
1161
1172
  }
1162
1173
  if (response.status >= 300 && response.status < 400) {
1163
- throw prerenderError(route, `\`redirect()\` tidak didukung saat prerender (${pathname})`);
1174
+ throw prerenderError(route, `\`redirect()\` is not supported during prerender (${pathname})`);
1164
1175
  }
1165
1176
  if (response.status !== 200) {
1166
- throw prerenderError(route, `status ${response.status} saat merender ${pathname}`);
1177
+ throw prerenderError(route, `status ${response.status} while rendering ${pathname}`);
1167
1178
  }
1168
1179
  const html = await response.text();
1169
1180
  assertNoLeak(html, pathname, route.file, options, env);
@@ -1184,7 +1195,7 @@ async function runPrerender(options) {
1184
1195
  `);
1185
1196
  const emitted = emitStaticFiles(options.clientDir, options.staticConfig, entries);
1186
1197
  const extras = [...wrote404 ? [NOT_FOUND_FILE] : [], ...emitted];
1187
- log(`[moons] prerender selesai: ${written} file${extras.length > 0 ? ` + ${extras.join(" + ")}` : ""} (${skipped} dilewati)`);
1198
+ log(`[moons] prerender done: ${written} file${extras.length > 0 ? ` + ${extras.join(" + ")}` : ""} (${skipped} skipped)`);
1188
1199
  return { written, skipped };
1189
1200
  }
1190
1201
  function emitStaticFiles(clientDir, staticConfig, entries) {
@@ -1249,7 +1260,7 @@ async function renderStaticNotFound(options, env, log) {
1249
1260
  headers: { "x-moons-prerender": "1" }
1250
1261
  }));
1251
1262
  if (response.status !== 404) {
1252
- log(`[moons] prerender: ${NOT_FOUND_FILE} dilewati (sentinel \`${NOT_FOUND_SENTINEL}\` → ${response.status})`);
1263
+ log(`[moons] prerender: ${NOT_FOUND_FILE} skipped (sentinel \`${NOT_FOUND_SENTINEL}\` → ${response.status})`);
1253
1264
  return null;
1254
1265
  }
1255
1266
  const html = await response.text();
@@ -1268,11 +1279,11 @@ function assertNoLeak(html, pathname, file, options, env) {
1268
1279
  throw new MoonsError3({
1269
1280
  code: "E_PRERENDER_LEAK",
1270
1281
  file,
1271
- message: `hasil prerender \`${pathname}\` memuat ${labels}`,
1272
- details: "HTML statis permanen — rahasia yang ter-prerender tidak bisa ditarik kembali.",
1282
+ message: `prerender output \`${pathname}\` contains ${labels}`,
1283
+ details: "Static HTML is permanent — a prerendered secret cannot be retracted.",
1273
1284
  suggestions: [
1274
- "Jangan mengembalikan nilai env rahasia dari loader yang di-prerender",
1275
- "Matikan `prerender` untuk route ini atau pindahkan datanya ke serverFn request-time"
1285
+ "Do not return secret env values from a loader that runs at prerender time",
1286
+ "Turn off `prerender` for this route or move the data to a request-time serverFn"
1276
1287
  ]
1277
1288
  });
1278
1289
  }
@@ -1290,35 +1301,35 @@ async function resolvePathnames(route, module) {
1290
1301
  throw new MoonsError3({
1291
1302
  code: "E_PRERENDER_NO_PATHS",
1292
1303
  file: route.file,
1293
- message: "route dynamic ber-`prerender = true` tanpa `export const paths`",
1294
- details: "Prerender butuh daftar param untuk merender tiap path.",
1304
+ message: "dynamic route with `prerender = true` without `export const paths`",
1305
+ details: "Prerendering needs a param list to render each path.",
1295
1306
  suggestions: [
1296
- 'Tambahkan `export const paths = () => [{ id: "…" }]`',
1297
- "Atau hapus `prerender` agar dirender SSR saat request"
1307
+ 'Add `export const paths = () => [{ id: "…" }]`',
1308
+ "Or remove `prerender` so the route renders at request time (SSR)"
1298
1309
  ]
1299
1310
  });
1300
1311
  }
1301
1312
  const raw = await paths();
1302
1313
  if (!Array.isArray(raw)) {
1303
- throw prerenderError(route, "`paths()` harus mengembalikan array");
1314
+ throw prerenderError(route, "`paths()` must return an array");
1304
1315
  }
1305
1316
  const expected = new Set(route.params);
1306
1317
  const result = [];
1307
1318
  for (const item of raw) {
1308
1319
  if (item === null || typeof item !== "object" || Array.isArray(item)) {
1309
- throw prerenderError(route, "item `paths()` harus objek param string");
1320
+ throw prerenderError(route, "`paths()` items must be objects with string params");
1310
1321
  }
1311
1322
  const record = item;
1312
1323
  const keys = Object.keys(record);
1313
1324
  for (const name of expected) {
1314
1325
  const value = record[name];
1315
1326
  if (typeof value !== "string" || value.length === 0) {
1316
- throw prerenderError(route, `param \`${name}\` wajib string non-kosong`);
1327
+ throw prerenderError(route, `param \`${name}\` must be a non-empty string`);
1317
1328
  }
1318
1329
  }
1319
1330
  for (const key of keys) {
1320
1331
  if (!expected.has(key)) {
1321
- throw prerenderError(route, `param \`${key}\` tidak ada di pola route`);
1332
+ throw prerenderError(route, `param \`${key}\` is not in the route pattern`);
1322
1333
  }
1323
1334
  }
1324
1335
  result.push(buildPathname(route, record));
@@ -1351,7 +1362,7 @@ function buildPathname(route, params) {
1351
1362
  }
1352
1363
  function encodeSegment(route, value) {
1353
1364
  if (value.length === 0 || /^\.+$/.test(value)) {
1354
- throw prerenderError(route, `nilai param \`${value}\` tidak valid untuk path file`);
1365
+ throw prerenderError(route, `param value \`${value}\` is not valid for a file path`);
1355
1366
  }
1356
1367
  return encodeURIComponent(value);
1357
1368
  }
@@ -1362,9 +1373,9 @@ function outputFile(clientDir, pathname) {
1362
1373
  if (file !== base && !file.startsWith(base + sep2)) {
1363
1374
  throw new MoonsError3({
1364
1375
  code: "E_PRERENDER_INVALID",
1365
- message: `path prerender keluar dari direktori client (\`${pathname}\`)`,
1366
- details: "Nama file harus berada di dalam `dist/client`.",
1367
- suggestions: ["Periksa nilai param `paths` — jangan memakai `..`"]
1376
+ message: `prerender path escapes the client directory (\`${pathname}\`)`,
1377
+ details: "The file name must stay inside `dist/client`.",
1378
+ suggestions: ["Check the `paths` param values — do not use `..`"]
1368
1379
  });
1369
1380
  }
1370
1381
  return file;
@@ -1374,10 +1385,10 @@ function prerenderError(route, message) {
1374
1385
  code: "E_PRERENDER_INVALID",
1375
1386
  file: route.file,
1376
1387
  message,
1377
- details: "Kontrak prerender mengikuti.",
1388
+ details: "The prerender contract is enforced.",
1378
1389
  suggestions: [
1379
- "Perbaiki bentuk `paths`/param agar cocok dengan pola route",
1380
- "Jangan memakai `redirect()` pada route yang di-prerender"
1390
+ "Fix the `paths`/params shape so it matches the route pattern",
1391
+ "Do not use `redirect()` on a prerendered route"
1381
1392
  ]
1382
1393
  });
1383
1394
  }
@@ -1401,4 +1412,4 @@ export {
1401
1412
  toWebRequest2 as toWebRequest
1402
1413
  };
1403
1414
 
1404
- //# debugId=49CA3636C1DE25F564756E2164756E21
1415
+ //# debugId=ACB67C3B410A13D264756E2164756E21