dinou 4.0.14 → 4.0.15

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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [4.0.15] - 2026-06-29
9
+
10
+ ### Added
11
+ - **VFS Routing Cache**: Introduced an in-memory Virtual File System (VFS) to cache filesystem structures under `src/` at production startup, resolving dynamic routes entirely in-memory and eliminating synchronous event loop-blocking disk I/O.
12
+
13
+ ### Fixed
14
+ - **Cross-Platform Separators**: Normalized Server Functions manifest keys and runtime lookup checks to consistently use forward slashes (`/`), preventing OS path format mismatches (`\` vs `/`) and subsequent `400 Bad Request` execution errors.
15
+
8
16
  ## [4.0.14] - 2026-06-29
9
17
  ### Added
10
18
  - **Soft Redirects**: Exposes the SPA transition navigation internally via `window.__DINOU_ROUTER_NAVIGATE__` to enable smooth client-side redirects from both Server Functions and RSC payloads, keeping the React client state intact and preventing full browser page reloads.
@@ -1,5 +1,5 @@
1
1
  const path = require("path");
2
- const { existsSync } = require("fs");
2
+ const { existsSync } = require("./vfs");
3
3
  const React = require("react");
4
4
  const {
5
5
  getFilePathAndDynamicParams,
@@ -1,5 +1,5 @@
1
1
  const path = require("path");
2
- const { existsSync, readdirSync } = require("fs");
2
+ const { existsSync, readdirSync } = require("./vfs");
3
3
  const React = require("react");
4
4
 
5
5
  function safeDecode(val) {
@@ -1,5 +1,5 @@
1
1
  const path = require("path");
2
- const { existsSync } = require("fs");
2
+ const { existsSync } = require("./vfs");
3
3
  const React = require("react");
4
4
  const {
5
5
  getFilePathAndDynamicParams,
@@ -918,10 +918,11 @@ app.post("/____server_function____", async (req, res) => {
918
918
  return res.status(404).json({ error: "File not found" });
919
919
  }
920
920
 
921
+ const normalizedKey = relativePath.replace(/\\/g, "/");
921
922
  let allowedExports;
922
923
  if (serverFunctionsManifest) {
923
924
  // Prod: use manifest (relativePath is already normalized)
924
- allowedExports = serverFunctionsManifest[relativePath];
925
+ allowedExports = serverFunctionsManifest[normalizedKey];
925
926
  } else {
926
927
  if (!isDevelopment) {
927
928
  return res
@@ -0,0 +1,68 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const isDevelopment = process.env.NODE_ENV !== "production";
5
+ const vfs = {};
6
+
7
+ function buildVfs(dir) {
8
+ if (!fs.existsSync(dir)) return;
9
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
10
+ const children = [];
11
+
12
+ for (const entry of entries) {
13
+ const fullPath = path.join(dir, entry.name);
14
+ const isDirectory = entry.isDirectory();
15
+ children.push({
16
+ name: entry.name,
17
+ isDirectory
18
+ });
19
+ if (isDirectory) {
20
+ buildVfs(fullPath);
21
+ } else {
22
+ vfs[fullPath] = { type: "file" };
23
+ }
24
+ }
25
+
26
+ vfs[dir] = {
27
+ type: "directory",
28
+ children
29
+ };
30
+ }
31
+
32
+ if (!isDevelopment) {
33
+ const srcDir = path.resolve(process.cwd(), "src");
34
+ buildVfs(srcDir);
35
+ }
36
+
37
+ function existsSync(filePath) {
38
+ if (isDevelopment) {
39
+ return fs.existsSync(filePath);
40
+ }
41
+ const normalized = path.resolve(filePath);
42
+ return !!vfs[normalized];
43
+ }
44
+
45
+ function readdirSync(dirPath, options) {
46
+ if (isDevelopment) {
47
+ return fs.readdirSync(dirPath, options);
48
+ }
49
+ const normalized = path.resolve(dirPath);
50
+ const entry = vfs[normalized];
51
+ if (!entry || entry.type !== "directory") {
52
+ throw new Error(`ENOTDIR: not a directory, readdir '${dirPath}'`);
53
+ }
54
+
55
+ if (options && options.withFileTypes) {
56
+ return entry.children.map(child => ({
57
+ name: child.name,
58
+ isDirectory: () => child.isDirectory,
59
+ isFile: () => !child.isDirectory
60
+ }));
61
+ }
62
+ return entry.children.map(child => child.name);
63
+ }
64
+
65
+ module.exports = {
66
+ existsSync,
67
+ readdirSync
68
+ };
@@ -19,7 +19,7 @@ export default function serverFunctionsPlugin(manifestData = {}) {
19
19
  const exports = parseExports(code);
20
20
  if (exports.length === 0) return null;
21
21
 
22
- const relativePath = path.relative(root, args.path);
22
+ const relativePath = path.relative(root, args.path).replace(/\\/g, "/");
23
23
  serverFunctions.set(relativePath, new Set(exports)); // Save exports as a Set to guarantee uniqueness
24
24
 
25
25
  const fileUrl = `file:///${relativePath}`;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dinou-ejected",
3
- "version": "4.0.14",
3
+ "version": "4.0.15",
4
4
  "main": "index.js",
5
5
  "private": true,
6
6
  "exports": {
@@ -17,7 +17,7 @@ function serverFunctionsPlugin() {
17
17
  const exports = parseExports(code);
18
18
  if (exports.length === 0) return null;
19
19
 
20
- const relativePath = path.relative(root, id);
20
+ const relativePath = path.relative(root, id).replace(/\\/g, "/");
21
21
  serverFunctions.set(relativePath, new Set(exports)); // Save exports as a Set for uniqueness
22
22
 
23
23
  const fileUrl = `file:///${relativePath}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dinou",
3
- "version": "4.0.14",
3
+ "version": "4.0.15",
4
4
  "description": "Full-Stack React 19 framework with React Server Components, Server Functions, and Streaming SSR.",
5
5
  "main": "index.js",
6
6
  "bin": {