@moonsjs/vite 0.1.17 → 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.
@@ -5,7 +5,7 @@ import { join, resolve } from "node:path";
5
5
  var ROOT_PLACEHOLDER2 = '<div id="root"></div>';
6
6
  var DEFAULT_TEMPLATE2 = [
7
7
  "<!doctype html>",
8
- '<html lang="id">',
8
+ '<html lang="en">',
9
9
  "<head>",
10
10
  ' <meta charset="utf-8" />',
11
11
  ' <meta name="viewport" content="width=device-width, initial-scale=1" />',
@@ -23,11 +23,11 @@ function injectShell2(template, markup, payload, meta) {
23
23
  if (!template.includes(ROOT_PLACEHOLDER2)) {
24
24
  throw new MoonsError({
25
25
  code: "E_INTERNAL",
26
- message: `shell HTML tidak memuat \`${ROOT_PLACEHOLDER2}\``,
27
- details: "index.html aplikasi wajib memuat elemen root tersebut agar SSR bisa meng-inject markup.",
26
+ message: `shell HTML does not contain \`${ROOT_PLACEHOLDER2}\``,
27
+ details: "The app's index.html must contain that root element so SSR can inject markup.",
28
28
  suggestions: [
29
- `Tambahkan ${ROOT_PLACEHOLDER2} ke index.html`,
30
- "Atau hapus index.html untuk memakai shell bawaan"
29
+ `Add ${ROOT_PLACEHOLDER2} to index.html`,
30
+ "Or remove index.html to use the built-in shell"
31
31
  ]
32
32
  });
33
33
  }
@@ -81,9 +81,9 @@ function createFileShell2(options = {}) {
81
81
  throw new MoonsError({
82
82
  code: "E_INTERNAL",
83
83
  file,
84
- message: "`dist/client/index.html` tidak ditemukan",
85
- details: "Build client harus dijalankan sebelum server produksi dinyalakan.",
86
- suggestions: ["Jalankan `bun run build` lebih dulu"]
84
+ message: "`dist/client/index.html` not found",
85
+ details: "The client build must run before starting the production server.",
86
+ suggestions: ["Run `bun run build` first"]
87
87
  });
88
88
  }
89
89
  cached = readFileSync(file, "utf8");
@@ -108,7 +108,7 @@ class InvalidRequestError extends Error {
108
108
  class BodyLimitError extends Error {
109
109
  limit;
110
110
  constructor(limit) {
111
- super(`request body melebihi batas ${limit} byte`);
111
+ super(`request body exceeds the ${limit} byte limit`);
112
112
  this.name = "BodyLimitError";
113
113
  this.limit = limit;
114
114
  }
@@ -122,7 +122,7 @@ function resolveRequestUrl(req, options = {}) {
122
122
  try {
123
123
  return new URL(`${proto ?? "http"}://${host}${req.url ?? "/"}`);
124
124
  } catch {
125
- throw new InvalidRequestError(`header Host tidak valid: ${JSON.stringify(hostHeader)}`);
125
+ throw new InvalidRequestError(`invalid Host header: ${JSON.stringify(hostHeader)}`);
126
126
  }
127
127
  }
128
128
  function resolveClientIp(req, trustProxy) {
@@ -298,13 +298,13 @@ function emptyDevtoolsData() {
298
298
  return { routes: [], apiRoutes: [], serverFns: [], canary: false, version: "" };
299
299
  }
300
300
  function renderDevtoolsHtml(data) {
301
- const rows = (items) => items.length === 0 ? '<tr><td colspan="3" class="muted">(kosong)</td></tr>' : items.map((item) => `<tr><td><code>${esc(item.path)}</code></td><td><span class="kind k-${esc(item.kind)}">${esc(item.kind)}</span></td><td class="muted">${esc(item.file)}</td></tr>`).join(`
301
+ const rows = (items) => items.length === 0 ? '<tr><td colspan="3" class="muted">(empty)</td></tr>' : items.map((item) => `<tr><td><code>${esc(item.path)}</code></td><td><span class="kind k-${esc(item.kind)}">${esc(item.kind)}</span></td><td class="muted">${esc(item.file)}</td></tr>`).join(`
302
302
  `);
303
- const fnRows = data.serverFns.length === 0 ? '<tr><td colspan="3" class="muted">(tidak ada serverFn)</td></tr>' : data.serverFns.map((fn) => `<tr><td><code>${esc(fn.hash)}</code></td><td><code>${esc(fn.name)}</code></td><td class="muted">${esc(fn.file)}</td></tr>`).join(`
303
+ const fnRows = data.serverFns.length === 0 ? '<tr><td colspan="3" class="muted">(no serverFn)</td></tr>' : data.serverFns.map((fn) => `<tr><td><code>${esc(fn.hash)}</code></td><td><code>${esc(fn.name)}</code></td><td class="muted">${esc(fn.file)}</td></tr>`).join(`
304
304
  `);
305
- const canary = data.canary ? '<span class="ok">aktif</span> (canary terpasang)' : '<span class="muted">nonaktif</span> (tanpa canary)';
305
+ const canary = data.canary ? '<span class="ok">enabled</span> (canary installed)' : '<span class="muted">disabled</span> (no canary)';
306
306
  return `<!doctype html>
307
- <html lang="id">
307
+ <html lang="en">
308
308
  <head>
309
309
  <meta charset="utf-8">
310
310
  <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -398,7 +398,7 @@ function installDevMiddleware2(server, options) {
398
398
  } catch (error) {
399
399
  res.statusCode = 500;
400
400
  res.setHeader("content-type", "text/plain; charset=utf-8");
401
- res.end(`[moons] devtools gagal dirender: ${String(error)}`);
401
+ res.end(`[moons] devtools failed to render: ${String(error)}`);
402
402
  }
403
403
  return;
404
404
  }
@@ -497,7 +497,7 @@ function renderApiModule(apiRoutes, apiMiddlewares, projectRoot) {
497
497
  const middlewareEntries = apiMiddlewares.map((middleware) => ` ${JSON.stringify(middleware.file)}: () => import(${JSON.stringify(rootSpecifier(projectRoot, middleware.file))}),`).join(`
498
498
  `);
499
499
  return [
500
- "// Generated by @moons. Jangan diubah manual.",
500
+ "// Generated by @moons. Do not edit manually.",
501
501
  `export const apiRoutes = ${JSON.stringify(apiRoutes, null, 2)};`,
502
502
  `export const apiMiddlewares = ${JSON.stringify(apiMiddlewares, null, 2)};`,
503
503
  "export const loadApi = {",
@@ -517,7 +517,7 @@ function renderRoutesModule(routes, routesDir, projectRoot) {
517
517
  return ` ${JSON.stringify(key)}: () => import(${JSON.stringify(rootSpecifier(projectRoot, route.file))}),`;
518
518
  });
519
519
  return [
520
- "// Generated by @moons. Jangan diubah manual.",
520
+ "// Generated by @moons. Do not edit manually.",
521
521
  `export const routes = ${JSON.stringify(routes, null, 2)};`,
522
522
  "",
523
523
  `export const clientManifest = ${JSON.stringify(manifest, null, 2)};`,
@@ -537,4 +537,4 @@ function rootSpecifier(projectRoot, file) {
537
537
 
538
538
  export { NOT_FOUND_ROUTE_KEY, renderApiModule, renderRoutesModule, rootSpecifier, VIRTUAL_MODULES, fromNullPrefixed, ROOT_PLACEHOLDER2, DEFAULT_TEMPLATE2, injectShell2, createDevShell2, createFileShell2, InvalidRequestError, BodyLimitError, resolveRequestUrl, resolveClientIp, toWebRequest2, DEFAULT_SECURITY_HEADERS, sendNodeResponse2, installDevMiddleware2 };
539
539
 
540
- //# debugId=9EF4B5AD3ED4C6D264756E2164756E21
540
+ //# debugId=E364F1DD2BD3F4F264756E2164756E21
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/server/shell.ts", "../src/server/node.ts", "../src/virtual-modules.ts", "../src/server/dev.ts", "../src/devtools.ts", "../src/manifest.ts"],
4
+ "sourcesContent": [
5
+ "import { MoonsError } from \"@moonsjs/core\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { ViteDevServer } from \"vite\";\n\nexport type Shell = (\n markup: string,\n payload: string,\n url: string,\n meta?: Record<string, unknown>,\n) => string | Promise<string>;\n\nexport const ROOT_PLACEHOLDER = '<div id=\"root\"></div>';\n\nexport const DEFAULT_TEMPLATE = [\n \"<!doctype html>\",\n '<html lang=\"en\">',\n \"<head>\",\n ' <meta charset=\"utf-8\" />',\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\n \" <title>Moons</title>\",\n \"</head>\",\n \"<body>\",\n ` ${ROOT_PLACEHOLDER}`,\n ' <script type=\"module\" src=\"/src/client/main.tsx\"></script>',\n \"</body>\",\n \"</html>\",\n \"\",\n].join(\"\\n\");\n\n/** Inject markup SSR + payload devalue ke shell HTML (PRD §8.2, §17, Addendum B.1). */\nexport function injectShell(\n template: string,\n markup: string,\n payload: string,\n meta?: Record<string, unknown>,\n): string {\n if (!template.includes(ROOT_PLACEHOLDER)) {\n throw new MoonsError({\n code: \"E_INTERNAL\",\n message: `shell HTML does not contain \\`${ROOT_PLACEHOLDER}\\``,\n details: \"The app's index.html must contain that root element so SSR can inject markup.\",\n suggestions: [\n `Add ${ROOT_PLACEHOLDER} to index.html`,\n \"Or remove index.html to use the built-in shell\",\n ],\n });\n }\n const dataScript = `<script id=\"__moons_data\" type=\"application/x-moons\">${payload}</script>`;\n const withRoot = template.replace(ROOT_PLACEHOLDER, `<div id=\"root\">${markup}</div>${dataScript}`);\n return applyMeta(withRoot, meta);\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\");\n}\n\n/**\n * Terapkan meta route ke `<head>` (Addendum B.1): `<title>` + description/og.\n * Tag yang sudah ada (mis. di index.html) ditimpa; yang belum ada disisipkan\n * sebelum `</head>`.\n */\nfunction applyMeta(html: string, meta: Record<string, unknown> | undefined): string {\n if (meta === undefined) {\n return html;\n }\n let out = html;\n if (typeof meta.title === \"string\") {\n const title = `<title>${escapeHtml(meta.title)}</title>`;\n out = /<title>[\\s\\S]*?<\\/title>/.test(out)\n ? out.replace(/<title>[\\s\\S]*?<\\/title>/, title)\n : out.replace(\"</head>\", ` ${title}\\n</head>`);\n }\n\n const tags: Array<{ attr: \"name\" | \"property\"; key: string; value: unknown }> = [\n { attr: \"name\", key: \"description\", value: meta.description },\n { attr: \"property\", key: \"og:title\", value: meta.title },\n { attr: \"property\", key: \"og:description\", value: meta.description },\n ];\n for (const tag of tags) {\n if (typeof tag.value !== \"string\") {\n continue;\n }\n const rendered = `<meta ${tag.attr}=\"${tag.key}\" content=\"${escapeHtml(tag.value)}\" />`;\n const pattern = new RegExp(`<meta[^>]*${tag.attr}=\"${tag.key}\"[^>]*>`);\n out = pattern.test(out)\n ? out.replace(pattern, rendered)\n : out.replace(\"</head>\", ` ${rendered}\\n</head>`);\n }\n return out;\n}\n\n/**\n * Shell untuk dev: baca `index.html` dari disk setiap request (selalu segar)\n * lalu lewatkan ke Vite `transformIndexHtml` (inject `/@vite/client`, dsb).\n */\nexport function createDevShell(server: ViteDevServer, options: { templatePath: string }): Shell {\n return async (markup, payload, url, meta) => {\n const template = existsSync(options.templatePath)\n ? readFileSync(options.templatePath, \"utf8\")\n : DEFAULT_TEMPLATE;\n const html = injectShell(template, markup, payload, meta);\n return await server.transformIndexHtml(url, html);\n };\n}\n\n/** Shell untuk produksi: baca `dist/client/index.html` sekali dari cwd. */\nexport function createFileShell(options: { clientDir?: string } = {}): Shell {\n let cached: string | null = null;\n return async (markup, payload, _url, meta) => {\n if (cached === null) {\n const clientDir = resolve(options.clientDir ?? process.cwd(), \"dist/client\");\n const file = join(clientDir, \"index.html\");\n if (!existsSync(file)) {\n throw new MoonsError({\n code: \"E_INTERNAL\",\n file,\n message: \"`dist/client/index.html` not found\",\n details: \"The client build must run before starting the production server.\",\n suggestions: [\"Run `bun run build` first\"],\n });\n }\n cached = readFileSync(file, \"utf8\");\n }\n return injectShell(cached, markup, payload, meta);\n };\n}\n",
6
+ "import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { CLIENT_IP_HEADER } from \"@moonsjs/core\";\n\n/** Batas transport default (Addendum J.2); RPC punya batas lebih kecil dari config. */\nexport const MAX_TRANSPORT_BODY_BYTES = 32 * 1024 * 1024;\n\n/** Request tidak valid (Host/URL malformed) → dipetakan ke 400 oleh server. */\nexport class InvalidRequestError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidRequestError\";\n }\n}\n\n/** Body melebihi batas → dipetakan ke 413; pembacaan dihentikan saat melewati. */\nexport class BodyLimitError extends Error {\n readonly limit: number;\n constructor(limit: number) {\n super(`request body exceeds the ${limit} byte limit`);\n this.name = \"BodyLimitError\";\n this.limit = limit;\n }\n}\n\nexport interface ToWebRequestOptions {\n /** Addendum J.1: percayai `x-forwarded-proto`/`x-forwarded-host` (default false). */\n trustProxy?: boolean;\n /** Batas pembacaan body (byte). Default: `MAX_TRANSPORT_BODY_BYTES`. */\n maxBodyBytes?: number;\n}\n\n/**\n * Rekonstruksi URL request (Addendum J.1). Tanpa `trustProxy`, origin/host\n * murni dari header `Host` (perilaku lama, kebal spoofing `x-forwarded-*`).\n * Malformed → `InvalidRequestError` (server memetakan ke 400).\n */\nexport function resolveRequestUrl(\n req: IncomingMessage,\n options: ToWebRequestOptions = {},\n): URL {\n const hostHeader = req.headers.host ?? \"localhost\";\n const trustProxy = options.trustProxy === true;\n const proto = trustProxy ? forwardedProtocol(req.headers[\"x-forwarded-proto\"]) : null;\n const forwardedHostValue = trustProxy ? forwardedHost(req.headers[\"x-forwarded-host\"]) : null;\n const host = forwardedHostValue ?? hostHeader;\n try {\n return new URL(`${proto ?? \"http\"}://${host}${req.url ?? \"/\"}`);\n } catch {\n throw new InvalidRequestError(`invalid Host header: ${JSON.stringify(hostHeader)}`);\n }\n}\n\n/** IP klien tepercaya (Addendum J.1): XFF (hanya bila trustProxy) → socket. */\nexport function resolveClientIp(req: IncomingMessage, trustProxy: boolean): string | null {\n if (trustProxy) {\n const forwarded = firstHeaderValue(req.headers[\"x-forwarded-for\"]);\n if (forwarded !== null && isPlausibleIp(forwarded)) {\n return normalizeIp(forwarded);\n }\n }\n const remote = req.socket?.remoteAddress ?? null;\n return remote === null ? null : normalizeIp(remote);\n}\n\n/** `::ffff:127.0.0.1` → `127.0.0.1`; buang zone id (`%eth0`). */\nfunction normalizeIp(value: string): string {\n const withoutZone = stripZoneId(value);\n return withoutZone.startsWith(\"::ffff:\") ? withoutZone.slice(7) : withoutZone;\n}\n\n/** Konversi request Node → Web `Request` (body diteruskan untuk non-GET/HEAD, §7.3 + J.1/J.2). */\nexport async function toWebRequest(\n req: IncomingMessage,\n options: ToWebRequestOptions = {},\n): Promise<Request> {\n const url = resolveRequestUrl(req, options);\n const method = req.method ?? \"GET\";\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined || key === \"content-length\" || key === CLIENT_IP_HEADER) {\n // content-length tidak disalin; fetch menghitung ulang dari body.\n // x-moons-client-ip selalu ditimpa nilai tepercaya di bawah.\n continue;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(key, item);\n }\n } else {\n headers.set(key, value);\n }\n }\n\n const clientIp = resolveClientIp(req, options.trustProxy === true);\n if (clientIp !== null) {\n headers.set(CLIENT_IP_HEADER, clientIp);\n }\n\n if (method === \"GET\" || method === \"HEAD\") {\n return new Request(url, { method, headers });\n }\n\n const body = await readBody(req, options.maxBodyBytes ?? MAX_TRANSPORT_BODY_BYTES);\n // View zero-copy (bukan salinan) — Buffer selalu di atas ArrayBuffer biasa.\n const view = new Uint8Array(body.buffer as ArrayBuffer, body.byteOffset, body.byteLength);\n return new Request(url, { method, headers, body: view });\n}\n\n/**\n * Baca body dengan batas tegas (Addendum J.2): begitu melewati `maxBytes`,\n * pembacaan dihentikan (pause) dan `BodyLimitError` dilempar — tidak pernah\n * seluruh body besar masuk memori, dan hanya satu salinan buffer dibuat.\n */\nexport function readBody(req: IncomingMessage, maxBytes: number): Promise<Buffer> {\n return new Promise((resolvePromise, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let settled = false;\n\n const fail = (error: Error): void => {\n if (settled) {\n return;\n }\n settled = true;\n req.pause();\n reject(error);\n };\n\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) {\n return;\n }\n size += chunk.length;\n if (size > maxBytes) {\n fail(new BodyLimitError(maxBytes));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (!settled) {\n settled = true;\n resolvePromise(Buffer.concat(chunks));\n }\n });\n req.on(\"error\", (error) => fail(error));\n });\n}\n\n/** Addendum J.7: default aman runtime; `config.headers` tetap menang (cek hasHeader). */\nexport const DEFAULT_SECURITY_HEADERS: Record<string, string> = {\n \"x-content-type-options\": \"nosniff\",\n \"referrer-policy\": \"strict-origin-when-cross-origin\",\n};\n\n/** Tulis Web `Response` ke `ServerResponse` Node (streaming bila ada body). */\nexport async function sendNodeResponse(res: ServerResponse, response: Response): Promise<void> {\n res.statusCode = response.status;\n response.headers.forEach((value, key) => {\n // `set-cookie` ditangani terpisah: forEach menggabungkan nilai ganda\n // menjadi satu string (cookie kedua hilang — D.2).\n if (key === \"set-cookie\") {\n return;\n }\n res.setHeader(key, value);\n });\n const getSetCookie = (response.headers as { getSetCookie?: () => string[] }).getSetCookie;\n if (typeof getSetCookie === \"function\") {\n const cookies = getSetCookie.call(response.headers);\n if (cookies.length > 0) {\n res.setHeader(\"set-cookie\", cookies);\n }\n } else {\n const single = response.headers.get(\"set-cookie\");\n if (single !== null) {\n res.setHeader(\"set-cookie\", single);\n }\n }\n\n for (const [name, value] of Object.entries(DEFAULT_SECURITY_HEADERS)) {\n if (!res.hasHeader(name)) {\n res.setHeader(name, value);\n }\n }\n\n if (response.body === null || res.req?.method === \"HEAD\") {\n res.end();\n return;\n }\n\n // Addendum J.2: stream langsung — tidak ada `arrayBuffer()` penuh di memori.\n await pipeline(\n Readable.fromWeb(response.body as unknown as import(\"node:stream/web\").ReadableStream),\n res,\n );\n}\n\nfunction firstHeaderValue(value: string | string[] | undefined): string | null {\n if (value === undefined) {\n return null;\n }\n const raw = Array.isArray(value) ? value[0] : value;\n if (raw === undefined) {\n return null;\n }\n const first = raw.split(\",\")[0]?.trim() ?? \"\";\n return first.length === 0 ? null : first;\n}\n\nfunction forwardedProtocol(value: string | string[] | undefined): \"http\" | \"https\" | null {\n const first = firstHeaderValue(value)?.toLowerCase() ?? null;\n if (first === \"https\") {\n return \"https\";\n }\n if (first === \"http\") {\n return \"http\";\n }\n return null;\n}\n\nfunction forwardedHost(value: string | string[] | undefined): string | null {\n const first = firstHeaderValue(value);\n if (first === null || first.length === 0 || first.length > 255) {\n return null;\n }\n // hostname[:port] — tolak karakter di luar himpunan aman (spasi, CRLF, /, @, ?).\n if (!/^[A-Za-z0-9.\\-[\\]:]+$/.test(first)) {\n return null;\n }\n return first;\n}\n\nfunction isPlausibleIp(value: string): boolean {\n return value.length <= 64 && /^[0-9a-fA-F:.]+$/.test(value);\n}\n\nfunction stripZoneId(value: string): string {\n const index = value.indexOf(\"%\");\n return index === -1 ? value : value.slice(0, index);\n}\n",
7
+ "/** Nama virtual module PRD §9.4. Semua di-resolve ke id `\\0`-prefix. */\nexport const VIRTUAL_MODULES = {\n routes: \"virtual:moons/routes\",\n api: \"virtual:moons/api\",\n config: \"virtual:moons/config\",\n clientEntry: \"virtual:moons/client-entry\",\n serverEntry: \"virtual:moons/server-entry\",\n} as const;\n\nexport type VirtualModuleName = (typeof VIRTUAL_MODULES)[keyof typeof VIRTUAL_MODULES];\n\nexport function isVirtualModuleId(id: string): id is VirtualModuleName {\n const clean = id.startsWith(\"\\0\") ? id.slice(1) : id;\n return (Object.values(VIRTUAL_MODULES) as string[]).includes(clean);\n}\n\nexport function toNullPrefixed(id: VirtualModuleName): `\\0${string}` {\n return `\\0${id}`;\n}\n\nexport function fromNullPrefixed(id: string): VirtualModuleName | null {\n const clean = id.startsWith(\"\\0\") ? id.slice(1) : id;\n return isVirtualModuleId(clean) ? (clean as VirtualModuleName) : null;\n}\n",
8
+ "import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ViteDevServer } from \"vite\";\nimport { VIRTUAL_MODULES } from \"../virtual-modules\";\nimport type { PayloadScanOptions } from \"../payload-scan\";\nimport type { PluginRuntime } from \"./plugins\";\nimport type { Shell } from \"./shell\";\nimport { createDevShell } from \"./shell\";\nimport {\n BodyLimitError,\n InvalidRequestError,\n resolveRequestUrl,\n sendNodeResponse,\n toWebRequest,\n} from \"./node\";\nimport { DEFAULT_BODY_LIMIT_BYTES } from \"@moonsjs/core\";\nimport { emptyDevtoolsData, renderDevtoolsHtml, type DevtoolsData } from \"../devtools\";\n\ninterface DevApp {\n handler: (request: Request) => Promise<Response>;\n plugins: PluginRuntime;\n}\n\ntype DevServerEntry = {\n createDevApp: (\n shell: Shell,\n payloadScan?: import(\"../payload-scan\").PayloadScanOptions,\n ) => DevApp;\n};\n\n/**\n * Middleware SSR dev: dipasang sebelum middleware internal Vite, menangani\n * request HTML dan RPC serverFn (`/_moons/fn/*`) — modul/aset tetap Vite.\n * Handler + lifecycle plugin (Addendum B.4) diinisialisasi sekali.\n */\nexport function installDevMiddleware(\n server: ViteDevServer,\n options: {\n templatePath: string;\n payloadScan?: PayloadScanOptions;\n apiPrefix?: string;\n /** Addendum J.1: paritas dev dengan produksi (trustProxy/bodyLimitBytes). */\n trustProxy?: boolean;\n bodyLimitBytes?: number;\n /** Addendum I.4: datasource devtools overlay (dipanggil per request). */\n devtools?: () => DevtoolsData;\n },\n): void {\n let appPromise: Promise<DevApp> | null = null;\n\n const getApp = (): Promise<DevApp> => {\n if (appPromise === null) {\n appPromise = (async () => {\n const entry = (await server.ssrLoadModule(VIRTUAL_MODULES.serverEntry)) as DevServerEntry;\n const app = entry.createDevApp(createDevShell(server, options), options.payloadScan);\n await app.plugins.start();\n await app.plugins.ready();\n return app;\n })();\n }\n return appPromise;\n };\n\n server.httpServer?.once(\"close\", () => {\n void appPromise?.then((app) => app.plugins.shutdown());\n });\n\n server.middlewares.use((req, res, next) => {\n // Addendum I.4 (W4): overlay dev-only — selalu data segar per request.\n if (\n (req.method === \"GET\" || req.method === \"HEAD\") &&\n new URL(req.url ?? \"/\", \"http://localhost\").pathname === \"/__moons/devtools\"\n ) {\n try {\n const data = options.devtools?.() ?? emptyDevtoolsData();\n res.statusCode = 200;\n res.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n res.end(req.method === \"HEAD\" ? undefined : renderDevtoolsHtml(data));\n } catch (error) {\n res.statusCode = 500;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(`[moons] devtools failed to render: ${String(error)}`);\n }\n return;\n }\n\n if (!shouldHandle(req, options.apiPrefix)) {\n next();\n return;\n }\n void handleRequest(getApp, req, res, options).catch((error: unknown) => {\n if (error instanceof InvalidRequestError) {\n res.statusCode = 400;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(\"Bad Request\");\n return;\n }\n if (error instanceof BodyLimitError) {\n res.statusCode = 413;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(\"Payload Too Large\");\n res.once(\"finish\", () => req.destroy());\n return;\n }\n next(error as Error);\n });\n });\n}\n\nasync function handleRequest(\n getApp: () => Promise<DevApp>,\n req: IncomingMessage,\n res: ServerResponse,\n options: { trustProxy?: boolean; bodyLimitBytes?: number },\n): Promise<void> {\n const app = await getApp();\n const url = resolveRequestUrl(req, { trustProxy: options.trustProxy === true });\n const isRpc = url.pathname.startsWith(\"/_moons/fn/\");\n const request = await toWebRequest(req, {\n trustProxy: options.trustProxy === true,\n ...(isRpc ? { maxBodyBytes: options.bodyLimitBytes ?? DEFAULT_BODY_LIMIT_BYTES } : {}),\n });\n const response = await app.handler(request);\n await sendNodeResponse(res, response);\n}\n\nfunction shouldHandle(req: IncomingMessage, apiPrefix: string | undefined): boolean {\n const path = new URL(req.url ?? \"/\", \"http://localhost\").pathname;\n\n // RPC serverFn (semua method): dulu hanya GET/HEAD HTML yang dirutekan,\n // sehingga `POST /_moons/fn/<hash>` jatuh ke Vite (\"Cannot POST\", 404) dan\n // serverFn tidak bisa dipakai di dev (temuan eval T2 ronde 2; decisions/0016).\n if (path.startsWith(\"/_moons/fn/\")) {\n return true;\n }\n\n // Addendum D.1: endpoint `src/api` (semua method) dirutekan ke runtime.\n if (apiPrefix !== undefined && (path === apiPrefix || path.startsWith(`${apiPrefix}/`))) {\n return true;\n }\n\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n return false;\n }\n if (path.startsWith(\"/@\") || path.startsWith(\"/node_modules/\") || path.startsWith(\"/__\")) {\n return false;\n }\n const lastSegment = path.split(\"/\").pop() ?? \"\";\n if (lastSegment.includes(\".\")) {\n return false;\n }\n const accept = req.headers.accept ?? \"\";\n return accept.includes(\"text/html\") || accept.includes(\"*/*\");\n}\n",
9
+ "/**\n * Addendum I.4 (W4): devtools overlay dev-only — tabel route, registry\n * serverFn (hash §12), endpoint `src/api`, dan status payload-scan.\n * Renderer murni (tanpa I/O) supaya bisa diuji unit; middleware dev yang\n * menyajikannya di `/__moons/devtools`.\n */\n\nexport interface DevtoolsRoute {\n /** Path URL (bentuk PRD §6.1), mis. \"/users/:id\". */\n path: string;\n /** \"page\" | \"layout\" | \"error\" | \"404\" | \"middleware\" | \"api\". */\n kind: string;\n /** Path relatif terhadap root app (posix). */\n file: string;\n}\n\nexport interface DevtoolsFn {\n file: string;\n name: string;\n hash: string;\n}\n\nexport interface DevtoolsData {\n routes: DevtoolsRoute[];\n apiRoutes: DevtoolsRoute[];\n serverFns: DevtoolsFn[];\n /** Canary `leakTest` terpasang → payload-scan aktif (A1). */\n canary: boolean;\n version: string;\n}\n\nexport function emptyDevtoolsData(): DevtoolsData {\n return { routes: [], apiRoutes: [], serverFns: [], canary: false, version: \"\" };\n}\n\nexport function renderDevtoolsHtml(data: DevtoolsData): string {\n const rows = (items: DevtoolsRoute[]): string =>\n items.length === 0\n ? '<tr><td colspan=\"3\" class=\"muted\">(empty)</td></tr>'\n : items\n .map(\n (item) =>\n `<tr><td><code>${esc(item.path)}</code></td><td><span class=\"kind k-${esc(item.kind)}\">${esc(item.kind)}</span></td><td class=\"muted\">${esc(item.file)}</td></tr>`,\n )\n .join(\"\\n\");\n\n const fnRows =\n data.serverFns.length === 0\n ? '<tr><td colspan=\"3\" class=\"muted\">(no serverFn)</td></tr>'\n : data.serverFns\n .map(\n (fn) =>\n `<tr><td><code>${esc(fn.hash)}</code></td><td><code>${esc(fn.name)}</code></td><td class=\"muted\">${esc(fn.file)}</td></tr>`,\n )\n .join(\"\\n\");\n\n const canary = data.canary\n ? '<span class=\"ok\">enabled</span> (canary installed)'\n : '<span class=\"muted\">disabled</span> (no canary)';\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>moons devtools</title>\n<style>\n :root { color-scheme: dark; }\n body { margin: 0; padding: 2rem; background: #101418; color: #e6edf3;\n font: 14px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }\n h1 { font-size: 1.1rem; margin: 0 0 .25rem; }\n h2 { font-size: .95rem; margin: 2rem 0 .5rem; color: #9fb0c0; }\n .muted { color: #8b98a5; }\n .ok { color: #3fb950; font-weight: 600; }\n .meta { margin-bottom: 1.5rem; }\n table { border-collapse: collapse; width: 100%; max-width: 1100px; }\n th, td { text-align: left; padding: .35rem .75rem .35rem 0; border-bottom: 1px solid #1f2630; }\n th { color: #8b98a5; font-weight: 600; font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }\n code { color: #79c0ff; }\n .kind { border: 1px solid #30363d; border-radius: 4px; padding: 0 .35rem; font-size: .8rem; }\n .k-page { color: #d2a8ff; } .k-layout { color: #7ee787; }\n .k-error { color: #ff7b72; } .k-404 { color: #ff7b72; }\n .k-middleware { color: #e3b341; } .k-api { color: #79c0ff; }\n</style>\n</head>\n<body>\n<h1>moons devtools</h1>\n<div class=\"meta muted\">@moonsjs/vite ${esc(data.version)} · dev-only · payload-scan: ${canary}</div>\n\n<h2>Routes (${data.routes.length})</h2>\n<table><thead><tr><th>path</th><th>kind</th><th>file</th></tr></thead>\n<tbody>\n${rows(data.routes)}\n</tbody></table>\n\n<h2>API (${data.apiRoutes.length})</h2>\n<table><thead><tr><th>path</th><th>kind</th><th>file</th></tr></thead>\n<tbody>\n${rows(data.apiRoutes)}\n</tbody></table>\n\n<h2>serverFn registry (${data.serverFns.length})</h2>\n<table><thead><tr><th>hash</th><th>export</th><th>file</th></tr></thead>\n<tbody>\n${fnRows}\n</tbody></table>\n</body>\n</html>\n`;\n}\n\nfunction esc(value: string): string {\n return value.replace(/[&<>\"']/g, (char) => {\n switch (char) {\n case \"&\":\n return \"&amp;\";\n case \"<\":\n return \"&lt;\";\n case \">\":\n return \"&gt;\";\n case '\"':\n return \"&quot;\";\n default:\n return \"&#39;\";\n }\n });\n}\n",
10
+ "import { findLayouts, routeKeyFromFile, type ApiRouteDefinition, type RouteDefinition } from \"@moonsjs/core\";\nimport { relative, sep } from \"node:path\";\n\nexport interface ClientManifestEntry {\n page: string;\n layouts: string[];\n}\n\n/** Key manifest untuk halaman kustom `_404` dan `_error` (§11.2). */\nexport const NOT_FOUND_ROUTE_KEY = \"__moons/404\";\nexport const ERROR_ROUTE_KEY = \"__moons/error\";\n\n/**\n * Manifest route untuk client (PRD §8.2): key = key route file\n * (routeKeyFromFile), nilai = page + layout chain dari root ke leaf.\n */\nexport function buildClientManifest(\n routes: RouteDefinition[],\n routesDir: string,\n): Record<string, ClientManifestEntry> {\n const manifest: Record<string, ClientManifestEntry> = {};\n for (const route of routes) {\n if (route.kind !== \"page\") {\n continue;\n }\n const key = routeKeyFromFile(routesDir, route.file);\n manifest[key] = {\n page: key,\n layouts: findLayouts(route, routes).map((layout) => routeKeyFromFile(routesDir, layout.file)),\n };\n }\n\n const notFound = routes.find((route) => route.kind === \"404\");\n if (notFound !== undefined) {\n manifest[NOT_FOUND_ROUTE_KEY] = { page: routeKeyFromFile(routesDir, notFound.file), layouts: [] };\n }\n const errorRoute = routes.find((route) => route.kind === \"error\");\n if (errorRoute !== undefined) {\n manifest[ERROR_ROUTE_KEY] = { page: routeKeyFromFile(routesDir, errorRoute.file), layouts: [] };\n }\n // D.3: semua boundary `_error` (termasuk bersarang) ikut manifest — payload\n // memakai key file boundary yang dipilih runtime.\n for (const route of routes) {\n if (route.kind !== \"error\") {\n continue;\n }\n const key = routeKeyFromFile(routesDir, route.file);\n manifest[key] = { page: key, layouts: [] };\n }\n\n return manifest;\n}\n\n/** `virtual:moons/api` (Addendum D.1 + D.3): tabel API + middleware + peta import(). */\nexport function renderApiModule(\n apiRoutes: ApiRouteDefinition[],\n apiMiddlewares: ReadonlyArray<{ file: string; dir: string }>,\n projectRoot: string,\n): string {\n const loadEntries = apiRoutes\n .map(\n (route) =>\n ` ${JSON.stringify(route.path)}: () => import(${JSON.stringify(rootSpecifier(projectRoot, route.file))}),`,\n )\n .join(\"\\n\");\n const middlewareEntries = apiMiddlewares\n .map(\n (middleware) =>\n ` ${JSON.stringify(middleware.file)}: () => import(${JSON.stringify(rootSpecifier(projectRoot, middleware.file))}),`,\n )\n .join(\"\\n\");\n return [\n \"// Generated by @moons. Do not edit manually.\",\n `export const apiRoutes = ${JSON.stringify(apiRoutes, null, 2)};`,\n `export const apiMiddlewares = ${JSON.stringify(apiMiddlewares, null, 2)};`,\n \"export const loadApi = {\",\n loadEntries,\n \"};\",\n \"export const loadApiMiddleware = {\",\n middlewareEntries,\n \"};\",\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Isi `virtual:moons/routes`: daftar RouteDefinition, manifest client, dan\n * peta import() dinamis per file route (specifier root-relative).\n */\nexport function renderRoutesModule(\n routes: RouteDefinition[],\n routesDir: string,\n projectRoot: string,\n): string {\n const manifest = buildClientManifest(routes, routesDir);\n\n const loadEntries = routes.map((route) => {\n const key = routeKeyFromFile(routesDir, route.file);\n return ` ${JSON.stringify(key)}: () => import(${JSON.stringify(rootSpecifier(projectRoot, route.file))}),`;\n });\n\n return [\n \"// Generated by @moons. Do not edit manually.\",\n `export const routes = ${JSON.stringify(routes, null, 2)};`,\n \"\",\n `export const clientManifest = ${JSON.stringify(manifest, null, 2)};`,\n \"\",\n \"export const loadRoute = {\",\n ...loadEntries,\n \"};\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Path file absolut project → specifier root-relative yang bisa di-resolve Vite. */\nexport function rootSpecifier(projectRoot: string, file: string): string {\n let rel = relative(projectRoot, file);\n rel = sep === \"/\" ? rel : rel.split(sep).join(\"/\");\n return rel.startsWith(\"/\") ? rel : `/${rel}`;\n}\n"
11
+ ],
12
+ "mappings": "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",
13
+ "debugId": "E364F1DD2BD3F4F264756E2164756E21",
14
+ "names": []
15
+ }
@@ -103,17 +103,17 @@ function createServerOnlyClassifier(options = {}) {
103
103
  };
104
104
  }
105
105
  function serverOnlyImportError(input) {
106
- const sourceLabel = input.source === "builtin" ? "Sumber kecocokan: daftar bawaan paket server-only framework." : input.source === "config" ? "Sumber kecocokan: `security.serverOnlyPackages` di moons.config.ts." : `Sumber kecocokan: marker \`"moons": { "serverOnly": true }\` di ${input.markerPath ?? "package.json"}.`;
106
+ const sourceLabel = input.source === "builtin" ? "Match source: framework built-in server-only package list." : input.source === "config" ? "Match source: `security.serverOnlyPackages` in moons.config.ts." : `Match source: marker \`"moons": { "serverOnly": true }\` in ${input.markerPath ?? "package.json"}.`;
107
107
  return new MoonsError({
108
108
  code: "E_FORBIDDEN_SERVER_IMPORT",
109
109
  file: input.relativeFile,
110
110
  line: input.line,
111
- message: `mengimpor${input.dynamic === true ? " (dynamic)" : ""} \`${input.specifier}\` — paket server-only \`${input.packageName}\``,
111
+ message: `importing${input.dynamic === true ? " (dynamic)" : ""} \`${input.specifier}\` — server-only package \`${input.packageName}\``,
112
112
  details: `${sourceLabel}
113
- Paket server-only tidak boleh masuk bundle client.`,
113
+ Server-only packages must not enter the client bundle.`,
114
114
  suggestions: [
115
- "Pindahkan operasi tersebut ke `*.server.ts` dan panggil lewat serverFn",
116
- "Atau ganti/hapus import tersebut di modul client"
115
+ "Move the operation into `*.server.ts` and call it via a serverFn",
116
+ "Or replace/remove the import in the client module"
117
117
  ]
118
118
  });
119
119
  }
@@ -499,17 +499,17 @@ function renderServerFnStub2(source, filename, analysis, dependencies) {
499
499
  file: filename,
500
500
  line: fn.line,
501
501
  annotation: fn.name,
502
- message: `schema ${kind} serverFn \`${fn.name}\` merujuk nilai server \`${reference}\` dari \`${serverImport.specifier}\``,
503
- details: `\`${reference}\` diimpor dari file server lain dan bukan serverFn — stub client tidak mengekspornya (dulu gagal dengan error mentah bundler \`MISSING_EXPORT\`).
502
+ message: `schema ${kind} of serverFn \`${fn.name}\` references server value \`${reference}\` from \`${serverImport.specifier}\``,
503
+ details: `\`${reference}\` is imported from another server file and is not a serverFn — the client stub does not export it (previously a raw bundler \`MISSING_EXPORT\` error).
504
504
 
505
- ` + `Pilih salah satu:
506
- ` + ` // a) pindahkan konstanta/schema ke modul client-safe
505
+ ` + `Pick one:
506
+ ` + ` // a) move the constant/schema to a client-safe module
507
507
  ` + ` import { ${reference} } from "../shared/schemas";
508
- ` + ` // b) inline nilai yang memang publik di schema, atau
509
- ` + " // c) validasi tambahan di handler (server) tanpa mereferensikan nilai server di schema",
508
+ ` + ` // b) inline the value if it is genuinely public in the schema, or
509
+ ` + " // c) do extra validation in the server handler without referencing server values in the schema",
510
510
  suggestions: [
511
- "Pindahkan deklarasi yang dirujuk ke modul bersama yang client-safe (mis. `src/shared/schemas.ts`)",
512
- "Atau lakukan validasi di handler server, bukan di ekspresi schema"
511
+ "Move the referenced declaration to a client-safe shared module (e.g. `src/shared/schemas.ts`)",
512
+ "Or do the validation in the server handler instead of the schema expression"
513
513
  ]
514
514
  });
515
515
  }
@@ -523,22 +523,22 @@ function renderServerFnStub2(source, filename, analysis, dependencies) {
523
523
  file: filename,
524
524
  line: fn.line,
525
525
  annotation: fn.name,
526
- message: `schema ${kind} serverFn \`${fn.name}\` merujuk deklarasi lokal \`${reference}\``,
527
- details: `\`${reference}\` dibuang dari stub client, sehingga nilainya \`undefined\` di browser (crash hydration).
526
+ message: `schema ${kind} of serverFn \`${fn.name}\` references local declaration \`${reference}\``,
527
+ details: `\`${reference}\` is dropped from the client stub, so its value is \`undefined\` in the browser (hydration crash).
528
528
 
529
- ` + `Sebelum (ditolak):
530
- ` + ` const ${reference} = z.object({ … }); // deklarasi lokal di file yang sama
529
+ ` + `Before (rejected):
530
+ ` + ` const ${reference} = z.object({ … }); // local declaration in the same file
531
531
  ` + ` export const ${fn.name} = serverFn({ ${kind}: ${reference}, … });
532
532
 
533
- ` + `Sesudah (pilih salah satu):
534
- ` + ` // a) inline schema di argumen
533
+ ` + `After (pick one):
534
+ ` + ` // a) inline the schema in the argument
535
535
  ` + ` export const ${fn.name} = serverFn({ ${kind}: z.object({ … }), … });
536
- ` + ` // b) pindahkan deklarasi ke modul bersama yang boleh masuk client (mis. src/shared/schemas.ts)
536
+ ` + ` // b) move the declaration to a shared module that may enter the client (e.g. src/shared/schemas.ts)
537
537
  ` + ` import { ${reference} } from "../shared/schemas";`,
538
538
  suggestions: [
539
- `Inline schema di argumen \`serverFn({ ${kind}: … })\`, atau`,
540
- `Pindahkan \`${reference}\` ke modul bersama (mis. \`src/shared/schemas.ts\`) lalu impor dari sana`,
541
- "Jangan memindahkan nilai rahasia ke shared — schema harus client-safe (zod/Standard Schema)"
539
+ `Inline the schema in the argument \`serverFn({ ${kind}: … })\`, or`,
540
+ `Move \`${reference}\` to a shared module (e.g. \`src/shared/schemas.ts\`) and import it from there`,
541
+ "Never move secret values to shared — the schema must be client-safe (zod/Standard Schema)"
542
542
  ]
543
543
  });
544
544
  }
@@ -551,9 +551,9 @@ function renderServerFnStub2(source, filename, analysis, dependencies) {
551
551
  throw new MoonsError2({
552
552
  code: "E_SERVER_IMPORT_IN_CLIENT",
553
553
  file: filename,
554
- message: `tidak bisa me-resolve \`${reExport.source}\` untuk re-export`,
555
- details: "Re-export dari `*.server.ts` harus bisa di-resolve statis.",
556
- suggestions: ["Perbaiki path re-export", "Hindari re-export dinamis dari file server"]
554
+ message: `cannot resolve \`${reExport.source}\` for re-export`,
555
+ details: "Re-exports from `*.server.ts` must be statically resolvable.",
556
+ suggestions: ["Fix the re-export path", "Avoid dynamic re-exports from server files"]
557
557
  });
558
558
  }
559
559
  const targetNames = serverFnExportNames2(target, (specifier) => dependencies.analyzeSpecifier(filename, specifier));
@@ -569,9 +569,9 @@ function renderServerFnStub2(source, filename, analysis, dependencies) {
569
569
  throw new MoonsError2({
570
570
  code: "E_SERVER_IMPORT_IN_CLIENT",
571
571
  file: filename,
572
- message: `\`export *\` dari \`${specifier}\` tidak bisa di-resolve`,
573
- details: "`export *` dari `*.server.ts` wajib bisa di-expand statis.",
574
- suggestions: ["Perbaiki path re-export", "Ganti menjadi named re-export"]
572
+ message: `\`export *\` from \`${specifier}\` cannot be resolved`,
573
+ details: "`export *` from `*.server.ts` must be statically expandable.",
574
+ suggestions: ["Fix the re-export path", "Replace it with named re-exports"]
575
575
  });
576
576
  }
577
577
  const targetNames = serverFnExportNames2(target, (nested) => dependencies.analyzeSpecifier(filename, nested));
@@ -580,7 +580,7 @@ function renderServerFnStub2(source, filename, analysis, dependencies) {
580
580
  }
581
581
  }
582
582
  if (importLines.length === 0 && exportLines.length === 0) {
583
- return { code: `// @moons: tidak ada serverFn yang di-export
583
+ return { code: `// @moons: no serverFn exported
584
584
  export {};
585
585
  `, referenced };
586
586
  }
@@ -637,11 +637,11 @@ function nonContractServerFileError2(file, projectRoot) {
637
637
  return new MoonsError2({
638
638
  code: "E_SERVER_IMPORT_IN_CLIENT",
639
639
  file: toRelative(projectRoot, file),
640
- message: "file server memakai ekstensi di luar kontrak `*.server.ts`",
641
- details: "Transform zero-trust hanya menangani `*.server.ts`/`*.server.tsx`; file server dengan ekstensi lain tidak bisa di-stub dan tidak boleh masuk graph client.",
640
+ message: "server file uses an extension outside the `*.server.ts` contract",
641
+ details: "The zero-trust transform only handles `*.server.ts`/`*.server.tsx`; server files with other extensions cannot be stubbed and must not enter the client graph.",
642
642
  suggestions: [
643
- "Ganti nama file menjadi `*.server.ts`/`*.server.tsx` dan taruh `serverFn` di dalamnya",
644
- "Atau pindahkan nilai yang dibutuhkan ke file non-server"
643
+ "Rename the file to `*.server.ts`/`*.server.tsx` and put the `serverFn` inside it",
644
+ "Or move the value you need into a non-server file"
645
645
  ]
646
646
  });
647
647
  }
@@ -791,11 +791,11 @@ async function checkClientModuleImports2(context) {
791
791
  code: "E_FORBIDDEN_SERVER_IMPORT",
792
792
  file: relativeFile,
793
793
  line,
794
- message: `mengimpor \`${specifier}\` — \`moons.config.ts\` adalah modul server-only`,
795
- details: "Config memuat `context`/`middleware` (kode server) dan tidak pernah masuk bundle client (Addendum J.4).",
794
+ message: `importing \`${specifier}\` — \`moons.config.ts\` is a server-only module`,
795
+ details: "The config contains `context`/`middleware` (server code) and never enters the client bundle (Addendum J.4).",
796
796
  suggestions: [
797
- "Pindahkan nilai yang dibutuhkan ke modul client-safe (mis. `src/shared/…`)",
798
- "Ambil nilai dari server lewat loader/serverFn"
797
+ "Move the value you need into a client-safe module (e.g. `src/shared/…`)",
798
+ "Get the value from the server via a loader/serverFn"
799
799
  ]
800
800
  });
801
801
  };
@@ -804,11 +804,11 @@ async function checkClientModuleImports2(context) {
804
804
  code: "E_FORBIDDEN_SERVER_IMPORT",
805
805
  file: relativeFile,
806
806
  line,
807
- message: `mengimpor \`${specifier}\` — \`${toRelative(projectRoot, resolvedTarget)}\` ada di \`src/server/**\` (server-only)`,
808
- details: "Direktori `src/server/**` hanya untuk kode server (Addendum J.4); suffix `.server.ts` bukan satu-satunya jaminan.",
807
+ message: `importing \`${specifier}\` — \`${toRelative(projectRoot, resolvedTarget)}\` is inside \`src/server/**\` (server-only)`,
808
+ details: "The `src/server/**` directory is for server code only (Addendum J.4); the `.server.ts` suffix is not the only guarantee.",
809
809
  suggestions: [
810
- "Pindahkan kode yang client-safe ke `src/shared/`",
811
- "Akses nilainya dari server lewat loader/serverFn"
810
+ "Move client-safe code to `src/shared/`",
811
+ "Access the value from the server via a loader/serverFn"
812
812
  ]
813
813
  });
814
814
  };
@@ -818,11 +818,11 @@ async function checkClientModuleImports2(context) {
818
818
  code: "E_FORBIDDEN_SERVER_IMPORT",
819
819
  file: relativeFile,
820
820
  line,
821
- message: `mengimpor \`${specifier}\` — \`${relApi}\` adalah modul API server-only`,
822
- details: "`src/api/**` hanya berjalan di server (endpoint HTTP) dan tidak pernah masuk bundle client.",
821
+ message: `importing \`${specifier}\` — \`${relApi}\` is a server-only API module`,
822
+ details: "`src/api/**` only runs on the server (HTTP endpoints) and never enters the client bundle.",
823
823
  suggestions: [
824
- "Pindahkan operasi tersebut ke `*.server.ts` dan panggil lewat serverFn",
825
- "Jangan mengimpor file `src/api` dari route/komponen client"
824
+ "Move the operation into `*.server.ts` and call it via a serverFn",
825
+ "Do not import `src/api` files from client routes/components"
826
826
  ]
827
827
  });
828
828
  };
@@ -862,8 +862,8 @@ async function checkClientModuleImports2(context) {
862
862
  message,
863
863
  details,
864
864
  suggestions: [
865
- "Buat server function yang mengembalikan data yang dibutuhkan, atau",
866
- "Jika nilainya memang publik, definisikan ulang konstanta itu di modul yang boleh masuk client (jangan memindahkan nilai rahasia)"
865
+ "Create a server function that returns the data you need, or",
866
+ "If the value is genuinely public, redefine the constant in a client-safe module (never move secret values)"
867
867
  ]
868
868
  });
869
869
  };
@@ -872,11 +872,11 @@ async function checkClientModuleImports2(context) {
872
872
  code: "E_FORBIDDEN_SERVER_IMPORT",
873
873
  file: relativeFile,
874
874
  line,
875
- message: `mengimpor \`${specifier}\` di modul client`,
876
- details: "Modul server-only hanya boleh di-import dari *.server.ts.",
875
+ message: `importing \`${specifier}\` in a client module`,
876
+ details: "Server-only modules may only be imported from *.server.ts.",
877
877
  suggestions: [
878
- "Pindahkan operasi tersebut ke serverFn, atau",
879
- "Hapus import tersebut"
878
+ "Move the operation into a serverFn, or",
879
+ "Remove the import"
880
880
  ]
881
881
  });
882
882
  };
@@ -970,11 +970,11 @@ async function checkClientModuleImports2(context) {
970
970
  code: "E_SERVER_IMPORT_IN_CLIENT",
971
971
  file: relativeFile,
972
972
  line,
973
- message: `mengimpor \`${name}\` — berasal dari \`${toRelative(projectRoot, serverFile)}\` (via rantai re-export)`,
974
- details: `\`${toRelative(projectRoot, resolvedTarget)}\` me-re-export nilai dari file server; ` + "stub client tidak mengekspornya (dulu gagal dengan error mentah bundler `MISSING_EXPORT`).",
973
+ message: `importing \`${name}\` — comes from \`${toRelative(projectRoot, serverFile)}\` (via a re-export chain)`,
974
+ details: `\`${toRelative(projectRoot, resolvedTarget)}\` re-exports a value from a server file; ` + "the client stub does not export it (previously a raw bundler `MISSING_EXPORT` error).",
975
975
  suggestions: [
976
- "Buat server function yang mengembalikan data yang dibutuhkan, atau",
977
- "Definisikan ulang konstanta publik itu di modul client-safe (jangan pindahkan nilai rahasia)"
976
+ "Create a server function that returns the data you need, or",
977
+ "Redefine the public constant in a client-safe module (never move secret values)"
978
978
  ]
979
979
  });
980
980
  }
@@ -985,17 +985,17 @@ async function checkClientModuleImports2(context) {
985
985
  const targetRelative = resolvedTarget === null ? rawSpecifier : toRelative(projectRoot, resolvedTarget);
986
986
  if (statement.type === "ExportAllDeclaration") {
987
987
  if (resolvedTarget === null) {
988
- reject(line, `\`export *\` dari \`${targetRelative}\` tidak bisa di-resolve`, "Re-export dari `*.server.ts` wajib bisa di-resolve statis.");
988
+ reject(line, `\`export *\` from \`${targetRelative}\` cannot be resolved`, "Re-exports from `*.server.ts` must be statically resolvable.");
989
989
  }
990
990
  continue;
991
991
  }
992
992
  const specifiers = statement["specifiers"] ?? [];
993
993
  for (const item of specifiers) {
994
994
  if (item.type === "ImportDefaultSpecifier") {
995
- reject(line, `mengimpor default dari ${targetRelative}`, "Default export dari file server tidak boleh menyeberang ke client.");
995
+ reject(line, `importing the default export from ${targetRelative}`, "Default exports from a server file must not cross to the client.");
996
996
  }
997
997
  if (item.type === "ImportNamespaceSpecifier") {
998
- reject(line, `mengimpor namespace (\`* as\`) dari ${targetRelative}`, "Import namespace dari file server tidak boleh menyeberang ke client.");
998
+ reject(line, `importing the namespace (\`* as\`) from ${targetRelative}`, "Namespace imports from a server file must not cross to the client.");
999
999
  }
1000
1000
  const name = item["local"]?.["name"] ?? "";
1001
1001
  const specifierKind = item["importKind"];
@@ -1004,7 +1004,7 @@ async function checkClientModuleImports2(context) {
1004
1004
  continue;
1005
1005
  }
1006
1006
  if (name.length > 0 && !await isAllowedServerFnImport(resolvedTarget, specifier, name)) {
1007
- reject(line, `mengimpor \`${name}\` dari ${targetRelative}`, "Kode server tidak boleh muncul di client.\nHanya `serverFn` yang diizinkan menyeberang.");
1007
+ reject(line, `importing \`${name}\` from ${targetRelative}`, "Server code must not appear in the client.\nOnly `serverFn` may cross.");
1008
1008
  }
1009
1009
  }
1010
1010
  }
@@ -1044,11 +1044,11 @@ async function checkClientModuleImports2(context) {
1044
1044
  code: "E_ENV_IN_CLIENT",
1045
1045
  file: relativeFile,
1046
1046
  line: node.loc?.start.line ?? 1,
1047
- message: `memanggil \`${callText}\` di modul yang masuk client`,
1048
- details: "`env()` hanya legal di server.",
1047
+ message: `calling \`${callText}\` in a client module`,
1048
+ details: "`env()` is only legal on the server.",
1049
1049
  suggestions: [
1050
- "Pindahkan pemanggilan ke dalam serverFn, atau",
1051
- "Gunakan import.meta.env untuk nilai publik"
1050
+ "Move the call into a serverFn, or",
1051
+ "Use import.meta.env for public values"
1052
1052
  ]
1053
1053
  });
1054
1054
  });
@@ -1097,7 +1097,7 @@ async function checkClientModuleImports2(context) {
1097
1097
  continue;
1098
1098
  }
1099
1099
  const targetRelative = resolvedTarget === null ? request.text : toRelative(projectRoot, resolvedTarget);
1100
- reject(request.node.loc?.start.line ?? 1, `mengimpor (dynamic) \`${request.text}\` dari ${targetRelative}`, "Client tidak boleh melakukan dynamic import ke `*.server.ts`.");
1100
+ reject(request.node.loc?.start.line ?? 1, `importing (dynamic) \`${request.text}\` from ${targetRelative}`, "The client must not dynamically import `*.server.ts`.");
1101
1101
  }
1102
1102
  walk(ast, (node) => {
1103
1103
  if (node.type !== "CallExpression") {
@@ -1120,11 +1120,11 @@ async function checkClientModuleImports2(context) {
1120
1120
  code: "E_SERVER_IMPORT_IN_CLIENT",
1121
1121
  file: relativeFile,
1122
1122
  line: node.loc?.start.line ?? 1,
1123
- message: `mengimpor (glob) \`${pattern}\` di modul client`,
1124
- details: "`import.meta.glob` yang mencakup `*.server.ts` dilarang di client.",
1123
+ message: `importing (glob) \`${pattern}\` in a client module`,
1124
+ details: "`import.meta.glob` patterns covering `*.server.ts` are forbidden in the client.",
1125
1125
  suggestions: [
1126
- "Import statis `serverFn` dari `*.server.ts` (hanya itu yang boleh menyeberang), atau",
1127
- "Pindahkan kebutuhan data ke dalam server function"
1126
+ "Statically import the `serverFn` from `*.server.ts` (that is all that may cross), or",
1127
+ "Move the data requirement into a server function"
1128
1128
  ]
1129
1129
  });
1130
1130
  }
@@ -1216,7 +1216,7 @@ function checkRouteModuleExports2(context) {
1216
1216
  return;
1217
1217
  }
1218
1218
  const allowed = base === "_layout.tsx" ? ["default", "loader"] : base === "_error.tsx" || base === "_404.tsx" ? ["default"] : ["default", "loader", "meta", "prerender", "paths", "revalidate"];
1219
- const allowedText = `Hanya ${allowed.map((name) => `\`${name}\``).join(", ")} yang boleh di-export.`;
1219
+ const allowedText = `Only ${allowed.map((name) => `\`${name}\``).join(", ")} may be exported.`;
1220
1220
  const relativeFile = toRelative(projectRoot, file);
1221
1221
  const ast = parseModule(code, file);
1222
1222
  const declarations = [];
@@ -1231,11 +1231,11 @@ function checkRouteModuleExports2(context) {
1231
1231
  code: "E_ROUTE_EXPORT_INVALID",
1232
1232
  file: relativeFile,
1233
1233
  line: statement.loc?.start.line ?? 1,
1234
- message: "`export *` tidak diizinkan di modul route",
1234
+ message: "`export *` is not allowed in route modules",
1235
1235
  details: allowedText,
1236
1236
  suggestions: [
1237
- `Pindahkan isi file ke luar ${toRelative(projectRoot, routesDir)}/, atau`,
1238
- "Hapus `export *` tersebut"
1237
+ `Move the file contents outside ${toRelative(projectRoot, routesDir)}/, or`,
1238
+ "Remove the `export *`"
1239
1239
  ]
1240
1240
  });
1241
1241
  }
@@ -1262,11 +1262,11 @@ function checkRouteModuleExports2(context) {
1262
1262
  code: "E_ROUTE_EXPORT_INVALID",
1263
1263
  file: relativeFile,
1264
1264
  line: entry.line,
1265
- message: `export \`${entry.name}\` tidak diizinkan di modul route`,
1265
+ message: `export \`${entry.name}\` is not allowed in route modules`,
1266
1266
  details: allowedText,
1267
1267
  suggestions: [
1268
- `Pindahkan \`${entry.name}\` ke file di luar ${toRelative(projectRoot, routesDir)}/, atau`,
1269
- "Hapus export tersebut"
1268
+ `Move \`${entry.name}\` to a file outside ${toRelative(projectRoot, routesDir)}/, or`,
1269
+ "Remove the export"
1270
1270
  ]
1271
1271
  });
1272
1272
  }
@@ -1275,9 +1275,9 @@ function checkRouteModuleExports2(context) {
1275
1275
  throw new MoonsError2({
1276
1276
  code: "E_ROUTE_EXPORT_INVALID",
1277
1277
  file: relativeFile,
1278
- message: "modul route wajib mengekspor `default`",
1279
- details: "Setiap file route harus punya `export default` berupa komponen React.",
1280
- suggestions: ["Tambahkan `export default function Page() { ... }`"]
1278
+ message: "route module must export `default`",
1279
+ details: "Every route file needs an `export default` React component.",
1280
+ suggestions: ["Add `export default function Page() { ... }`"]
1281
1281
  });
1282
1282
  }
1283
1283
  }
@@ -1332,4 +1332,4 @@ function buildFnRegistry2(projectRoot, files) {
1332
1332
 
1333
1333
  export { isServerFilePath2, isNonContractServerFile2, isServerSpecifier2, resolveServerSpecifierSync2, stripQuery2, analyzeServerFile2, serverFnExportNames2, renderServerFnStub2, injectServerFnMeta2, nonContractServerFileError2, checkClientModuleImports2, checkRouteModuleExports2, scanServerFiles2, buildFnRegistry2 };
1334
1334
 
1335
- //# debugId=0D0964765353A35764756E2164756E21
1335
+ //# debugId=B9056013E70896ED64756E2164756E21