@modern-js/runtime-utils 2.65.1 → 2.65.3-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.
- package/dist/cjs/browser/nestedRoutes.js +11 -11
- package/dist/cjs/node/index.js +3 -6
- package/dist/cjs/node/storer/container.js +8 -15
- package/dist/cjs/universal/async_storage.js +31 -0
- package/dist/cjs/{node/storage.js → universal/async_storage.server.js} +18 -12
- package/dist/cjs/universal/cache.js +247 -0
- package/dist/esm/browser/nestedRoutes.js +12 -12
- package/dist/esm/node/index.js +2 -4
- package/dist/esm/node/storer/container.js +8 -5
- package/dist/esm/universal/async_storage.js +7 -0
- package/dist/esm/{node/storage.js → universal/async_storage.server.js} +13 -8
- package/dist/esm/universal/cache.js +355 -0
- package/dist/esm-node/browser/nestedRoutes.js +11 -11
- package/dist/esm-node/node/index.js +2 -4
- package/dist/esm-node/node/storer/container.js +8 -5
- package/dist/esm-node/universal/async_storage.js +7 -0
- package/dist/esm-node/{node/storage.js → universal/async_storage.server.js} +13 -8
- package/dist/esm-node/universal/cache.js +216 -0
- package/dist/types/browser/nestedRoutes.d.ts +2 -3
- package/dist/types/node/index.d.ts +1 -4
- package/dist/types/node/loaderContext/index.d.ts +1 -13
- package/dist/types/node/storer/container.d.ts +1 -1
- package/dist/types/node/storer/index.d.ts +1 -1
- package/dist/types/server/nestedRoutes.d.ts +1 -2
- package/dist/types/universal/async_storage.d.ts +2 -0
- package/dist/types/universal/async_storage.server.d.ts +18 -0
- package/dist/types/universal/cache.d.ts +28 -0
- package/package.json +15 -10
- package/dist/types/node/storage.d.ts +0 -5
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { LRUCache } from "lru-cache";
|
|
2
|
+
import { getAsyncLocalStorage } from "./async_storage";
|
|
3
|
+
const CacheSize = {
|
|
4
|
+
KB: 1024,
|
|
5
|
+
MB: 1024 * 1024,
|
|
6
|
+
GB: 1024 * 1024 * 1024
|
|
7
|
+
};
|
|
8
|
+
const CacheTime = {
|
|
9
|
+
SECOND: 1e3,
|
|
10
|
+
MINUTE: 60 * 1e3,
|
|
11
|
+
HOUR: 60 * 60 * 1e3,
|
|
12
|
+
DAY: 24 * 60 * 60 * 1e3,
|
|
13
|
+
WEEK: 7 * 24 * 60 * 60 * 1e3,
|
|
14
|
+
MONTH: 30 * 24 * 60 * 60 * 1e3
|
|
15
|
+
};
|
|
16
|
+
const isServer = typeof window === "undefined";
|
|
17
|
+
const requestCacheMap = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
let lruCache;
|
|
19
|
+
let cacheConfig = {
|
|
20
|
+
maxSize: CacheSize.GB
|
|
21
|
+
};
|
|
22
|
+
const tagFnMap = /* @__PURE__ */ new Map();
|
|
23
|
+
function addTagFnRelation(tag, fn) {
|
|
24
|
+
let fns = tagFnMap.get(tag);
|
|
25
|
+
if (!fns) {
|
|
26
|
+
fns = /* @__PURE__ */ new Set();
|
|
27
|
+
tagFnMap.set(tag, fns);
|
|
28
|
+
}
|
|
29
|
+
fns.add(fn);
|
|
30
|
+
}
|
|
31
|
+
function configureCache(config) {
|
|
32
|
+
cacheConfig = {
|
|
33
|
+
...cacheConfig,
|
|
34
|
+
...config
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function getLRUCache() {
|
|
38
|
+
if (!lruCache) {
|
|
39
|
+
lruCache = new LRUCache({
|
|
40
|
+
maxSize: cacheConfig.maxSize,
|
|
41
|
+
sizeCalculation: (value) => {
|
|
42
|
+
if (!value.size) {
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
let size = 0;
|
|
46
|
+
for (const [k, item] of value.entries()) {
|
|
47
|
+
size += k.length * 2;
|
|
48
|
+
size += estimateObjectSize(item.data);
|
|
49
|
+
size += 8;
|
|
50
|
+
}
|
|
51
|
+
return size;
|
|
52
|
+
},
|
|
53
|
+
updateAgeOnGet: true,
|
|
54
|
+
updateAgeOnHas: true
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return lruCache;
|
|
58
|
+
}
|
|
59
|
+
function estimateObjectSize(data) {
|
|
60
|
+
const type = typeof data;
|
|
61
|
+
if (type === "number")
|
|
62
|
+
return 8;
|
|
63
|
+
if (type === "boolean")
|
|
64
|
+
return 4;
|
|
65
|
+
if (type === "string")
|
|
66
|
+
return Math.max(data.length * 2, 1);
|
|
67
|
+
if (data === null || data === void 0)
|
|
68
|
+
return 1;
|
|
69
|
+
if (ArrayBuffer.isView(data)) {
|
|
70
|
+
return Math.max(data.byteLength, 1);
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(data)) {
|
|
73
|
+
return Math.max(data.reduce((acc, item) => acc + estimateObjectSize(item), 0), 1);
|
|
74
|
+
}
|
|
75
|
+
if (data instanceof Map || data instanceof Set) {
|
|
76
|
+
return 1024;
|
|
77
|
+
}
|
|
78
|
+
if (data instanceof Date) {
|
|
79
|
+
return 8;
|
|
80
|
+
}
|
|
81
|
+
if (type === "object") {
|
|
82
|
+
return Math.max(Object.entries(data).reduce((acc, [key, value]) => acc + key.length * 2 + estimateObjectSize(value), 0), 1);
|
|
83
|
+
}
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
function generateKey(args) {
|
|
87
|
+
return JSON.stringify(args, (_, value) => {
|
|
88
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
89
|
+
return Object.keys(value).sort().reduce((result, key) => {
|
|
90
|
+
result[key] = value[key];
|
|
91
|
+
return result;
|
|
92
|
+
}, {});
|
|
93
|
+
}
|
|
94
|
+
return value;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function cache(fn, options) {
|
|
98
|
+
const { tag = "default", maxAge = CacheTime.MINUTE * 5, revalidate = 0 } = options || {};
|
|
99
|
+
const store = getLRUCache();
|
|
100
|
+
const tags = Array.isArray(tag) ? tag : [
|
|
101
|
+
tag
|
|
102
|
+
];
|
|
103
|
+
tags.forEach((t) => addTagFnRelation(t, fn));
|
|
104
|
+
return async (...args) => {
|
|
105
|
+
if (isServer && typeof options === "undefined") {
|
|
106
|
+
var _storage_useContext;
|
|
107
|
+
const storage = getAsyncLocalStorage();
|
|
108
|
+
const request = storage === null || storage === void 0 ? void 0 : (_storage_useContext = storage.useContext()) === null || _storage_useContext === void 0 ? void 0 : _storage_useContext.request;
|
|
109
|
+
if (request) {
|
|
110
|
+
let requestCache = requestCacheMap.get(request);
|
|
111
|
+
if (!requestCache) {
|
|
112
|
+
requestCache = /* @__PURE__ */ new Map();
|
|
113
|
+
requestCacheMap.set(request, requestCache);
|
|
114
|
+
}
|
|
115
|
+
const key = generateKey(args);
|
|
116
|
+
if (requestCache.has(key)) {
|
|
117
|
+
return requestCache.get(key);
|
|
118
|
+
}
|
|
119
|
+
const promise = fn(...args);
|
|
120
|
+
requestCache.set(key, promise);
|
|
121
|
+
try {
|
|
122
|
+
const data = await promise;
|
|
123
|
+
return data;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
requestCache.delete(key);
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} else if (typeof options !== "undefined") {
|
|
130
|
+
let tagCache = store.get(fn);
|
|
131
|
+
if (!tagCache) {
|
|
132
|
+
tagCache = /* @__PURE__ */ new Map();
|
|
133
|
+
}
|
|
134
|
+
const key = generateKey(args);
|
|
135
|
+
const cached = tagCache.get(key);
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
if (cached) {
|
|
138
|
+
const age = now - cached.timestamp;
|
|
139
|
+
if (age < maxAge) {
|
|
140
|
+
return cached.data;
|
|
141
|
+
}
|
|
142
|
+
if (revalidate > 0 && age < maxAge + revalidate) {
|
|
143
|
+
if (!cached.isRevalidating) {
|
|
144
|
+
cached.isRevalidating = true;
|
|
145
|
+
Promise.resolve().then(async () => {
|
|
146
|
+
try {
|
|
147
|
+
const newData = await fn(...args);
|
|
148
|
+
tagCache.set(key, {
|
|
149
|
+
data: newData,
|
|
150
|
+
timestamp: Date.now(),
|
|
151
|
+
isRevalidating: false
|
|
152
|
+
});
|
|
153
|
+
store.set(fn, tagCache);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
cached.isRevalidating = false;
|
|
156
|
+
if (isServer) {
|
|
157
|
+
var _storage_useContext_monitors, _storage_useContext2;
|
|
158
|
+
const storage = getAsyncLocalStorage();
|
|
159
|
+
storage === null || storage === void 0 ? void 0 : (_storage_useContext2 = storage.useContext()) === null || _storage_useContext2 === void 0 ? void 0 : (_storage_useContext_monitors = _storage_useContext2.monitors) === null || _storage_useContext_monitors === void 0 ? void 0 : _storage_useContext_monitors.error(error.message);
|
|
160
|
+
} else {
|
|
161
|
+
console.error("Background revalidation failed:", error);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return cached.data;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const data = await fn(...args);
|
|
170
|
+
tagCache.set(key, {
|
|
171
|
+
data,
|
|
172
|
+
timestamp: now,
|
|
173
|
+
isRevalidating: false
|
|
174
|
+
});
|
|
175
|
+
store.set(fn, tagCache);
|
|
176
|
+
return data;
|
|
177
|
+
} else {
|
|
178
|
+
console.warn("The cache function will not work because it runs on the browser and there are no options are provided.");
|
|
179
|
+
return fn(...args);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function withRequestCache(handler) {
|
|
184
|
+
if (!isServer) {
|
|
185
|
+
return handler;
|
|
186
|
+
}
|
|
187
|
+
return async (req, ...args) => {
|
|
188
|
+
const storage = getAsyncLocalStorage();
|
|
189
|
+
return storage.run({
|
|
190
|
+
request: req
|
|
191
|
+
}, () => handler(req, ...args));
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function revalidateTag(tag) {
|
|
195
|
+
const fns = tagFnMap.get(tag);
|
|
196
|
+
if (fns) {
|
|
197
|
+
fns.forEach((fn) => {
|
|
198
|
+
lruCache === null || lruCache === void 0 ? void 0 : lruCache.delete(fn);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function clearStore() {
|
|
203
|
+
lruCache === null || lruCache === void 0 ? void 0 : lruCache.clear();
|
|
204
|
+
lruCache = void 0;
|
|
205
|
+
tagFnMap.clear();
|
|
206
|
+
}
|
|
207
|
+
export {
|
|
208
|
+
CacheSize,
|
|
209
|
+
CacheTime,
|
|
210
|
+
cache,
|
|
211
|
+
clearStore,
|
|
212
|
+
configureCache,
|
|
213
|
+
generateKey,
|
|
214
|
+
revalidateTag,
|
|
215
|
+
withRequestCache
|
|
216
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { NestedRoute
|
|
3
|
-
export declare const transformNestedRoutes: (routes: NestedRoute[]
|
|
2
|
+
import type { NestedRoute } from '@modern-js/types';
|
|
3
|
+
export declare const transformNestedRoutes: (routes: NestedRoute[]) => import("react-router-dom").RouteObject[];
|
|
4
4
|
type DeferredDataComponentType = (props?: {
|
|
5
5
|
nonce?: string;
|
|
6
6
|
}) => JSX.Element | null;
|
|
@@ -8,6 +8,5 @@ export declare const renderNestedRoute: (nestedRoute: NestedRoute, options?: {
|
|
|
8
8
|
parent?: NestedRoute;
|
|
9
9
|
DeferredDataComponent?: DeferredDataComponentType;
|
|
10
10
|
props?: Record<string, any>;
|
|
11
|
-
reporter?: Reporter;
|
|
12
11
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
13
12
|
export {};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
/**
|
|
3
2
|
* ssr helpers
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
declare const run: <O>(context: IncomingHttpHeaders, cb: () => O | Promise<O>) => Promise<O>, useHeaders: () => IncomingHttpHeaders;
|
|
7
|
-
export { run, useHeaders };
|
|
4
|
+
export { storage } from '../universal/async_storage.server';
|
|
8
5
|
export { serializeJson } from './serialize';
|
|
9
6
|
export * from './loaderContext';
|
|
10
7
|
export * from './stream';
|
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
import type { Reporter } from '@modern-js/types';
|
|
2
|
-
export { createRequestContext, type RequestContext } from './createRequestCtx';
|
|
2
|
+
export { createRequestContext, type RequestContext, } from './createRequestCtx';
|
|
3
3
|
export type { LoaderContext } from './createLoaderCtx';
|
|
4
|
-
/**
|
|
5
|
-
* @deprecated
|
|
6
|
-
* Use `context.get('reporter')` instead of `context.get(reporterCtx)`. The `reporterCtx` will be removed in next major version.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
*
|
|
10
|
-
* const loader = ({ context }: LoaderFunctionArgs) => {
|
|
11
|
-
* const reporter = context?.get('reporter')
|
|
12
|
-
* // doSomething
|
|
13
|
-
* }
|
|
14
|
-
*
|
|
15
|
-
*/
|
|
16
4
|
export declare const reporterCtx: import("./createLoaderCtx").LoaderContext<Reporter>;
|
|
@@ -8,7 +8,7 @@ interface MemoryContainerOptions {
|
|
|
8
8
|
* MemoryContainer, it use lur-cache as cahe layer.
|
|
9
9
|
* It has a Time to Live, by default as 1 hour.
|
|
10
10
|
*/
|
|
11
|
-
export declare class MemoryContainer<K, V
|
|
11
|
+
export declare class MemoryContainer<K extends string, V extends {}> implements Container<K, V> {
|
|
12
12
|
private static BYTE;
|
|
13
13
|
private static KB;
|
|
14
14
|
private static MB;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type ServerContext = Pick<ModernServerContext, 'logger' | 'req' | 'reporter' | 'res' | 'params' | 'headers' | 'method' | 'url' | 'host' | 'protocol' | 'origin' | 'href' | 'path' | 'query'>;
|
|
1
|
+
import type { ServerRoute } from '@modern-js/types';
|
|
3
2
|
export declare const matchEntry: (pathname: string, entries: ServerRoute[]) => ServerRoute | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IncomingHttpHeaders } from 'http';
|
|
3
|
+
import type { Monitors } from '@modern-js/types';
|
|
4
|
+
declare const storage: {
|
|
5
|
+
run: <O>(context: {
|
|
6
|
+
monitors?: Monitors | undefined;
|
|
7
|
+
headers?: IncomingHttpHeaders | undefined;
|
|
8
|
+
request?: Request | undefined;
|
|
9
|
+
}, cb: () => O | Promise<O>) => Promise<O>;
|
|
10
|
+
useContext: () => {
|
|
11
|
+
monitors?: Monitors | undefined;
|
|
12
|
+
headers?: IncomingHttpHeaders | undefined;
|
|
13
|
+
request?: Request | undefined;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
type Storage = typeof storage;
|
|
17
|
+
export { storage, type Storage };
|
|
18
|
+
export declare const getAsyncLocalStorage: () => Storage;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const CacheSize: {
|
|
2
|
+
readonly KB: 1024;
|
|
3
|
+
readonly MB: number;
|
|
4
|
+
readonly GB: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const CacheTime: {
|
|
7
|
+
readonly SECOND: 1000;
|
|
8
|
+
readonly MINUTE: number;
|
|
9
|
+
readonly HOUR: number;
|
|
10
|
+
readonly DAY: number;
|
|
11
|
+
readonly WEEK: number;
|
|
12
|
+
readonly MONTH: number;
|
|
13
|
+
};
|
|
14
|
+
interface CacheOptions {
|
|
15
|
+
tag?: string | string[];
|
|
16
|
+
maxAge?: number;
|
|
17
|
+
revalidate?: number;
|
|
18
|
+
}
|
|
19
|
+
interface CacheConfig {
|
|
20
|
+
maxSize: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function configureCache(config: CacheConfig): void;
|
|
23
|
+
export declare function generateKey(args: unknown[]): string;
|
|
24
|
+
export declare function cache<T extends (...args: any[]) => Promise<any>>(fn: T, options?: CacheOptions): T;
|
|
25
|
+
export declare function withRequestCache<T extends (req: Request, ...args: any[]) => Promise<any>>(handler: T): T;
|
|
26
|
+
export declare function revalidateTag(tag: string): void;
|
|
27
|
+
export declare function clearStore(): void;
|
|
28
|
+
export {};
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"modern",
|
|
16
16
|
"modern.js"
|
|
17
17
|
],
|
|
18
|
-
"version": "2.65.
|
|
18
|
+
"version": "2.65.3-alpha.0",
|
|
19
19
|
"_comment": "Provide ESM and CJS exports, ESM is used by runtime package, for treeshaking",
|
|
20
20
|
"exports": {
|
|
21
21
|
"./router": {
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
},
|
|
31
31
|
"./browser": {
|
|
32
32
|
"types": "./dist/types/browser/index.d.ts",
|
|
33
|
-
"jsnext:source": "./src/browser/index.d.ts",
|
|
34
33
|
"require": "./dist/cjs/browser/index.js",
|
|
35
34
|
"default": "./dist/esm/browser/index.js"
|
|
36
35
|
},
|
|
@@ -83,12 +82,16 @@
|
|
|
83
82
|
"types": "./dist/types/merge.d.ts",
|
|
84
83
|
"require": "./dist/cjs/merge.js",
|
|
85
84
|
"default": "./dist/esm/merge.js"
|
|
85
|
+
},
|
|
86
|
+
"./cache": {
|
|
87
|
+
"types": "./dist/types/universal/cache.d.ts",
|
|
88
|
+
"require": "./dist/cjs/universal/cache.js",
|
|
89
|
+
"default": "./dist/esm/universal/cache.js"
|
|
86
90
|
}
|
|
87
91
|
},
|
|
88
92
|
"publishConfig": {
|
|
89
93
|
"registry": "https://registry.npmjs.org/",
|
|
90
|
-
"access": "public"
|
|
91
|
-
"provenance": true
|
|
94
|
+
"access": "public"
|
|
92
95
|
},
|
|
93
96
|
"typesVersions": {
|
|
94
97
|
"*": {
|
|
@@ -130,16 +133,20 @@
|
|
|
130
133
|
],
|
|
131
134
|
"merge": [
|
|
132
135
|
"./dist/types/merge.d.ts"
|
|
136
|
+
],
|
|
137
|
+
"cache": [
|
|
138
|
+
"./dist/types/universal/cache.d.ts"
|
|
133
139
|
]
|
|
134
140
|
}
|
|
135
141
|
},
|
|
136
142
|
"dependencies": {
|
|
137
143
|
"@remix-run/router": "1.20.0",
|
|
138
144
|
"@swc/helpers": "0.5.13",
|
|
139
|
-
"lru-cache": "^
|
|
145
|
+
"lru-cache": "^10.4.3",
|
|
140
146
|
"react-router-dom": "6.27.0",
|
|
141
147
|
"serialize-javascript": "^6.0.0",
|
|
142
|
-
"@modern-js/
|
|
148
|
+
"@modern-js/types": "2.65.2",
|
|
149
|
+
"@modern-js/utils": "2.65.2"
|
|
143
150
|
},
|
|
144
151
|
"peerDependencies": {
|
|
145
152
|
"react": ">=17.0.0",
|
|
@@ -155,16 +162,14 @@
|
|
|
155
162
|
},
|
|
156
163
|
"devDependencies": {
|
|
157
164
|
"@types/jest": "^29",
|
|
158
|
-
"@types/lru-cache": "^5.1.1",
|
|
159
165
|
"@types/node": "^14",
|
|
160
166
|
"@types/serialize-javascript": "^5.0.1",
|
|
161
167
|
"jest": "^29",
|
|
162
168
|
"react": "^18.3.1",
|
|
163
169
|
"react-dom": "^18.3.1",
|
|
164
170
|
"typescript": "^5",
|
|
165
|
-
"@
|
|
166
|
-
"@scripts/
|
|
167
|
-
"@scripts/jest-config": "2.65.1"
|
|
171
|
+
"@scripts/build": "2.65.2",
|
|
172
|
+
"@scripts/jest-config": "2.65.2"
|
|
168
173
|
},
|
|
169
174
|
"sideEffects": false,
|
|
170
175
|
"scripts": {
|