webspresso 0.0.104 → 0.0.105
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/.agents/AGENTS.md +6 -0
- package/.agents/ARCHITECTURE.md +23 -0
- package/.agents/CACHE.md +147 -0
- package/.agents/CLI.md +1 -1
- package/.agents/EARLY_HINTS.md +120 -0
- package/.agents/HEALTH.md +118 -0
- package/.agents/LIMITS.md +109 -0
- package/.agents/ORM.md +155 -1
- package/.agents/PLUGINS.md +28 -0
- package/.agents/RESILIENCE.md +235 -0
- package/AGENTS.md +6 -0
- package/README.md +2 -2
- package/bin/commands/add-docker.js +6 -6
- package/bin/commands/dev.js +1 -1
- package/bin/commands/new.js +5 -5
- package/bin/commands/openapi.js +1 -1
- package/bin/commands/start.js +1 -1
- package/bin/utils/project.js +2 -2
- package/core/auth/hash.js +5 -4
- package/core/auth/jwt.js +5 -54
- package/core/auth/magic-link.js +4 -5
- package/core/auth/manager.js +3 -5
- package/core/auth/oauth.js +8 -17
- package/core/auth/totp.js +2 -1
- package/core/cache/byte-size.js +123 -0
- package/core/cache/index.js +21 -0
- package/core/cache/lru.js +477 -0
- package/core/cache/tagged-lru.js +346 -0
- package/core/commands/registry.js +6 -20
- package/core/early-hints/index.js +267 -0
- package/core/errors/domain.js +48 -0
- package/core/errors/http.js +31 -0
- package/core/errors/index.js +10 -0
- package/core/errors/normalize.js +88 -2
- package/core/events/discovery.js +6 -24
- package/core/events/dispatcher.js +2 -7
- package/core/health/index.js +41 -0
- package/core/health/middleware.js +213 -0
- package/core/health/normalize.js +140 -0
- package/core/health/probes.js +203 -0
- package/core/limits/index.js +34 -0
- package/core/limits/middleware.js +317 -0
- package/core/limits/normalize.js +222 -0
- package/core/orm/batch.js +70 -0
- package/core/orm/cache/fingerprint.js +2 -2
- package/core/orm/cache/memory-provider.js +27 -110
- package/core/orm/index.js +71 -8
- package/core/orm/json-fields.js +22 -8
- package/core/orm/model.js +10 -0
- package/core/orm/query-builder.js +63 -9
- package/core/orm/repository.js +318 -19
- package/core/orm/schema-helpers.js +8 -10
- package/core/orm/scopes.js +321 -1
- package/core/orm/transaction.js +10 -2
- package/core/orm/utils.js +4 -86
- package/core/queue/adapters/database.js +4 -8
- package/core/queue/manager.js +55 -41
- package/core/resilience/circuit-breaker.js +391 -0
- package/core/resilience/errors.js +63 -0
- package/core/resilience/index.js +53 -0
- package/core/resilience/registry.js +97 -0
- package/core/resilience/retry.js +203 -0
- package/core/schema-cache.js +9 -2
- package/core/shutdown/http-adapter.js +4 -1
- package/core/shutdown/shutdown-manager.js +120 -19
- package/core/utils/array.js +12 -0
- package/core/utils/base64url.js +8 -0
- package/core/utils/crypto.js +118 -0
- package/core/utils/ensure-leading-slash.js +6 -0
- package/core/utils/escape-regexp.js +7 -0
- package/core/utils/fetch.js +6 -5
- package/core/utils/fs.js +144 -0
- package/core/utils/hmac.js +6 -0
- package/core/utils/json.js +98 -0
- package/core/utils/object.js +106 -0
- package/core/utils/random.js +9 -0
- package/core/utils/regex.js +77 -0
- package/core/utils/sha256.js +6 -0
- package/core/utils/string.js +87 -0
- package/core/utils/strip-trailing-slash.js +6 -0
- package/core/utils/time.js +96 -0
- package/core/utils/timeout.js +348 -0
- package/core/utils/url.js +106 -0
- package/core/utils/walk.js +7 -0
- package/index.d.ts +616 -1
- package/index.js +148 -0
- package/package.json +1 -2
- package/plugins/admin-panel/core/api-extensions.js +2 -5
- package/plugins/admin-panel/index.js +1 -1
- package/plugins/admin-panel/modules/menu.js +1 -1
- package/plugins/api-key/crypto.js +3 -3
- package/plugins/api-key/scopes.js +5 -7
- package/plugins/audit-log/api-handlers.js +6 -15
- package/plugins/audit-log/parse.js +3 -3
- package/plugins/csrf/index.js +5 -9
- package/plugins/dashboard/app.js +2 -2
- package/plugins/dashboard/index.js +5 -5
- package/plugins/data-exchange/record-selection.js +2 -5
- package/plugins/email/api-handlers.js +5 -18
- package/plugins/email/auth-bridge.js +3 -2
- package/plugins/health-check.js +2 -1
- package/plugins/index.js +2 -0
- package/plugins/lazy.js +5 -6
- package/plugins/mcp/discovery.js +18 -17
- package/plugins/polar/src/api.js +2 -1
- package/plugins/polar/src/webhooks.js +5 -6
- package/plugins/rate-limit/index.js +3 -1
- package/plugins/redirect/index.js +9 -7
- package/plugins/rls/index.js +170 -0
- package/plugins/site-analytics/tracking.js +2 -2
- package/plugins/sitemap.js +3 -2
- package/plugins/swagger.js +4 -3
- package/plugins/upload/index.js +4 -3
- package/plugins/upload/orphan-pruner.js +4 -3
- package/plugins/webhooks/crypto.js +6 -11
- package/plugins/webhooks/dispatcher.js +8 -8
- package/plugins/webhooks/index.js +6 -0
- package/plugins/webhooks/matcher.js +7 -11
- package/plugins/webhooks/sender.js +8 -11
- package/src/app-context.js +41 -5
- package/src/dev/banner.js +6 -2
- package/src/file-router.js +1 -3
- package/src/helpers.js +159 -24
- package/src/modules/module-discovery.js +1 -9
- package/src/pages/page-loader.js +53 -1
- package/src/plugin-manager.js +90 -17
- package/src/server.js +136 -25
- package/src/services/discovery.js +19 -40
- package/src/services/executor.js +86 -25
- package/src/services/memoize.js +3 -25
- package/src/services/registry.js +6 -0
- package/src/template/precompile.js +8 -19
package/core/auth/jwt.js
CHANGED
|
@@ -5,60 +5,17 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const crypto = require('crypto');
|
|
8
|
-
const { safeCompare } = require('../utils/crypto');
|
|
8
|
+
const { safeCompare, hmacSha256, base64UrlEncode, base64UrlDecode } = require('../utils/crypto');
|
|
9
|
+
const { parseDurationSec } = require('../utils/time');
|
|
9
10
|
|
|
10
11
|
const HS256_HEADER_BASE64URL = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9';
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* Base64URL encode string or Buffer
|
|
14
|
-
* @param {string|Buffer} input
|
|
15
|
-
* @returns {string}
|
|
16
|
-
*/
|
|
17
|
-
function base64UrlEncode(input) {
|
|
18
|
-
return Buffer.isBuffer(input)
|
|
19
|
-
? input.toString('base64url')
|
|
20
|
-
: Buffer.from(String(input), 'utf8').toString('base64url');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Base64URL decode string
|
|
25
|
-
* @param {string} input
|
|
26
|
-
* @returns {string}
|
|
27
|
-
*/
|
|
28
|
-
function base64UrlDecode(input) {
|
|
29
|
-
return Buffer.from(String(input), 'base64url').toString('utf8');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
13
|
/**
|
|
33
14
|
* Parse duration string (e.g. '1h', '7d', '15m', '3600') into seconds
|
|
34
15
|
* @param {string|number} duration
|
|
35
16
|
* @returns {number} Seconds
|
|
36
17
|
*/
|
|
37
|
-
|
|
38
|
-
if (typeof duration === 'number') {
|
|
39
|
-
return duration;
|
|
40
|
-
}
|
|
41
|
-
if (typeof duration !== 'string') {
|
|
42
|
-
return 3600; // Default 1 hour
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const match = duration.trim().match(/^(\d+)\s*([smhd])?$/i);
|
|
46
|
-
if (!match) {
|
|
47
|
-
const parsed = parseInt(duration, 10);
|
|
48
|
-
return isNaN(parsed) ? 3600 : parsed;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const value = parseInt(match[1], 10);
|
|
52
|
-
const unit = (match[2] || 's').toLowerCase();
|
|
53
|
-
|
|
54
|
-
switch (unit) {
|
|
55
|
-
case 's': return value;
|
|
56
|
-
case 'm': return value * 60;
|
|
57
|
-
case 'h': return value * 3600;
|
|
58
|
-
case 'd': return value * 86400;
|
|
59
|
-
default: return value;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
18
|
+
const parseDuration = parseDurationSec;
|
|
62
19
|
|
|
63
20
|
/**
|
|
64
21
|
* Sign payload to create HS256 JWT string
|
|
@@ -110,10 +67,7 @@ function signJwt(payload, secret, options = {}) {
|
|
|
110
67
|
const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));
|
|
111
68
|
|
|
112
69
|
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
113
|
-
const encodedSignature =
|
|
114
|
-
.createHmac('sha256', secret)
|
|
115
|
-
.update(signingInput)
|
|
116
|
-
.digest('base64url');
|
|
70
|
+
const encodedSignature = hmacSha256(signingInput, secret, 'base64url');
|
|
117
71
|
|
|
118
72
|
return `${signingInput}.${encodedSignature}`;
|
|
119
73
|
}
|
|
@@ -166,10 +120,7 @@ function verifyJwt(token, secret, options = {}) {
|
|
|
166
120
|
|
|
167
121
|
// Re-compute signature
|
|
168
122
|
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
169
|
-
const expectedSignatureBuf =
|
|
170
|
-
.createHmac('sha256', secret)
|
|
171
|
-
.update(signingInput)
|
|
172
|
-
.digest();
|
|
123
|
+
const expectedSignatureBuf = hmacSha256(signingInput, secret, 'buffer');
|
|
173
124
|
|
|
174
125
|
const actualSignatureBuf = Buffer.from(encodedSignature, 'base64url');
|
|
175
126
|
|
package/core/auth/magic-link.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
+
const { stripTrailingSlash, ensureLeadingSlash } = require('../utils/url');
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Construct full magic link verification URL
|
|
11
13
|
*
|
|
@@ -16,10 +18,7 @@
|
|
|
16
18
|
* @returns {string}
|
|
17
19
|
*/
|
|
18
20
|
function buildMagicLinkUrl(options = {}) {
|
|
19
|
-
|
|
20
|
-
while (baseUrl.endsWith('/')) {
|
|
21
|
-
baseUrl = baseUrl.slice(0, -1);
|
|
22
|
-
}
|
|
21
|
+
const baseUrl = stripTrailingSlash(options.baseUrl || process.env.BASE_URL || 'http://localhost:7331');
|
|
23
22
|
const path = options.path || '/auth/magic-link/verify';
|
|
24
23
|
const token = options.token;
|
|
25
24
|
|
|
@@ -27,7 +26,7 @@ function buildMagicLinkUrl(options = {}) {
|
|
|
27
26
|
throw new Error('Token is required to build magic link URL');
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
const cleanPath = path
|
|
29
|
+
const cleanPath = ensureLeadingSlash(path);
|
|
31
30
|
const sep = cleanPath.includes('?') ? '&' : '?';
|
|
32
31
|
return `${baseUrl}${cleanPath}${sep}token=${encodeURIComponent(token)}`;
|
|
33
32
|
}
|
package/core/auth/manager.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
consumeAuthToken,
|
|
16
16
|
hash: hashPassword,
|
|
17
17
|
} = require('./tokens');
|
|
18
|
+
const { stripTrailingSlash, ensureLeadingSlash } = require('../utils/url');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Authentication error
|
|
@@ -959,12 +960,9 @@ class AuthManager extends EventEmitter {
|
|
|
959
960
|
tokenTtlMs
|
|
960
961
|
);
|
|
961
962
|
|
|
962
|
-
|
|
963
|
-
while (baseUrl.endsWith('/')) {
|
|
964
|
-
baseUrl = baseUrl.slice(0, -1);
|
|
965
|
-
}
|
|
963
|
+
const baseUrl = stripTrailingSlash(options.baseUrl || this.config.baseUrl || process.env.BASE_URL || 'http://localhost:7331');
|
|
966
964
|
const pathSegment = options.path || '/auth/magic-link/verify';
|
|
967
|
-
const cleanPath = pathSegment
|
|
965
|
+
const cleanPath = ensureLeadingSlash(pathSegment);
|
|
968
966
|
const sep = cleanPath.includes('?') ? '&' : '?';
|
|
969
967
|
|
|
970
968
|
const hasCustomUrl = typeof options.buildMagicLinkUrl === 'function' || Boolean(options.path) || Boolean(options.baseUrl);
|
package/core/auth/oauth.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const crypto = require('crypto');
|
|
9
9
|
const { fetchWithTimeout } = require('../utils/fetch');
|
|
10
|
-
const { safeCompare } = require('../utils/crypto');
|
|
10
|
+
const { safeCompare, sha256, hmacSha256, base64UrlEncode, base64UrlDecode, randomToken } = require('../utils/crypto');
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Custom error class for OAuth operations
|
|
@@ -110,11 +110,8 @@ const DEFAULT_PROVIDERS = {
|
|
|
110
110
|
* @returns {{ codeVerifier: string, codeChallenge: string, codeChallengeMethod: 'S256' }}
|
|
111
111
|
*/
|
|
112
112
|
function generatePKCE() {
|
|
113
|
-
const codeVerifier =
|
|
114
|
-
const codeChallenge =
|
|
115
|
-
.createHash('sha256')
|
|
116
|
-
.update(codeVerifier)
|
|
117
|
-
.digest('base64url');
|
|
113
|
+
const codeVerifier = randomToken(32, 'base64url');
|
|
114
|
+
const codeChallenge = sha256(codeVerifier, 'base64url');
|
|
118
115
|
|
|
119
116
|
return {
|
|
120
117
|
codeVerifier,
|
|
@@ -137,14 +134,11 @@ function createSignedState(payload, secret) {
|
|
|
137
134
|
const dataWithMeta = {
|
|
138
135
|
...payload,
|
|
139
136
|
timestamp: Date.now(),
|
|
140
|
-
nonce:
|
|
137
|
+
nonce: randomToken(16, 'hex'),
|
|
141
138
|
};
|
|
142
139
|
|
|
143
|
-
const body =
|
|
144
|
-
const signature =
|
|
145
|
-
.createHmac('sha256', secret)
|
|
146
|
-
.update(body)
|
|
147
|
-
.digest('base64url');
|
|
140
|
+
const body = base64UrlEncode(JSON.stringify(dataWithMeta));
|
|
141
|
+
const signature = hmacSha256(body, secret, 'base64url');
|
|
148
142
|
|
|
149
143
|
return `${body}.${signature}`;
|
|
150
144
|
}
|
|
@@ -167,10 +161,7 @@ function verifySignedState(stateString, secret, maxAgeMs = 10 * 60 * 1000) {
|
|
|
167
161
|
}
|
|
168
162
|
|
|
169
163
|
const [body, signature] = parts;
|
|
170
|
-
const expectedSignature =
|
|
171
|
-
.createHmac('sha256', secret)
|
|
172
|
-
.update(body)
|
|
173
|
-
.digest('base64url');
|
|
164
|
+
const expectedSignature = hmacSha256(body, secret, 'base64url');
|
|
174
165
|
|
|
175
166
|
if (!safeCompare(signature, expectedSignature)) {
|
|
176
167
|
throw new OAuthError('OAuth state signature verification failed', 'INVALID_STATE');
|
|
@@ -178,7 +169,7 @@ function verifySignedState(stateString, secret, maxAgeMs = 10 * 60 * 1000) {
|
|
|
178
169
|
|
|
179
170
|
let payload;
|
|
180
171
|
try {
|
|
181
|
-
const jsonStr =
|
|
172
|
+
const jsonStr = base64UrlDecode(body);
|
|
182
173
|
payload = JSON.parse(jsonStr);
|
|
183
174
|
} catch {
|
|
184
175
|
throw new OAuthError('Failed to parse OAuth state payload', 'INVALID_STATE');
|
package/core/auth/totp.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const crypto = require('crypto');
|
|
8
|
+
const { sha256 } = require('../utils/crypto');
|
|
8
9
|
const { generateQrSvg, generateQrDataUri, createQrMatrix } = require('./qrcode');
|
|
9
10
|
|
|
10
11
|
const RFC4648_BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
@@ -376,7 +377,7 @@ function hashRecoveryCode(code) {
|
|
|
376
377
|
throw new TypeError('Recovery code must be a string');
|
|
377
378
|
}
|
|
378
379
|
const clean = code.toLowerCase().replace(/[\s-]/g, '');
|
|
379
|
-
return
|
|
380
|
+
return sha256(clean);
|
|
380
381
|
}
|
|
381
382
|
|
|
382
383
|
/**
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Webspresso - Zero-Dependency Approximate Byte Sizer
|
|
5
|
+
* Fast, non-blocking memory footprint calculator for LRU cache entries.
|
|
6
|
+
* Handles strings, numbers, buffers, dates, arrays, and objects with recursion depth limits.
|
|
7
|
+
* @module core/cache/byte-size
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const MAX_RECURSION_DEPTH = 6;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Approximates memory byte size of any JavaScript value.
|
|
14
|
+
*
|
|
15
|
+
* @param {*} value - Value to calculate size for
|
|
16
|
+
* @param {number} [depth=0] - Current recursion depth
|
|
17
|
+
* @param {Set} [seen=null] - Circular reference protection set
|
|
18
|
+
* @returns {number} Approximate size in bytes
|
|
19
|
+
*/
|
|
20
|
+
function approximateByteSize(value, depth = 0, seen = null) {
|
|
21
|
+
if (value === null || value === undefined) {
|
|
22
|
+
return 8;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const type = typeof value;
|
|
26
|
+
|
|
27
|
+
if (type === 'boolean') {
|
|
28
|
+
return 4;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (type === 'number') {
|
|
32
|
+
return 8;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (type === 'string') {
|
|
36
|
+
// UTF-8 byte length (fast Buffer approximation)
|
|
37
|
+
return Buffer.byteLength(value, 'utf8') + 8;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (type === 'bigint') {
|
|
41
|
+
return 16;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (type === 'symbol') {
|
|
45
|
+
return 16;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (type === 'function') {
|
|
49
|
+
return 32;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Buffers and TypedArrays
|
|
53
|
+
if (Buffer.isBuffer(value)) {
|
|
54
|
+
return value.length + 32;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (ArrayBuffer.isView(value)) {
|
|
58
|
+
return value.byteLength + 32;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (value instanceof ArrayBuffer) {
|
|
62
|
+
return value.byteLength + 32;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Guard against deep nesting
|
|
66
|
+
if (depth > MAX_RECURSION_DEPTH) {
|
|
67
|
+
return 64;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (type === 'object') {
|
|
71
|
+
if (!seen) {
|
|
72
|
+
seen = new Set();
|
|
73
|
+
}
|
|
74
|
+
if (seen.has(value)) {
|
|
75
|
+
return 0; // Prevent infinite recursion on circular references
|
|
76
|
+
}
|
|
77
|
+
seen.add(value);
|
|
78
|
+
|
|
79
|
+
let bytes = 32; // V8 object header baseline
|
|
80
|
+
|
|
81
|
+
if (Array.isArray(value)) {
|
|
82
|
+
for (let i = 0; i < value.length; i++) {
|
|
83
|
+
bytes += approximateByteSize(value[i], depth + 1, seen);
|
|
84
|
+
}
|
|
85
|
+
return bytes;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (value instanceof Date) {
|
|
89
|
+
return 16;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (value instanceof RegExp) {
|
|
93
|
+
return value.source.length * 2 + 32;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (value instanceof Map) {
|
|
97
|
+
for (const [k, v] of value.entries()) {
|
|
98
|
+
bytes += approximateByteSize(k, depth + 1, seen) + approximateByteSize(v, depth + 1, seen);
|
|
99
|
+
}
|
|
100
|
+
return bytes;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (value instanceof Set) {
|
|
104
|
+
for (const item of value.values()) {
|
|
105
|
+
bytes += approximateByteSize(item, depth + 1, seen);
|
|
106
|
+
}
|
|
107
|
+
return bytes;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const keys = Object.keys(value);
|
|
111
|
+
for (let i = 0; i < keys.length; i++) {
|
|
112
|
+
const k = keys[i];
|
|
113
|
+
bytes += Buffer.byteLength(k, 'utf8') + 8 + approximateByteSize(value[k], depth + 1, seen);
|
|
114
|
+
}
|
|
115
|
+
return bytes;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return 16;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
approximateByteSize,
|
|
123
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Webspresso - High-Performance LRU & Tagged Cache Engine
|
|
5
|
+
* Zero-dependency, memory-bounded, leak-free cache primitives with stampede protection.
|
|
6
|
+
* @module core/cache
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { LruCache, createLruCache, safeClone } = require('./lru');
|
|
10
|
+
const { TaggedLruCache, createTaggedLruCache, normalizeTags } = require('./tagged-lru');
|
|
11
|
+
const { approximateByteSize } = require('./byte-size');
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
LruCache,
|
|
15
|
+
createLruCache,
|
|
16
|
+
TaggedLruCache,
|
|
17
|
+
createTaggedLruCache,
|
|
18
|
+
approximateByteSize,
|
|
19
|
+
safeClone,
|
|
20
|
+
normalizeTags,
|
|
21
|
+
};
|