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
@@ -0,0 +1,522 @@
1
+ // dinou/core/storage-adapter.js
2
+ // Pluggable storage abstraction for ISR/ISG cache and static artifacts.
3
+ // Supports FileSystem (Node.js/Netlify/Docker), Cloudflare KV (Edge), and Memory.
4
+
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+
8
+ class StorageAdapter {
9
+ async get(key) {
10
+ throw new Error("StorageAdapter.get must be implemented");
11
+ }
12
+ async set(key, content, metadata = null) {
13
+ throw new Error("StorageAdapter.set must be implemented");
14
+ }
15
+ async has(key) {
16
+ throw new Error("StorageAdapter.has must be implemented");
17
+ }
18
+ async delete(key) {
19
+ throw new Error("StorageAdapter.delete must be implemented");
20
+ }
21
+ }
22
+
23
+ /**
24
+ * FileSystem storage adapter (Default for Node.js, Netlify, Docker).
25
+ * Reads and writes directly to .dinou/dist2.
26
+ */
27
+ class FileSystemStorage extends StorageAdapter {
28
+ constructor(baseDir = path.resolve(process.cwd(), ".dinou/dist2")) {
29
+ super();
30
+ this.baseDir = baseDir;
31
+ }
32
+
33
+ _resolve(key) {
34
+ let cleanKey = String(key || "")
35
+ .replace(/^\/+/, "")
36
+ .replace(/\\/g, "/")
37
+ .replace(/\/+$/, "");
38
+ if (!cleanKey) {
39
+ return path.join(this.baseDir, "index.html");
40
+ }
41
+ const ext = path.extname(cleanKey);
42
+ if (!ext) {
43
+ return path.join(this.baseDir, cleanKey, "index.html");
44
+ }
45
+ return path.join(this.baseDir, cleanKey);
46
+ }
47
+
48
+ async get(key) {
49
+ let targetPath = this._resolve(key);
50
+ if (fs.existsSync(targetPath) && fs.statSync(targetPath).isDirectory()) {
51
+ targetPath = path.join(targetPath, "index.html");
52
+ }
53
+ if (!fs.existsSync(targetPath)) return null;
54
+ const content = fs.readFileSync(targetPath, "utf8");
55
+
56
+ const cleanKey = String(key || "").replace(/\\/g, "/");
57
+ const isMetaJson = cleanKey.endsWith("metadata.json") || path.basename(targetPath).endsWith("metadata.json");
58
+
59
+ // If key itself is a metadata json file, its metadata IS its own parsed content
60
+ if (isMetaJson) {
61
+ let metadata = null;
62
+ try {
63
+ metadata = JSON.parse(content);
64
+ } catch (e) {}
65
+ return { content, metadata };
66
+ }
67
+
68
+ // Check companion metadata if available
69
+ let metadata = null;
70
+ const isLayoutKey = cleanKey.includes("layout.rsc") || cleanKey.includes("layout.metadata.json");
71
+ const isSlotKey = cleanKey.includes("slot");
72
+ const metaFileName = isLayoutKey
73
+ ? "layout.metadata.json"
74
+ : isSlotKey
75
+ ? "slot.metadata.json"
76
+ : "metadata.json";
77
+ const metaPath = path.join(path.dirname(targetPath), metaFileName);
78
+ if (fs.existsSync(metaPath)) {
79
+ try {
80
+ metadata = JSON.parse(fs.readFileSync(metaPath, "utf8"));
81
+ } catch (e) {}
82
+ }
83
+ return { content, metadata };
84
+ }
85
+
86
+ async set(key, content, metadata = null) {
87
+ let targetPath = this._resolve(key);
88
+ if (fs.existsSync(targetPath) && fs.statSync(targetPath).isDirectory()) {
89
+ targetPath = path.join(targetPath, "index.html");
90
+ }
91
+ const dir = path.dirname(targetPath);
92
+ if (!fs.existsSync(dir)) {
93
+ fs.mkdirSync(dir, { recursive: true });
94
+ }
95
+ fs.writeFileSync(targetPath, content, "utf8");
96
+
97
+ // Only write a companion metadata file if targetPath is NOT itself a metadata file
98
+ const cleanKey = String(key || "").replace(/\\/g, "/");
99
+ const isMetaJson = cleanKey.endsWith("metadata.json") || path.basename(targetPath).endsWith("metadata.json");
100
+ if (metadata && !isMetaJson) {
101
+ const isLayoutKey = cleanKey.includes("layout.rsc") || cleanKey.includes("layout.metadata.json");
102
+ const isSlotKey = cleanKey.includes("slot");
103
+ const metaFileName = isLayoutKey
104
+ ? "layout.metadata.json"
105
+ : isSlotKey
106
+ ? "slot.metadata.json"
107
+ : "metadata.json";
108
+ const metaPath = path.join(dir, metaFileName);
109
+ fs.writeFileSync(metaPath, JSON.stringify(metadata, null, 2), "utf8");
110
+ }
111
+ }
112
+
113
+ async has(key) {
114
+ let targetPath = this._resolve(key);
115
+ if (fs.existsSync(targetPath) && fs.statSync(targetPath).isDirectory()) {
116
+ targetPath = path.join(targetPath, "index.html");
117
+ }
118
+ return fs.existsSync(targetPath);
119
+ }
120
+
121
+ async delete(key) {
122
+ let targetPath = this._resolve(key);
123
+ if (fs.existsSync(targetPath)) {
124
+ fs.rmSync(targetPath, { force: true, recursive: true });
125
+ }
126
+ const isSlotKey = String(key || "").includes("slot");
127
+ if (isSlotKey) {
128
+ const dir = path.dirname(targetPath);
129
+ if (fs.existsSync(dir)) {
130
+ try {
131
+ fs.rmSync(dir, { force: true, recursive: true });
132
+ } catch (e) {}
133
+ }
134
+ }
135
+ }
136
+
137
+ async keys(prefix = "") {
138
+ if (!fs.existsSync(this.baseDir)) return [];
139
+ const results = [];
140
+ const walk = (dir, rel = "") => {
141
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
142
+ for (const entry of entries) {
143
+ const entryRel = rel ? `${rel}/${entry.name}` : entry.name;
144
+ if (entry.isDirectory()) {
145
+ walk(path.join(dir, entry.name), entryRel);
146
+ } else {
147
+ const cleanRel = entryRel.replace(/\\/g, "/");
148
+ if (!prefix || cleanRel.startsWith(prefix)) {
149
+ results.push(cleanRel);
150
+ }
151
+ }
152
+ }
153
+ };
154
+ walk(this.baseDir);
155
+ return results;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Cloudflare KV Storage Adapter for Cloudflare Workers.
161
+ */
162
+ class CloudflareKVStorage extends StorageAdapter {
163
+ constructor(kvNamespace) {
164
+ super();
165
+ this.kv = kvNamespace;
166
+ }
167
+
168
+ _cleanKey(key) {
169
+ return String(key).replace(/^\/+/, "").replace(/\\/g, "/").replace(/\/+$/, "");
170
+ }
171
+
172
+ async get(key) {
173
+ if (!this.kv) return null;
174
+ const cleanKey = this._cleanKey(key);
175
+ let res = await this.kv.getWithMetadata(cleanKey, "text");
176
+ if ((!res || res.value === null) && !cleanKey.includes(".")) {
177
+ res = await this.kv.getWithMetadata(cleanKey ? `${cleanKey}/index.html` : "index.html", "text");
178
+ }
179
+ if (!res || res.value === null) return null;
180
+ return {
181
+ content: res.value,
182
+ metadata: res.metadata || null,
183
+ };
184
+ }
185
+
186
+ async set(key, content, metadata = null) {
187
+ if (!this.kv) return;
188
+ const cleanKey = this._cleanKey(key);
189
+ const options = metadata ? { metadata } : undefined;
190
+ await this.kv.put(cleanKey, content, options);
191
+ if (cleanKey.endsWith("/index.html")) {
192
+ const folderKey = cleanKey.slice(0, -11);
193
+ await this.kv.put(folderKey, content, options);
194
+ }
195
+ }
196
+
197
+ async has(key) {
198
+ if (!this.kv) return false;
199
+ const cleanKey = this._cleanKey(key);
200
+ const val = await this.kv.get(cleanKey);
201
+ return val !== null;
202
+ }
203
+
204
+ async delete(key) {
205
+ if (!this.kv) return;
206
+ const cleanKey = this._cleanKey(key);
207
+ await this.kv.delete(cleanKey);
208
+ }
209
+
210
+ async keys(prefix = "") {
211
+ if (!this.kv || typeof this.kv.list !== "function") return [];
212
+ try {
213
+ const options = prefix ? { prefix } : undefined;
214
+ const res = await this.kv.list(options);
215
+ return res && res.keys ? res.keys.map((k) => k.name) : [];
216
+ } catch (e) {
217
+ return [];
218
+ }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * In-memory storage adapter.
224
+ */
225
+ class MemoryStorage extends StorageAdapter {
226
+ constructor() {
227
+ super();
228
+ this.store = new Map();
229
+ }
230
+
231
+ _cleanKey(key) {
232
+ return String(key).replace(/^\/+/, "").replace(/\\/g, "/").replace(/\/+$/, "");
233
+ }
234
+
235
+ async get(key) {
236
+ const cleanKey = this._cleanKey(key);
237
+ let item = this.store.get(cleanKey);
238
+ if (!item && !cleanKey.includes(".")) {
239
+ item = this.store.get(cleanKey ? `${cleanKey}/index.html` : "index.html");
240
+ }
241
+ if (!item) return null;
242
+ return { content: item.content, metadata: item.metadata };
243
+ }
244
+
245
+ async set(key, content, metadata = null) {
246
+ const cleanKey = this._cleanKey(key);
247
+ this.store.set(cleanKey, { content, metadata });
248
+ if (cleanKey.endsWith("/index.html")) {
249
+ const folderKey = cleanKey.slice(0, -11);
250
+ this.store.set(folderKey, { content, metadata });
251
+ }
252
+ }
253
+
254
+ async has(key) {
255
+ const cleanKey = this._cleanKey(key);
256
+ return this.store.has(cleanKey) || this.store.has(cleanKey ? `${cleanKey}/index.html` : "index.html");
257
+ }
258
+
259
+ async delete(key) {
260
+ const cleanKey = this._cleanKey(key);
261
+ this.store.delete(cleanKey);
262
+ this.store.delete(cleanKey ? `${cleanKey}/index.html` : "index.html");
263
+ }
264
+
265
+ keys() {
266
+ return Array.from(this.store.keys());
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Redis storage adapter for multi-server / clustered deployments.
272
+ * Shares ISR / ISG cache across multiple Node instances.
273
+ */
274
+ class RedisStorage extends StorageAdapter {
275
+ constructor(redisClient, prefix = "dinou:cache:") {
276
+ super();
277
+ this.redis = redisClient;
278
+ this.prefix = prefix;
279
+ }
280
+
281
+ _k(key) {
282
+ return this.prefix + key.replace(/^\/+/, "").replace(/\\/g, "/");
283
+ }
284
+
285
+ async get(key) {
286
+ if (!this.redis) return null;
287
+ const raw = await this.redis.get(this._k(key));
288
+ if (!raw) return null;
289
+ try {
290
+ return JSON.parse(raw);
291
+ } catch (e) {
292
+ return { content: raw, metadata: null };
293
+ }
294
+ }
295
+
296
+ async set(key, content, metadata = null) {
297
+ if (!this.redis) return;
298
+ const value = JSON.stringify({ content, metadata });
299
+ await this.redis.set(this._k(key), value);
300
+ }
301
+
302
+ async has(key) {
303
+ if (!this.redis) return false;
304
+ return (await this.redis.exists(this._k(key))) === 1;
305
+ }
306
+
307
+ async delete(key) {
308
+ if (!this.redis) return;
309
+ await this.redis.del(this._k(key));
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Deno KV Storage Adapter for Deno Deploy & Edge.
315
+ */
316
+ class DenoKVStorage extends StorageAdapter {
317
+ constructor(kvInstance = null) {
318
+ super();
319
+ this.kv = kvInstance;
320
+ }
321
+
322
+ async _getKV() {
323
+ if (!this.kv) {
324
+ if (typeof Deno !== "undefined" && typeof Deno.openKv === "function") {
325
+ const kvUrl = Deno.env.get("DENO_KV_URL");
326
+ if (kvUrl) {
327
+ this.kv = await Deno.openKv(kvUrl);
328
+ } else if (Deno.env.get("DENO_DEPLOYMENT_ID")) {
329
+ this.kv = await Deno.openKv();
330
+ } else {
331
+ const cwd = typeof Deno.cwd === "function" ? Deno.cwd() : process.cwd();
332
+ const kvPath = Deno.env.get("DENO_KV_PATH") || path.resolve(cwd, ".dinou/kv.db");
333
+ this.kv = await Deno.openKv(kvPath);
334
+ }
335
+ }
336
+ }
337
+ return this.kv;
338
+ }
339
+
340
+ _cleanKey(key) {
341
+ return String(key).replace(/^\/+/, "").replace(/\\/g, "/");
342
+ }
343
+
344
+ async get(key) {
345
+ const kv = await this._getKV();
346
+ if (!kv) return null;
347
+ const cleanKey = this._cleanKey(key);
348
+ let matchedKey = cleanKey;
349
+ let res = await kv.get(["dinou_cache", cleanKey]);
350
+ if ((!res || res.value === null) && !cleanKey.includes(".")) {
351
+ matchedKey = cleanKey ? `${cleanKey}/index.html` : "index.html";
352
+ res = await kv.get(["dinou_cache", matchedKey]);
353
+ }
354
+ if (!res || res.value === null) return null;
355
+ const val = res.value;
356
+ if (val && val.chunked) {
357
+ const chunkPromises = [];
358
+ for (let i = 0; i < val.totalChunks; i++) {
359
+ chunkPromises.push(kv.get(["dinou_cache_chunk", matchedKey, i]));
360
+ }
361
+ const chunkResults = await Promise.all(chunkPromises);
362
+ let fullContent = "";
363
+ for (const chunkRes of chunkResults) {
364
+ if (chunkRes && typeof chunkRes.value === "string") {
365
+ fullContent += chunkRes.value;
366
+ }
367
+ }
368
+ return {
369
+ content: fullContent,
370
+ metadata: val.metadata || null,
371
+ };
372
+ }
373
+ return {
374
+ content: typeof val === "string" ? val : (val && val.content !== undefined ? val.content : ""),
375
+ metadata: val && typeof val === "object" && val.metadata ? val.metadata : null,
376
+ };
377
+ }
378
+
379
+ async set(key, content, metadata = null) {
380
+ const kv = await this._getKV();
381
+ if (!kv) return;
382
+ const cleanKey = this._cleanKey(key);
383
+ const KV_CHUNK_SIZE = 16384;
384
+
385
+ const writeKey = async (targetKey) => {
386
+ if (typeof content === "string" && content.length > KV_CHUNK_SIZE) {
387
+ const totalChunks = Math.ceil(content.length / KV_CHUNK_SIZE);
388
+ const atomic = kv.atomic();
389
+ atomic.set(["dinou_cache", targetKey], {
390
+ chunked: true,
391
+ totalChunks,
392
+ metadata,
393
+ });
394
+ for (let i = 0; i < totalChunks; i++) {
395
+ const chunk = content.slice(i * KV_CHUNK_SIZE, (i + 1) * KV_CHUNK_SIZE);
396
+ atomic.set(["dinou_cache_chunk", targetKey, i], chunk);
397
+ }
398
+ await atomic.commit();
399
+ } else {
400
+ await kv.set(["dinou_cache", targetKey], {
401
+ chunked: false,
402
+ content,
403
+ metadata,
404
+ });
405
+ }
406
+ };
407
+
408
+ await writeKey(cleanKey);
409
+
410
+ if (cleanKey.endsWith("/index.html")) {
411
+ const folderKey = cleanKey.slice(0, -11);
412
+ await writeKey(folderKey);
413
+ } else if (cleanKey === "index.html") {
414
+ await writeKey("");
415
+ }
416
+ }
417
+
418
+ async has(key) {
419
+ const kv = await this._getKV();
420
+ if (!kv) return false;
421
+ const cleanKey = this._cleanKey(key);
422
+ const res = await kv.get(["dinou_cache", cleanKey]);
423
+ if (res && res.value !== null) return true;
424
+ if (!cleanKey.includes(".")) {
425
+ const fallbackRes = await kv.get(["dinou_cache", cleanKey ? `${cleanKey}/index.html` : "index.html"]);
426
+ return fallbackRes && fallbackRes.value !== null;
427
+ }
428
+ return false;
429
+ }
430
+
431
+ async delete(key) {
432
+ const kv = await this._getKV();
433
+ if (!kv) return;
434
+ const cleanKey = this._cleanKey(key);
435
+
436
+ const delKey = async (targetKey) => {
437
+ const res = await kv.get(["dinou_cache", targetKey]);
438
+ if (res && res.value && res.value.chunked) {
439
+ const atomic = kv.atomic();
440
+ for (let i = 0; i < res.value.totalChunks; i++) {
441
+ atomic.delete(["dinou_cache_chunk", targetKey, i]);
442
+ }
443
+ atomic.delete(["dinou_cache", targetKey]);
444
+ await atomic.commit();
445
+ } else {
446
+ await kv.delete(["dinou_cache", targetKey]);
447
+ }
448
+ };
449
+
450
+ await delKey(cleanKey);
451
+
452
+ if (cleanKey.endsWith("/index.html")) {
453
+ const folderKey = cleanKey.slice(0, -11);
454
+ await delKey(folderKey);
455
+ } else if (cleanKey === "index.html") {
456
+ await delKey("");
457
+ }
458
+ }
459
+
460
+ async keys(prefix = "dinou_cache") {
461
+ const kv = await this._getKV();
462
+ if (!kv) return [];
463
+ try {
464
+ const entries = kv.list({ prefix: [prefix] });
465
+ const result = [];
466
+ for await (const entry of entries) {
467
+ if (entry.key && entry.key.length >= 2 && typeof entry.key[1] === "string") {
468
+ result.push(entry.key[1]);
469
+ }
470
+ }
471
+ return result;
472
+ } catch (e) {
473
+ return [];
474
+ }
475
+ }
476
+ }
477
+
478
+ const DINOU_STORAGE_KEY = Symbol.for("dinou.storage.adapter");
479
+
480
+ function getStorageAdapter() {
481
+ if (globalThis[DINOU_STORAGE_KEY]) {
482
+ return globalThis[DINOU_STORAGE_KEY];
483
+ }
484
+ let adapter = null;
485
+ const runtime =
486
+ (typeof globalThis !== "undefined" && globalThis.__DINOU_RUNTIME__) ||
487
+ (typeof process !== "undefined" && process.env && process.env.DINOU_RUNTIME) ||
488
+ "";
489
+
490
+ const isEdge =
491
+ runtime === "edge" ||
492
+ runtime === "deno-edge" ||
493
+ (typeof runtime === "string" && runtime.includes("edge")) ||
494
+ (typeof Deno !== "undefined" && !runtime.includes("node"));
495
+
496
+ if (isEdge) {
497
+ if (typeof Deno !== "undefined" && typeof Deno.openKv === "function") {
498
+ adapter = new DenoKVStorage();
499
+ } else {
500
+ adapter = new MemoryStorage();
501
+ }
502
+ } else {
503
+ adapter = new FileSystemStorage();
504
+ }
505
+ globalThis[DINOU_STORAGE_KEY] = adapter;
506
+ return adapter;
507
+ }
508
+
509
+ function setStorageAdapter(adapter) {
510
+ globalThis[DINOU_STORAGE_KEY] = adapter;
511
+ }
512
+
513
+ module.exports = {
514
+ StorageAdapter,
515
+ FileSystemStorage,
516
+ CloudflareKVStorage,
517
+ DenoKVStorage,
518
+ MemoryStorage,
519
+ RedisStorage,
520
+ getStorageAdapter,
521
+ setStorageAdapter,
522
+ };
@@ -1,16 +1,16 @@
1
- function resolveRelativeUrl(href, currentPathname) {
2
- if (!href || typeof href !== "string") {
3
- return "/";
4
- }
5
- if (href.startsWith("/") || href.includes("://")) {
6
- return href;
7
- }
8
- let base = currentPathname || "/";
9
- if (!base.endsWith("/")) {
10
- base += "/";
11
- }
12
- const resolved = new URL(href, "http://localhost" + base);
13
- return resolved.pathname + resolved.search + resolved.hash;
14
- }
15
-
16
- module.exports = { resolveRelativeUrl };
1
+ function resolveRelativeUrl(href, currentPathname) {
2
+ if (!href || typeof href !== "string") {
3
+ return "/";
4
+ }
5
+ if (href.startsWith("/") || href.includes("://")) {
6
+ return href;
7
+ }
8
+ let base = currentPathname || "/";
9
+ if (!base.endsWith("/")) {
10
+ base += "/";
11
+ }
12
+ const resolved = new URL(href, "http://localhost" + base);
13
+ return resolved.pathname + resolved.search + resolved.hash;
14
+ }
15
+
16
+ module.exports = { resolveRelativeUrl };