eclipsa 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/{action-CgM3EJUS.mjs → action-DqgkV3zb.mjs} +1584 -996
  2. package/action-DqgkV3zb.mjs.map +1 -0
  3. package/{client-COjBNTyt.mjs → client-DKPmN-wJ.mjs} +126 -40
  4. package/client-DKPmN-wJ.mjs.map +1 -0
  5. package/{component-D_nEtZ69.d.mts → component-yvTtnI00.d.mts} +2 -2
  6. package/core/client/mod.d.mts +2 -2
  7. package/core/client/mod.mjs +3 -3
  8. package/core/dev-client/mod.d.mts +1 -1
  9. package/core/dev-client/mod.mjs +10 -5
  10. package/core/dev-client/mod.mjs.map +1 -1
  11. package/core/internal.d.mts +2 -2
  12. package/core/internal.mjs +2 -2
  13. package/core/prod-client/mod.d.mts +3 -3
  14. package/core/prod-client/mod.mjs +4 -4
  15. package/{internal-Csbm0Qnv.d.mts → internal-CHYAJznU.d.mts} +116 -72
  16. package/jsx/jsx-dev-runtime.d.mts +8 -2
  17. package/jsx/jsx-dev-runtime.mjs +2 -2
  18. package/jsx/jsx-runtime.d.mts +1 -1
  19. package/jsx/mod.d.mts +1 -1
  20. package/jsx/mod.mjs +1 -1
  21. package/{jsx-dev-runtime-CY60yQJY.mjs → jsx-dev-runtime-DpbWQ4Q0.mjs} +8 -3
  22. package/jsx-dev-runtime-DpbWQ4Q0.mjs.map +1 -0
  23. package/{mod-DLb8ple9.d.mts → mod-aLmFvMYF.d.mts} +4 -4
  24. package/mod.d.mts +20 -38
  25. package/mod.mjs +19 -145
  26. package/mod.mjs.map +1 -1
  27. package/package.json +6 -2
  28. package/{signal-B7AfIKrr.d.mts → signal-CosyYdtA.d.mts} +1 -1
  29. package/{signal-CJtquEfo.mjs → signal-DBzloBrN.mjs} +2 -2
  30. package/{signal-CJtquEfo.mjs.map → signal-DBzloBrN.mjs.map} +1 -1
  31. package/ssr-CRg57Wn2.mjs +157 -0
  32. package/ssr-CRg57Wn2.mjs.map +1 -0
  33. package/ssr-D8F-DtCv.d.mts +37 -0
  34. package/{types-BCEY9lGp.d.mts → types-Cu9gFlEY.d.mts} +6 -2
  35. package/{types-DKpefD_u.d.mts → types-DDeNM8UH.d.mts} +1 -1
  36. package/vite/build/runtime.d.mts +4 -0
  37. package/vite/build/runtime.mjs +4 -0
  38. package/vite/mod.mjs +272 -50
  39. package/vite/mod.mjs.map +1 -1
  40. package/web-utils/mod.d.mts +1 -1
  41. package/web-utils/mod.mjs +2 -2
  42. package/action-CgM3EJUS.mjs.map +0 -1
  43. package/client-COjBNTyt.mjs.map +0 -1
  44. package/jsx-dev-runtime-CY60yQJY.mjs.map +0 -1
  45. /package/{jsx-runtime-BFDPoxFn.d.mts → jsx-runtime-QIK6ADiK.d.mts} +0 -0
@@ -246,6 +246,10 @@ type NamespacedIntrinsicElementProps = {
246
246
  [name: string]: AttributeValue | undefined;
247
247
  };
248
248
  declare namespace JSX {
249
+ interface SSRRaw {
250
+ __e_ssr_raw: true;
251
+ value: string;
252
+ }
249
253
  interface SSRTemplate {
250
254
  __e_ssr_template: true;
251
255
  strings: readonly string[];
@@ -259,7 +263,7 @@ declare namespace JSX {
259
263
  key?: string | number | symbol | null;
260
264
  isStatic: boolean;
261
265
  metadata?: Metadata;
262
- } | SSRTemplate | string | number | undefined | null | boolean | ((() => Element) & {
266
+ } | SSRRaw | SSRTemplate | string | number | undefined | null | boolean | ((() => Element) & {
263
267
  key?: string | number | symbol;
264
268
  });
265
269
  interface Metadata {
@@ -279,4 +283,4 @@ declare namespace JSX {
279
283
  }
280
284
  //#endregion
281
285
  export { JSX as i, DelegatedEvent as n, EventHandler as r, BindTarget as t };
282
- //# sourceMappingURL=types-BCEY9lGp.d.mts.map
286
+ //# sourceMappingURL=types-Cu9gFlEY.d.mts.map
@@ -8,4 +8,4 @@ type Insertable = string | number | boolean | undefined | null | Node | Insertab
8
8
  type ClientElementLike = Insertable | Insertable[];
9
9
  //#endregion
10
10
  export { Insertable as n, ClientElementLike as t };
11
- //# sourceMappingURL=types-DKpefD_u.d.mts.map
11
+ //# sourceMappingURL=types-DDeNM8UH.d.mts.map
@@ -0,0 +1,4 @@
1
+ import { $t as APP_HOOKS_ELEMENT_ID, An as resolveReroute, At as primeActionState, Cn as createRequestFetch, Dn as markPublicError, Dt as getActionFormSubmissionId, Fn as withServerRequestContext, Gn as escapeInlineScriptText, Kn as escapeJSONScriptText, Mn as serializePublicValue, Nn as toPublicError, On as registerClientHooks, Ot as getNormalizedActionInput, Pt as primeLocationState, Q as hasLoader, Sn as attachRequestFetch, Tt as executeAction, Vt as RESUME_FINAL_STATE_ELEMENT_ID, Wn as deserializeValue, Xt as composeRouteMetadata, Z as executeLoader, Zt as renderRouteMetadataHead, _n as ServerHooksModule, an as BaseAppVariables, at as ACTION_CONTENT_TYPE, en as AppContext, fn as PublicError, gn as ResolvedHooks, hn as Reroute, it as resolvePendingLoaders, jn as runHandleError, kt as hasAction, ln as HandleFetch, mn as RequestFetch, nn as AppHooksManifest, nt as primeLoaderState, on as Handle, rn as AppHooksModule, sn as HandleError, wn as deserializePublicValue, xn as WithAppEnv, yn as Transport } from "../../internal-CHYAJznU.mjs";
2
+ import { Fragment, jsxDEV } from "../../jsx/jsx-dev-runtime.mjs";
3
+ import { a as renderSSR, c as serializeResumePayload, i as getStreamingResumeBootstrapScriptContent, o as renderSSRAsync, s as renderSSRStream } from "../../ssr-D8F-DtCv.mjs";
4
+ export { ACTION_CONTENT_TYPE, APP_HOOKS_ELEMENT_ID, type AppContext, type AppHooksManifest, type AppHooksModule, type BaseAppVariables, Fragment, type Handle, type HandleError, type HandleFetch, type PublicError, RESUME_FINAL_STATE_ELEMENT_ID, type RequestFetch, type Reroute, type ResolvedHooks, type ServerHooksModule, type Transport, type WithAppEnv, attachRequestFetch, composeRouteMetadata, createRequestFetch, deserializePublicValue, deserializeValue, escapeInlineScriptText, escapeJSONScriptText, executeAction, executeLoader, getActionFormSubmissionId, getNormalizedActionInput, getStreamingResumeBootstrapScriptContent, hasAction, hasLoader, jsxDEV, markPublicError, primeActionState, primeLoaderState, primeLocationState, registerClientHooks, renderRouteMetadataHead, renderSSR, renderSSRAsync, renderSSRStream, resolvePendingLoaders, resolveReroute, runHandleError, serializePublicValue, serializeResumePayload, toPublicError, withServerRequestContext };
@@ -0,0 +1,4 @@
1
+ import { Fn as deserializePublicValue, Hn as runHandleError, Jn as escapeInlineScriptText, Kn as withServerRequestContext, L as primeLocationState, Mn as APP_HOOKS_ELEMENT_ID, Nn as attachRequestFetch, Pn as createRequestFetch, Rn as markPublicError, St as RESUME_FINAL_STATE_ELEMENT_ID, Un as serializePublicValue, Vn as resolveReroute, Wn as toPublicError, Yn as escapeJSONScriptText, bn as renderRouteMetadataHead, c as getActionFormSubmissionId, cn as resolvePendingLoaders, d as primeActionState, en as executeLoader, l as getNormalizedActionInput, o as executeAction, on as primeLoaderState, qn as deserializeValue, t as ACTION_CONTENT_TYPE, tn as hasLoader, u as hasAction, yn as composeRouteMetadata, zn as registerClientHooks } from "../../action-DqgkV3zb.mjs";
2
+ import { a as jsxDEV, t as Fragment } from "../../jsx-dev-runtime-DpbWQ4Q0.mjs";
3
+ import { a as serializeResumePayload, i as renderSSRStream, n as renderSSR, r as renderSSRAsync, t as getStreamingResumeBootstrapScriptContent } from "../../ssr-CRg57Wn2.mjs";
4
+ export { ACTION_CONTENT_TYPE, APP_HOOKS_ELEMENT_ID, Fragment, RESUME_FINAL_STATE_ELEMENT_ID, attachRequestFetch, composeRouteMetadata, createRequestFetch, deserializePublicValue, deserializeValue, escapeInlineScriptText, escapeJSONScriptText, executeAction, executeLoader, getActionFormSubmissionId, getNormalizedActionInput, getStreamingResumeBootstrapScriptContent, hasAction, hasLoader, jsxDEV, markPublicError, primeActionState, primeLoaderState, primeLocationState, registerClientHooks, renderRouteMetadataHead, renderSSR, renderSSRAsync, renderSSRStream, resolvePendingLoaders, resolveReroute, runHandleError, serializePublicValue, serializeResumePayload, toPublicError, withServerRequestContext };
package/vite/mod.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { Nn as withServerRequestContext, On as resolveReroute, Tn as markPublicError, _t as ROUTE_MANIFEST_ELEMENT_ID, bn as attachRequestFetch, bt as ROUTE_PREFLIGHT_REQUEST_HEADER, ht as ROUTE_DATA_REQUEST_HEADER, kn as runHandleError, mt as ROUTE_DATA_ENDPOINT, on as composeRouteMetadata, sn as renderRouteMetadataHead, xn as createRequestFetch, yn as APP_HOOKS_ELEMENT_ID, yt as ROUTE_PREFLIGHT_ENDPOINT, z as primeLocationState } from "../action-CgM3EJUS.mjs";
2
- import { i as jsxDEV, t as Fragment } from "../jsx-dev-runtime-CY60yQJY.mjs";
1
+ import { Hn as runHandleError, Kn as withServerRequestContext, L as primeLocationState, Mn as APP_HOOKS_ELEMENT_ID, Nn as attachRequestFetch, Pn as createRequestFetch, Rn as markPublicError, Vn as resolveReroute, _n as ROUTE_RPC_URL_HEADER, bn as renderRouteMetadataHead, fn as ROUTE_MANIFEST_ELEMENT_ID, hn as ROUTE_PREFLIGHT_REQUEST_HEADER, ln as ROUTE_DATA_ENDPOINT, mn as ROUTE_PREFLIGHT_ENDPOINT, un as ROUTE_DATA_REQUEST_HEADER, yn as composeRouteMetadata } from "../action-DqgkV3zb.mjs";
2
+ import { a as jsxDEV, t as Fragment } from "../jsx-dev-runtime-DpbWQ4Q0.mjs";
3
3
  import { t as RESUME_HMR_EVENT } from "../resume-hmr-qTpLc5o-.mjs";
4
4
  import { createServerModuleRunner, transformWithOxc } from "vite";
5
5
  import { Hono } from "hono";
@@ -10,8 +10,8 @@ import path from "node:path";
10
10
  import ts from "typescript";
11
11
  import { runRustAnalyzeCompiler, runRustCompiler } from "@eclipsa/optimizer";
12
12
  import { cwd } from "node:process";
13
+ import { fileURLToPath, pathToFileURL } from "node:url";
13
14
  import { toSSG } from "hono/ssg";
14
- import { pathToFileURL } from "node:url";
15
15
  //#region vite/utils/routing.ts
16
16
  const normalizeRoutePath = (pathname) => {
17
17
  const normalizedPath = pathname.trim() || "/";
@@ -386,15 +386,19 @@ const annotateOptimizedRootComponents = (source, id) => {
386
386
  const insertions = [];
387
387
  const visit = (node) => {
388
388
  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "__eclipsaComponent") {
389
+ if (node.arguments.length >= 5) return;
389
390
  const lastArgument = node.arguments.at(-1);
390
- insertions.push(lastArgument ? lastArgument.end : node.expression.end + 1);
391
+ insertions.push({
392
+ code: node.arguments.length >= 4 ? ", { optimizedRoot: true }" : ", undefined, { optimizedRoot: true }",
393
+ index: lastArgument ? lastArgument.end : node.expression.end + 1
394
+ });
391
395
  }
392
396
  ts.forEachChild(node, visit);
393
397
  };
394
398
  visit(sourceFile);
395
399
  if (insertions.length === 0) return source;
396
400
  let nextSource = source;
397
- for (const index of [...insertions].sort((left, right) => right - left)) nextSource = nextSource.slice(0, index) + ", { optimizedRoot: true }" + nextSource.slice(index);
401
+ for (const insertion of [...insertions].sort((left, right) => right.index - left.index)) nextSource = nextSource.slice(0, insertion.index) + insertion.code + nextSource.slice(insertion.index);
398
402
  return nextSource;
399
403
  };
400
404
  const analyzeModule = async (source, id = "analyze-input.tsx") => {
@@ -776,13 +780,51 @@ const collectAppSymbols = async (root) => {
776
780
  }
777
781
  return [...symbols.values()];
778
782
  };
783
+ const collectReachableAnalyzableFiles = async (entryFiles) => {
784
+ const pending = [...entryFiles];
785
+ const visited = /* @__PURE__ */ new Set();
786
+ const reachable = /* @__PURE__ */ new Set();
787
+ const entryFileSet = new Set(entryFiles.map((filePath) => stripQuery(filePath)));
788
+ while (pending.length > 0) {
789
+ const next = pending.pop();
790
+ if (!next) continue;
791
+ const filePath = stripQuery(next);
792
+ if (visited.has(filePath) || !isAnalyzableSourceFile(filePath)) continue;
793
+ visited.add(filePath);
794
+ let source;
795
+ try {
796
+ source = await fs.readFile(filePath, "utf8");
797
+ } catch (error) {
798
+ const code = error?.code;
799
+ if (entryFileSet.has(filePath) && code !== "ENOENT" && code !== "ENOTDIR") throw error;
800
+ continue;
801
+ }
802
+ try {
803
+ await loadAnalyzedModule(filePath, source);
804
+ } catch (error) {
805
+ if (entryFileSet.has(filePath)) throw error;
806
+ continue;
807
+ }
808
+ reachable.add(filePath);
809
+ const imports = ts.preProcessFile(source, true, true).importedFiles;
810
+ for (const imported of imports) {
811
+ const resolvedFilePath = resolveImportedModule(imported.fileName, filePath);
812
+ if (!resolvedFilePath || !isAnalyzableSourceFile(resolvedFilePath)) continue;
813
+ pending.push(resolvedFilePath);
814
+ }
815
+ }
816
+ return [...reachable];
817
+ };
779
818
  const collectAppActions = async (root) => {
780
819
  const appDir = path$1.join(root, "app");
781
820
  const files = await fg(path$1.join(appDir, "**/*.{ts,tsx}").replaceAll("\\", "/"));
782
821
  const result = [];
783
822
  for (const filePath of files) {
784
823
  const analyzed = await loadAnalyzedModule(filePath);
785
- result.push(...analyzed.actions.values());
824
+ result.push(...[...analyzed.actions.values()].map((action) => ({
825
+ filePath,
826
+ id: action.id
827
+ })));
786
828
  }
787
829
  return result;
788
830
  };
@@ -792,7 +834,10 @@ const collectAppLoaders = async (root) => {
792
834
  const result = [];
793
835
  for (const filePath of files) {
794
836
  const analyzed = await loadAnalyzedModule(filePath);
795
- result.push(...analyzed.loaders.values());
837
+ result.push(...[...analyzed.loaders.values()].map((loader) => ({
838
+ filePath,
839
+ id: loader.id
840
+ })));
796
841
  }
797
842
  return result;
798
843
  };
@@ -831,7 +876,7 @@ const shouldInvalidateDevApp = (root, filePath, event) => {
831
876
  const RESUME_PAYLOAD_PLACEHOLDER = "__ECLIPSA_RESUME_PAYLOAD__";
832
877
  const ROUTE_MANIFEST_PLACEHOLDER = "__ECLIPSA_ROUTE_MANIFEST__";
833
878
  const APP_HOOKS_PLACEHOLDER = "__ECLIPSA_APP_HOOKS__";
834
- const replaceHeadPlaceholder = (html, placeholder, value) => html.replace(placeholder, value);
879
+ const replaceHeadPlaceholder = (html, placeholder, value) => html.replaceAll(placeholder, value);
835
880
  const splitHtmlForStreaming = (html) => {
836
881
  const bodyCloseIndex = html.lastIndexOf("</body>");
837
882
  if (bodyCloseIndex >= 0) return {
@@ -848,6 +893,37 @@ const splitHtmlForStreaming = (html) => {
848
893
  suffix: ""
849
894
  };
850
895
  };
896
+ const toIdsByFilePath$1 = (entries) => {
897
+ const idsByFilePath = /* @__PURE__ */ new Map();
898
+ for (const entry of entries) {
899
+ const existing = idsByFilePath.get(entry.filePath);
900
+ if (existing) {
901
+ existing.push(entry.id);
902
+ continue;
903
+ }
904
+ idsByFilePath.set(entry.filePath, [entry.id]);
905
+ }
906
+ return idsByFilePath;
907
+ };
908
+ const getRouteReachableEntryFiles$1 = (route) => [
909
+ route.error?.filePath,
910
+ ...route.layouts.map((layout) => layout.filePath),
911
+ route.loading?.filePath,
912
+ route.notFound?.filePath,
913
+ route.page?.filePath
914
+ ].filter((filePath) => typeof filePath === "string");
915
+ const createRouteServerAccessEntries$1 = async (routes, actions, loaders) => {
916
+ const actionIdsByFilePath = toIdsByFilePath$1(actions);
917
+ const loaderIdsByFilePath = toIdsByFilePath$1(loaders);
918
+ return await Promise.all(routes.map(async (route) => {
919
+ const reachableFiles = await collectReachableAnalyzableFiles(getRouteReachableEntryFiles$1(route));
920
+ return {
921
+ actionIds: new Set(reachableFiles.flatMap((filePath) => actionIdsByFilePath.get(filePath) ?? [])),
922
+ loaderIds: new Set(reachableFiles.flatMap((filePath) => loaderIdsByFilePath.get(filePath) ?? [])),
923
+ route
924
+ };
925
+ }));
926
+ };
851
927
  const ROUTE_SLOT_ROUTE_KEY = Symbol.for("eclipsa.route-slot-route");
852
928
  const createRouteSlot = (route, startLayoutIndex) => {
853
929
  const slot = {
@@ -983,6 +1059,8 @@ const createDevApp = async (init) => {
983
1059
  const allSymbols = await deps.collectAppSymbols(init.resolvedConfig.root);
984
1060
  const actionModules = new Map(actions.map((action) => [action.id, action.filePath]));
985
1061
  const loaderModules = new Map(loaders.map((loader) => [loader.id, loader.filePath]));
1062
+ const routeServerAccessEntries = await createRouteServerAccessEntries$1(routes, actions, loaders);
1063
+ const routeServerAccessByRoute = new Map(routeServerAccessEntries.map((entry) => [entry.route, entry]));
986
1064
  const symbolUrls = Object.fromEntries(allSymbols.map((symbol) => [symbol.id, deps.createDevSymbolUrl(init.resolvedConfig.root, symbol.filePath, symbol.id)]));
987
1065
  const routeManifest = createRouteManifest(routes, (entry) => deps.createDevModuleUrl(init.resolvedConfig.root, entry));
988
1066
  const appHooksPath = path$1.join(init.resolvedConfig.root, "app/+hooks.ts");
@@ -996,6 +1074,32 @@ const createDevApp = async (init) => {
996
1074
  return c;
997
1075
  };
998
1076
  const reroutePathname = (request, pathname, baseUrl) => normalizeRoutePath(resolveReroute(appHooks.reroute, request, pathname, baseUrl));
1077
+ const getRouteServerAccess = (route) => routeServerAccessByRoute.get(route) ?? {
1078
+ actionIds: /* @__PURE__ */ new Set(),
1079
+ loaderIds: /* @__PURE__ */ new Set(),
1080
+ route
1081
+ };
1082
+ const resolveRouteForCurrentUrl = (request, currentUrl) => {
1083
+ const resolvedPathname = reroutePathname(request, normalizeRoutePath(currentUrl.pathname), currentUrl.href);
1084
+ const match = matchRoute(routes, resolvedPathname);
1085
+ if (match?.route.page) return match;
1086
+ const fallback = findSpecialRoute(routes, resolvedPathname, "notFound");
1087
+ if (fallback?.route.notFound) return fallback;
1088
+ return null;
1089
+ };
1090
+ const getRpcCurrentRoute = (requestContext) => {
1091
+ const requestUrl = getRequestUrl(requestContext.req.raw);
1092
+ const routeUrlHeader = requestContext.req.header(ROUTE_RPC_URL_HEADER);
1093
+ if (!routeUrlHeader) return null;
1094
+ let currentUrl;
1095
+ try {
1096
+ currentUrl = new URL(routeUrlHeader, requestUrl);
1097
+ } catch {
1098
+ return null;
1099
+ }
1100
+ if (currentUrl.origin !== requestUrl.origin) return null;
1101
+ return resolveRouteForCurrentUrl(requestContext.req.raw, currentUrl);
1102
+ };
999
1103
  const resolveRequest = async (c, handler) => {
1000
1104
  const requestContext = prepareRequestContext(c);
1001
1105
  const execute = (nextContext = requestContext) => withServerRequestContext(nextContext, {
@@ -1063,7 +1167,7 @@ const createDevApp = async (init) => {
1063
1167
  params,
1064
1168
  url: getRequestUrl(c.req.raw)
1065
1169
  });
1066
- const document = SSRRoot({
1170
+ const document = jsxDEV(SSRRoot, {
1067
1171
  children: createRouteElement(pathname, params, Page, Layouts, options?.routeError),
1068
1172
  head: {
1069
1173
  type: Fragment,
@@ -1116,7 +1220,7 @@ const createDevApp = async (init) => {
1116
1220
  }
1117
1221
  ] }
1118
1222
  }
1119
- });
1223
+ }, null, false, {});
1120
1224
  applyRequestParams(c, params);
1121
1225
  const { html, payload, chunks } = await renderSSRStream(() => document, {
1122
1226
  prepare(container) {
@@ -1286,19 +1390,25 @@ const createDevApp = async (init) => {
1286
1390
  const { executeAction, hasAction } = await init.runner.import("eclipsa");
1287
1391
  const id = requestContext.req.param("id");
1288
1392
  if (!id) return requestContext.text("Not Found", 404);
1393
+ const routeMatch = getRpcCurrentRoute(requestContext);
1394
+ if (!routeMatch) return requestContext.text("Bad Request", 400);
1395
+ if (!getRouteServerAccess(routeMatch.route).actionIds.has(id)) return requestContext.text("Not Found", 404);
1289
1396
  const modulePath = actionModules.get(id);
1290
1397
  if (!modulePath) return requestContext.text("Not Found", 404);
1291
1398
  if (!hasAction(id)) await init.runner.import(modulePath);
1292
- return executeAction(id, requestContext);
1399
+ return composeRouteMiddlewares(routeMatch.route, requestContext, routeMatch.params, async () => executeAction(id, requestContext));
1293
1400
  }));
1294
1401
  app.get("/__eclipsa/loader/:id", async (c) => resolveRequest(c, async (requestContext) => {
1295
1402
  const { executeLoader, hasLoader } = await init.runner.import("eclipsa");
1296
1403
  const id = requestContext.req.param("id");
1297
1404
  if (!id) return requestContext.text("Not Found", 404);
1405
+ const routeMatch = getRpcCurrentRoute(requestContext);
1406
+ if (!routeMatch) return requestContext.text("Bad Request", 400);
1407
+ if (!getRouteServerAccess(routeMatch.route).loaderIds.has(id)) return requestContext.text("Not Found", 404);
1298
1408
  const modulePath = loaderModules.get(id);
1299
1409
  if (!modulePath) return requestContext.text("Not Found", 404);
1300
1410
  if (!hasLoader(id)) await init.runner.import(modulePath);
1301
- return executeLoader(id, requestContext);
1411
+ return composeRouteMiddlewares(routeMatch.route, requestContext, routeMatch.params, async () => executeLoader(id, requestContext));
1302
1412
  }));
1303
1413
  app.get(ROUTE_PREFLIGHT_ENDPOINT, async (c) => resolveRequest(c, async (requestContext) => {
1304
1414
  const href = requestContext.req.query("href");
@@ -1331,6 +1441,7 @@ const createDevApp = async (init) => {
1331
1441
  const { ACTION_CONTENT_TYPE, deserializePublicValue, executeAction, getNormalizedActionInput, getActionFormSubmissionId, hasAction, primeActionState } = await init.runner.import("eclipsa");
1332
1442
  const actionId = await getActionFormSubmissionId(requestContext);
1333
1443
  if (!actionId) return match.route.server ? invokeRouteServer(match.route.server.filePath, requestContext, match.params) : renderMatchedPage(match, requestContext);
1444
+ if (!getRouteServerAccess(match.route).actionIds.has(actionId)) return requestContext.text("Not Found", 404);
1334
1445
  const modulePath = actionModules.get(actionId);
1335
1446
  if (!modulePath) return requestContext.text("Not Found", 404);
1336
1447
  if (!hasAction(actionId)) await init.runner.import(modulePath);
@@ -1765,18 +1876,49 @@ const createSerializedRoutes = (routes) => JSON.stringify(routes.map((route) =>
1765
1876
  segments: route.segments,
1766
1877
  server: route.server ? createBuildServerModuleUrl(route.server) : null
1767
1878
  })));
1879
+ const toIdsByFilePath = (entries) => {
1880
+ const idsByFilePath = /* @__PURE__ */ new Map();
1881
+ for (const entry of entries) {
1882
+ const existing = idsByFilePath.get(entry.filePath);
1883
+ if (existing) {
1884
+ existing.push(entry.id);
1885
+ continue;
1886
+ }
1887
+ idsByFilePath.set(entry.filePath, [entry.id]);
1888
+ }
1889
+ return idsByFilePath;
1890
+ };
1891
+ const getRouteReachableEntryFiles = (route) => [
1892
+ route.error?.filePath,
1893
+ ...route.layouts.map((layout) => layout.filePath),
1894
+ route.loading?.filePath,
1895
+ route.notFound?.filePath,
1896
+ route.page?.filePath
1897
+ ].filter((filePath) => typeof filePath === "string");
1898
+ const createRouteServerAccessEntries = async (routes, actions, loaders) => {
1899
+ const actionIdsByFilePath = toIdsByFilePath(actions);
1900
+ const loaderIdsByFilePath = toIdsByFilePath(loaders);
1901
+ return await Promise.all(routes.map(async (route) => {
1902
+ const reachableFiles = await collectReachableAnalyzableFiles(getRouteReachableEntryFiles(route));
1903
+ return {
1904
+ actionIds: reachableFiles.flatMap((filePath) => actionIdsByFilePath.get(filePath) ?? []),
1905
+ loaderIds: reachableFiles.flatMap((filePath) => loaderIdsByFilePath.get(filePath) ?? [])
1906
+ };
1907
+ }));
1908
+ };
1768
1909
  const createActionTable = (actions) => actions.map((action) => ` ${JSON.stringify(action.id)}: ${JSON.stringify(createBuildServerActionUrl(action.id))},`).join("\n");
1769
1910
  const createLoaderTable = (loaders) => loaders.map((loader) => ` ${JSON.stringify(loader.id)}: ${JSON.stringify(createBuildServerLoaderUrl(loader.id))},`).join("\n");
1770
1911
  const createPageRouteEntries = (routes) => routes.flatMap((route, routeIndex) => route.page ? toHonoRoutePaths(route.segments).map((honoPath) => ({
1771
1912
  path: honoPath,
1772
1913
  routeIndex
1773
1914
  })) : []);
1774
- const renderAppModule = (actions, appHooksClientUrl, appHooksServerUrl, loaders, routes, routeManifest, serverHooksUrl, symbolUrls, stylesheetUrls, chunkCacheUrls) => {
1915
+ const renderAppModule = (actions, appHooksClientUrl, appHooksServerUrl, loaders, routes, routeServerAccessEntries, routeManifest, serverHooksUrl, symbolUrls, stylesheetUrls, chunkCacheUrls) => {
1775
1916
  const serializedRoutes = createSerializedRoutes(routes);
1776
1917
  const serializedPageRouteEntries = JSON.stringify(createPageRouteEntries(routes));
1777
1918
  const actionTable = createActionTable(actions);
1778
1919
  const loaderTable = createLoaderTable(loaders);
1779
1920
  const serializedAppHooksManifest = JSON.stringify({ client: appHooksClientUrl });
1921
+ const serializedRouteServerAccessEntries = JSON.stringify(routeServerAccessEntries);
1780
1922
  const serializedAppHooksServerUrl = JSON.stringify(appHooksServerUrl);
1781
1923
  const serializedSymbolUrls = JSON.stringify(symbolUrls);
1782
1924
  return `import userApp from "./entries/server_entry.mjs";
@@ -1791,6 +1933,7 @@ const loaders = {
1791
1933
  ${loaderTable}
1792
1934
  };
1793
1935
  const routes = ${serializedRoutes};
1936
+ const routeServerAccessEntries = ${serializedRouteServerAccessEntries};
1794
1937
  const pageRouteEntries = ${serializedPageRouteEntries};
1795
1938
  const appHooksManifest = ${serializedAppHooksManifest};
1796
1939
  const appHooksServerUrl = ${serializedAppHooksServerUrl};
@@ -2045,6 +2188,40 @@ const prepareRequestContext = (c, hooks) => {
2045
2188
  const reroutePathname = (hooks, request, pathname, baseUrl) =>
2046
2189
  normalizeRoutePath(resolveReroute(hooks.reroute, request, pathname, baseUrl));
2047
2190
 
2191
+ const getRouteServerAccess = (route) => {
2192
+ const routeIndex = routes.indexOf(route);
2193
+ const entry = routeIndex >= 0 ? routeServerAccessEntries[routeIndex] : null;
2194
+ return entry ?? { actionIds: [], loaderIds: [] };
2195
+ };
2196
+
2197
+ const resolveRouteForCurrentUrl = (hooks, request, currentUrl) => {
2198
+ const resolvedPathname = reroutePathname(hooks, request, normalizeRoutePath(currentUrl.pathname), currentUrl.href);
2199
+ const match = matchRoute(resolvedPathname);
2200
+ if (match?.route?.page) {
2201
+ return match;
2202
+ }
2203
+ const fallback = findSpecialRoute(resolvedPathname, "notFound");
2204
+ return fallback?.route?.notFound ? fallback : null;
2205
+ };
2206
+
2207
+ const getRpcCurrentRoute = (hooks, c) => {
2208
+ const requestUrl = getRequestUrl(c.req.raw);
2209
+ const routeUrlHeader = c.req.header(${JSON.stringify(ROUTE_RPC_URL_HEADER)});
2210
+ if (!routeUrlHeader) {
2211
+ return null;
2212
+ }
2213
+ let currentUrl;
2214
+ try {
2215
+ currentUrl = new URL(routeUrlHeader, requestUrl);
2216
+ } catch {
2217
+ return null;
2218
+ }
2219
+ if (currentUrl.origin !== requestUrl.origin) {
2220
+ return null;
2221
+ }
2222
+ return resolveRouteForCurrentUrl(hooks, c.req.raw, currentUrl);
2223
+ };
2224
+
2048
2225
  const resolveRequest = async (c, handler) => {
2049
2226
  const { appHooks, serverHooks } = await hooksPromise;
2050
2227
  const requestContext = prepareRequestContext(c, serverHooks);
@@ -2545,29 +2722,65 @@ for (const pageRouteEntry of pageRouteEntries) {
2545
2722
  });
2546
2723
  }
2547
2724
 
2548
- app.post("/__eclipsa/action/:id", async (c) => {
2549
- const id = c.req.param("id");
2550
- const moduleUrl = actions[id];
2551
- if (!moduleUrl) {
2552
- return c.text("Not Found", 404);
2553
- }
2554
- if (!hasAction(id)) {
2555
- await import(moduleUrl);
2556
- }
2557
- return executeAction(id, c);
2558
- });
2559
-
2560
- app.get("/__eclipsa/loader/:id", async (c) => {
2561
- const id = c.req.param("id");
2562
- const moduleUrl = loaders[id];
2563
- if (!moduleUrl) {
2564
- return c.text("Not Found", 404);
2565
- }
2566
- if (!hasLoader(id)) {
2567
- await import(moduleUrl);
2568
- }
2569
- return executeLoader(id, c);
2570
- });
2725
+ app.post("/__eclipsa/action/:id", async (c) =>
2726
+ resolveRequest(c, async (requestContext, appHooks) => {
2727
+ const id = requestContext.req.param("id");
2728
+ if (!id) {
2729
+ return requestContext.text("Not Found", 404);
2730
+ }
2731
+ const routeMatch = getRpcCurrentRoute(appHooks, requestContext);
2732
+ if (!routeMatch) {
2733
+ return requestContext.text("Bad Request", 400);
2734
+ }
2735
+ const routeAccess = getRouteServerAccess(routeMatch.route);
2736
+ if (!routeAccess.actionIds.includes(id)) {
2737
+ return requestContext.text("Not Found", 404);
2738
+ }
2739
+ const moduleUrl = actions[id];
2740
+ if (!moduleUrl) {
2741
+ return requestContext.text("Not Found", 404);
2742
+ }
2743
+ if (!hasAction(id)) {
2744
+ await import(moduleUrl);
2745
+ }
2746
+ return composeRouteMiddlewares(
2747
+ routeMatch.route,
2748
+ requestContext,
2749
+ routeMatch.params,
2750
+ async () => executeAction(id, requestContext),
2751
+ );
2752
+ }),
2753
+ );
2754
+
2755
+ app.get("/__eclipsa/loader/:id", async (c) =>
2756
+ resolveRequest(c, async (requestContext, appHooks) => {
2757
+ const id = requestContext.req.param("id");
2758
+ if (!id) {
2759
+ return requestContext.text("Not Found", 404);
2760
+ }
2761
+ const routeMatch = getRpcCurrentRoute(appHooks, requestContext);
2762
+ if (!routeMatch) {
2763
+ return requestContext.text("Bad Request", 400);
2764
+ }
2765
+ const routeAccess = getRouteServerAccess(routeMatch.route);
2766
+ if (!routeAccess.loaderIds.includes(id)) {
2767
+ return requestContext.text("Not Found", 404);
2768
+ }
2769
+ const moduleUrl = loaders[id];
2770
+ if (!moduleUrl) {
2771
+ return requestContext.text("Not Found", 404);
2772
+ }
2773
+ if (!hasLoader(id)) {
2774
+ await import(moduleUrl);
2775
+ }
2776
+ return composeRouteMiddlewares(
2777
+ routeMatch.route,
2778
+ requestContext,
2779
+ routeMatch.params,
2780
+ async () => executeLoader(id, requestContext),
2781
+ );
2782
+ }),
2783
+ );
2571
2784
 
2572
2785
  app.get(${JSON.stringify(ROUTE_PREFLIGHT_ENDPOINT)}, async (c) => {
2573
2786
  const href = c.req.query("href");
@@ -2632,6 +2845,10 @@ app.all("*", async (c) => {
2632
2845
  ? invokeRouteServer(match.route.server, c, match.params)
2633
2846
  : renderMatchedPage(match, c);
2634
2847
  }
2848
+ const routeAccess = getRouteServerAccess(match.route);
2849
+ if (!routeAccess.actionIds.includes(actionId)) {
2850
+ return c.text("Not Found", 404);
2851
+ }
2635
2852
  const moduleUrl = actions[actionId];
2636
2853
  if (!moduleUrl) {
2637
2854
  return c.text("Not Found", 404);
@@ -2782,6 +2999,7 @@ const build = async (builder, userConfig, options) => {
2782
2999
  const actions = await collectAppActions(root);
2783
3000
  const loaders = await collectAppLoaders(root);
2784
3001
  const routes = await createRoutes(root);
3002
+ const routeServerAccessEntries = await createRouteServerAccessEntries(routes, actions, loaders);
2785
3003
  const staticPageRoutes = routes.filter((route) => route.page && resolveRouteRenderMode(route, options.output) === "static");
2786
3004
  const dynamicPageRoutes = routes.filter((route) => route.page && resolveRouteRenderMode(route, options.output) === "dynamic");
2787
3005
  if (options.output === "ssg") {
@@ -2814,7 +3032,7 @@ const build = async (builder, userConfig, options) => {
2814
3032
  const serverDir = path$1.join(root, "dist/server");
2815
3033
  const appModulePath = path$1.join(root, "dist/ssr/eclipsa_app.mjs");
2816
3034
  await fs.mkdir(path$1.dirname(appModulePath), { recursive: true });
2817
- await fs.writeFile(appModulePath, renderAppModule(actions, appHooksClientUrl, appHooksServerUrl, loaders, routes, routeManifest, serverHooksUrl, symbolUrls, stylesheetUrls, chunkCacheAssets.map((asset) => asset.url)));
3035
+ await fs.writeFile(appModulePath, renderAppModule(actions, appHooksClientUrl, appHooksServerUrl, loaders, routes, routeServerAccessEntries, routeManifest, serverHooksUrl, symbolUrls, stylesheetUrls, chunkCacheAssets.map((asset) => asset.url)));
2818
3036
  const staticPrerenderTargets = await resolveStaticPrerenderTargets(root, staticPageRoutes);
2819
3037
  const prerenderStaticRoutes = async () => {
2820
3038
  if (staticPrerenderTargets.concretePaths.size === 0 && staticPrerenderTargets.dynamicParamsByPattern.size === 0) return;
@@ -2890,6 +3108,7 @@ const createEclipsaNitroConfig = (root, nitroConfig) => {
2890
3108
  };
2891
3109
  //#endregion
2892
3110
  //#region vite/config.ts
3111
+ const ECLIPSA_RUNTIME_ENTRY_PATH = fileURLToPath(import.meta.resolve("eclipsa/vite/build/runtime"));
2893
3112
  const fileExists = async (filePath) => {
2894
3113
  try {
2895
3114
  await fs.access(filePath);
@@ -2920,7 +3139,7 @@ const createConfig = (options) => async (userConfig) => {
2920
3139
  const ssrInput = Object.fromEntries([
2921
3140
  ["server_entry", path.join(root, "app/+server-entry.ts")],
2922
3141
  ["ssr_root", path.join(root, "app/+ssr-root.tsx")],
2923
- ["eclipsa_runtime", path.join(root, "../packages/eclipsa/vite/build/runtime.ts")],
3142
+ ["eclipsa_runtime", ECLIPSA_RUNTIME_ENTRY_PATH],
2924
3143
  ...hasAppHooks ? [["app_hooks", appHooksPath]] : [],
2925
3144
  ...hasServerHooks ? [["server_hooks", serverHooksPath]] : [],
2926
3145
  ...actions.map((action) => [`action__${action.id}`, action.filePath]),
@@ -2950,19 +3169,22 @@ const createConfig = (options) => async (userConfig) => {
2950
3169
  preserveEntrySignatures: "allow-extension"
2951
3170
  }
2952
3171
  } },
2953
- ssr: { build: {
2954
- emptyOutDir: true,
2955
- outDir: path.join(root, "dist/ssr"),
2956
- rollupOptions: {
2957
- input: ssrInput,
2958
- output: {
2959
- assetFileNames: "assets/[name]-[hash][extname]",
2960
- chunkFileNames: "chunks/[name]-[hash].mjs",
2961
- entryFileNames: "entries/[name].mjs"
2962
- },
2963
- preserveEntrySignatures: "allow-extension"
3172
+ ssr: {
3173
+ resolve: { noExternal: [/^eclipsa(?:\/|$)/] },
3174
+ build: {
3175
+ emptyOutDir: true,
3176
+ outDir: path.join(root, "dist/ssr"),
3177
+ rollupOptions: {
3178
+ input: ssrInput,
3179
+ output: {
3180
+ assetFileNames: "assets/[name]-[hash][extname]",
3181
+ chunkFileNames: "chunks/[name]-[hash].mjs",
3182
+ entryFileNames: "entries/[name].mjs"
3183
+ },
3184
+ preserveEntrySignatures: "allow-extension"
3185
+ }
2964
3186
  }
2965
- } }
3187
+ }
2966
3188
  },
2967
3189
  builder: { async buildApp(builder) {
2968
3190
  await build(builder, userConfig, options);