@moonsjs/vite 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/transform.ts CHANGED
@@ -467,6 +467,9 @@ export function renderServerFnStub(
467
467
  const localNames = localTopLevelNames(ast);
468
468
  const importLines: string[] = [];
469
469
  const referenced: string[] = [];
470
+ // Addendum J.4 (D2): nama yang diimpor dari file `*.server.ts` lain — dipakai
471
+ // untuk mendeteksi referensi schema ke nilai server (dulu `MISSING_EXPORT`).
472
+ const serverImportedNames = new Map<string, { specifier: string; imported: string }>();
470
473
  let needsCreateStub = analysis.serverFns.length > 0;
471
474
 
472
475
  for (const statement of topLevelStatements(ast)) {
@@ -495,6 +498,9 @@ export function renderServerFnStub(
495
498
  const local = ((item["local"] as AstNode | undefined)?.["name"] as string) ?? "";
496
499
  if (local.length > 0 && isUsed(local, retainedText)) {
497
500
  kept.push(item);
501
+ const imported =
502
+ ((item["imported"] as AstNode | undefined)?.["name"] as string | undefined) ?? local;
503
+ serverImportedNames.set(local, { specifier, imported });
498
504
  }
499
505
  }
500
506
  } else {
@@ -532,7 +538,37 @@ export function renderServerFnStub(
532
538
  ["output", output],
533
539
  ] as const) {
534
540
  for (const reference of referencedIdentifiers(slice, filename)) {
535
- if (reference.length === 0 || !localNames.has(reference)) {
541
+ if (reference.length === 0) {
542
+ continue;
543
+ }
544
+ const serverImport = serverImportedNames.get(reference);
545
+ if (serverImport !== undefined) {
546
+ const target = dependencies.analyzeSpecifier(filename, serverImport.specifier);
547
+ const isServerFn =
548
+ target !== null && target.serverFns.some((fn) => fn.name === serverImport.imported);
549
+ if (!isServerFn) {
550
+ throw new MoonsError({
551
+ code: "E_SCHEMA_SERVER_REF",
552
+ file: filename,
553
+ line: fn.line,
554
+ annotation: fn.name,
555
+ message: `schema ${kind} serverFn \`${fn.name}\` merujuk nilai server \`${reference}\` dari \`${serverImport.specifier}\``,
556
+ details:
557
+ `\`${reference}\` diimpor dari file server lain dan bukan serverFn — stub client tidak mengekspornya (dulu gagal dengan error mentah bundler \`MISSING_EXPORT\`).\n\n` +
558
+ "Pilih salah satu:\n" +
559
+ " // a) pindahkan konstanta/schema ke modul client-safe\n" +
560
+ ` import { ${reference} } from "../shared/schemas";\n` +
561
+ " // b) inline nilai yang memang publik di schema, atau\n" +
562
+ " // c) validasi tambahan di handler (server) tanpa mereferensikan nilai server di schema",
563
+ suggestions: [
564
+ "Pindahkan deklarasi yang dirujuk ke modul bersama yang client-safe (mis. `src/shared/schemas.ts`)",
565
+ "Atau lakukan validasi di handler server, bukan di ekspresi schema",
566
+ ],
567
+ });
568
+ }
569
+ continue;
570
+ }
571
+ if (!localNames.has(reference)) {
536
572
  continue;
537
573
  }
538
574
  throw new MoonsError({
@@ -653,6 +689,10 @@ export interface ClientImportCheckContext {
653
689
  analyze: (absoluteFile: string) => ServerFileAnalysis | null;
654
690
  /** Direktori `src/api` (absolut) — modul API server-only (Addendum D.1). */
655
691
  apiDir?: string;
692
+ /** File config (absolut) — server-only (Addendum J.4). */
693
+ configFile?: string;
694
+ /** Direktori `src/server` (absolut) — server-only (Addendum J.4). */
695
+ serverDir?: string;
656
696
  /** Paket server-only tambahan dari `security.serverOnlyPackages` (Addendum C). */
657
697
  serverOnlyPackages?: ReadonlyArray<string>;
658
698
  /** Cache package.json lintas modul (performa; dikelola plugin). */
@@ -718,11 +758,151 @@ export function nonContractServerFileError(file: string, projectRoot: string): M
718
758
  }
719
759
 
720
760
  /** Periksa import modul client terhadap §12.4/§12.7/§12.8 — berbasis hasil resolve. */
761
+ interface TraceFileRecord {
762
+ ast: AstNode | null;
763
+ mentionsServer: boolean;
764
+ }
765
+
766
+ /**
767
+ * Addendum K.1: telusuri apakah `name` dari sebuah modul client pada akhirnya
768
+ * berasal dari `*.server.ts` (langsung atau lewat rantai re-export/barrel),
769
+ * dan bukan serverFn. Mengembalikan file server yang bermasalah, atau null.
770
+ */
771
+ async function traceValueToServer(input: {
772
+ file: string;
773
+ name: string;
774
+ resolveSafe: (specifier: string, importer: string) => Promise<string | null>;
775
+ analyze: (absoluteFile: string) => ServerFileAnalysis | null;
776
+ cache: Map<string, TraceFileRecord>;
777
+ seen: Set<string>;
778
+ }): Promise<string | null> {
779
+ const key = `${input.file}#${input.name}`;
780
+ if (input.seen.has(key) || input.seen.size > 64) {
781
+ return null;
782
+ }
783
+ input.seen.add(key);
784
+
785
+ const record = loadTraceFile(input.file, input.cache);
786
+ if (record === null || record.ast === null || !record.mentionsServer) {
787
+ return null;
788
+ }
789
+ const ast = record.ast;
790
+ if (localTopLevelNames(ast).has(input.name)) {
791
+ return null; // didefinisikan lokal = kode client
792
+ }
793
+
794
+ const importBindings = new Map<string, { specifier: string; imported: string }>();
795
+ const edges: Array<{ specifier: string; local: string }> = [];
796
+
797
+ for (const statement of topLevelStatements(ast)) {
798
+ if (statement.type === "ImportDeclaration") {
799
+ const source = (statement["source"] as AstNode | null | undefined)?.["value"];
800
+ if (typeof source !== "string") {
801
+ continue;
802
+ }
803
+ for (const item of (statement["specifiers"] ?? []) as AstNode[]) {
804
+ if (item.type !== "ImportSpecifier") {
805
+ continue;
806
+ }
807
+ const local = (item["local"] as AstNode | undefined)?.["name"] as string | undefined;
808
+ const imported = ((item["imported"] as AstNode | undefined)?.["name"] as string | undefined) ?? local;
809
+ if (local !== undefined && imported !== undefined) {
810
+ importBindings.set(local, { specifier: source, imported });
811
+ }
812
+ }
813
+ continue;
814
+ }
815
+
816
+ if (statement.type === "ExportNamedDeclaration") {
817
+ const source = (statement["source"] as AstNode | null | undefined)?.["value"];
818
+ for (const item of (statement["specifiers"] ?? []) as AstNode[]) {
819
+ if (item.type !== "ExportSpecifier") {
820
+ continue;
821
+ }
822
+ const local = (item["local"] as AstNode | undefined)?.["name"] as string | undefined;
823
+ const exported = ((item["exported"] as AstNode | undefined)?.["name"] as string | undefined) ?? local;
824
+ if (local === undefined || exported !== input.name) {
825
+ continue;
826
+ }
827
+ if (typeof source === "string") {
828
+ edges.push({ specifier: source, local });
829
+ } else {
830
+ const binding = importBindings.get(local);
831
+ if (binding !== undefined) {
832
+ edges.push({ specifier: binding.specifier, local: binding.imported });
833
+ }
834
+ }
835
+ }
836
+ continue;
837
+ }
838
+
839
+ const exportedAll = statement["exported"];
840
+ if (
841
+ statement.type === "ExportAllDeclaration" &&
842
+ (exportedAll === null || exportedAll === undefined)
843
+ ) {
844
+ const source = (statement["source"] as AstNode | null | undefined)?.["value"];
845
+ if (typeof source === "string") {
846
+ edges.push({ specifier: source, local: input.name });
847
+ }
848
+ }
849
+ }
850
+
851
+ for (const edge of edges) {
852
+ const next = await input.resolveSafe(edge.specifier, input.file);
853
+ if (next === null) {
854
+ continue;
855
+ }
856
+ if (isServerFilePath(next)) {
857
+ const analysis = input.analyze(next);
858
+ const isServerFn =
859
+ analysis !== null && analysis.serverFns.some((fn) => fn.name === edge.local);
860
+ if (!isServerFn) {
861
+ return next;
862
+ }
863
+ continue;
864
+ }
865
+ const deeper = await traceValueToServer({
866
+ ...input,
867
+ file: next,
868
+ name: edge.local,
869
+ });
870
+ if (deeper !== null) {
871
+ return deeper;
872
+ }
873
+ }
874
+ return null;
875
+ }
876
+
877
+ function loadTraceFile(file: string, cache: Map<string, TraceFileRecord>): TraceFileRecord | null {
878
+ const cached = cache.get(file);
879
+ if (cached !== undefined) {
880
+ return cached;
881
+ }
882
+ let record: TraceFileRecord | null = null;
883
+ try {
884
+ const source = readFileSync(file, "utf8");
885
+ // Pra-filter murah: hanya modul yang (a) menyebut `.server` atau (b)
886
+ // punya re-export (`export … from`) yang bisa menjadi mata rantai barrel.
887
+ const isBarrel = /(^|\n)\s*export\s+(\*|\{[^}]*\})\s+from\s/.test(source);
888
+ const mentionsServer = source.includes(".server") || isBarrel;
889
+ record = {
890
+ ast: mentionsServer ? parseModule(source, file) : null,
891
+ mentionsServer,
892
+ };
893
+ } catch {
894
+ record = null;
895
+ }
896
+ cache.set(file, record ?? { ast: null, mentionsServer: false });
897
+ return record;
898
+ }
899
+
721
900
  export async function checkClientModuleImports(
722
901
  context: ClientImportCheckContext,
723
902
  ): Promise<void> {
724
903
  const { file, code, projectRoot, resolve, analyze } = context;
725
904
  const ast = parseModule(code, file);
905
+ const traceCache = new Map<string, TraceFileRecord>();
726
906
  const relativeFile = toRelative(projectRoot, file);
727
907
  // Addendum C: gate paket server-only (daftar bawaan ∪ security.serverOnlyPackages ∪ marker).
728
908
  const serverOnly = createServerOnlyClassifier({
@@ -739,6 +919,54 @@ export async function checkClientModuleImports(
739
919
  }
740
920
  }
741
921
 
922
+ // Addendum J.4: config + direktori `src/server/**` server-only.
923
+ let configFileReal: string | null = null;
924
+ if (context.configFile !== undefined) {
925
+ try {
926
+ configFileReal = realpathSync(context.configFile);
927
+ } catch {
928
+ configFileReal = context.configFile;
929
+ }
930
+ }
931
+ let serverDirReal: string | null = null;
932
+ if (context.serverDir !== undefined) {
933
+ try {
934
+ serverDirReal = realpathSync(context.serverDir);
935
+ } catch {
936
+ serverDirReal = context.serverDir;
937
+ }
938
+ }
939
+
940
+ const rejectConfigImport = (line: number, specifier: string): never => {
941
+ throw new MoonsError({
942
+ code: "E_FORBIDDEN_SERVER_IMPORT",
943
+ file: relativeFile,
944
+ line,
945
+ message: `mengimpor \`${specifier}\` — \`moons.config.ts\` adalah modul server-only`,
946
+ details:
947
+ "Config memuat `context`/`middleware` (kode server) dan tidak pernah masuk bundle client (Addendum J.4).",
948
+ suggestions: [
949
+ "Pindahkan nilai yang dibutuhkan ke modul client-safe (mis. `src/shared/…`)",
950
+ "Ambil nilai dari server lewat loader/serverFn",
951
+ ],
952
+ });
953
+ };
954
+
955
+ const rejectServerDirImport = (line: number, specifier: string, resolvedTarget: string): never => {
956
+ throw new MoonsError({
957
+ code: "E_FORBIDDEN_SERVER_IMPORT",
958
+ file: relativeFile,
959
+ line,
960
+ message: `mengimpor \`${specifier}\` — \`${toRelative(projectRoot, resolvedTarget)}\` ada di \`src/server/**\` (server-only)`,
961
+ details:
962
+ "Direktori `src/server/**` hanya untuk kode server (Addendum J.4); suffix `.server.ts` bukan satu-satunya jaminan.",
963
+ suggestions: [
964
+ "Pindahkan kode yang client-safe ke `src/shared/`",
965
+ "Akses nilainya dari server lewat loader/serverFn",
966
+ ],
967
+ });
968
+ };
969
+
742
970
  const rejectApiImport = (line: number, specifier: string, resolvedTarget: string): never => {
743
971
  const relApi = toRelative(projectRoot, resolvedTarget);
744
972
  throw new MoonsError({
@@ -852,6 +1080,24 @@ export async function checkClientModuleImports(
852
1080
  rejectApiImport(line, rawSpecifier, resolvedTarget);
853
1081
  }
854
1082
 
1083
+ // Addendum J.4: `moons.config.ts` + direktori `src/server/**` server-only
1084
+ // (impor nilai; type-only dikecualikan — J.5).
1085
+ if (!typeOnly && resolvedTarget !== null) {
1086
+ if (configFileReal !== null && resolvedTarget === configFileReal) {
1087
+ rejectConfigImport(line, rawSpecifier);
1088
+ }
1089
+ if (
1090
+ serverDirReal !== null &&
1091
+ isInsideDir(resolvedTarget, serverDirReal) &&
1092
+ !isServerFilePath(resolvedTarget) &&
1093
+ // File non-kontrak (`*.server.mts` dll.) punya error khusus yang
1094
+ // lebih tepat — biarkan jatuh ke pemeriksaan di bawah.
1095
+ !isNonContractServerFile(resolvedTarget)
1096
+ ) {
1097
+ rejectServerDirImport(line, rawSpecifier, resolvedTarget);
1098
+ }
1099
+ }
1100
+
855
1101
  // Addendum C: paket server-only (nama paket hasil resolve — subpath,
856
1102
  // alias, tsconfig paths, symlink/workspace ikut tertangkap).
857
1103
  if (!typeOnly) {
@@ -884,6 +1130,65 @@ export async function checkClientModuleImports(
884
1130
  const serverBySpecifier = isServerSpecifier(specifier);
885
1131
  const serverByResolve = resolvedTarget !== null && isServerFilePath(resolvedTarget);
886
1132
  if (!serverBySpecifier && !serverByResolve) {
1133
+ // Addendum K.1: rantai re-export ke `*.server.ts` (barrel) — pesan Moons
1134
+ // menggantikan `MISSING_EXPORT` bundler. Hanya nilai (bukan type-only).
1135
+ if (!typeOnly && resolvedTarget !== null && /[.](ts|tsx)$/.test(resolvedTarget)) {
1136
+ const requestedNames: string[] = [];
1137
+ if (statement.type === "ImportDeclaration") {
1138
+ for (const item of (statement["specifiers"] ?? []) as AstNode[]) {
1139
+ if (item.type !== "ImportSpecifier") {
1140
+ continue;
1141
+ }
1142
+ const kind = (item["importKind"] ?? statement["importKind"]) as string | undefined;
1143
+ if (kind === "type") {
1144
+ continue;
1145
+ }
1146
+ const imported = (item["imported"] as AstNode | undefined)?.["name"] as string | undefined;
1147
+ if (imported !== undefined) {
1148
+ requestedNames.push(imported);
1149
+ }
1150
+ }
1151
+ } else if (statement.type === "ExportNamedDeclaration") {
1152
+ for (const item of (statement["specifiers"] ?? []) as AstNode[]) {
1153
+ const kind = (item["importKind"] ??
1154
+ statement["importKind"] ??
1155
+ item["exportKind"] ??
1156
+ statement["exportKind"]) as string | undefined;
1157
+ if (kind === "type") {
1158
+ continue;
1159
+ }
1160
+ const local = (item["local"] as AstNode | undefined)?.["name"] as string | undefined;
1161
+ if (local !== undefined) {
1162
+ requestedNames.push(local);
1163
+ }
1164
+ }
1165
+ }
1166
+ for (const name of requestedNames) {
1167
+ const serverFile = await traceValueToServer({
1168
+ file: resolvedTarget,
1169
+ name,
1170
+ resolveSafe,
1171
+ analyze,
1172
+ cache: traceCache,
1173
+ seen: new Set<string>(),
1174
+ });
1175
+ if (serverFile !== null) {
1176
+ throw new MoonsError({
1177
+ code: "E_SERVER_IMPORT_IN_CLIENT",
1178
+ file: relativeFile,
1179
+ line,
1180
+ message: `mengimpor \`${name}\` — berasal dari \`${toRelative(projectRoot, serverFile)}\` (via rantai re-export)`,
1181
+ details:
1182
+ `\`${toRelative(projectRoot, resolvedTarget)}\` me-re-export nilai dari file server; ` +
1183
+ "stub client tidak mengekspornya (dulu gagal dengan error mentah bundler `MISSING_EXPORT`).",
1184
+ suggestions: [
1185
+ "Buat server function yang mengembalikan data yang dibutuhkan, atau",
1186
+ "Definisikan ulang konstanta publik itu di modul client-safe (jangan pindahkan nilai rahasia)",
1187
+ ],
1188
+ });
1189
+ }
1190
+ }
1191
+ }
887
1192
  continue;
888
1193
  }
889
1194
 
@@ -906,9 +1211,13 @@ export async function checkClientModuleImports(
906
1211
  reject(line, `mengimpor namespace (\`* as\`) dari ${targetRelative}`, "Import namespace dari file server tidak boleh menyeberang ke client.");
907
1212
  }
908
1213
  const name = ((item["local"] as AstNode | undefined)?.["name"] as string) ?? "";
909
- const kind = (item["importKind"] as string | undefined) ?? (statement["importKind"] as string | undefined);
910
- if (kind === "type") {
911
- reject(line, `mengimpor type \`${name}\` dari ${targetRelative}`, "Import dari file server tidak diizinkan di modul client.");
1214
+ // Type-only (Addendum J.5): Babel menaruh `importKind: "type"` di
1215
+ // statement untuk `import type { X }`, dan di specifier untuk
1216
+ // `import { type X }` — keduanya harus dibaca (D1).
1217
+ const specifierKind = item["importKind"] as string | undefined;
1218
+ const statementKind = statement["importKind"] as string | undefined;
1219
+ if (specifierKind === "type" || statementKind === "type") {
1220
+ continue;
912
1221
  }
913
1222
  if (name.length > 0 && !(await isAllowedServerFnImport(resolvedTarget, specifier, name))) {
914
1223
  reject(
@@ -1,15 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/server/shell.ts", "../src/server/node.ts", "../src/virtual-modules.ts", "../src/devtools.ts", "../src/server/dev.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=\"id\">',\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 tidak memuat \\`${ROOT_PLACEHOLDER}\\``,\n details: \"index.html aplikasi wajib memuat elemen root tersebut agar SSR bisa meng-inject markup.\",\n suggestions: [\n `Tambahkan ${ROOT_PLACEHOLDER} ke index.html`,\n \"Atau hapus index.html untuk memakai shell bawaan\",\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` tidak ditemukan\",\n details: \"Build client harus dijalankan sebelum server produksi dinyalakan.\",\n suggestions: [\"Jalankan `bun run build` lebih dulu\"],\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\";\n\nconst MAX_BODY_BYTES = 32 * 1024 * 1024; // batas defensif transport; limit RPC §7.3 tetap berlaku\n\n/** Konversi request Node → Web `Request` (body diteruskan untuk non-GET/HEAD, §7.3). */\nexport async function toWebRequest(req: IncomingMessage): Promise<Request> {\n const host = req.headers.host ?? \"localhost\";\n const url = `http://${host}${req.url ?? \"/\"}`;\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\") {\n // content-length tidak disalin; fetch menghitung ulang dari body.\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 if (method === \"GET\" || method === \"HEAD\") {\n return new Request(url, { method, headers });\n }\n\n const body = await readBody(req);\n return new Request(url, { method, headers, body: new Uint8Array(body) });\n}\n\nfunction readBody(req: IncomingMessage): Promise<Buffer> {\n return new Promise((resolvePromise, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n\n req.on(\"data\", (chunk: Buffer) => {\n size += chunk.length;\n if (size > MAX_BODY_BYTES) {\n req.destroy();\n reject(new Error(\"request body melebihi batas transport\"));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => resolvePromise(Buffer.concat(chunks)));\n req.on(\"error\", (error) => reject(error));\n });\n}\n\n/** Tulis Web `Response` ke `ServerResponse` Node. */\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 const body = Buffer.from(await response.arrayBuffer());\n res.end(body);\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
- "/**\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\">(kosong)</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\">(tidak ada 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\">aktif</span> (canary terpasang)'\n : '<span class=\"muted\">nonaktif</span> (tanpa canary)';\n\n return `<!doctype html>\n<html lang=\"id\">\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",
9
- "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 { sendNodeResponse, toWebRequest } from \"./node\";\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 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 gagal dirender: ${String(error)}`);\n }\n return;\n }\n\n if (!shouldHandle(req, options.apiPrefix)) {\n next();\n return;\n }\n void handleRequest(getApp, req, res).catch((error: unknown) => {\n next(error as Error);\n });\n });\n}\n\nasync function handleRequest(\n getApp: () => Promise<DevApp>,\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n const app = await getApp();\n const response = await app.handler(await toWebRequest(req));\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",
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. Jangan diubah manual.\",\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. Jangan diubah manual.\",\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": "5E942E7C571A3A0664756E2164756E21",
14
- "names": []
15
- }