defuss-ssg 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { v as validateProjectDir, b as build, s as serve } from './serve-BZhZ9J90.mjs';
2
+ import { v as validateProjectDir, b as build, s as serve } from './serve-w6eyG8f3.mjs';
3
3
  import { join, dirname, resolve } from 'node:path';
4
4
  import { existsSync, readFileSync } from 'node:fs';
5
5
  import { spawn } from 'node:child_process';
package/dist/index.cjs CHANGED
@@ -57,14 +57,28 @@ const autoHydratePlugin = {
57
57
  if (Array.isArray(node)) {
58
58
  node.map((child) => processDefussComponents(child));
59
59
  } else if (node && typeof node === "object") {
60
+ const vnode = node;
60
61
  let clientSrcFile = null;
61
- if (node.sourceInfo && typeof node.sourceInfo === "object" && typeof node.sourceInfo.fileName === "string" && node.sourceInfo.fileName.includes(tmpComponents) && node.type !== "script" && // skip already inserted hydration scripts
62
- node.type !== "head" && // skip head elements
63
- node.type !== "link" && // skip head elements
64
- node.type !== "meta" && // skip head elements
65
- node.type !== "title") {
66
- clientSrcFile = node.sourceInfo.fileName.replaceAll(tmp, "").replaceAll(node_path.sep, "/").replace(/\.t?sx?$/, ".js");
62
+ if (vnode.sourceInfo && typeof vnode.sourceInfo === "object" && typeof vnode.sourceInfo.fileName === "string" && vnode.sourceInfo.fileName.includes(tmpComponents) && vnode.type !== "script" && // skip already inserted hydration scripts
63
+ vnode.type !== "head" && // skip head elements
64
+ vnode.type !== "link" && // skip head elements
65
+ vnode.type !== "meta" && // skip head elements
66
+ vnode.type !== "title") {
67
+ clientSrcFile = vnode.sourceInfo.fileName.replaceAll(tmp, "").replaceAll(node_path.sep, "/").replace(/\.t?sx?$/, ".js");
67
68
  const id = `dh_${Math.random().toString(36).slice(2)}`;
69
+ console.log(`[auto-hydrate] Found component node. type="${vnode.type}", sourceInfo.fileName="${vnode.sourceInfo?.fileName}", hasComponentProps=${!!vnode.componentProps}, componentProps=`, JSON.stringify(vnode.componentProps)?.slice(0, 300));
70
+ console.log(`[auto-hydrate] Node keys:`, Object.keys(vnode));
71
+ const componentProps = {};
72
+ if (vnode.componentProps) {
73
+ for (const [key, value] of Object.entries(vnode.componentProps)) {
74
+ if (typeof value === "function" || typeof value === "undefined") continue;
75
+ try {
76
+ JSON.stringify(value);
77
+ componentProps[key] = value;
78
+ } catch {
79
+ }
80
+ }
81
+ }
68
82
  node = {
69
83
  // Hydration wrapper
70
84
  type: "div",
@@ -73,7 +87,7 @@ const autoHydratePlugin = {
73
87
  "data-hydrate": "true"
74
88
  },
75
89
  children: [
76
- node,
90
+ vnode,
77
91
  {
78
92
  // Hydration script
79
93
  type: "script",
@@ -82,7 +96,7 @@ const autoHydratePlugin = {
82
96
  `
83
97
  ;(async function(){
84
98
 
85
- const props = ${JSON.stringify(props || {})};
99
+ const props = ${JSON.stringify(componentProps)};
86
100
  const cacheBust = "?v=" + Date.now();
87
101
  console.log("[hydrate:${id}] Starting hydration, cacheBust=" + cacheBust);
88
102
  console.log("[hydrate:${id}] Importing runtime from /components/runtime.js" + cacheBust);
@@ -142,10 +156,11 @@ const autoHydratePlugin = {
142
156
  };
143
157
  }
144
158
  if (!clientSrcFile) {
145
- if (typeof node.children !== "undefined" && Array.isArray(node.children)) {
146
- for (let i = 0; i < node.children.length; i++) {
147
- node.children[i] = processDefussComponents(
148
- node.children[i]
159
+ const v = node;
160
+ if (typeof v.children !== "undefined" && Array.isArray(v.children)) {
161
+ for (let i = 0; i < v.children.length; i++) {
162
+ v.children[i] = processDefussComponents(
163
+ v.children[i]
149
164
  );
150
165
  }
151
166
  }
@@ -185,6 +200,7 @@ const readConfig = async (projectDir, debug) => {
185
200
  config.tmp = config.tmp || configDefaults.tmp;
186
201
  config.remarkPlugins = config.remarkPlugins || configDefaults.remarkPlugins;
187
202
  config.rehypePlugins = config.rehypePlugins || configDefaults.rehypePlugins;
203
+ config.rpc = config.rpc ?? configDefaults.rpc;
188
204
  return config;
189
205
  };
190
206
  const configDefaults = {
@@ -195,7 +211,8 @@ const configDefaults = {
195
211
  tmp: ".ssg-temp",
196
212
  plugins: [tailwind.tailwindPlugin, autoHydratePlugin],
197
213
  remarkPlugins,
198
- rehypePlugins
214
+ rehypePlugins,
215
+ rpc: true
199
216
  };
200
217
 
201
218
  const validateProjectDir = (projectDir) => {
@@ -557,6 +574,7 @@ const build = async ({
557
574
  if (debug) {
558
575
  console.log(`Incremental build \u2014 changeKind: ${changeKind}, file: ${changedRelative}`);
559
576
  }
577
+ console.log(`[build] changeKind=${changeKind}, changedRelative=${changedRelative}`);
560
578
  }
561
579
  const isFullBuild = changeKind === "full" || changeKind === "config";
562
580
  const tempExists = node_fs.existsSync(config.tmp);
@@ -621,10 +639,17 @@ const build = async ({
621
639
  node_path.join(tmpComponentsDir, "runtime.ts")
622
640
  );
623
641
  console.timeEnd("[build] copy-hydration");
642
+ if (changeKind === "component" || isFullBuild) {
643
+ const staleJsFiles = await glob.async(node_path.join(tmpComponentsDir, "**/*.js"));
644
+ for (const f of staleJsFiles) {
645
+ node_fs.rmSync(f);
646
+ }
647
+ }
648
+ let pageBuildResult = null;
624
649
  if (changeKind !== "asset") {
625
650
  console.time("[build] esbuild-pages");
626
651
  const pageEntryPoints = changeKind === "page" ? [node_path.join(config.tmp, changedRelative)] : [node_path.join(tmpPagesDir, "**/*.mdx")];
627
- await esbuild.build({
652
+ pageBuildResult = await esbuild.build({
628
653
  entryPoints: pageEntryPoints,
629
654
  format: "esm",
630
655
  bundle: true,
@@ -632,6 +657,7 @@ const build = async ({
632
657
  jsxDev: true,
633
658
  target: ["esnext"],
634
659
  outdir: tmpPagesDir,
660
+ metafile: true,
635
661
  plugins: [
636
662
  mdx({
637
663
  jsxImportSource: "defuss",
@@ -654,7 +680,8 @@ const build = async ({
654
680
  bundle: true,
655
681
  splitting: true,
656
682
  target: ["esnext"],
657
- outdir: tmpComponentsDir
683
+ outdir: tmpComponentsDir,
684
+ allowOverwrite: true
658
685
  });
659
686
  console.timeEnd("[build] esbuild-components");
660
687
  }
@@ -664,6 +691,33 @@ const build = async ({
664
691
  if (changeKind === "page") {
665
692
  const jsFile = node_path.join(config.tmp, changedRelative.replace(/\.mdx$/, ".js"));
666
693
  outputFiles = node_fs.existsSync(jsFile) ? [jsFile] : [];
694
+ } else if (changeKind === "component" && pageBuildResult?.metafile) {
695
+ const changedBaseName = changedRelative.replace(/\.[^.]+$/, "");
696
+ outputFiles = [];
697
+ console.log(`[build] component-dep: looking for pages depending on "${changedBaseName}"`);
698
+ for (const [outputPath, meta] of Object.entries(pageBuildResult.metafile.outputs)) {
699
+ if (outputPath.endsWith(".map")) continue;
700
+ const inputPaths = Object.keys(meta.inputs);
701
+ const dependsOnChanged = inputPaths.some((input) => {
702
+ const inputNorm = input.replace(/\.[^.]+$/, "");
703
+ return inputNorm.endsWith(changedBaseName);
704
+ });
705
+ console.log(`[build] component-dep: output="${outputPath}", inputs=${inputPaths.length}, dependsOnChanged=${dependsOnChanged}`);
706
+ if (dependsOnChanged) {
707
+ console.log(`[build] component-dep: matching inputs: ${inputPaths.filter((i) => i.replace(/\.[^.]+$/, "").endsWith(changedBaseName)).join(", ")}`);
708
+ }
709
+ if (dependsOnChanged) {
710
+ const resolved = node_path.resolve(outputPath);
711
+ if (resolved.endsWith(".js") && node_fs.existsSync(resolved)) {
712
+ outputFiles.push(resolved);
713
+ }
714
+ }
715
+ }
716
+ console.log(`[build] component-dep: ${outputFiles.length} page(s) affected out of ${Object.keys(pageBuildResult.metafile.outputs).filter((p) => !p.endsWith(".map")).length} total`);
717
+ if (outputFiles.length === 0) {
718
+ console.log(`[build] component-dep: FALLBACK \u2014 rendering all pages`);
719
+ outputFiles = await glob.async(node_path.join(tmpPagesDir, "**/*.js"));
720
+ }
667
721
  } else {
668
722
  outputFiles = await glob.async(node_path.join(tmpPagesDir, "**/*.js"));
669
723
  }
@@ -672,10 +726,8 @@ const build = async ({
672
726
  }
673
727
  for (const outputFile of outputFiles) {
674
728
  const outputHtmlFilePath = outputFile.replace(".js", ".html");
675
- const relativeOutputHtmlFilePath = outputHtmlFilePath.replace(
676
- `${tmpPagesDir}${node_path.sep}`,
677
- ""
678
- );
729
+ const resolvedTmpPagesDir = node_path.resolve(tmpPagesDir);
730
+ const relativeOutputHtmlFilePath = outputHtmlFilePath.replace(`${resolvedTmpPagesDir}${node_path.sep}`, "").replace(`${tmpPagesDir}${node_path.sep}`, "");
679
731
  const pageLabel = relativeOutputHtmlFilePath;
680
732
  if (debug) {
681
733
  console.log("Processing output file (JS):", outputFile);
@@ -814,6 +866,157 @@ const filePathToRoute = (filePath, config, cwd) => {
814
866
  return normalizedPath;
815
867
  };
816
868
 
869
+ const discoverRpcFile = (projectDir) => {
870
+ const candidates = ["rpc.ts", "rpc.js"];
871
+ for (const candidate of candidates) {
872
+ const filePath = node_path.join(projectDir, candidate);
873
+ if (node_fs.existsSync(filePath)) {
874
+ return filePath;
875
+ }
876
+ }
877
+ return null;
878
+ };
879
+ const compileRpcModule = async (rpcFilePath, projectDir, debug = false) => {
880
+ const outDir = node_path.join(projectDir, ".rpc");
881
+ if (!node_fs.existsSync(outDir)) {
882
+ node_fs.mkdirSync(outDir, { recursive: true });
883
+ }
884
+ if (debug) {
885
+ console.log(`Compiling RPC module: ${rpcFilePath}`);
886
+ }
887
+ console.time("[rpc] esbuild-compile");
888
+ await esbuild.build({
889
+ entryPoints: [rpcFilePath],
890
+ format: "esm",
891
+ bundle: true,
892
+ platform: "node",
893
+ target: ["esnext"],
894
+ outdir: outDir,
895
+ outExtension: { ".js": ".mjs" },
896
+ // Mark defuss-rpc as external so it uses the installed version
897
+ external: ["defuss-rpc", "defuss-rpc/*"]
898
+ });
899
+ console.timeEnd("[rpc] esbuild-compile");
900
+ const compiledPath = node_path.join(outDir, "rpc.mjs");
901
+ if (debug) {
902
+ console.log(`RPC module compiled to: ${compiledPath}`);
903
+ }
904
+ return compiledPath;
905
+ };
906
+ const loadRpcModule = async (compiledPath) => {
907
+ const code = await promises.readFile(compiledPath, "utf-8");
908
+ const encoded = Buffer.from(code).toString("base64");
909
+ const dataUrl = `data:text/javascript;base64,${encoded}`;
910
+ return import(dataUrl);
911
+ };
912
+ const buildRpcNamespace = (moduleExports) => {
913
+ const namespace = {};
914
+ if (moduleExports.default && typeof moduleExports.default === "object") {
915
+ const defaultObj = moduleExports.default;
916
+ for (const [key, value] of Object.entries(defaultObj)) {
917
+ if (value && (typeof value === "object" || typeof value === "function")) {
918
+ namespace[key] = value;
919
+ }
920
+ }
921
+ }
922
+ for (const [key, value] of Object.entries(moduleExports)) {
923
+ if (key === "default") continue;
924
+ if (value && (typeof value === "object" || typeof value === "function")) {
925
+ namespace[key] = value;
926
+ }
927
+ }
928
+ return namespace;
929
+ };
930
+ const initializeRpc = async (projectDir, config, debug = false) => {
931
+ if (config.rpc === false) {
932
+ if (debug) {
933
+ console.log("RPC disabled in config");
934
+ }
935
+ return false;
936
+ }
937
+ let rpcFilePath = null;
938
+ if (typeof config.rpc === "string") {
939
+ const customPath = node_path.resolve(projectDir, config.rpc);
940
+ if (node_fs.existsSync(customPath)) {
941
+ rpcFilePath = customPath;
942
+ }
943
+ } else {
944
+ rpcFilePath = discoverRpcFile(projectDir);
945
+ }
946
+ if (!rpcFilePath) {
947
+ if (debug) {
948
+ console.log("No rpc.ts/rpc.js found in project root");
949
+ }
950
+ return false;
951
+ }
952
+ let rpcServer;
953
+ try {
954
+ rpcServer = await import('defuss-rpc/server.js');
955
+ } catch {
956
+ console.warn(
957
+ "[defuss-ssg] defuss-rpc is not installed. Install it to enable RPC support:\n npm install defuss-rpc"
958
+ );
959
+ return false;
960
+ }
961
+ try {
962
+ const compiledPath = await compileRpcModule(rpcFilePath, projectDir, debug);
963
+ const moduleExports = await loadRpcModule(compiledPath);
964
+ const namespace = buildRpcNamespace(moduleExports);
965
+ if (Object.keys(namespace).length === 0) {
966
+ console.warn(
967
+ "[defuss-ssg] rpc.ts was found but exports no RPC namespace entries"
968
+ );
969
+ return false;
970
+ }
971
+ rpcServer.clearRpcServer();
972
+ rpcServer.createRpcServer(namespace);
973
+ console.log(
974
+ `RPC initialized with ${Object.keys(namespace).length} namespace(s): ${Object.keys(namespace).join(", ")}`
975
+ );
976
+ return true;
977
+ } catch (error) {
978
+ console.error("[defuss-ssg] Failed to initialize RPC:", error);
979
+ return false;
980
+ }
981
+ };
982
+ const handleRpcRequest = async (req, res) => {
983
+ try {
984
+ const { rpcRoute } = await import('defuss-rpc/server.js');
985
+ const protocol = req.protocol || "http";
986
+ const host = req.get?.("host") || req.headers?.host || "localhost";
987
+ const url = `${protocol}://${host}${req.originalUrl}`;
988
+ const reqInit = {
989
+ method: req.method,
990
+ headers: req.headers
991
+ };
992
+ if (["POST", "PUT", "PATCH", "DELETE"].includes(req.method)) {
993
+ try {
994
+ const chunks = [];
995
+ await new Promise((resolve2, reject) => {
996
+ req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
997
+ req.on("end", resolve2);
998
+ req.on("error", reject);
999
+ });
1000
+ if (chunks.length > 0) {
1001
+ reqInit.body = Buffer.concat(chunks);
1002
+ }
1003
+ } catch {
1004
+ }
1005
+ }
1006
+ const request = new Request(url, reqInit);
1007
+ const response = await rpcRoute({ request });
1008
+ const responseText = await response.text();
1009
+ const contentType = response.headers.get("content-type") || "application/json";
1010
+ res.status(response.status).set("Content-Type", contentType).send(responseText);
1011
+ } catch (error) {
1012
+ console.error("[defuss-ssg] RPC request error:", error);
1013
+ res.status(500).json({
1014
+ error: "Internal server error",
1015
+ message: error instanceof Error ? error.message : String(error)
1016
+ });
1017
+ }
1018
+ };
1019
+
817
1020
  const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
818
1021
  const { WebSocketServer } = require$1("ultimate-ws");
819
1022
  const isPortAvailable = async (port) => {
@@ -851,6 +1054,21 @@ const serve = async ({
851
1054
  console.time("[serve] register-endpoints");
852
1055
  await registerEndpoints(app, projectDir, config, debug);
853
1056
  console.timeEnd("[serve] register-endpoints");
1057
+ let rpcActive = false;
1058
+ console.time("[serve] rpc-init");
1059
+ try {
1060
+ rpcActive = await initializeRpc(projectDir, config, debug);
1061
+ if (rpcActive) {
1062
+ app.post("/rpc", handleRpcRequest);
1063
+ app.post("/rpc/schema", handleRpcRequest);
1064
+ console.log("RPC routes mounted at /rpc and /rpc/schema");
1065
+ }
1066
+ } catch (error) {
1067
+ if (debug) {
1068
+ console.warn("[serve] RPC initialization failed:", error);
1069
+ }
1070
+ }
1071
+ console.timeEnd("[serve] rpc-init");
854
1072
  app.use((_req, res, next) => {
855
1073
  res.set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
856
1074
  res.set("Pragma", "no-cache");
@@ -879,15 +1097,36 @@ const serve = async ({
879
1097
  }
880
1098
  isBuilding = true;
881
1099
  try {
882
- console.time("[serve] rebuild");
883
- await build({ projectDir, debug, mode: "serve", changedFile: filePath });
884
- console.timeEnd("[serve] rebuild");
1100
+ const rpcFile2 = discoverRpcFile(projectDir);
1101
+ const isRpcFile = rpcFile2 && filePath === rpcFile2;
1102
+ if (isRpcFile) {
1103
+ console.time("[serve] rpc-reload");
1104
+ try {
1105
+ const wasActive = rpcActive;
1106
+ rpcActive = await initializeRpc(projectDir, config, debug);
1107
+ if (!wasActive && rpcActive) {
1108
+ app.post("/rpc", handleRpcRequest);
1109
+ app.post("/rpc/schema", handleRpcRequest);
1110
+ console.log("RPC routes mounted at /rpc and /rpc/schema");
1111
+ }
1112
+ } catch (error) {
1113
+ console.error("RPC reload failed:", error);
1114
+ }
1115
+ console.timeEnd("[serve] rpc-reload");
1116
+ } else {
1117
+ console.time("[serve] rebuild");
1118
+ await build({ projectDir, debug, mode: "serve", changedFile: filePath });
1119
+ console.timeEnd("[serve] rebuild");
1120
+ }
1121
+ const pagesDir2 = node_path.join(projectDir, config.pages);
1122
+ const isPageFile = filePath.startsWith(pagesDir2 + "/") || filePath.startsWith(pagesDir2 + node_path.sep);
1123
+ const reloadPath = isPageFile ? filePathToRoute(filePath, config, projectDir) : void 0;
885
1124
  liveReloadServer.clients.forEach((client) => {
886
1125
  if (client.readyState === 1) {
887
1126
  client.send(
888
1127
  JSON.stringify({
889
1128
  command: "reload",
890
- path: filePathToRoute(filePath, config, projectDir)
1129
+ ...reloadPath ? { path: reloadPath } : {}
891
1130
  })
892
1131
  );
893
1132
  }
@@ -906,7 +1145,12 @@ const serve = async ({
906
1145
  }
907
1146
  }
908
1147
  };
909
- const watcher = chokidar.watch([pagesDir, componentsDir, assetsDir], {
1148
+ const watchPaths = [pagesDir, componentsDir, assetsDir];
1149
+ const rpcFile = discoverRpcFile(projectDir);
1150
+ if (rpcFile) {
1151
+ watchPaths.push(rpcFile);
1152
+ }
1153
+ const watcher = chokidar.watch(watchPaths, {
910
1154
  ignored: /(^|[\/\\])\../,
911
1155
  // Ignore dotfiles
912
1156
  persistent: true,
@@ -938,9 +1182,13 @@ exports.HTTP_METHODS = HTTP_METHODS;
938
1182
  exports.build = build;
939
1183
  exports.buildEndpoints = buildEndpoints;
940
1184
  exports.compileEndpoints = compileEndpoints;
1185
+ exports.compileRpcModule = compileRpcModule;
941
1186
  exports.configDefaults = configDefaults;
942
1187
  exports.discoverEndpointSourceFiles = discoverEndpointSourceFiles;
1188
+ exports.discoverRpcFile = discoverRpcFile;
943
1189
  exports.endpointFileToRoute = endpointFileToRoute;
1190
+ exports.handleRpcRequest = handleRpcRequest;
1191
+ exports.initializeRpc = initializeRpc;
944
1192
  exports.readConfig = readConfig;
945
1193
  exports.registerEndpoints = registerEndpoints;
946
1194
  exports.rehypePlugins = rehypePlugins;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RehypePlugins, a as RemarkPlugins, S as SsgConfig, B as BuildOptions, b as Status } from './types-QQrIkB7n.cjs';
2
- export { c as BuildMode, P as PluginFn, d as PluginFnPageDom, e as PluginFnPageHtml, f as PluginFnPageVdom, g as PluginFnPrePost, h as SsgPlugin, i as StatusCode } from './types-QQrIkB7n.cjs';
1
+ import { R as RehypePlugins, a as RemarkPlugins, S as SsgConfig, B as BuildOptions, b as Status } from './types-Be8_Y5-t.cjs';
2
+ export { c as BuildMode, P as PluginFn, d as PluginFnPageDom, e as PluginFnPageHtml, f as PluginFnPageVdom, g as PluginFnPrePost, h as SsgPlugin, i as StatusCode } from './types-Be8_Y5-t.cjs';
3
3
  import '@mdx-js/esbuild';
4
4
  import 'defuss/server';
5
5
 
@@ -158,5 +158,43 @@ declare const buildEndpoints: (projectDir: string, config: SsgConfig, debug?: bo
158
158
  */
159
159
  declare const registerEndpoints: (app: any, projectDir: string, config: SsgConfig, debug?: boolean) => Promise<void>;
160
160
 
161
- export { BuildOptions, HTTP_METHODS, RehypePlugins, RemarkPlugins, SsgConfig, Status, build, buildEndpoints, compileEndpoints, configDefaults, discoverEndpointSourceFiles, endpointFileToRoute, readConfig, registerEndpoints, rehypePlugins, remarkPlugins, resolveEndpoints, serve };
161
+ /**
162
+ * Discover the RPC file in the project root.
163
+ * Looks for `rpc.ts` or `rpc.js`.
164
+ *
165
+ * @param projectDir The project root directory
166
+ * @returns The absolute path to the RPC file, or null if not found
167
+ */
168
+ declare const discoverRpcFile: (projectDir: string) => string | null;
169
+ /**
170
+ * Compile the RPC module source file with esbuild into the `.rpc/` directory.
171
+ *
172
+ * @param rpcFilePath Absolute path to the rpc.ts / rpc.js source file
173
+ * @param projectDir The project root directory
174
+ * @param debug Enable verbose logging
175
+ * @returns Absolute path to the compiled .mjs file
176
+ */
177
+ declare const compileRpcModule: (rpcFilePath: string, projectDir: string, debug?: boolean) => Promise<string>;
178
+ /**
179
+ * Initialize the RPC server from the project's rpc.ts/rpc.js file.
180
+ *
181
+ * This function discovers, compiles, loads, and registers the RPC module.
182
+ * It dynamically imports `defuss-rpc/server.js` to avoid a hard dependency.
183
+ *
184
+ * @param projectDir The project root directory
185
+ * @param config The SSG config
186
+ * @param debug Enable verbose logging
187
+ * @returns true if RPC was set up successfully, false if no rpc.ts found or defuss-rpc not installed
188
+ */
189
+ declare const initializeRpc: (projectDir: string, config: SsgConfig, debug?: boolean) => Promise<boolean>;
190
+ /**
191
+ * Handle an RPC request by forwarding to defuss-rpc's rpcRoute handler.
192
+ * Converts Express req/res to Web Request/Response.
193
+ *
194
+ * @param req Express request
195
+ * @param res Express response
196
+ */
197
+ declare const handleRpcRequest: (req: any, res: any) => Promise<void>;
198
+
199
+ export { BuildOptions, HTTP_METHODS, RehypePlugins, RemarkPlugins, SsgConfig, Status, build, buildEndpoints, compileEndpoints, compileRpcModule, configDefaults, discoverEndpointSourceFiles, discoverRpcFile, endpointFileToRoute, handleRpcRequest, initializeRpc, readConfig, registerEndpoints, rehypePlugins, remarkPlugins, resolveEndpoints, serve };
162
200
  export type { APIRoute, EndpointContext, EndpointHandler, EndpointModule, HttpMethod, ResolvedEndpoint };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RehypePlugins, a as RemarkPlugins, S as SsgConfig, B as BuildOptions, b as Status } from './types-QQrIkB7n.mjs';
2
- export { c as BuildMode, P as PluginFn, d as PluginFnPageDom, e as PluginFnPageHtml, f as PluginFnPageVdom, g as PluginFnPrePost, h as SsgPlugin, i as StatusCode } from './types-QQrIkB7n.mjs';
1
+ import { R as RehypePlugins, a as RemarkPlugins, S as SsgConfig, B as BuildOptions, b as Status } from './types-Be8_Y5-t.mjs';
2
+ export { c as BuildMode, P as PluginFn, d as PluginFnPageDom, e as PluginFnPageHtml, f as PluginFnPageVdom, g as PluginFnPrePost, h as SsgPlugin, i as StatusCode } from './types-Be8_Y5-t.mjs';
3
3
  import '@mdx-js/esbuild';
4
4
  import 'defuss/server';
5
5
 
@@ -158,5 +158,43 @@ declare const buildEndpoints: (projectDir: string, config: SsgConfig, debug?: bo
158
158
  */
159
159
  declare const registerEndpoints: (app: any, projectDir: string, config: SsgConfig, debug?: boolean) => Promise<void>;
160
160
 
161
- export { BuildOptions, HTTP_METHODS, RehypePlugins, RemarkPlugins, SsgConfig, Status, build, buildEndpoints, compileEndpoints, configDefaults, discoverEndpointSourceFiles, endpointFileToRoute, readConfig, registerEndpoints, rehypePlugins, remarkPlugins, resolveEndpoints, serve };
161
+ /**
162
+ * Discover the RPC file in the project root.
163
+ * Looks for `rpc.ts` or `rpc.js`.
164
+ *
165
+ * @param projectDir The project root directory
166
+ * @returns The absolute path to the RPC file, or null if not found
167
+ */
168
+ declare const discoverRpcFile: (projectDir: string) => string | null;
169
+ /**
170
+ * Compile the RPC module source file with esbuild into the `.rpc/` directory.
171
+ *
172
+ * @param rpcFilePath Absolute path to the rpc.ts / rpc.js source file
173
+ * @param projectDir The project root directory
174
+ * @param debug Enable verbose logging
175
+ * @returns Absolute path to the compiled .mjs file
176
+ */
177
+ declare const compileRpcModule: (rpcFilePath: string, projectDir: string, debug?: boolean) => Promise<string>;
178
+ /**
179
+ * Initialize the RPC server from the project's rpc.ts/rpc.js file.
180
+ *
181
+ * This function discovers, compiles, loads, and registers the RPC module.
182
+ * It dynamically imports `defuss-rpc/server.js` to avoid a hard dependency.
183
+ *
184
+ * @param projectDir The project root directory
185
+ * @param config The SSG config
186
+ * @param debug Enable verbose logging
187
+ * @returns true if RPC was set up successfully, false if no rpc.ts found or defuss-rpc not installed
188
+ */
189
+ declare const initializeRpc: (projectDir: string, config: SsgConfig, debug?: boolean) => Promise<boolean>;
190
+ /**
191
+ * Handle an RPC request by forwarding to defuss-rpc's rpcRoute handler.
192
+ * Converts Express req/res to Web Request/Response.
193
+ *
194
+ * @param req Express request
195
+ * @param res Express response
196
+ */
197
+ declare const handleRpcRequest: (req: any, res: any) => Promise<void>;
198
+
199
+ export { BuildOptions, HTTP_METHODS, RehypePlugins, RemarkPlugins, SsgConfig, Status, build, buildEndpoints, compileEndpoints, compileRpcModule, configDefaults, discoverEndpointSourceFiles, discoverRpcFile, endpointFileToRoute, handleRpcRequest, initializeRpc, readConfig, registerEndpoints, rehypePlugins, remarkPlugins, resolveEndpoints, serve };
162
200
  export type { APIRoute, EndpointContext, EndpointHandler, EndpointModule, HttpMethod, ResolvedEndpoint };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { H as HTTP_METHODS, b as build, a as buildEndpoints, c as compileEndpoints, d as configDefaults, e as discoverEndpointSourceFiles, f as endpointFileToRoute, r as readConfig, g as registerEndpoints, h as rehypePlugins, i as remarkPlugins, j as resolveEndpoints, s as serve } from './serve-BZhZ9J90.mjs';
1
+ export { H as HTTP_METHODS, b as build, a as buildEndpoints, c as compileEndpoints, d as compileRpcModule, e as configDefaults, f as discoverEndpointSourceFiles, g as discoverRpcFile, h as endpointFileToRoute, i as handleRpcRequest, j as initializeRpc, r as readConfig, k as registerEndpoints, l as rehypePlugins, m as remarkPlugins, n as resolveEndpoints, s as serve } from './serve-w6eyG8f3.mjs';
2
2
  import 'chokidar';
3
3
  import 'ultimate-express';
4
4
  import 'node:path';
@@ -1,4 +1,4 @@
1
- import { h as SsgPlugin, g as PluginFnPrePost } from '../types-QQrIkB7n.cjs';
1
+ import { h as SsgPlugin, g as PluginFnPrePost } from '../types-Be8_Y5-t.cjs';
2
2
  import '@mdx-js/esbuild';
3
3
  import 'defuss/server';
4
4
 
@@ -1,4 +1,4 @@
1
- import { h as SsgPlugin, g as PluginFnPrePost } from '../types-QQrIkB7n.mjs';
1
+ import { h as SsgPlugin, g as PluginFnPrePost } from '../types-Be8_Y5-t.mjs';
2
2
  import '@mdx-js/esbuild';
3
3
  import 'defuss/server';
4
4