dinou 6.1.0 → 7.0.0

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 (135) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +9461 -1080
  3. package/cli.js +392 -169
  4. package/dinou/adapters/bun.js +62 -0
  5. package/dinou/adapters/cloudflare.js +65 -0
  6. package/dinou/adapters/deno.js +48 -0
  7. package/dinou/adapters/netlify.js +85 -0
  8. package/dinou/adapters/node.mjs +31 -0
  9. package/dinou/bun/build.mjs +1077 -0
  10. package/dinou/cloudflare/build.mjs +1121 -0
  11. package/dinou/cloudflare/shims/child_process.js +14 -0
  12. package/dinou/cloudflare/shims/empty.js +2 -0
  13. package/dinou/cloudflare/shims/fs.js +99 -0
  14. package/dinou/cloudflare/shims/os.js +29 -0
  15. package/dinou/config.js +1 -0
  16. package/dinou/config.mjs +2 -0
  17. package/dinou/constants.js +29 -4
  18. package/dinou/core/babel-esm-loader.js +66 -37
  19. package/dinou/core/bailout-proxy.js +61 -0
  20. package/dinou/core/build-static-pages.js +309 -157
  21. package/dinou/core/cache-revalidate.js +471 -144
  22. package/dinou/core/cache-slot.js +387 -0
  23. package/dinou/core/client-redirect.jsx +12 -7
  24. package/dinou/core/client-webpack.jsx +457 -82
  25. package/dinou/core/client.jsx +452 -82
  26. package/dinou/core/config.js +11 -0
  27. package/dinou/core/dinou-paths.js +52 -0
  28. package/dinou/core/edge-ssr.js +122 -0
  29. package/dinou/core/export-static.mjs +64 -0
  30. package/dinou/core/generate-route-types.js +252 -0
  31. package/dinou/core/generate-static-page.js +55 -94
  32. package/dinou/core/generate-static-rsc.js +47 -15
  33. package/dinou/core/generate-static.js +212 -10
  34. package/dinou/core/generating-isg.js +5 -0
  35. package/dinou/core/get-asset-from-manifest.js +9 -3
  36. package/dinou/core/get-error-jsx.js +221 -41
  37. package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
  38. package/dinou/core/get-jsx.js +172 -95
  39. package/dinou/core/handler.js +2296 -0
  40. package/dinou/core/http-adapter.js +115 -0
  41. package/dinou/core/import-module.js +83 -53
  42. package/dinou/core/layout-functions.js +181 -0
  43. package/dinou/core/link.jsx +18 -6
  44. package/dinou/core/load-dinou-config.js +60 -0
  45. package/dinou/core/manifest-provider.js +261 -0
  46. package/dinou/core/navigation-utils.js +4 -0
  47. package/dinou/core/navigation.js +50 -39
  48. package/dinou/core/parse-exports.js +207 -2
  49. package/dinou/core/ppr-context.js +105 -0
  50. package/dinou/core/ppr-runtime.js +263 -0
  51. package/dinou/core/redirect.jsx +20 -19
  52. package/dinou/core/render-jsx-to-client-jsx.js +133 -15
  53. package/dinou/core/request-context.js +47 -13
  54. package/dinou/core/resolve-module-ppr.js +32 -0
  55. package/dinou/core/revalidating.js +28 -9
  56. package/dinou/core/route-generator.js +107 -0
  57. package/dinou/core/rsc-renderer.js +90 -0
  58. package/dinou/core/run-ssg.js +19 -0
  59. package/dinou/core/scan-dependency-components.js +218 -0
  60. package/dinou/core/server-function-proxy-webpack.js +3 -0
  61. package/dinou/core/server-function-proxy.js +3 -0
  62. package/dinou/core/slot.js +302 -0
  63. package/dinou/core/status-manifest.js +41 -1
  64. package/dinou/core/storage-adapter.js +522 -0
  65. package/dinou/core/url-resolver.js +16 -16
  66. package/dinou/core/vfs.js +139 -68
  67. package/dinou/deno/build.mjs +1112 -0
  68. package/dinou/deno/seed-kv.js +115 -0
  69. package/dinou/esbuild/build.mjs +51 -1
  70. package/dinou/esbuild/dev.mjs +290 -224
  71. package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
  72. package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
  73. package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
  74. package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
  75. package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
  76. package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
  77. package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
  78. package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
  79. package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
  80. package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
  81. package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
  82. package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
  83. package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
  84. package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
  85. package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
  86. package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
  87. package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
  88. package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
  89. package/dinou/index.d.ts +315 -11
  90. package/dinou/index.js +12 -6
  91. package/dinou/index.mjs +9 -0
  92. package/dinou/node/build.mjs +195 -0
  93. package/dinou/node/bundle-dual-engine.mjs +1012 -0
  94. package/dinou/node/dev.mjs +2589 -0
  95. package/dinou/node/port-selector.mjs +164 -0
  96. package/dinou/node/terminal-status.mjs +345 -0
  97. package/dinou/package.json +17 -14
  98. package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
  99. package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
  100. package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
  101. package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
  102. package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
  103. package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
  104. package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
  105. package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
  106. package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
  107. package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
  108. package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
  109. package/dinou/rollup/rollup.config.js +155 -43
  110. package/dinou/server.d.ts +114 -12
  111. package/dinou/server.js +35 -10
  112. package/dinou/server.mjs +30 -9
  113. package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
  114. package/dinou/webpack/loaders/server-functions-loader.js +22 -1
  115. package/dinou/webpack/loaders/swc-loader.js +76 -0
  116. package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
  117. package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
  118. package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
  119. package/dinou/webpack/webpack.config.js +227 -29
  120. package/eject.js +173 -94
  121. package/package.json +110 -107
  122. package/dinou/core/asset-require-hook.js +0 -58
  123. package/dinou/core/client-error-webpack.jsx +0 -432
  124. package/dinou/core/client-error.jsx +0 -432
  125. package/dinou/core/concurrency-manager.js +0 -48
  126. package/dinou/core/context-proxy.js +0 -68
  127. package/dinou/core/generate-static-pages.js +0 -130
  128. package/dinou/core/generate-static-rscs.js +0 -151
  129. package/dinou/core/get-ssg-metadata.js +0 -31
  130. package/dinou/core/register-loader.mjs +0 -11
  131. package/dinou/core/register-paths.js +0 -27
  132. package/dinou/core/render-app-to-html.js +0 -430
  133. package/dinou/core/render-html.js +0 -407
  134. package/dinou/core/server.js +0 -1542
  135. package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
@@ -1,144 +1,471 @@
1
- const path = require("path");
2
- const fs = require("fs").promises;
3
- const { existsSync, copyFileSync } = require("fs");
4
- const generateStaticPage = require("./generate-static-page");
5
- const { buildStaticPage } = require("./build-static-pages");
6
- const generateStaticRSC = require("./generate-static-rsc");
7
- const { safeRename } = require("./safe-rename");
8
- const { updateStatus } = require("./status-manifest");
9
-
10
- const { getContext } = require("./request-context");
11
- const { resolveRelativeUrl } = require("./url-resolver");
12
-
13
- async function walkMetadataFiles(dir, fileList = []) {
14
- try {
15
- const files = await fs.readdir(dir);
16
- for (const file of files) {
17
- const filePath = path.join(dir, file);
18
- const stat = await fs.stat(filePath);
19
- if (stat.isDirectory()) {
20
- await walkMetadataFiles(filePath, fileList);
21
- } else if (file === "metadata.json") {
22
- fileList.push(filePath);
23
- }
24
- }
25
- } catch (err) {
26
- // Ignore read errors for individual folders/files
27
- }
28
- return fileList;
29
- }
30
-
31
- async function revalidatePath(reqPath) {
32
- let targetPath = reqPath;
33
-
34
- if (targetPath && !targetPath.startsWith("/") && !targetPath.includes("://")) {
35
- const ctx = getContext();
36
- let currentPathname = "/";
37
- if (ctx && ctx.req) {
38
- const referer = ctx.req.headers?.referer;
39
- if (referer) {
40
- try {
41
- currentPathname = new URL(referer).pathname;
42
- } catch (e) { }
43
- } else {
44
- currentPathname = ctx.req.path || "/";
45
- }
46
- }
47
- targetPath = resolveRelativeUrl(targetPath, currentPathname);
48
- }
49
-
50
- let cleanPath = targetPath;
51
- if (!cleanPath.startsWith("/")) {
52
- cleanPath = "/" + cleanPath;
53
- }
54
- if (cleanPath !== "/" && cleanPath.endsWith("/")) {
55
- cleanPath = cleanPath.slice(0, -1);
56
- }
57
-
58
- const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
59
- const reqPathWithSlash = cleanPath.endsWith("/") ? cleanPath : cleanPath + "/";
60
-
61
- // Check if there is an existing page to copy to _old
62
- try {
63
- if (existsSync(path.join(dist2Folder, reqPathWithSlash, "index.html"))) {
64
- copyFileSync(
65
- path.join(dist2Folder, reqPathWithSlash, "index.html"),
66
- path.join(dist2Folder, reqPathWithSlash, "index._old.html")
67
- );
68
- }
69
- if (existsSync(path.join(dist2Folder, reqPathWithSlash, "rsc.rsc"))) {
70
- copyFileSync(
71
- path.join(dist2Folder, reqPathWithSlash, "rsc.rsc"),
72
- path.join(dist2Folder, reqPathWithSlash, "rsc._old.rsc")
73
- );
74
- }
75
- } catch (e) {
76
- // Ignore copy errors
77
- }
78
-
79
- console.log(`[Revalidate] Starting on-demand revalidation for ${cleanPath}...`);
80
- try {
81
- const isDynamic = {};
82
- await buildStaticPage(cleanPath, isDynamic);
83
- if (isDynamic.value) {
84
- console.log(`[Revalidate] Bailout detected for ${cleanPath}. Switching to dynamic (skipping static write).`);
85
- return;
86
- }
87
-
88
- const rscResult = await generateStaticRSC(cleanPath);
89
- if (!rscResult.success) {
90
- console.warn(`⚠️ [Revalidate] RSC generation failed for ${cleanPath}.`);
91
- if (rscResult.tempPath && existsSync(rscResult.tempPath)) {
92
- await fs.unlink(rscResult.tempPath).catch(() => { });
93
- }
94
- return;
95
- }
96
-
97
- await safeRename(rscResult.tempPath, rscResult.finalPath);
98
-
99
- const pageResult = await generateStaticPage(cleanPath);
100
- if (pageResult.success) {
101
- await safeRename(pageResult.tempPath, pageResult.finalPath);
102
- updateStatus(cleanPath, pageResult.status);
103
- console.log(`✅ [Revalidate] Successfully revalidated ${cleanPath} (Status: ${pageResult.status})`);
104
- } else {
105
- console.warn(`⚠️ [Revalidate] HTML generation failed for ${cleanPath}.`);
106
- if (pageResult.tempPath && existsSync(pageResult.tempPath)) {
107
- await fs.unlink(pageResult.tempPath).catch(() => { });
108
- }
109
- }
110
- } catch (e) {
111
- console.warn(`⚠️ [Revalidate] Failed to revalidate ${cleanPath}:`, e.message || e);
112
- }
113
- }
114
-
115
- async function revalidateTag(tag) {
116
- console.log(`[Revalidate] Starting on-demand revalidation for tag: "${tag}"...`);
117
- const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
118
- if (!existsSync(dist2Folder)) return;
119
-
120
- const metadataFiles = await walkMetadataFiles(dist2Folder);
121
- const revalidatePromises = [];
122
-
123
- for (const fileOfMeta of metadataFiles) {
124
- try {
125
- const content = await fs.readFile(fileOfMeta, "utf8");
126
- const metadata = JSON.parse(content);
127
- if (metadata && Array.isArray(metadata.tags) && metadata.tags.includes(tag)) {
128
- // Calculate the request path
129
- const relative = path.relative(dist2Folder, path.dirname(fileOfMeta));
130
- const reqPath = "/" + relative.replace(/\\/g, "/");
131
- revalidatePromises.push(revalidatePath(reqPath));
132
- }
133
- } catch (err) {
134
- console.error(`[Revalidate] Error reading tags from ${fileOfMeta}:`, err);
135
- }
136
- }
137
-
138
- await Promise.all(revalidatePromises);
139
- }
140
-
141
- module.exports = {
142
- revalidatePath,
143
- revalidateTag,
144
- };
1
+ const path = require("path");
2
+ const fs = require("fs").promises;
3
+ const { existsSync, copyFileSync } = require("fs");
4
+ let _generateStaticPage = null;
5
+ function getGenerateStaticPage() {
6
+ if (!_generateStaticPage) _generateStaticPage = require("./generate-static-page");
7
+ return _generateStaticPage;
8
+ }
9
+ let _buildStaticPage = null;
10
+ function getBuildStaticPage() {
11
+ if (!_buildStaticPage) _buildStaticPage = require("./build-static-pages").buildStaticPage;
12
+ return _buildStaticPage;
13
+ }
14
+ let _generateStaticRSC = null;
15
+ function getGenerateStaticRSC() {
16
+ if (!_generateStaticRSC) _generateStaticRSC = require("./generate-static-rsc");
17
+ return _generateStaticRSC;
18
+ }
19
+ let _safeRename = null;
20
+ function getSafeRename() {
21
+ if (!_safeRename) _safeRename = require("./safe-rename").safeRename;
22
+ return _safeRename;
23
+ }
24
+ let _updateStatus = null;
25
+ function getUpdateStatus() {
26
+ if (!_updateStatus) _updateStatus = require("./status-manifest").updateStatus;
27
+ return _updateStatus;
28
+ }
29
+ const { getContext } = require("./request-context");
30
+ const { resolveRelativeUrl } = require("./url-resolver");
31
+ const { getStorageAdapter } = require("./storage-adapter");
32
+ const { isEdgeRuntime } = require("./rsc-renderer");
33
+
34
+ async function walkMetadataFiles(dir, fileList = []) {
35
+ try {
36
+ const entries = await fs.readdir(dir, { withFileTypes: true });
37
+ for (const entry of entries) {
38
+ const entryPath = path.join(dir, entry.name);
39
+ if (entry.isDirectory()) {
40
+ await walkMetadataFiles(entryPath, fileList);
41
+ } else if (
42
+ entry.name === "metadata.json" ||
43
+ entry.name === "layout.metadata.json" ||
44
+ entry.name === "slot.metadata.json"
45
+ ) {
46
+ fileList.push(entryPath);
47
+ }
48
+ }
49
+ } catch (err) {
50
+ // Ignore read errors for individual folders/files
51
+ }
52
+ return fileList;
53
+ }
54
+
55
+ function normalizeRevalidatePath(reqPath) {
56
+ let targetPath = reqPath;
57
+
58
+ if (targetPath && !targetPath.startsWith("/") && !targetPath.includes("://")) {
59
+ const ctx = getContext();
60
+ let currentPathname = "/";
61
+ if (ctx && ctx.req) {
62
+ const headerPath =
63
+ ctx.req.headers?.["x-dinou-current-path"] ||
64
+ ctx.req.headers?.["X-Dinou-Current-Path"];
65
+ const referer = ctx.req.headers?.referer;
66
+ if (headerPath) {
67
+ currentPathname = headerPath;
68
+ } else if (referer) {
69
+ try {
70
+ currentPathname = new URL(referer).pathname;
71
+ } catch (e) { }
72
+ } else {
73
+ currentPathname = ctx.req.path || "/";
74
+ }
75
+ }
76
+ targetPath = resolveRelativeUrl(targetPath, currentPathname);
77
+ }
78
+
79
+ let cleanPath = targetPath;
80
+ if (!cleanPath.startsWith("/")) {
81
+ cleanPath = "/" + cleanPath;
82
+ }
83
+ if (cleanPath !== "/" && cleanPath.endsWith("/")) {
84
+ cleanPath = cleanPath.slice(0, -1);
85
+ }
86
+ return cleanPath;
87
+ }
88
+
89
+ async function revalidatePage(reqPath) {
90
+ const cleanPath = normalizeRevalidatePath(reqPath);
91
+
92
+ if (isEdgeRuntime()) {
93
+ const storage = getStorageAdapter();
94
+ const cleanPathKey = cleanPath.replace(/^\/+/, "").replace(/\/+$/, "");
95
+ const htmlKey = cleanPathKey ? `${cleanPathKey}/index.html` : "index.html";
96
+ const metaKey = cleanPathKey ? `${cleanPathKey}/metadata.json` : "metadata.json";
97
+ const rscKey = cleanPathKey ? `${cleanPathKey}/page.rsc` : "page.rsc";
98
+ const legacyRscKey = cleanPathKey ? `${cleanPathKey}/rsc.rsc` : "rsc.rsc";
99
+
100
+ let cached = await storage.get(htmlKey);
101
+ if (!cached && cleanPathKey) {
102
+ cached = await storage.get(cleanPathKey);
103
+ }
104
+ let currentMeta = (cached && cached.metadata) || {};
105
+ try {
106
+ const metaItem = await storage.get(metaKey);
107
+ if (metaItem && metaItem.content) {
108
+ currentMeta = JSON.parse(metaItem.content);
109
+ }
110
+ } catch (e) {}
111
+
112
+ const prevGenTime = (currentMeta && currentMeta.generatedAt) || 0;
113
+ const now = Date.now();
114
+ const newGenTime = now <= prevGenTime ? prevGenTime + 100 : now;
115
+ currentMeta.generatedAt = newGenTime;
116
+
117
+ let newHtml = cached ? cached.content : "";
118
+ if (newHtml) {
119
+ newHtml = newHtml.replace(
120
+ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/g,
121
+ new Date(newGenTime).toISOString()
122
+ );
123
+ }
124
+ await storage.set(htmlKey, newHtml, currentMeta);
125
+ if (cleanPathKey) {
126
+ await storage.set(cleanPathKey, newHtml, currentMeta);
127
+ }
128
+ await storage.set(metaKey, JSON.stringify(currentMeta));
129
+
130
+ try {
131
+ let cachedRsc = await storage.get(rscKey);
132
+ if (!cachedRsc) {
133
+ cachedRsc = await storage.get(legacyRscKey);
134
+ }
135
+ if (cachedRsc && cachedRsc.content) {
136
+ const newRsc = cachedRsc.content.replace(
137
+ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/g,
138
+ new Date(newGenTime).toISOString()
139
+ );
140
+ await storage.set(rscKey, newRsc);
141
+ }
142
+ } catch (e) {}
143
+
144
+ console.log(`✅ [Edge Revalidate] Successfully revalidated page ${cleanPath} (generatedAt: ${newGenTime})`);
145
+ return;
146
+ }
147
+
148
+ const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
149
+ const reqPathWithSlash = cleanPath.endsWith("/") ? cleanPath : cleanPath + "/";
150
+
151
+ // Check if there is an existing page to copy to _old
152
+ try {
153
+ if (existsSync(path.join(dist2Folder, reqPathWithSlash, "index.html"))) {
154
+ copyFileSync(
155
+ path.join(dist2Folder, reqPathWithSlash, "index.html"),
156
+ path.join(dist2Folder, reqPathWithSlash, "index._old.html")
157
+ );
158
+ }
159
+ if (existsSync(path.join(dist2Folder, reqPathWithSlash, "page.rsc"))) {
160
+ copyFileSync(
161
+ path.join(dist2Folder, reqPathWithSlash, "page.rsc"),
162
+ path.join(dist2Folder, reqPathWithSlash, "page._old.rsc")
163
+ );
164
+ }
165
+ if (existsSync(path.join(dist2Folder, reqPathWithSlash, "rsc.rsc"))) {
166
+ copyFileSync(
167
+ path.join(dist2Folder, reqPathWithSlash, "rsc.rsc"),
168
+ path.join(dist2Folder, reqPathWithSlash, "rsc._old.rsc")
169
+ );
170
+ }
171
+ } catch (e) {
172
+ // Ignore copy errors
173
+ }
174
+
175
+ console.log(`[Revalidate] Starting on-demand page revalidation for ${cleanPath}...`);
176
+ try {
177
+ const isDynamic = {};
178
+ await getBuildStaticPage()(cleanPath, isDynamic);
179
+ if (isDynamic.value) {
180
+ console.log(`[Revalidate] Bailout detected for ${cleanPath}. Switching to dynamic (skipping static write).`);
181
+ return;
182
+ }
183
+
184
+ const rscResult = await getGenerateStaticRSC()(cleanPath);
185
+ if (!rscResult.success) {
186
+ console.warn(`⚠️ [Revalidate] RSC generation failed for ${cleanPath}.`);
187
+ if (rscResult.tempPath && existsSync(rscResult.tempPath)) {
188
+ await fs.unlink(rscResult.tempPath).catch(() => { });
189
+ }
190
+ return;
191
+ }
192
+
193
+ await getSafeRename()(rscResult.tempPath, rscResult.finalPath);
194
+
195
+ const pageResult = await getGenerateStaticPage()(cleanPath);
196
+ if (pageResult.success) {
197
+ await getSafeRename()(pageResult.tempPath, pageResult.finalPath);
198
+ getUpdateStatus()(cleanPath, pageResult.status);
199
+ console.log(`✅ [Revalidate] Successfully revalidated page ${cleanPath} (Status: ${pageResult.status})`);
200
+ } else {
201
+ console.warn(`⚠️ [Revalidate] HTML generation failed for ${cleanPath}.`);
202
+ if (pageResult.tempPath && existsSync(pageResult.tempPath)) {
203
+ await fs.unlink(pageResult.tempPath).catch(() => { });
204
+ }
205
+ }
206
+ } catch (e) {
207
+ console.warn(`⚠️ [Revalidate] Failed to revalidate page ${cleanPath}:`, e.message || e);
208
+ }
209
+ }
210
+
211
+ async function revalidatePath(reqPath, options = {}) {
212
+ const isCascade = options === "layout" || (typeof options === "object" && options?.cascade === true);
213
+ const cleanPath = normalizeRevalidatePath(reqPath);
214
+ let layoutRevalidated = false;
215
+
216
+ if (isEdgeRuntime()) {
217
+ // 1. Revalidar la página
218
+ await revalidatePage(cleanPath);
219
+
220
+ // 2. Paridad Edge: Revalidar layout si existe para este segmento
221
+ const storage = getStorageAdapter();
222
+ const cleanPathKey = cleanPath.replace(/^\/+/, "").replace(/\/+$/, "");
223
+ const layoutRscKey = cleanPathKey ? `${cleanPathKey}/layout.rsc` : "layout.rsc";
224
+ const layoutMetaKey = cleanPathKey ? `${cleanPathKey}/layout.metadata.json` : "layout.metadata.json";
225
+
226
+ const cachedLayout = await storage.get(layoutRscKey);
227
+ const cachedLayoutMeta = await storage.get(layoutMetaKey);
228
+ if (cachedLayout || cachedLayoutMeta) {
229
+ layoutRevalidated = await revalidateLayout(cleanPath);
230
+ }
231
+
232
+ // 3. Cascada en Edge: solo si se solicitó cascade y se revalidó un layout
233
+ if (isCascade && layoutRevalidated && typeof storage.keys === "function") {
234
+ const allKeys = await storage.keys();
235
+ const prefix = cleanPathKey ? `${cleanPathKey}/` : "";
236
+ const childPaths = new Set();
237
+ for (const key of allKeys) {
238
+ if (key.startsWith(prefix) && (key.endsWith("metadata.json") || key.endsWith("index.html"))) {
239
+ if (!key.endsWith("layout.metadata.json") && !key.endsWith("slot.metadata.json")) {
240
+ const relKey = key.replace(/\/(?:metadata\.json|index\.html)$/, "").replace(/^(?:metadata\.json|index\.html)$/, "");
241
+ const childPath = "/" + relKey;
242
+ if (childPath !== cleanPath) {
243
+ childPaths.add(childPath);
244
+ }
245
+ }
246
+ }
247
+ }
248
+ await Promise.all(Array.from(childPaths).map((p) => revalidatePage(p)));
249
+ }
250
+ return;
251
+ }
252
+
253
+ const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
254
+ const reqPathWithSlash = cleanPath.endsWith("/") ? cleanPath : cleanPath + "/";
255
+ const targetDir = path.join(dist2Folder, reqPathWithSlash);
256
+
257
+ // 1. Revalidar la página
258
+ await revalidatePage(cleanPath);
259
+
260
+ // 2. Revalidar el layout si existe en este segmento
261
+ const layoutFinalPath = path.join(targetDir, "layout.rsc");
262
+ const layoutOldPath = path.join(targetDir, "layout._old.rsc");
263
+ if (existsSync(layoutFinalPath) || existsSync(layoutOldPath)) {
264
+ layoutRevalidated = await revalidateLayout(cleanPath);
265
+ }
266
+
267
+ // 3. Cascada en Node: solo si se solicitó cascade y se revalidó un layout
268
+ if (isCascade && layoutRevalidated) {
269
+ if (existsSync(targetDir)) {
270
+ const childFiles = await walkMetadataFiles(targetDir);
271
+ const childPaths = new Set();
272
+ for (const fileOfMeta of childFiles) {
273
+ if (path.basename(fileOfMeta) === "metadata.json") {
274
+ const relative = path.relative(dist2Folder, path.dirname(fileOfMeta));
275
+ const childPath = "/" + relative.replace(/\\/g, "/");
276
+ if (childPath !== cleanPath) {
277
+ childPaths.add(childPath);
278
+ }
279
+ }
280
+ }
281
+ await Promise.all(Array.from(childPaths).map((p) => revalidatePage(p)));
282
+ }
283
+ }
284
+ }
285
+
286
+ async function revalidateLayout(cleanPath) {
287
+ let targetPath = cleanPath;
288
+ if (!targetPath.startsWith("/")) {
289
+ targetPath = "/" + targetPath;
290
+ }
291
+ if (targetPath !== "/" && targetPath.endsWith("/")) {
292
+ targetPath = targetPath.slice(0, -1);
293
+ }
294
+
295
+ if (isEdgeRuntime()) {
296
+ const storage = getStorageAdapter();
297
+ const cleanPathKey = targetPath.replace(/^\/+/, "").replace(/\/+$/, "");
298
+ const layoutRscKey = cleanPathKey ? `${cleanPathKey}/layout.rsc` : "layout.rsc";
299
+ const layoutMetaKey = cleanPathKey ? `${cleanPathKey}/layout.metadata.json` : "layout.metadata.json";
300
+
301
+ let cached = await storage.get(layoutRscKey);
302
+ let currentMeta = (cached && cached.metadata) || {};
303
+ try {
304
+ const metaItem = await storage.get(layoutMetaKey);
305
+ if (metaItem && metaItem.content) {
306
+ currentMeta = JSON.parse(metaItem.content);
307
+ }
308
+ } catch (e) {}
309
+
310
+ const prevGenTime = (currentMeta && currentMeta.generatedAt) || 0;
311
+ const now = Date.now();
312
+ const newGenTime = now <= prevGenTime ? prevGenTime + 100 : now;
313
+ currentMeta.generatedAt = newGenTime;
314
+
315
+ await storage.set(layoutMetaKey, JSON.stringify(currentMeta));
316
+ if (cached && cached.content) {
317
+ await storage.set(layoutRscKey, cached.content, currentMeta);
318
+ }
319
+ console.log(`✅ [Edge Revalidate] Successfully revalidated layout ${targetPath} (generatedAt: ${newGenTime})`);
320
+ return true;
321
+ }
322
+
323
+ const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
324
+ const reqPathWithSlash = targetPath.endsWith("/") ? targetPath : targetPath + "/";
325
+ const layoutFinalPath = path.join(dist2Folder, reqPathWithSlash, "layout.rsc");
326
+ const layoutOldPath = path.join(dist2Folder, reqPathWithSlash, "layout._old.rsc");
327
+
328
+ if (existsSync(layoutFinalPath)) {
329
+ try {
330
+ copyFileSync(layoutFinalPath, layoutOldPath);
331
+ } catch (e) {}
332
+ }
333
+
334
+ console.log(`[Revalidate] Starting on-demand layout revalidation for ${targetPath}...`);
335
+ try {
336
+ const layoutRscResult = await getGenerateStaticRSC()(targetPath, { segment: "layout" });
337
+ if (layoutRscResult && layoutRscResult.success) {
338
+ await getSafeRename()(layoutRscResult.tempPath, layoutRscResult.finalPath);
339
+ console.log(`✅ [Revalidate] Successfully revalidated layout ${targetPath}`);
340
+ return true;
341
+ }
342
+ } catch (err) {
343
+ console.warn(`⚠️ [Revalidate] Failed to revalidate layout ${targetPath}:`, err.message || err);
344
+ }
345
+ return false;
346
+ }
347
+
348
+ async function revalidateTag(tag, options = {}) {
349
+ const isCascade = options === "layout" || (typeof options === "object" && options?.cascade === true);
350
+ console.log(`[Revalidate] Starting on-demand revalidation for tag: "${tag}" (cascade: ${isCascade})...`);
351
+
352
+ if (isEdgeRuntime()) {
353
+ const storage = getStorageAdapter();
354
+ if (typeof storage.keys === "function") {
355
+ const allKeys = await storage.keys();
356
+ const targetPaths = new Set();
357
+ const targetLayoutPaths = new Set();
358
+ const targetSlotKeys = new Set();
359
+ for (const rawKey of allKeys) {
360
+ const key = String(rawKey || "").replace(/\\/g, "/");
361
+ if (key.includes("slot")) {
362
+ try {
363
+ const item = await storage.get(key);
364
+ let meta = item?.metadata;
365
+ if (!meta && item?.content) {
366
+ try { meta = JSON.parse(item.content); } catch (e) {}
367
+ }
368
+ if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
369
+ targetSlotKeys.add(key);
370
+ }
371
+ } catch (e) {}
372
+ } else if (key.endsWith("layout.metadata.json") || key.endsWith("layout.rsc")) {
373
+ try {
374
+ const item = await storage.get(key);
375
+ let meta = item?.metadata;
376
+ if (!meta && item?.content) {
377
+ try { meta = JSON.parse(item.content); } catch (e) {}
378
+ }
379
+ if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
380
+ const cleanKey = key
381
+ .replace(/\/(?:layout\.metadata\.json|layout\.rsc)$/, "")
382
+ .replace(/^(?:layout\.metadata\.json|layout\.rsc)$/, "");
383
+ const normPath = cleanKey ? (cleanKey.startsWith("/") ? cleanKey : "/" + cleanKey) : "/";
384
+ targetLayoutPaths.add(normPath);
385
+ }
386
+ } catch (e) {}
387
+ } else if (key.endsWith("metadata.json")) {
388
+ try {
389
+ const item = await storage.get(key);
390
+ let meta = item?.metadata;
391
+ if (!meta && item?.content) {
392
+ try { meta = JSON.parse(item.content); } catch (e) {}
393
+ }
394
+ if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
395
+ const cleanKey = key
396
+ .replace(/\/metadata\.json$/, "")
397
+ .replace(/^metadata\.json$/, "");
398
+ const normPath = cleanKey ? (cleanKey.startsWith("/") ? cleanKey : "/" + cleanKey) : "/";
399
+ targetPaths.add(normPath);
400
+ }
401
+ } catch (e) {}
402
+ } else if (key.endsWith("index.html")) {
403
+ try {
404
+ const item = await storage.get(key);
405
+ const meta = item?.metadata;
406
+ if (meta && Array.isArray(meta.tags) && meta.tags.includes(tag)) {
407
+ const cleanKey = key
408
+ .replace(/\/index\.html$/, "")
409
+ .replace(/^index\.html$/, "");
410
+ const normPath = cleanKey ? (cleanKey.startsWith("/") ? cleanKey : "/" + cleanKey) : "/";
411
+ targetPaths.add(normPath);
412
+ }
413
+ } catch (e) {}
414
+ }
415
+ }
416
+ await Promise.all([
417
+ ...Array.from(targetPaths).map((p) => revalidatePage(p)),
418
+ ...Array.from(targetLayoutPaths).map((p) =>
419
+ isCascade ? revalidatePath(p, { cascade: true }) : revalidateLayout(p)
420
+ ),
421
+ ...Array.from(targetSlotKeys).map((k) => storage.delete(k)),
422
+ ]);
423
+ }
424
+ return;
425
+ }
426
+
427
+ const dist2Folder = path.resolve(process.cwd(), ".dinou/dist2");
428
+ if (!existsSync(dist2Folder)) return;
429
+
430
+ const metadataFiles = await walkMetadataFiles(dist2Folder);
431
+ const revalidatePromises = [];
432
+
433
+ for (const fileOfMeta of metadataFiles) {
434
+ try {
435
+ const content = await fs.readFile(fileOfMeta, "utf8");
436
+ const metadata = JSON.parse(content);
437
+ if (metadata && Array.isArray(metadata.tags) && metadata.tags.includes(tag)) {
438
+ if (path.basename(fileOfMeta) === "slot.metadata.json") {
439
+ const slotFolder = path.dirname(fileOfMeta);
440
+ console.log(`✅ [Revalidate] Invalidating cache slot directory: ${slotFolder}`);
441
+ revalidatePromises.push(
442
+ fs.rm(slotFolder, { recursive: true, force: true }).catch(() => {})
443
+ );
444
+ } else if (path.basename(fileOfMeta) === "layout.metadata.json") {
445
+ const relative = path.relative(dist2Folder, path.dirname(fileOfMeta));
446
+ const reqPath = "/" + relative.replace(/\\/g, "/");
447
+ if (isCascade) {
448
+ revalidatePromises.push(revalidatePath(reqPath, { cascade: true }));
449
+ } else {
450
+ revalidatePromises.push(revalidateLayout(reqPath));
451
+ }
452
+ } else {
453
+ const relative = path.relative(dist2Folder, path.dirname(fileOfMeta));
454
+ const reqPath = "/" + relative.replace(/\\/g, "/");
455
+ revalidatePromises.push(revalidatePage(reqPath));
456
+ }
457
+ }
458
+ } catch (err) {
459
+ console.error(`[Revalidate] Error reading tags from ${fileOfMeta}:`, err);
460
+ }
461
+ }
462
+
463
+ await Promise.all(revalidatePromises);
464
+ }
465
+
466
+ module.exports = {
467
+ revalidatePath,
468
+ revalidatePage,
469
+ revalidateTag,
470
+ revalidateLayout,
471
+ };