pulse-js-framework 1.4.9 → 1.5.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/cli/analyze.js +8 -7
- package/cli/build.js +14 -13
- package/cli/dev.js +28 -7
- package/cli/format.js +13 -12
- package/cli/index.js +20 -1
- package/cli/lint.js +8 -7
- package/cli/release.js +493 -0
- package/compiler/parser.js +41 -20
- package/compiler/transformer/constants.js +54 -0
- package/compiler/transformer/export.js +33 -0
- package/compiler/transformer/expressions.js +273 -0
- package/compiler/transformer/imports.js +101 -0
- package/compiler/transformer/index.js +319 -0
- package/compiler/transformer/router.js +95 -0
- package/compiler/transformer/state.js +118 -0
- package/compiler/transformer/store.js +97 -0
- package/compiler/transformer/style.js +130 -0
- package/compiler/transformer/view.js +428 -0
- package/compiler/transformer.js +17 -1310
- package/core/errors.js +300 -0
- package/package.json +8 -5
- package/runtime/dom.js +61 -10
- package/runtime/lru-cache.js +145 -0
- package/runtime/native.js +6 -1
- package/runtime/pulse.js +46 -2
- package/runtime/router.js +4 -1
- package/runtime/store.js +35 -1
- package/runtime/utils.js +348 -0
- package/types/index.d.ts +19 -0
- package/types/lru-cache.d.ts +118 -0
- package/types/utils.d.ts +255 -0
package/runtime/store.js
CHANGED
|
@@ -97,7 +97,11 @@ export function createStore(initialState = {}, options = {}) {
|
|
|
97
97
|
*/
|
|
98
98
|
function createPulse(key, value) {
|
|
99
99
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
100
|
-
//
|
|
100
|
+
// Create a pulse for the nested object itself (for $setState support)
|
|
101
|
+
const objectPulse = pulse(value);
|
|
102
|
+
pulses[key] = objectPulse;
|
|
103
|
+
|
|
104
|
+
// Also create nested pulses for individual properties
|
|
101
105
|
const nested = {};
|
|
102
106
|
for (const [k, v] of Object.entries(value)) {
|
|
103
107
|
nested[k] = createPulse(`${key}.${k}`, v);
|
|
@@ -142,16 +146,46 @@ export function createStore(initialState = {}, options = {}) {
|
|
|
142
146
|
return snapshot;
|
|
143
147
|
}
|
|
144
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Update nested pulses recursively when a parent object is updated
|
|
151
|
+
* @private
|
|
152
|
+
* @param {string} prefix - The key prefix (e.g., 'user')
|
|
153
|
+
* @param {Object} obj - The new object value
|
|
154
|
+
*/
|
|
155
|
+
function updateNestedPulses(prefix, obj) {
|
|
156
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
157
|
+
const fullKey = `${prefix}.${k}`;
|
|
158
|
+
if (pulses[fullKey]) {
|
|
159
|
+
pulses[fullKey].set(v);
|
|
160
|
+
// Recursively update deeper nested objects
|
|
161
|
+
if (typeof v === 'object' && v !== null && !Array.isArray(v)) {
|
|
162
|
+
updateNestedPulses(fullKey, v);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
145
168
|
/**
|
|
146
169
|
* Set multiple values at once (batched)
|
|
170
|
+
* Supports both top-level and nested object updates
|
|
147
171
|
* @param {Object} updates - Key-value pairs to update
|
|
148
172
|
* @returns {void}
|
|
173
|
+
* @example
|
|
174
|
+
* // Top-level update
|
|
175
|
+
* store.$setState({ count: 5 });
|
|
176
|
+
*
|
|
177
|
+
* // Nested object update
|
|
178
|
+
* store.$setState({ user: { name: 'Jane', age: 25 } });
|
|
149
179
|
*/
|
|
150
180
|
function setState(updates) {
|
|
151
181
|
batch(() => {
|
|
152
182
|
for (const [key, value] of Object.entries(updates)) {
|
|
153
183
|
if (pulses[key]) {
|
|
154
184
|
pulses[key].set(value);
|
|
185
|
+
// If the value is an object, also update nested pulses
|
|
186
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
187
|
+
updateNestedPulses(key, value);
|
|
188
|
+
}
|
|
155
189
|
}
|
|
156
190
|
}
|
|
157
191
|
});
|
package/runtime/utils.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Runtime Utilities
|
|
3
|
+
* @module pulse-js-framework/runtime/utils
|
|
4
|
+
*
|
|
5
|
+
* Common utility functions for the Pulse framework runtime.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// XSS Prevention
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* HTML entity escape map
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
const HTML_ESCAPES = {
|
|
17
|
+
'&': '&',
|
|
18
|
+
'<': '<',
|
|
19
|
+
'>': '>',
|
|
20
|
+
'"': '"',
|
|
21
|
+
"'": '''
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Regex for HTML special characters
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
const HTML_ESCAPE_REGEX = /[&<>"']/g;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Escape HTML special characters to prevent XSS attacks.
|
|
32
|
+
* Use this when inserting untrusted content into HTML.
|
|
33
|
+
*
|
|
34
|
+
* @param {*} str - Value to escape (will be converted to string)
|
|
35
|
+
* @returns {string} Escaped string safe for HTML insertion
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* escapeHtml('<script>alert("xss")</script>')
|
|
39
|
+
* // Returns: '<script>alert("xss")</script>'
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // Safe to insert into HTML
|
|
43
|
+
* element.innerHTML = `<div>${escapeHtml(userInput)}</div>`;
|
|
44
|
+
*/
|
|
45
|
+
export function escapeHtml(str) {
|
|
46
|
+
if (str == null) return '';
|
|
47
|
+
return String(str).replace(HTML_ESCAPE_REGEX, char => HTML_ESCAPES[char]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Unescape HTML entities back to their original characters.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} str - HTML-escaped string
|
|
54
|
+
* @returns {string} Unescaped string
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* unescapeHtml('<div>')
|
|
58
|
+
* // Returns: '<div>'
|
|
59
|
+
*/
|
|
60
|
+
export function unescapeHtml(str) {
|
|
61
|
+
if (str == null) return '';
|
|
62
|
+
return String(str)
|
|
63
|
+
.replace(/&/g, '&')
|
|
64
|
+
.replace(/</g, '<')
|
|
65
|
+
.replace(/>/g, '>')
|
|
66
|
+
.replace(/"/g, '"')
|
|
67
|
+
.replace(/'/g, "'");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Explicitly set innerHTML with a warning.
|
|
72
|
+
* This function is intentionally named to make it obvious that
|
|
73
|
+
* it's potentially dangerous and bypasses XSS protection.
|
|
74
|
+
*
|
|
75
|
+
* SECURITY WARNING: Only use this with trusted, sanitized HTML.
|
|
76
|
+
* Never use with user-provided content without proper sanitization.
|
|
77
|
+
*
|
|
78
|
+
* @param {HTMLElement} element - Target element
|
|
79
|
+
* @param {string} html - HTML string to insert
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* // Only use with trusted HTML!
|
|
83
|
+
* dangerouslySetInnerHTML(container, sanitizedHtml);
|
|
84
|
+
*/
|
|
85
|
+
export function dangerouslySetInnerHTML(element, html) {
|
|
86
|
+
element.innerHTML = html;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create a text node from a value, safely escaping it.
|
|
91
|
+
* This is the recommended way to insert dynamic text content.
|
|
92
|
+
*
|
|
93
|
+
* Note: DOM textContent is already safe from XSS, but this function
|
|
94
|
+
* provides a consistent API and handles null/undefined values.
|
|
95
|
+
*
|
|
96
|
+
* @param {*} value - Value to convert to text node
|
|
97
|
+
* @returns {Text} Safe text node
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* const node = createSafeTextNode(userInput);
|
|
101
|
+
* container.appendChild(node);
|
|
102
|
+
*/
|
|
103
|
+
export function createSafeTextNode(value) {
|
|
104
|
+
return document.createTextNode(value == null ? '' : String(value));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Attribute Handling
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Escape a value for use in an HTML attribute.
|
|
113
|
+
* Escapes quotes and special characters.
|
|
114
|
+
*
|
|
115
|
+
* @param {*} value - Value to escape
|
|
116
|
+
* @returns {string} Escaped string safe for attribute values
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* const safe = escapeAttribute(userInput);
|
|
120
|
+
* element.setAttribute('data-value', safe);
|
|
121
|
+
*/
|
|
122
|
+
export function escapeAttribute(value) {
|
|
123
|
+
if (value == null) return '';
|
|
124
|
+
return String(value)
|
|
125
|
+
.replace(/&/g, '&')
|
|
126
|
+
.replace(/"/g, '"')
|
|
127
|
+
.replace(/'/g, ''')
|
|
128
|
+
.replace(/</g, '<')
|
|
129
|
+
.replace(/>/g, '>');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Safely set an attribute on an element.
|
|
134
|
+
* Validates attribute names to prevent attribute injection.
|
|
135
|
+
*
|
|
136
|
+
* @param {HTMLElement} element - Target element
|
|
137
|
+
* @param {string} name - Attribute name
|
|
138
|
+
* @param {*} value - Attribute value
|
|
139
|
+
* @returns {boolean} True if attribute was set
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* safeSetAttribute(element, 'data-id', userId);
|
|
143
|
+
*/
|
|
144
|
+
export function safeSetAttribute(element, name, value) {
|
|
145
|
+
// Validate attribute name (prevent injection attacks)
|
|
146
|
+
if (!/^[a-zA-Z][a-zA-Z0-9\-_:.]*$/.test(name)) {
|
|
147
|
+
console.warn(`Invalid attribute name: ${name}`);
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Prevent dangerous attributes
|
|
152
|
+
const dangerousAttrs = ['onclick', 'onerror', 'onload', 'onmouseover', 'onfocus', 'onblur'];
|
|
153
|
+
if (dangerousAttrs.includes(name.toLowerCase())) {
|
|
154
|
+
console.warn(`Potentially dangerous attribute blocked: ${name}`);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
element.setAttribute(name, value == null ? '' : String(value));
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ============================================================================
|
|
163
|
+
// URL Validation
|
|
164
|
+
// ============================================================================
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Validate and sanitize a URL to prevent javascript: and data: XSS.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} url - URL to validate
|
|
170
|
+
* @param {Object} [options] - Validation options
|
|
171
|
+
* @param {boolean} [options.allowData=false] - Allow data: URLs
|
|
172
|
+
* @param {boolean} [options.allowRelative=true] - Allow relative URLs
|
|
173
|
+
* @returns {string|null} Sanitized URL or null if invalid
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const safeUrl = sanitizeUrl(userProvidedUrl);
|
|
177
|
+
* if (safeUrl) {
|
|
178
|
+
* link.href = safeUrl;
|
|
179
|
+
* }
|
|
180
|
+
*/
|
|
181
|
+
export function sanitizeUrl(url, options = {}) {
|
|
182
|
+
const { allowData = false, allowRelative = true } = options;
|
|
183
|
+
|
|
184
|
+
if (url == null || url === '') return null;
|
|
185
|
+
|
|
186
|
+
const trimmed = String(url).trim();
|
|
187
|
+
|
|
188
|
+
// Check for javascript: protocol (case insensitive, handles encoding)
|
|
189
|
+
const lowerUrl = trimmed.toLowerCase().replace(/\s/g, '');
|
|
190
|
+
if (lowerUrl.startsWith('javascript:')) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check for data: protocol unless explicitly allowed
|
|
195
|
+
if (!allowData && lowerUrl.startsWith('data:')) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Allow relative URLs
|
|
200
|
+
if (allowRelative && (trimmed.startsWith('/') || trimmed.startsWith('.') || !trimmed.includes(':'))) {
|
|
201
|
+
return trimmed;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Only allow http: and https: protocols
|
|
205
|
+
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
206
|
+
return trimmed;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ============================================================================
|
|
213
|
+
// Deep Clone
|
|
214
|
+
// ============================================================================
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Deep clone an object or array.
|
|
218
|
+
* Handles nested objects, arrays, dates, and primitive values.
|
|
219
|
+
*
|
|
220
|
+
* @template T
|
|
221
|
+
* @param {T} obj - Object to clone
|
|
222
|
+
* @returns {T} Deep clone of the object
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* const clone = deepClone(originalObject);
|
|
226
|
+
*/
|
|
227
|
+
export function deepClone(obj) {
|
|
228
|
+
if (obj === null || typeof obj !== 'object') {
|
|
229
|
+
return obj;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (obj instanceof Date) {
|
|
233
|
+
return new Date(obj.getTime());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (Array.isArray(obj)) {
|
|
237
|
+
return obj.map(item => deepClone(item));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const clone = {};
|
|
241
|
+
for (const key in obj) {
|
|
242
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
243
|
+
clone[key] = deepClone(obj[key]);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return clone;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Debounce / Throttle
|
|
251
|
+
// ============================================================================
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Create a debounced version of a function.
|
|
255
|
+
* The function will only be called after the specified delay
|
|
256
|
+
* has passed without any new calls.
|
|
257
|
+
*
|
|
258
|
+
* @param {Function} fn - Function to debounce
|
|
259
|
+
* @param {number} delay - Delay in milliseconds
|
|
260
|
+
* @returns {Function} Debounced function
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* const debouncedSearch = debounce(search, 300);
|
|
264
|
+
* input.addEventListener('input', debouncedSearch);
|
|
265
|
+
*/
|
|
266
|
+
export function debounce(fn, delay) {
|
|
267
|
+
let timeoutId = null;
|
|
268
|
+
|
|
269
|
+
const debounced = function(...args) {
|
|
270
|
+
if (timeoutId) {
|
|
271
|
+
clearTimeout(timeoutId);
|
|
272
|
+
}
|
|
273
|
+
timeoutId = setTimeout(() => {
|
|
274
|
+
fn.apply(this, args);
|
|
275
|
+
timeoutId = null;
|
|
276
|
+
}, delay);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
debounced.cancel = () => {
|
|
280
|
+
if (timeoutId) {
|
|
281
|
+
clearTimeout(timeoutId);
|
|
282
|
+
timeoutId = null;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
return debounced;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Create a throttled version of a function.
|
|
291
|
+
* The function will be called at most once per specified interval.
|
|
292
|
+
*
|
|
293
|
+
* @param {Function} fn - Function to throttle
|
|
294
|
+
* @param {number} interval - Minimum interval between calls in milliseconds
|
|
295
|
+
* @returns {Function} Throttled function
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* const throttledScroll = throttle(handleScroll, 100);
|
|
299
|
+
* window.addEventListener('scroll', throttledScroll);
|
|
300
|
+
*/
|
|
301
|
+
export function throttle(fn, interval) {
|
|
302
|
+
let lastCall = 0;
|
|
303
|
+
let timeoutId = null;
|
|
304
|
+
|
|
305
|
+
const throttled = function(...args) {
|
|
306
|
+
const now = Date.now();
|
|
307
|
+
const remaining = interval - (now - lastCall);
|
|
308
|
+
|
|
309
|
+
if (remaining <= 0) {
|
|
310
|
+
if (timeoutId) {
|
|
311
|
+
clearTimeout(timeoutId);
|
|
312
|
+
timeoutId = null;
|
|
313
|
+
}
|
|
314
|
+
lastCall = now;
|
|
315
|
+
fn.apply(this, args);
|
|
316
|
+
} else if (!timeoutId) {
|
|
317
|
+
timeoutId = setTimeout(() => {
|
|
318
|
+
lastCall = Date.now();
|
|
319
|
+
timeoutId = null;
|
|
320
|
+
fn.apply(this, args);
|
|
321
|
+
}, remaining);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
throttled.cancel = () => {
|
|
326
|
+
if (timeoutId) {
|
|
327
|
+
clearTimeout(timeoutId);
|
|
328
|
+
timeoutId = null;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
return throttled;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export default {
|
|
336
|
+
// XSS Prevention
|
|
337
|
+
escapeHtml,
|
|
338
|
+
unescapeHtml,
|
|
339
|
+
dangerouslySetInnerHTML,
|
|
340
|
+
createSafeTextNode,
|
|
341
|
+
escapeAttribute,
|
|
342
|
+
safeSetAttribute,
|
|
343
|
+
sanitizeUrl,
|
|
344
|
+
// Utilities
|
|
345
|
+
deepClone,
|
|
346
|
+
debounce,
|
|
347
|
+
throttle
|
|
348
|
+
};
|
package/types/index.d.ts
CHANGED
|
@@ -161,3 +161,22 @@ export {
|
|
|
161
161
|
SourceMapConsumer,
|
|
162
162
|
encodeVLQ
|
|
163
163
|
} from './sourcemap';
|
|
164
|
+
|
|
165
|
+
// LRU Cache
|
|
166
|
+
export { LRUCache } from './lru-cache';
|
|
167
|
+
|
|
168
|
+
// Utilities (XSS Prevention, etc.)
|
|
169
|
+
export {
|
|
170
|
+
escapeHtml,
|
|
171
|
+
unescapeHtml,
|
|
172
|
+
dangerouslySetInnerHTML,
|
|
173
|
+
createSafeTextNode,
|
|
174
|
+
escapeAttribute,
|
|
175
|
+
safeSetAttribute,
|
|
176
|
+
SanitizeUrlOptions,
|
|
177
|
+
sanitizeUrl,
|
|
178
|
+
deepClone,
|
|
179
|
+
CancellableFunction,
|
|
180
|
+
debounce,
|
|
181
|
+
throttle
|
|
182
|
+
} from './utils';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse Framework - LRU Cache Type Definitions
|
|
3
|
+
* @module pulse-js-framework/runtime/lru-cache
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* LRU (Least Recently Used) Cache implementation.
|
|
8
|
+
* Uses Map's insertion order for efficient O(1) operations.
|
|
9
|
+
* When capacity is reached, the least recently accessed item is evicted.
|
|
10
|
+
*
|
|
11
|
+
* @template K - Key type
|
|
12
|
+
* @template V - Value type
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const cache = new LRUCache<string, number>(100);
|
|
17
|
+
* cache.set('key1', 42);
|
|
18
|
+
* cache.get('key1'); // 42 - moves 'key1' to most recently used
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class LRUCache<K = unknown, V = unknown> {
|
|
22
|
+
/**
|
|
23
|
+
* Create an LRU cache
|
|
24
|
+
* @param capacity - Maximum number of items to store (must be > 0)
|
|
25
|
+
* @throws {Error} If capacity is <= 0
|
|
26
|
+
*/
|
|
27
|
+
constructor(capacity: number);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get an item from the cache.
|
|
31
|
+
* Accessing an item moves it to the "most recently used" position.
|
|
32
|
+
*
|
|
33
|
+
* @param key - Cache key
|
|
34
|
+
* @returns The cached value, or undefined if not found
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const value = cache.get('myKey');
|
|
39
|
+
* if (value !== undefined) {
|
|
40
|
+
* // Use the cached value
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
get(key: K): V | undefined;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Set an item in the cache.
|
|
48
|
+
* If the cache is at capacity, evicts the least recently used item.
|
|
49
|
+
*
|
|
50
|
+
* @param key - Cache key
|
|
51
|
+
* @param value - Value to store
|
|
52
|
+
* @returns this (for chaining)
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* cache.set('key1', 'value1').set('key2', 'value2');
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
set(key: K, value: V): this;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if a key exists in the cache.
|
|
63
|
+
* Note: This does NOT update the item's "recently used" position.
|
|
64
|
+
*
|
|
65
|
+
* @param key - Cache key
|
|
66
|
+
* @returns true if key exists
|
|
67
|
+
*/
|
|
68
|
+
has(key: K): boolean;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Delete an item from the cache.
|
|
72
|
+
*
|
|
73
|
+
* @param key - Cache key
|
|
74
|
+
* @returns true if item was deleted, false if key didn't exist
|
|
75
|
+
*/
|
|
76
|
+
delete(key: K): boolean;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Clear all items from the cache.
|
|
80
|
+
*/
|
|
81
|
+
clear(): void;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get the current number of items in the cache.
|
|
85
|
+
*/
|
|
86
|
+
readonly size: number;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the maximum capacity of the cache.
|
|
90
|
+
*/
|
|
91
|
+
readonly capacity: number;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get all keys in the cache (oldest to newest).
|
|
95
|
+
* @returns Iterator of keys
|
|
96
|
+
*/
|
|
97
|
+
keys(): IterableIterator<K>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get all values in the cache (oldest to newest).
|
|
101
|
+
* @returns Iterator of values
|
|
102
|
+
*/
|
|
103
|
+
values(): IterableIterator<V>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get all entries in the cache (oldest to newest).
|
|
107
|
+
* @returns Iterator of [key, value] pairs
|
|
108
|
+
*/
|
|
109
|
+
entries(): IterableIterator<[K, V]>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Iterate over all entries.
|
|
113
|
+
* @param callback - Called for each entry
|
|
114
|
+
*/
|
|
115
|
+
forEach(callback: (value: V, key: K, cache: LRUCache<K, V>) => void): void;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default LRUCache;
|