@modern-js/runtime-utils 2.69.5 → 3.0.0-alpha.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/dist/cjs/browser/deferreds.js +141 -145
  2. package/dist/cjs/browser/index.js +62 -21
  3. package/dist/cjs/browser/nestedRoutes.js +121 -147
  4. package/dist/cjs/merge.js +51 -44
  5. package/dist/cjs/node/fileReader.js +88 -97
  6. package/dist/cjs/node/index.js +87 -33
  7. package/dist/cjs/node/loaderContext/createLoaderCtx.js +43 -36
  8. package/dist/cjs/node/loaderContext/createRequestCtx.js +43 -41
  9. package/dist/cjs/node/loaderContext/index.js +38 -29
  10. package/dist/cjs/node/serialize.js +46 -40
  11. package/dist/cjs/node/storer/container.js +62 -58
  12. package/dist/cjs/node/storer/index.js +39 -31
  13. package/dist/cjs/node/storer/storage.js +82 -92
  14. package/dist/cjs/node/stream.js +89 -91
  15. package/dist/cjs/parsed.js +40 -38
  16. package/dist/cjs/router.js +88 -19
  17. package/dist/cjs/rsc.js +58 -0
  18. package/dist/cjs/server/index.js +55 -19
  19. package/dist/cjs/server/nestedRoutes.js +43 -39
  20. package/dist/cjs/time.js +53 -47
  21. package/dist/cjs/universal/async_storage.js +53 -37
  22. package/dist/cjs/universal/async_storage.server.js +63 -74
  23. package/dist/cjs/universal/cache.js +330 -381
  24. package/dist/cjs/universal/request.js +73 -65
  25. package/dist/cjs/url.js +33 -28
  26. package/dist/esm/browser/deferreds.mjs +118 -0
  27. package/dist/esm/browser/nestedRoutes.mjs +104 -0
  28. package/dist/esm/merge.mjs +26 -0
  29. package/dist/esm/node/fileReader.mjs +47 -0
  30. package/dist/{esm-node/node/index.js → esm/node/index.mjs} +2 -5
  31. package/dist/esm/node/loaderContext/createLoaderCtx.mjs +14 -0
  32. package/dist/esm/node/loaderContext/createRequestCtx.mjs +16 -0
  33. package/dist/{esm-node/node/loaderContext/index.js → esm/node/loaderContext/index.mjs} +1 -4
  34. package/dist/esm/node/serialize.mjs +5 -0
  35. package/dist/esm/node/storer/container.mjs +38 -0
  36. package/dist/{esm-node/node/storer/index.js → esm/node/storer/index.mjs} +2 -6
  37. package/dist/esm/node/storer/storage.mjs +53 -0
  38. package/dist/esm/node/stream.mjs +68 -0
  39. package/dist/esm/parsed.mjs +12 -0
  40. package/dist/esm/router.mjs +18 -0
  41. package/dist/esm/rsc.mjs +1 -0
  42. package/dist/esm/server/nestedRoutes.mjs +15 -0
  43. package/dist/esm/time.mjs +27 -0
  44. package/dist/esm/universal/async_storage.mjs +7 -0
  45. package/dist/esm/universal/async_storage.server.mjs +32 -0
  46. package/dist/esm/universal/cache.mjs +326 -0
  47. package/dist/esm/universal/request.mjs +40 -0
  48. package/dist/esm/url.mjs +5 -0
  49. package/dist/esm-node/browser/deferreds.mjs +118 -0
  50. package/dist/esm-node/browser/index.mjs +2 -0
  51. package/dist/esm-node/browser/nestedRoutes.mjs +104 -0
  52. package/dist/esm-node/merge.mjs +26 -0
  53. package/dist/esm-node/node/fileReader.mjs +47 -0
  54. package/dist/esm-node/node/index.mjs +5 -0
  55. package/dist/esm-node/node/loaderContext/createLoaderCtx.mjs +14 -0
  56. package/dist/esm-node/node/loaderContext/createRequestCtx.mjs +16 -0
  57. package/dist/esm-node/node/loaderContext/index.mjs +4 -0
  58. package/dist/esm-node/node/serialize.mjs +5 -0
  59. package/dist/esm-node/node/storer/container.mjs +38 -0
  60. package/dist/esm-node/node/storer/index.mjs +7 -0
  61. package/dist/esm-node/node/storer/storage.mjs +53 -0
  62. package/dist/esm-node/node/stream.mjs +68 -0
  63. package/dist/esm-node/parsed.mjs +12 -0
  64. package/dist/esm-node/router.mjs +18 -0
  65. package/dist/esm-node/rsc.mjs +1 -0
  66. package/dist/esm-node/server/index.mjs +1 -0
  67. package/dist/esm-node/server/nestedRoutes.mjs +15 -0
  68. package/dist/esm-node/time.mjs +27 -0
  69. package/dist/esm-node/universal/async_storage.mjs +24 -0
  70. package/dist/esm-node/universal/async_storage.server.mjs +32 -0
  71. package/dist/esm-node/universal/cache.mjs +326 -0
  72. package/dist/esm-node/universal/request.mjs +40 -0
  73. package/dist/esm-node/url.mjs +5 -0
  74. package/dist/types/browser/nestedRoutes.d.ts +2 -1
  75. package/dist/types/node/stream.d.ts +1 -1
  76. package/dist/types/router.d.ts +6 -1
  77. package/dist/types/rsc.d.ts +1 -0
  78. package/dist/types/universal/async_storage.d.ts +1 -1
  79. package/dist/types/universal/async_storage.server.d.ts +1 -1
  80. package/package.json +36 -46
  81. package/rslib.config.mts +27 -0
  82. package/rstest.config.ts +26 -0
  83. package/dist/cjs/node/router.js +0 -22
  84. package/dist/cjs/remixRouter.js +0 -43
  85. package/dist/esm/browser/deferreds.js +0 -227
  86. package/dist/esm/browser/index.js +0 -2
  87. package/dist/esm/browser/nestedRoutes.js +0 -177
  88. package/dist/esm/merge.js +0 -33
  89. package/dist/esm/node/fileReader.js +0 -140
  90. package/dist/esm/node/index.js +0 -8
  91. package/dist/esm/node/loaderContext/createLoaderCtx.js +0 -24
  92. package/dist/esm/node/loaderContext/createRequestCtx.js +0 -24
  93. package/dist/esm/node/loaderContext/index.js +0 -7
  94. package/dist/esm/node/router.js +0 -1
  95. package/dist/esm/node/serialize.js +0 -9
  96. package/dist/esm/node/storer/container.js +0 -94
  97. package/dist/esm/node/storer/index.js +0 -11
  98. package/dist/esm/node/storer/storage.js +0 -135
  99. package/dist/esm/node/stream.js +0 -87
  100. package/dist/esm/parsed.js +0 -20
  101. package/dist/esm/remixRouter.js +0 -7
  102. package/dist/esm/router.js +0 -1
  103. package/dist/esm/server/index.js +0 -1
  104. package/dist/esm/server/nestedRoutes.js +0 -23
  105. package/dist/esm/time.js +0 -32
  106. package/dist/esm/universal/async_storage.js +0 -18
  107. package/dist/esm/universal/async_storage.server.js +0 -43
  108. package/dist/esm/universal/cache.js +0 -1032
  109. package/dist/esm/universal/request.js +0 -51
  110. package/dist/esm/url.js +0 -10
  111. package/dist/esm-node/browser/deferreds.js +0 -138
  112. package/dist/esm-node/browser/nestedRoutes.js +0 -142
  113. package/dist/esm-node/merge.js +0 -29
  114. package/dist/esm-node/node/fileReader.js +0 -68
  115. package/dist/esm-node/node/loaderContext/createLoaderCtx.js +0 -19
  116. package/dist/esm-node/node/loaderContext/createRequestCtx.js +0 -24
  117. package/dist/esm-node/node/router.js +0 -1
  118. package/dist/esm-node/node/serialize.js +0 -9
  119. package/dist/esm-node/node/storer/container.js +0 -44
  120. package/dist/esm-node/node/storer/storage.js +0 -73
  121. package/dist/esm-node/node/stream.js +0 -80
  122. package/dist/esm-node/parsed.js +0 -20
  123. package/dist/esm-node/remixRouter.js +0 -7
  124. package/dist/esm-node/router.js +0 -1
  125. package/dist/esm-node/server/nestedRoutes.js +0 -21
  126. package/dist/esm-node/time.js +0 -31
  127. package/dist/esm-node/universal/async_storage.js +0 -18
  128. package/dist/esm-node/universal/async_storage.server.js +0 -45
  129. package/dist/esm-node/universal/cache.js +0 -401
  130. package/dist/esm-node/universal/request.js +0 -50
  131. package/dist/esm-node/url.js +0 -10
  132. package/dist/types/node/router.d.ts +0 -1
  133. package/dist/types/remixRouter.d.ts +0 -2
  134. /package/dist/{esm-node/browser/index.js → esm/browser/index.mjs} +0 -0
  135. /package/dist/{esm-node/server/index.js → esm/server/index.mjs} +0 -0
@@ -0,0 +1,53 @@
1
+ class Storage {
2
+ async keys() {
3
+ const _keys = [];
4
+ this.forEach?.((_, k)=>{
5
+ _keys.push(k);
6
+ });
7
+ return _keys;
8
+ }
9
+ async values() {
10
+ const _values = [];
11
+ this.forEach?.((v)=>{
12
+ _values.push(v);
13
+ });
14
+ return _values;
15
+ }
16
+ get(key) {
17
+ const uniqueKey = this.computedUniqueKey(key);
18
+ return this.container.get(uniqueKey);
19
+ }
20
+ async set(key, value) {
21
+ const uniqueKey = this.computedUniqueKey(key);
22
+ await this.container.set(uniqueKey, value);
23
+ return this;
24
+ }
25
+ has(key) {
26
+ const uniqueKey = this.computedUniqueKey(key);
27
+ return this.container.has(uniqueKey);
28
+ }
29
+ delete(key) {
30
+ const uniqueKey = this.computedUniqueKey(key);
31
+ return this.container.delete(uniqueKey);
32
+ }
33
+ async clear() {
34
+ const keys = await this.keys?.();
35
+ await Promise.all(keys?.map(async (key)=>this.container.delete(key)) || []);
36
+ }
37
+ forEach(fallbackFn) {
38
+ this.container.forEach?.((v, k)=>{
39
+ if (this.checkIsOwnkey(k)) fallbackFn(v, k, this);
40
+ });
41
+ }
42
+ computedUniqueKey(k) {
43
+ return `${this.namespace}:${k}`;
44
+ }
45
+ checkIsOwnkey(k) {
46
+ return k.startsWith(this.namespace);
47
+ }
48
+ constructor(namespace, container){
49
+ this.namespace = namespace;
50
+ this.container = container;
51
+ }
52
+ }
53
+ export { Storage };
@@ -0,0 +1,68 @@
1
+ import { Stream } from "stream";
2
+ const createReadableStreamFromReadable = (source)=>{
3
+ const pump = new StreamPump(source);
4
+ const stream = new ReadableStream(pump, pump);
5
+ return stream;
6
+ };
7
+ class StreamPump {
8
+ size(chunk) {
9
+ return chunk?.byteLength || 0;
10
+ }
11
+ start(controller) {
12
+ this.controller = controller;
13
+ this.stream.on('data', this.enqueue);
14
+ this.stream.once('error', this.error);
15
+ this.stream.once('end', this.close);
16
+ this.stream.once('close', this.close);
17
+ }
18
+ pull() {
19
+ this.resume();
20
+ }
21
+ cancel(reason) {
22
+ if (this.stream.destroy) this.stream.destroy(reason);
23
+ process.nextTick(()=>{
24
+ this.stream.off('data', this.enqueue);
25
+ this.stream.off('error', this.error);
26
+ this.stream.off('end', this.close);
27
+ this.stream.off('close', this.close);
28
+ });
29
+ }
30
+ enqueue(chunk) {
31
+ if (this.controller) try {
32
+ const bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);
33
+ const available = (this.controller.desiredSize || 0) - bytes.byteLength;
34
+ this.controller.enqueue(bytes);
35
+ if (available <= 0) this.pause();
36
+ } catch (error) {
37
+ this.controller.error(new Error('Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object'));
38
+ this.cancel();
39
+ }
40
+ }
41
+ pause() {
42
+ if (this.stream.pause) this.stream.pause();
43
+ }
44
+ resume() {
45
+ if (this.stream.readable && this.stream.resume) this.stream.resume();
46
+ }
47
+ close() {
48
+ if (this.controller) {
49
+ this.controller.close();
50
+ delete this.controller;
51
+ }
52
+ }
53
+ error(error) {
54
+ if (this.controller) {
55
+ this.controller.error(error);
56
+ delete this.controller;
57
+ }
58
+ }
59
+ constructor(stream){
60
+ this.highWaterMark = stream.readableHighWaterMark || new Stream.Readable().readableHighWaterMark;
61
+ this.accumalatedSize = 0;
62
+ this.stream = stream;
63
+ this.enqueue = this.enqueue.bind(this);
64
+ this.error = this.error.bind(this);
65
+ this.close = this.close.bind(this);
66
+ }
67
+ }
68
+ export { createReadableStreamFromReadable };
@@ -0,0 +1,12 @@
1
+ const parsedJSONFromElement = (id)=>{
2
+ const elements = document.querySelectorAll(`#${id}`);
3
+ if (0 === elements.length) return;
4
+ const element = elements[elements.length - 1];
5
+ if (element) try {
6
+ const parsed = JSON.parse(element?.textContent || '');
7
+ return parsed;
8
+ } catch (e) {
9
+ console.error(`parse ${id} error`, e);
10
+ }
11
+ };
12
+ export { parsedJSONFromElement };
@@ -0,0 +1,18 @@
1
+ export * from "react-router";
2
+ const DEFERRED_SYMBOL = Symbol('deferred');
3
+ const json = (data, init)=>{
4
+ const responseInit = init ? 'number' == typeof init ? {
5
+ status: init
6
+ } : init : void 0;
7
+ return new Response(JSON.stringify(data), responseInit);
8
+ };
9
+ const defer = (data, init)=>{
10
+ if (void 0 === init) return data;
11
+ {
12
+ const responseInit = init ? 'number' == typeof init ? {
13
+ status: init
14
+ } : init : void 0;
15
+ return new Response(JSON.stringify(data), responseInit);
16
+ }
17
+ };
18
+ export { DEFERRED_SYMBOL, defer, json };
@@ -0,0 +1 @@
1
+ export * from "react-router";
@@ -0,0 +1,15 @@
1
+ function sortByUrlPath(entries) {
2
+ entries.sort(function(a, b) {
3
+ const length1 = a.urlPath.length;
4
+ const length2 = b.urlPath.length;
5
+ if (length1 < length2) return 1;
6
+ if (length1 > length2) return -1;
7
+ return 0;
8
+ });
9
+ return entries;
10
+ }
11
+ const matchEntry = (pathname, entries)=>{
12
+ sortByUrlPath(entries);
13
+ return entries.find((entry)=>pathname.startsWith(entry.urlPath));
14
+ };
15
+ export { matchEntry };
@@ -0,0 +1,27 @@
1
+ function processHrtime(previousTimestamp) {
2
+ const now = new Date().getTime();
3
+ const clocktime = 1e-3 * now;
4
+ let seconds = Math.floor(clocktime);
5
+ let nanoseconds = Math.floor(clocktime % 1 * 1e9);
6
+ if (previousTimestamp) {
7
+ seconds -= previousTimestamp[0];
8
+ nanoseconds -= previousTimestamp[1];
9
+ if (nanoseconds < 0) {
10
+ seconds--;
11
+ nanoseconds += 1e9;
12
+ }
13
+ }
14
+ return [
15
+ seconds,
16
+ nanoseconds
17
+ ];
18
+ }
19
+ const getLatency = (hrtime)=>{
20
+ const [s, ns] = processHrtime(hrtime);
21
+ return 1e3 * s + ns / 1e6;
22
+ };
23
+ const time = ()=>{
24
+ const hrtime = processHrtime();
25
+ return ()=>getLatency(hrtime);
26
+ };
27
+ export { time };
@@ -0,0 +1,7 @@
1
+ const isBrowser = "u" > typeof window && void 0 !== window.document;
2
+ const getAsyncLocalStorage = async ()=>{
3
+ if (!isBrowser) return null;
4
+ console.error('You should not get async storage in browser');
5
+ return null;
6
+ };
7
+ export { getAsyncLocalStorage };
@@ -0,0 +1,32 @@
1
+ import { AsyncLocalStorage } from "async_hooks";
2
+ const createStorage = ()=>{
3
+ let storage;
4
+ if (void 0 !== AsyncLocalStorage) storage = new AsyncLocalStorage();
5
+ const run = (context, cb)=>{
6
+ if (!storage) throw new Error(`Unable to use async_hook, please confirm the node version >= 12.17
7
+ `);
8
+ return new Promise((resolve, reject)=>{
9
+ storage.run(context, ()=>{
10
+ try {
11
+ return resolve(cb());
12
+ } catch (error) {
13
+ return reject(error);
14
+ }
15
+ });
16
+ });
17
+ };
18
+ const useContext = ()=>{
19
+ if (!storage) throw new Error(`Unable to use async_hook, please confirm the node version >= 12.17
20
+ `);
21
+ const context = storage?.getStore();
22
+ if (!context) throw new Error("Can't call useContext out of scope, make sure @modern-js/runtime-utils is a single version in node_modules");
23
+ return context;
24
+ };
25
+ return {
26
+ run,
27
+ useContext
28
+ };
29
+ };
30
+ const async_storage_server_storage = createStorage();
31
+ const getAsyncLocalStorage = async ()=>async_storage_server_storage;
32
+ export { getAsyncLocalStorage, async_storage_server_storage as storage };
@@ -0,0 +1,326 @@
1
+ import { LRUCache } from "lru-cache";
2
+ import { getAsyncLocalStorage } from "./async_storage";
3
+ const CacheSize = {
4
+ KB: 1024,
5
+ MB: 1048576,
6
+ GB: 1073741824
7
+ };
8
+ const CacheTime = {
9
+ SECOND: 1000,
10
+ MINUTE: 60000,
11
+ HOUR: 3600000,
12
+ DAY: 86400000,
13
+ WEEK: 604800000,
14
+ MONTH: 2592000000
15
+ };
16
+ function estimateObjectSize(data) {
17
+ const type = typeof data;
18
+ if ('number' === type) return 8;
19
+ if ('boolean' === type) return 4;
20
+ if ('string' === type) return Math.max(2 * data.length, 1);
21
+ if (null == data) return 1;
22
+ if (ArrayBuffer.isView(data)) return Math.max(data.byteLength, 1);
23
+ if (Array.isArray(data)) return Math.max(data.reduce((acc, item)=>acc + estimateObjectSize(item), 0), 1);
24
+ if (data instanceof Map || data instanceof Set) return 1024;
25
+ if (data instanceof Date) return 8;
26
+ if ('object' === type) return Math.max(Object.entries(data).reduce((acc, [key, value])=>acc + 2 * key.length + estimateObjectSize(value), 0), 1);
27
+ return 1;
28
+ }
29
+ class MemoryContainer {
30
+ async get(key) {
31
+ return this.lru.get(key);
32
+ }
33
+ async set(key, value, options) {
34
+ if (options?.ttl) this.lru.set(key, value, {
35
+ ttl: 1000 * options.ttl
36
+ });
37
+ else this.lru.set(key, value);
38
+ }
39
+ async has(key) {
40
+ return this.lru.has(key);
41
+ }
42
+ async delete(key) {
43
+ return this.lru.delete(key);
44
+ }
45
+ async clear() {
46
+ this.lru.clear();
47
+ }
48
+ constructor(options){
49
+ this.lru = new LRUCache({
50
+ maxSize: options?.maxSize ?? CacheSize.GB,
51
+ sizeCalculation: estimateObjectSize,
52
+ updateAgeOnGet: true,
53
+ updateAgeOnHas: true
54
+ });
55
+ }
56
+ }
57
+ const isServer = "u" < typeof window;
58
+ const requestCacheMap = new WeakMap();
59
+ const TAG_PREFIX = 'tag:';
60
+ const CACHE_PREFIX = 'modernjs_cache:';
61
+ const ongoingRevalidations = new Map();
62
+ let cache_storage;
63
+ let cacheConfig = {
64
+ maxSize: CacheSize.GB
65
+ };
66
+ function getStorage() {
67
+ if (cache_storage) return cache_storage;
68
+ cache_storage = cacheConfig.container ? cacheConfig.container : new MemoryContainer({
69
+ maxSize: cacheConfig.maxSize
70
+ });
71
+ return cache_storage;
72
+ }
73
+ function configureCache(config) {
74
+ cacheConfig = {
75
+ ...cacheConfig,
76
+ ...config
77
+ };
78
+ cache_storage = void 0;
79
+ }
80
+ function generateKey(args) {
81
+ return JSON.stringify(args, (_, value)=>{
82
+ if (value && 'object' == typeof value && !Array.isArray(value)) return Object.keys(value).sort().reduce((result, key)=>{
83
+ result[key] = value[key];
84
+ return result;
85
+ }, {});
86
+ return value;
87
+ });
88
+ }
89
+ function generateStableFunctionId(fn) {
90
+ const fnString = fn.toString();
91
+ let hash = 0;
92
+ for(let i = 0; i < fnString.length; i++){
93
+ const char = fnString.charCodeAt(i);
94
+ hash = (hash << 5) - hash + char;
95
+ hash &= hash;
96
+ }
97
+ return `fn_${fn.name || 'anonymous'}_${Math.abs(hash).toString(36)}`;
98
+ }
99
+ function cache(fn, options) {
100
+ return async (...args)=>{
101
+ if (isServer && void 0 === options) {
102
+ const storage = await getAsyncLocalStorage();
103
+ const request = storage?.useContext()?.request;
104
+ if (request) {
105
+ let shouldDisableCaching = false;
106
+ if (cacheConfig.unstable_shouldDisable) shouldDisableCaching = await cacheConfig.unstable_shouldDisable({
107
+ request
108
+ });
109
+ if (shouldDisableCaching) return fn(...args);
110
+ let requestCache = requestCacheMap.get(request);
111
+ if (!requestCache) {
112
+ requestCache = new Map();
113
+ requestCacheMap.set(request, requestCache);
114
+ }
115
+ let fnCache = requestCache.get(fn);
116
+ if (!fnCache) {
117
+ fnCache = new Map();
118
+ requestCache.set(fn, fnCache);
119
+ }
120
+ const key = generateKey(args);
121
+ if (fnCache.has(key)) return fnCache.get(key);
122
+ const promise = fn(...args);
123
+ fnCache.set(key, promise);
124
+ try {
125
+ const data = await promise;
126
+ return data;
127
+ } catch (error) {
128
+ fnCache.delete(key);
129
+ throw error;
130
+ }
131
+ }
132
+ } else if (void 0 !== options) try {
133
+ const { tag, maxAge = 5 * CacheTime.MINUTE, revalidate = 0, customKey, onCache, getKey, unstable_shouldCache } = options;
134
+ let missReason;
135
+ const currentStorage = getStorage();
136
+ const now = Date.now();
137
+ const tags = tag ? Array.isArray(tag) ? tag : [
138
+ tag
139
+ ] : [];
140
+ const genKey = getKey ? getKey(...args) : generateKey(args);
141
+ let finalKey;
142
+ if (customKey) finalKey = customKey({
143
+ params: args,
144
+ fn,
145
+ generatedKey: genKey
146
+ });
147
+ else {
148
+ const functionId = generateStableFunctionId(fn);
149
+ finalKey = `${functionId}:${genKey}`;
150
+ }
151
+ const storageKey = `${CACHE_PREFIX}${finalKey}`;
152
+ let shouldDisableCaching = false;
153
+ if (isServer && cacheConfig.unstable_shouldDisable) {
154
+ const asyncStorage = await getAsyncLocalStorage();
155
+ const request = asyncStorage?.useContext()?.request;
156
+ if (request) shouldDisableCaching = await cacheConfig.unstable_shouldDisable({
157
+ request
158
+ });
159
+ }
160
+ if (shouldDisableCaching) missReason = 1;
161
+ else {
162
+ const cached = await currentStorage.get(storageKey);
163
+ if (cached) try {
164
+ const cacheItem = cached;
165
+ const age = now - cacheItem.timestamp;
166
+ if (age < maxAge) {
167
+ onCache?.({
168
+ status: 'hit',
169
+ key: finalKey,
170
+ params: args,
171
+ result: cacheItem.data
172
+ });
173
+ return cacheItem.data;
174
+ }
175
+ if (revalidate > 0 && age < maxAge + revalidate) {
176
+ onCache?.({
177
+ status: 'stale',
178
+ key: finalKey,
179
+ params: args,
180
+ result: cacheItem.data
181
+ });
182
+ if (!ongoingRevalidations.has(storageKey)) {
183
+ const revalidationPromise = (async ()=>{
184
+ try {
185
+ const newData = await fn(...args);
186
+ let shouldCache = true;
187
+ if (unstable_shouldCache) shouldCache = await unstable_shouldCache({
188
+ params: args,
189
+ result: newData
190
+ });
191
+ if (shouldCache) await setCacheItem(currentStorage, storageKey, newData, tags, maxAge, revalidate);
192
+ } catch (error) {
193
+ if (isServer) {
194
+ const asyncStorage = await getAsyncLocalStorage();
195
+ asyncStorage?.useContext()?.monitors?.error(error.message);
196
+ } else console.error('Background revalidation failed:', error);
197
+ } finally{
198
+ ongoingRevalidations.delete(storageKey);
199
+ }
200
+ })();
201
+ ongoingRevalidations.set(storageKey, revalidationPromise);
202
+ }
203
+ return cacheItem.data;
204
+ }
205
+ missReason = 3;
206
+ } catch (error) {
207
+ console.warn('Failed to parse cached data:', error);
208
+ missReason = 4;
209
+ }
210
+ else missReason = 2;
211
+ }
212
+ const data = await fn(...args);
213
+ if (!shouldDisableCaching) {
214
+ let shouldCache = true;
215
+ if (unstable_shouldCache) shouldCache = await unstable_shouldCache({
216
+ params: args,
217
+ result: data
218
+ });
219
+ if (shouldCache) await setCacheItem(currentStorage, storageKey, data, tags, maxAge, revalidate);
220
+ }
221
+ onCache?.({
222
+ status: 'miss',
223
+ key: finalKey,
224
+ params: args,
225
+ result: data,
226
+ reason: missReason
227
+ });
228
+ return data;
229
+ } catch (error) {
230
+ console.warn('Cache operation failed, falling back to direct execution:', error);
231
+ const data = await fn(...args);
232
+ const { onCache } = options;
233
+ try {
234
+ onCache?.({
235
+ status: 'miss',
236
+ key: 'cache_failed',
237
+ params: args,
238
+ result: data,
239
+ reason: 5
240
+ });
241
+ } catch (callbackError) {
242
+ console.warn('Failed to call onCache callback:', callbackError);
243
+ }
244
+ return data;
245
+ }
246
+ else {
247
+ console.warn('The cache function will not work because it runs on the browser and there are no options are provided.');
248
+ return fn(...args);
249
+ }
250
+ };
251
+ }
252
+ async function setCacheItem(storage, storageKey, data, tags, maxAge, revalidate) {
253
+ const newItem = {
254
+ data,
255
+ timestamp: Date.now(),
256
+ tags: tags.length > 0 ? tags : void 0
257
+ };
258
+ const ttl = (maxAge + revalidate) / 1000;
259
+ await storage.set(storageKey, newItem, {
260
+ ttl: ttl > 0 ? ttl : void 0
261
+ });
262
+ await updateTagRelationships(storage, storageKey, tags);
263
+ }
264
+ async function updateTagRelationships(storage, storageKey, tags) {
265
+ for (const tag of tags){
266
+ const tagStoreKey = `${TAG_PREFIX}${tag}`;
267
+ const keyList = await storage.get(tagStoreKey);
268
+ const keyArray = keyList || [];
269
+ if (!keyArray.includes(storageKey)) keyArray.push(storageKey);
270
+ await storage.set(tagStoreKey, keyArray);
271
+ }
272
+ }
273
+ async function removeKeyFromTags(storage, storageKey, tags) {
274
+ for (const tag of tags){
275
+ const tagStoreKey = `${TAG_PREFIX}${tag}`;
276
+ const keyList = await storage.get(tagStoreKey);
277
+ if (keyList) try {
278
+ const keyArray = Array.isArray(keyList) ? keyList : [];
279
+ const updatedKeyList = keyArray.filter((key)=>key !== storageKey);
280
+ if (updatedKeyList.length > 0) await storage.set(tagStoreKey, updatedKeyList);
281
+ else await storage.delete(tagStoreKey);
282
+ } catch (error) {
283
+ console.warn(`Failed to process tag key list for tag ${tag}:`, error);
284
+ }
285
+ }
286
+ }
287
+ function withRequestCache(handler) {
288
+ if (!isServer) return handler;
289
+ return async (req, ...args)=>{
290
+ const storage = await getAsyncLocalStorage();
291
+ return storage.run({
292
+ request: req
293
+ }, ()=>handler(req, ...args));
294
+ };
295
+ }
296
+ async function revalidateTag(tag) {
297
+ const currentStorage = getStorage();
298
+ const tagStoreKey = `${TAG_PREFIX}${tag}`;
299
+ const keyList = await currentStorage.get(tagStoreKey);
300
+ if (keyList) try {
301
+ const keyArray = Array.isArray(keyList) ? keyList : [];
302
+ for (const cacheKey of keyArray){
303
+ const cached = await currentStorage.get(cacheKey);
304
+ if (cached) try {
305
+ const cacheItem = cached;
306
+ if (cacheItem.tags) {
307
+ const otherTags = cacheItem.tags.filter((t)=>t !== tag);
308
+ await removeKeyFromTags(currentStorage, cacheKey, otherTags);
309
+ }
310
+ } catch (error) {
311
+ console.warn('Failed to parse cached data while revalidating:', error);
312
+ }
313
+ await currentStorage.delete(cacheKey);
314
+ }
315
+ await currentStorage.delete(tagStoreKey);
316
+ } catch (error) {
317
+ console.warn('Failed to process tag key list:', error);
318
+ }
319
+ }
320
+ async function clearStore() {
321
+ const currentStorage = getStorage();
322
+ await currentStorage.clear();
323
+ cache_storage = void 0;
324
+ ongoingRevalidations.clear();
325
+ }
326
+ export { CacheSize, CacheTime, cache, clearStore, configureCache, generateKey, revalidateTag, withRequestCache };
@@ -0,0 +1,40 @@
1
+ function parseQuery(req) {
2
+ const query = {};
3
+ const { url } = req;
4
+ const q = url.split('?')[1];
5
+ if (q) {
6
+ const search = new URLSearchParams(q);
7
+ search.forEach((v, k)=>{
8
+ query[k] = v;
9
+ });
10
+ }
11
+ return query;
12
+ }
13
+ function parseHeaders(request) {
14
+ const headersData = {};
15
+ request.headers.forEach((value, key)=>{
16
+ headersData[key] = value;
17
+ });
18
+ return headersData;
19
+ }
20
+ function getPathname(request) {
21
+ const match = request.url.match(/^https?:\/\/[^/]+(\/[^?]*)/);
22
+ return match ? match[1] : '/';
23
+ }
24
+ function getHost(request) {
25
+ const { headers } = request;
26
+ let host = headers.get('X-Forwarded-Host');
27
+ if (!host) host = headers.get('Host');
28
+ host = host?.split(/\s*,\s*/, 1)[0] || 'undefined';
29
+ return host;
30
+ }
31
+ function parseCookie(req) {
32
+ const _cookie = req.headers.get('Cookie');
33
+ const cookie = {};
34
+ _cookie?.trim().split(';').forEach((item)=>{
35
+ const [k, v] = item.trim().split('=');
36
+ if (k) cookie[k] = v;
37
+ });
38
+ return cookie;
39
+ }
40
+ export { getHost, getPathname, parseCookie, parseHeaders, parseQuery };
@@ -0,0 +1,5 @@
1
+ function normalizePathname(pathname) {
2
+ const normalized = '/' + pathname.replace(/^\/+|\/+$/g, '');
3
+ return normalized;
4
+ }
5
+ export { normalizePathname };