@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.
Files changed (29) hide show
  1. package/dist/cjs/browser/nestedRoutes.js +11 -11
  2. package/dist/cjs/node/index.js +3 -6
  3. package/dist/cjs/node/storer/container.js +8 -15
  4. package/dist/cjs/universal/async_storage.js +31 -0
  5. package/dist/cjs/{node/storage.js → universal/async_storage.server.js} +18 -12
  6. package/dist/cjs/universal/cache.js +247 -0
  7. package/dist/esm/browser/nestedRoutes.js +12 -12
  8. package/dist/esm/node/index.js +2 -4
  9. package/dist/esm/node/storer/container.js +8 -5
  10. package/dist/esm/universal/async_storage.js +7 -0
  11. package/dist/esm/{node/storage.js → universal/async_storage.server.js} +13 -8
  12. package/dist/esm/universal/cache.js +355 -0
  13. package/dist/esm-node/browser/nestedRoutes.js +11 -11
  14. package/dist/esm-node/node/index.js +2 -4
  15. package/dist/esm-node/node/storer/container.js +8 -5
  16. package/dist/esm-node/universal/async_storage.js +7 -0
  17. package/dist/esm-node/{node/storage.js → universal/async_storage.server.js} +13 -8
  18. package/dist/esm-node/universal/cache.js +216 -0
  19. package/dist/types/browser/nestedRoutes.d.ts +2 -3
  20. package/dist/types/node/index.d.ts +1 -4
  21. package/dist/types/node/loaderContext/index.d.ts +1 -13
  22. package/dist/types/node/storer/container.d.ts +1 -1
  23. package/dist/types/node/storer/index.d.ts +1 -1
  24. package/dist/types/server/nestedRoutes.d.ts +1 -2
  25. package/dist/types/universal/async_storage.d.ts +2 -0
  26. package/dist/types/universal/async_storage.server.d.ts +18 -0
  27. package/dist/types/universal/cache.d.ts +28 -0
  28. package/package.json +15 -10
  29. 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, Reporter } from '@modern-js/types';
3
- export declare const transformNestedRoutes: (routes: NestedRoute[], reporter?: Reporter) => import("react-router-dom").RouteObject[];
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
- import type { IncomingHttpHeaders } from 'http';
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 = unknown> implements Container<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,3 @@
1
1
  import { Storage } from './storage';
2
2
  export { Storage } from './storage';
3
- export declare function createMemoryStorage<V = unknown>(namespace: string): Storage<V>;
3
+ export declare function createMemoryStorage<V extends {} | null>(namespace: string): Storage<V>;
@@ -1,3 +1,2 @@
1
- import type { ModernServerContext, ServerRoute } from '@modern-js/types';
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,2 @@
1
+ import type { Storage } from './async_storage.server';
2
+ export declare const getAsyncLocalStorage: () => Storage | null;
@@ -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.1",
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": "^6.0.0",
145
+ "lru-cache": "^10.4.3",
140
146
  "react-router-dom": "6.27.0",
141
147
  "serialize-javascript": "^6.0.0",
142
- "@modern-js/utils": "2.65.1"
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
- "@modern-js/types": "2.65.1",
166
- "@scripts/build": "2.65.1",
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": {
@@ -1,5 +0,0 @@
1
- declare const createStorage: <T>() => {
2
- run: <O>(context: T, cb: () => O | Promise<O>) => Promise<O>;
3
- useContext: () => T;
4
- };
5
- export { createStorage };