@reckona/mreact-router 0.0.203 → 0.0.205

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 (140) hide show
  1. package/README.md +53 -9
  2. package/dist/actions.d.ts +6 -0
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +70 -10
  5. package/dist/actions.js.map +1 -1
  6. package/dist/adapters/aws-lambda.d.ts +15 -4
  7. package/dist/adapters/aws-lambda.d.ts.map +1 -1
  8. package/dist/adapters/aws-lambda.js +301 -63
  9. package/dist/adapters/aws-lambda.js.map +1 -1
  10. package/dist/adapters/cloudflare.d.ts +2 -1
  11. package/dist/adapters/cloudflare.d.ts.map +1 -1
  12. package/dist/adapters/cloudflare.js +18 -10
  13. package/dist/adapters/cloudflare.js.map +1 -1
  14. package/dist/adapters/edge.d.ts +1 -1
  15. package/dist/adapters/edge.d.ts.map +1 -1
  16. package/dist/adapters/edge.js.map +1 -1
  17. package/dist/adapters/node.d.ts +5 -3
  18. package/dist/adapters/node.d.ts.map +1 -1
  19. package/dist/adapters/node.js +2 -1
  20. package/dist/adapters/node.js.map +1 -1
  21. package/dist/adapters/static.d.ts.map +1 -1
  22. package/dist/adapters/static.js +24 -2
  23. package/dist/adapters/static.js.map +1 -1
  24. package/dist/build.d.ts +1 -0
  25. package/dist/build.d.ts.map +1 -1
  26. package/dist/build.js +91 -5
  27. package/dist/build.js.map +1 -1
  28. package/dist/built-runtime.d.ts +2 -0
  29. package/dist/built-runtime.d.ts.map +1 -1
  30. package/dist/built-runtime.js +16 -0
  31. package/dist/built-runtime.js.map +1 -1
  32. package/dist/cache.d.ts +3 -1
  33. package/dist/cache.d.ts.map +1 -1
  34. package/dist/cache.js +211 -13
  35. package/dist/cache.js.map +1 -1
  36. package/dist/cli.js +12 -1
  37. package/dist/cli.js.map +1 -1
  38. package/dist/client.d.ts.map +1 -1
  39. package/dist/client.js +32 -1
  40. package/dist/client.js.map +1 -1
  41. package/dist/config.d.ts +3 -0
  42. package/dist/config.d.ts.map +1 -1
  43. package/dist/config.js +21 -2
  44. package/dist/config.js.map +1 -1
  45. package/dist/csp.d.ts +1 -0
  46. package/dist/csp.d.ts.map +1 -1
  47. package/dist/csp.js +11 -6
  48. package/dist/csp.js.map +1 -1
  49. package/dist/dehydrate-policy.d.ts +3 -0
  50. package/dist/dehydrate-policy.d.ts.map +1 -0
  51. package/dist/dehydrate-policy.js +16 -0
  52. package/dist/dehydrate-policy.js.map +1 -0
  53. package/dist/dev-server.d.ts +7 -2
  54. package/dist/dev-server.d.ts.map +1 -1
  55. package/dist/dev-server.js +120 -11
  56. package/dist/dev-server.js.map +1 -1
  57. package/dist/http.d.ts +1 -1
  58. package/dist/http.d.ts.map +1 -1
  59. package/dist/http.js +147 -7
  60. package/dist/http.js.map +1 -1
  61. package/dist/index.d.ts +4 -3
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -0
  64. package/dist/index.js.map +1 -1
  65. package/dist/logger.d.ts +22 -1
  66. package/dist/logger.d.ts.map +1 -1
  67. package/dist/logger.js +22 -8
  68. package/dist/logger.js.map +1 -1
  69. package/dist/metadata.d.ts +8 -0
  70. package/dist/metadata.d.ts.map +1 -1
  71. package/dist/metadata.js +60 -22
  72. package/dist/metadata.js.map +1 -1
  73. package/dist/navigation-marker.d.ts.map +1 -1
  74. package/dist/navigation-marker.js +72 -9
  75. package/dist/navigation-marker.js.map +1 -1
  76. package/dist/navigation.d.ts +1 -0
  77. package/dist/navigation.d.ts.map +1 -1
  78. package/dist/navigation.js +1 -1
  79. package/dist/navigation.js.map +1 -1
  80. package/dist/node-server.d.ts +5 -2
  81. package/dist/node-server.d.ts.map +1 -1
  82. package/dist/node-server.js +75 -6
  83. package/dist/node-server.js.map +1 -1
  84. package/dist/prerender-entry.d.ts +14 -0
  85. package/dist/prerender-entry.d.ts.map +1 -1
  86. package/dist/prerender-entry.js +40 -3
  87. package/dist/prerender-entry.js.map +1 -1
  88. package/dist/render.d.ts +9 -1
  89. package/dist/render.d.ts.map +1 -1
  90. package/dist/render.js +102 -123
  91. package/dist/render.js.map +1 -1
  92. package/dist/serve.d.ts +8 -2
  93. package/dist/serve.d.ts.map +1 -1
  94. package/dist/serve.js +22 -20
  95. package/dist/serve.js.map +1 -1
  96. package/dist/session.d.ts.map +1 -1
  97. package/dist/session.js +52 -19
  98. package/dist/session.js.map +1 -1
  99. package/dist/upgrade.d.ts +59 -2
  100. package/dist/upgrade.d.ts.map +1 -1
  101. package/dist/upgrade.js +282 -1
  102. package/dist/upgrade.js.map +1 -1
  103. package/dist/vite-config.d.ts +2 -0
  104. package/dist/vite-config.d.ts.map +1 -1
  105. package/dist/vite-config.js +2 -0
  106. package/dist/vite-config.js.map +1 -1
  107. package/dist/vite.d.ts +4 -1
  108. package/dist/vite.d.ts.map +1 -1
  109. package/dist/vite.js +117 -6
  110. package/dist/vite.js.map +1 -1
  111. package/package.json +11 -11
  112. package/src/actions.ts +81 -11
  113. package/src/adapters/aws-lambda.ts +426 -115
  114. package/src/adapters/cloudflare.ts +29 -13
  115. package/src/adapters/edge.ts +2 -0
  116. package/src/adapters/node.ts +8 -10
  117. package/src/adapters/static.ts +31 -2
  118. package/src/build.ts +117 -10
  119. package/src/built-runtime.ts +28 -0
  120. package/src/cache.ts +262 -15
  121. package/src/cli.ts +14 -1
  122. package/src/client.ts +32 -1
  123. package/src/config.ts +43 -6
  124. package/src/csp.ts +14 -11
  125. package/src/dehydrate-policy.ts +23 -0
  126. package/src/dev-server.ts +188 -15
  127. package/src/http.ts +172 -6
  128. package/src/index.ts +14 -1
  129. package/src/logger.ts +47 -10
  130. package/src/metadata.ts +125 -60
  131. package/src/navigation-marker.ts +79 -9
  132. package/src/navigation.ts +1 -1
  133. package/src/node-server.ts +128 -22
  134. package/src/prerender-entry.ts +52 -3
  135. package/src/render.ts +807 -753
  136. package/src/serve.ts +49 -56
  137. package/src/session.ts +56 -25
  138. package/src/upgrade.ts +402 -3
  139. package/src/vite-config.ts +6 -0
  140. package/src/vite.ts +157 -5
package/src/vite.ts CHANGED
@@ -1,16 +1,19 @@
1
- import { readFile } from "node:fs/promises";
1
+ import { readFile, realpath } from "node:fs/promises";
2
2
  import type { ServerResponse } from "node:http";
3
- import { dirname } from "node:path";
3
+ import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
4
4
  import { formatDiagnostic } from "@reckona/mreact-compiler";
5
+ import type { DehydrateOptions } from "@reckona/mreact-query";
5
6
  import {
6
7
  createCompilerModuleContext,
7
8
  transformCompilerModuleContext,
8
9
  } from "@reckona/mreact-compiler/internal";
9
10
  import {
11
+ isFileLoadingAllowed,
10
12
  normalizePath,
11
13
  type Connect,
12
14
  type Plugin,
13
15
  type PluginOption,
16
+ type ResolvedConfig,
14
17
  type UserConfig,
15
18
  type ViteDevServer,
16
19
  } from "vite";
@@ -22,6 +25,7 @@ import {
22
25
  type ResolvedAppRouterProject,
23
26
  } from "./config.js";
24
27
  import type { AppRouterImportPolicy } from "./import-policy.js";
28
+ import { dehydrateOptionsFromModule } from "./dehydrate-policy.js";
25
29
  import {
26
30
  collectClientRouteReferences,
27
31
  createClientRouteInferenceCache,
@@ -77,6 +81,8 @@ export type {
77
81
  AppRouterRequestErrorLogEvent,
78
82
  AppRouterRequestStartLogEvent,
79
83
  AppRouterRequestTimingLogEvent,
84
+ AppRouterUpgradeErrorLogEvent,
85
+ AppRouterUpgradeRejectedLogEvent,
80
86
  } from "./logger.js";
81
87
  export type { RequestHostPolicy } from "./serve.js";
82
88
 
@@ -85,6 +91,7 @@ export type { RequestHostPolicy } from "./serve.js";
85
91
  */
86
92
  export interface AppRouterViteMiddlewareOptions extends AppRouterProjectOptions {
87
93
  allowedHosts?: readonly string[] | undefined;
94
+ dehydrateOptions?: DehydrateOptions | undefined;
88
95
  define?: UserConfig["define"] | undefined;
89
96
  hostPolicy?: RequestHostPolicy | undefined;
90
97
  importPolicy?: AppRouterImportPolicy | undefined;
@@ -106,6 +113,7 @@ type AppRouterViteRuntimeMiddlewareOptions = AppRouterViteMiddlewareOptions & {
106
113
  */
107
114
  export interface AppRouterVitePluginOptions extends AppRouterProjectOptions {
108
115
  allowedHosts?: readonly string[] | undefined;
116
+ dehydrateOptions?: DehydrateOptions | undefined;
109
117
  hostPolicy?: RequestHostPolicy | undefined;
110
118
  importPolicy?: AppRouterImportPolicy | undefined;
111
119
  logger?: AppRouterLogger | undefined;
@@ -123,6 +131,7 @@ const virtualReactiveDevtoolsId = "\0mreact-router-reactive-devtools";
123
131
  const mreactRouterConfigKey = "__mreactRouterConfig";
124
132
 
125
133
  type MreactRouterPluginConfig = ResolvedAppRouterProject & {
134
+ dehydrateOptions?: DehydrateOptions | undefined;
126
135
  importPolicy?: AppRouterImportPolicy | undefined;
127
136
  };
128
137
 
@@ -130,6 +139,11 @@ type MreactRouterPlugin = Plugin & {
130
139
  [mreactRouterConfigKey]: MreactRouterPluginConfig;
131
140
  };
132
141
 
142
+ interface DevCssViteFileSystemConfigs {
143
+ canonical: ResolvedConfig;
144
+ logical: ResolvedConfig;
145
+ }
146
+
133
147
  /**
134
148
  * Creates the app-router Vite plugin for route compilation and development middleware.
135
149
  */
@@ -138,6 +152,7 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
138
152
  const normalizedSourceDirs = project.allowedSourceDirs.map((directory) =>
139
153
  normalizePath(directory),
140
154
  );
155
+ const canonicalDevCssSourceRoot = resolveCanonicalDevCssSourceRoot(project.projectRoot);
141
156
  const packageFile = (monorepoDir: string, packageName: string, entry: string): string =>
142
157
  workspacePackageFile({
143
158
  currentFileUrl: import.meta.url,
@@ -229,11 +244,15 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
229
244
  // built-in CSS plugin) whose `transform` requires a dev-server environment the
230
245
  // lightweight navigation scan cannot provide. Mirrors what the build forwards.
231
246
  let userVitePlugins: readonly PluginOption[] | undefined;
247
+ let resolvedViteConfig: Promise<DevCssViteFileSystemConfigs> | undefined;
232
248
  let devServerModuleCacheVersion = 0;
233
249
 
234
250
  const plugin: MreactRouterPlugin = {
235
251
  [mreactRouterConfigKey]: {
236
252
  ...project,
253
+ ...(options.dehydrateOptions === undefined
254
+ ? {}
255
+ : { dehydrateOptions: options.dehydrateOptions }),
237
256
  ...(options.importPolicy === undefined ? {} : { importPolicy: options.importPolicy }),
238
257
  },
239
258
  enforce: "pre",
@@ -275,6 +294,9 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
275
294
  },
276
295
  };
277
296
  },
297
+ configResolved(config) {
298
+ resolvedViteConfig = resolveDevCssViteFileSystemConfigs(config);
299
+ },
278
300
  configureServer(server) {
279
301
  server.middlewares.use(createDevCssProxyMiddleware());
280
302
  server.middlewares.use(createClientRouteTransformMiddleware(project.routesDir, server));
@@ -327,8 +349,10 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
327
349
  load(id) {
328
350
  if (isDevCssSourceModuleId(id)) {
329
351
  return loadDevCssSourceModule({
352
+ allowedRoot: canonicalDevCssSourceRoot,
330
353
  cssFile: clientRequestPath(id),
331
354
  sourceDirs: project.allowedSourceDirs,
355
+ viteConfig: resolvedViteConfig,
332
356
  });
333
357
  }
334
358
 
@@ -412,6 +436,13 @@ export function prepareReactiveEffectRunDevtoolsEvent() { return undefined; }`;
412
436
  };
413
437
  },
414
438
  async resolveId(id, importer) {
439
+ const devCssFile = devCssSourceRequestFile(id, project.projectRoot);
440
+ if (devCssFile !== undefined) {
441
+ const queryStart = id.indexOf("?");
442
+ const query = queryStart === -1 ? "" : id.slice(queryStart);
443
+ return `${normalizePath(devCssFile)}${query}`;
444
+ }
445
+
415
446
  const runtimePath = runtimePaths.get(id);
416
447
 
417
448
  if (id === "@reckona/mreact-reactive-core") {
@@ -637,7 +668,8 @@ async function handleAppRouterViteRequest(
637
668
  return;
638
669
  }
639
670
 
640
- const request = nodeRequestToWebRequest(incoming, origin);
671
+ const request = nodeRequestToWebRequest(incoming, origin, outgoing);
672
+ const dehydrateOptions = await resolveDevDehydrateOptions(project, options);
641
673
  const routeTransformPlugins = options.navigationScanVitePlugins ?? options.vitePlugins;
642
674
  const routes = await scanAppRoutes({ appDir: project.routesDir });
643
675
  const routeMatcher = createRouteMatcher(routes);
@@ -658,6 +690,7 @@ async function handleAppRouterViteRequest(
658
690
  outgoing,
659
691
  await renderAppRequest({
660
692
  appDir: project.routesDir,
693
+ dehydrateOptions,
661
694
  define: options.define,
662
695
  dev: true,
663
696
  importPolicy: {
@@ -688,6 +721,29 @@ async function handleAppRouterViteRequest(
688
721
  }
689
722
  }
690
723
 
724
+ async function resolveDevDehydrateOptions(
725
+ project: ResolvedAppRouterProject,
726
+ options: AppRouterViteRuntimeMiddlewareOptions,
727
+ ): Promise<DehydrateOptions | undefined> {
728
+ if (options.dehydrateOptions !== undefined) {
729
+ return options.dehydrateOptions;
730
+ }
731
+ if (project.dehydratePolicyModule === undefined) {
732
+ return undefined;
733
+ }
734
+ if (options.viteDevServer === undefined) {
735
+ throw new Error("dehydratePolicyModule requires a Vite dev server module runner.");
736
+ }
737
+
738
+ const policyModule = await options.viteDevServer.ssrLoadModule(
739
+ normalizePath(project.dehydratePolicyModule),
740
+ );
741
+ return dehydrateOptionsFromModule(
742
+ policyModule,
743
+ `mreactRouter dehydratePolicyModule ${project.dehydratePolicyModule}`,
744
+ );
745
+ }
746
+
691
747
  function sendViteErrorOverlay(server: ViteDevServer | undefined, error: unknown): void {
692
748
  if (server === undefined) {
693
749
  return;
@@ -903,19 +959,115 @@ function isDevCssSourceModuleId(id: string): boolean {
903
959
  return new URLSearchParams(id.slice(id.indexOf("?") + 1)).has(devCssSourceQuery);
904
960
  }
905
961
 
962
+ function devCssSourceRequestFile(id: string, projectRoot: string): string | undefined {
963
+ if (!new URLSearchParams(id.slice(id.indexOf("?") + 1)).has(devCssSourceQuery)) {
964
+ return undefined;
965
+ }
966
+
967
+ const requestPath = clientRequestPath(id);
968
+ if (requestPath.startsWith("/@fs/")) {
969
+ return `/${requestPath.slice("/@fs/".length)}`;
970
+ }
971
+ if (isAbsolute(requestPath) && isPathInsideDirectory(projectRoot, requestPath)) {
972
+ return requestPath;
973
+ }
974
+
975
+ return resolve(projectRoot, `.${requestPath.startsWith("/") ? requestPath : `/${requestPath}`}`);
976
+ }
977
+
906
978
  async function loadDevCssSourceModule(options: {
979
+ allowedRoot: Promise<string | undefined>;
907
980
  cssFile: string;
908
981
  sourceDirs: readonly string[];
982
+ viteConfig: Promise<DevCssViteFileSystemConfigs> | undefined;
909
983
  }): Promise<string | undefined> {
910
- const code = await readFile(options.cssFile, "utf8");
984
+ const refusal = () => new Error("Dev CSS source is outside the configured project roots.");
985
+
986
+ if (options.cssFile.includes("\0")) {
987
+ throw refusal();
988
+ }
989
+
990
+ let allowedRoot: string | undefined;
991
+ let cssFile: string;
992
+ let viteConfig: DevCssViteFileSystemConfigs | undefined;
993
+ try {
994
+ [allowedRoot, cssFile, viteConfig] = await Promise.all([
995
+ options.allowedRoot,
996
+ realpath(options.cssFile),
997
+ options.viteConfig,
998
+ ]);
999
+ } catch {
1000
+ throw refusal();
1001
+ }
1002
+
1003
+ if (
1004
+ allowedRoot === undefined ||
1005
+ !isPathInsideDirectory(allowedRoot, cssFile) ||
1006
+ (viteConfig !== undefined &&
1007
+ (!isFileLoadingAllowed(viteConfig.logical, normalizePath(options.cssFile)) ||
1008
+ !isFileLoadingAllowed(viteConfig.canonical, normalizePath(cssFile))))
1009
+ ) {
1010
+ throw refusal();
1011
+ }
1012
+
1013
+ let code: string;
1014
+ try {
1015
+ code = await readFile(cssFile, "utf8");
1016
+ } catch {
1017
+ throw refusal();
1018
+ }
911
1019
 
912
1020
  return prependTailwindSourceDirectives({
913
1021
  code,
914
- cssFile: options.cssFile,
1022
+ cssFile,
915
1023
  sourceDirs: options.sourceDirs,
916
1024
  });
917
1025
  }
918
1026
 
1027
+ async function resolveCanonicalDevCssSourceRoot(directory: string): Promise<string | undefined> {
1028
+ try {
1029
+ return await realpath(directory);
1030
+ } catch {
1031
+ return undefined;
1032
+ }
1033
+ }
1034
+
1035
+ async function resolveDevCssViteFileSystemConfigs(
1036
+ config: ResolvedConfig,
1037
+ ): Promise<DevCssViteFileSystemConfigs> {
1038
+ const allow = await Promise.all(
1039
+ config.server.fs.allow.map(async (directory) => {
1040
+ try {
1041
+ return normalizePath(await realpath(directory));
1042
+ } catch {
1043
+ return normalizePath(directory);
1044
+ }
1045
+ }),
1046
+ );
1047
+
1048
+ return {
1049
+ canonical: {
1050
+ ...config,
1051
+ server: {
1052
+ ...config.server,
1053
+ fs: {
1054
+ ...config.server.fs,
1055
+ allow,
1056
+ },
1057
+ },
1058
+ },
1059
+ logical: config,
1060
+ };
1061
+ }
1062
+
1063
+ function isPathInsideDirectory(directory: string, candidate: string): boolean {
1064
+ const relativePath = relative(directory, candidate);
1065
+ return (
1066
+ relativePath === "" ||
1067
+ (relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath))
1068
+ );
1069
+ }
1070
+
919
1071
  function importerInRuntimePackage(
920
1072
  importer: string | undefined,
921
1073
  directories: readonly string[],