lume-js 1.0.0 → 2.0.0-alpha.2
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/README.md +10 -4
- package/package.json +7 -3
- package/src/addons/debug.js +330 -0
- package/src/addons/index.d.ts +98 -4
- package/src/addons/index.js +2 -1
- package/src/addons/repeat.js +75 -59
- package/src/core/bindDom.js +23 -11
- package/src/core/effect.js +91 -51
- package/src/core/state.js +104 -10
- package/src/index.d.ts +143 -2
package/README.md
CHANGED
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
Minimal reactive state management using only standard JavaScript and HTML. No custom syntax, no build step required, no framework lock-in.
|
|
6
6
|
|
|
7
|
+
> **Current Release:** v1.0.0 (stable) | **Next Release:** v2.0.0-alpha.2
|
|
8
|
+
> Install stable: `npm install lume-js@1.0.0`
|
|
9
|
+
> Install next: `npm install lume-js@next`
|
|
10
|
+
|
|
7
11
|
[](LICENSE)
|
|
8
|
-
[](package.json)
|
|
13
|
+
[](tests/)
|
|
10
14
|
[](dist/)
|
|
11
15
|
|
|
12
16
|
## Why Lume.js?
|
|
@@ -93,8 +97,10 @@ Full documentation is available in the [docs/](docs/) directory:
|
|
|
93
97
|
- **[Tutorial: Build Tic-Tac-Toe](docs/tutorials/build-tic-tac-toe.md)**
|
|
94
98
|
- **[Working with Arrays](docs/tutorials/working-with-arrays.md)**
|
|
95
99
|
- **API Reference**
|
|
96
|
-
- [Core (state, bindDom
|
|
97
|
-
- [
|
|
100
|
+
- [Core (state, bindDom)](docs/api/core/state.md)
|
|
101
|
+
- [Effect System](docs/api/core/effect.md)
|
|
102
|
+
- [Plugins (v2.0+)](docs/api/core/plugins.md)
|
|
103
|
+
- [Addons (computed, repeat, debug)](docs/api/addons/computed.md)
|
|
98
104
|
- **[Design Philosophy](docs/design/design-decisions.md)**
|
|
99
105
|
|
|
100
106
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lume-js",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-alpha.2",
|
|
4
4
|
"description": "Minimal reactive state management using only standard JavaScript and HTML - no custom syntax, no build step required",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"size": "node scripts/check-size.js",
|
|
24
24
|
"test": "vitest run",
|
|
25
25
|
"test:watch": "vitest",
|
|
26
|
-
"coverage": "vitest run --coverage"
|
|
26
|
+
"coverage": "vitest run --coverage",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"validate": "npm run size && npm run typecheck && npm test"
|
|
27
29
|
},
|
|
28
30
|
"files": [
|
|
29
31
|
"src",
|
|
@@ -53,7 +55,7 @@
|
|
|
53
55
|
"license": "MIT",
|
|
54
56
|
"repository": {
|
|
55
57
|
"type": "git",
|
|
56
|
-
"url": "https://github.com/sathvikc/lume-js.git"
|
|
58
|
+
"url": "git+https://github.com/sathvikc/lume-js.git"
|
|
57
59
|
},
|
|
58
60
|
"bugs": {
|
|
59
61
|
"url": "https://github.com/sathvikc/lume-js/issues"
|
|
@@ -69,6 +71,8 @@
|
|
|
69
71
|
"marked": "^17.0.1",
|
|
70
72
|
"postcss": "^8.5.6",
|
|
71
73
|
"tailwindcss": "^4.1.17",
|
|
74
|
+
"terser": "^5.44.1",
|
|
75
|
+
"typescript": "^5.9.3",
|
|
72
76
|
"vite": "^7.1.9",
|
|
73
77
|
"vitest": "^2.1.4"
|
|
74
78
|
},
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lume-JS Debug Addon
|
|
3
|
+
*
|
|
4
|
+
* Developer-friendly logging and inspection of reactive state operations.
|
|
5
|
+
* Critical for adoption - hard to debug = hard to adopt.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { createDebugPlugin, debug } from "lume-js/addons";
|
|
9
|
+
*
|
|
10
|
+
* const store = state({ count: 0 }, {
|
|
11
|
+
* plugins: [createDebugPlugin({ label: 'myStore' })]
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* debug.enable(); // Enable logging
|
|
15
|
+
* debug.filter('count'); // Only log 'count' key
|
|
16
|
+
* debug.stats(); // Show statistics
|
|
17
|
+
*
|
|
18
|
+
* @module addons/debug
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Global debug state
|
|
22
|
+
let globalEnabled = true;
|
|
23
|
+
let globalFilter = null; // string, RegExp, or null
|
|
24
|
+
const stats = new Map(); // label -> { gets: Map, sets: Map, notifies: Map }
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if a key matches the current filter
|
|
28
|
+
* @param {string} key
|
|
29
|
+
* @returns {boolean}
|
|
30
|
+
*/
|
|
31
|
+
function matchesFilter(key) {
|
|
32
|
+
if (globalFilter === null) return true;
|
|
33
|
+
if (typeof globalFilter === 'string') {
|
|
34
|
+
return key.includes(globalFilter);
|
|
35
|
+
}
|
|
36
|
+
if (globalFilter instanceof RegExp) {
|
|
37
|
+
return globalFilter.test(key);
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get or create stats entry for a label
|
|
44
|
+
* @param {string} label
|
|
45
|
+
* @returns {object}
|
|
46
|
+
*/
|
|
47
|
+
function getStats(label) {
|
|
48
|
+
if (!stats.has(label)) {
|
|
49
|
+
stats.set(label, {
|
|
50
|
+
gets: new Map(),
|
|
51
|
+
sets: new Map(),
|
|
52
|
+
notifies: new Map()
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return stats.get(label);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Increment a stat counter
|
|
60
|
+
* @param {string} label
|
|
61
|
+
* @param {'gets'|'sets'|'notifies'} type
|
|
62
|
+
* @param {string} key
|
|
63
|
+
*/
|
|
64
|
+
function incrementStat(label, type, key) {
|
|
65
|
+
const s = getStats(label);
|
|
66
|
+
const map = s[type];
|
|
67
|
+
map.set(key, (map.get(key) || 0) + 1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Format value for logging (truncate long values)
|
|
72
|
+
* @param {any} value
|
|
73
|
+
* @returns {string}
|
|
74
|
+
*/
|
|
75
|
+
function formatValue(value) {
|
|
76
|
+
try {
|
|
77
|
+
const json = JSON.stringify(value);
|
|
78
|
+
if (json && json.length > 100) {
|
|
79
|
+
return json.slice(0, 97) + '...';
|
|
80
|
+
}
|
|
81
|
+
return json;
|
|
82
|
+
} catch {
|
|
83
|
+
return String(value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create a debug plugin instance for a reactive state store.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} [options] - Configuration options
|
|
91
|
+
* @param {string} [options.label='store'] - Label for log messages
|
|
92
|
+
* @param {boolean} [options.logGet=false] - Log property reads (can be noisy)
|
|
93
|
+
* @param {boolean} [options.logSet=true] - Log property writes
|
|
94
|
+
* @param {boolean} [options.logNotify=true] - Log subscriber notifications
|
|
95
|
+
* @param {boolean} [options.trace=false] - Show stack trace for SET operations
|
|
96
|
+
* @returns {object} Plugin object for state()
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const store = state({ count: 0 }, {
|
|
100
|
+
* plugins: [createDebugPlugin({ label: 'counter' })]
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // With stack traces for debugging where state changes originate
|
|
105
|
+
* const store = state({ count: 0 }, {
|
|
106
|
+
* plugins: [createDebugPlugin({ label: 'counter', trace: true })]
|
|
107
|
+
* });
|
|
108
|
+
*/
|
|
109
|
+
export function createDebugPlugin(options = {}) {
|
|
110
|
+
const label = options.label ?? 'store';
|
|
111
|
+
|
|
112
|
+
// IMPORTANT: Do NOT destructure options here!
|
|
113
|
+
// Options may contain getters for dynamic runtime toggling (e.g., from UI).
|
|
114
|
+
// Destructuring would copy values once at creation time, breaking reactivity.
|
|
115
|
+
// Use getOpt() helper to read options dynamically in each hook.
|
|
116
|
+
const getOpt = (name, defaultVal) => {
|
|
117
|
+
const val = options[name];
|
|
118
|
+
return val !== undefined ? val : defaultVal;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
name: `debug:${label}`,
|
|
123
|
+
|
|
124
|
+
onInit: () => {
|
|
125
|
+
if (globalEnabled) {
|
|
126
|
+
console.log(`%c[${label}]%c initialized`, 'color: #888; font-weight: bold', 'color: inherit');
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
onGet: (key, value) => {
|
|
131
|
+
// Skip internal properties
|
|
132
|
+
if (typeof key === 'string' && key.startsWith('$')) {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
incrementStat(label, 'gets', key);
|
|
137
|
+
|
|
138
|
+
if (globalEnabled && getOpt('logGet', false) && matchesFilter(key)) {
|
|
139
|
+
console.log(
|
|
140
|
+
`%c[${label}]%c GET %c${key}%c = ${formatValue(value)}`,
|
|
141
|
+
'color: #888; font-weight: bold',
|
|
142
|
+
'color: #4CAF50',
|
|
143
|
+
'color: #2196F3; font-weight: bold',
|
|
144
|
+
'color: inherit'
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return value;
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
onSet: (key, newValue, oldValue) => {
|
|
152
|
+
// Skip internal properties
|
|
153
|
+
if (typeof key === 'string' && key.startsWith('$')) {
|
|
154
|
+
return newValue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
incrementStat(label, 'sets', key);
|
|
158
|
+
|
|
159
|
+
if (globalEnabled && getOpt('logSet', true) && matchesFilter(key)) {
|
|
160
|
+
console.log(
|
|
161
|
+
`%c[${label}]%c SET %c${key}%c: ${formatValue(oldValue)} → ${formatValue(newValue)}`,
|
|
162
|
+
'color: #888; font-weight: bold',
|
|
163
|
+
'color: #FF9800',
|
|
164
|
+
'color: #2196F3; font-weight: bold',
|
|
165
|
+
'color: inherit'
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Show stack trace if enabled (helps find where state changes originate)
|
|
169
|
+
if (getOpt('trace', false)) {
|
|
170
|
+
console.trace(`%c[${label}] Stack trace for ${key}`, 'color: #888');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return newValue;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
onSubscribe: (key) => {
|
|
178
|
+
if (globalEnabled && matchesFilter(key)) {
|
|
179
|
+
console.log(
|
|
180
|
+
`%c[${label}]%c SUBSCRIBE %c${key}`,
|
|
181
|
+
'color: #888; font-weight: bold',
|
|
182
|
+
'color: #9C27B0',
|
|
183
|
+
'color: #2196F3; font-weight: bold'
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
onNotify: (key, value) => {
|
|
189
|
+
// Skip internal properties
|
|
190
|
+
if (typeof key === 'string' && key.startsWith('$')) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
incrementStat(label, 'notifies', key);
|
|
195
|
+
|
|
196
|
+
if (globalEnabled && getOpt('logNotify', true) && matchesFilter(key)) {
|
|
197
|
+
console.log(
|
|
198
|
+
`%c[${label}]%c NOTIFY %c${key}%c = ${formatValue(value)}`,
|
|
199
|
+
'color: #888; font-weight: bold',
|
|
200
|
+
'color: #E91E63',
|
|
201
|
+
'color: #2196F3; font-weight: bold',
|
|
202
|
+
'color: inherit'
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Global debug controls
|
|
211
|
+
*/
|
|
212
|
+
export const debug = {
|
|
213
|
+
/**
|
|
214
|
+
* Enable debug logging globally
|
|
215
|
+
*/
|
|
216
|
+
enable() {
|
|
217
|
+
globalEnabled = true;
|
|
218
|
+
console.log('%c[lume-debug]%c Logging enabled', 'color: #888; font-weight: bold', 'color: #4CAF50');
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Disable debug logging globally
|
|
223
|
+
*/
|
|
224
|
+
disable() {
|
|
225
|
+
globalEnabled = false;
|
|
226
|
+
console.log('%c[lume-debug]%c Logging disabled', 'color: #888; font-weight: bold', 'color: #F44336');
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check if debug logging is currently enabled
|
|
231
|
+
* @returns {boolean}
|
|
232
|
+
*/
|
|
233
|
+
isEnabled() {
|
|
234
|
+
return globalEnabled;
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Filter logs by key pattern
|
|
239
|
+
* @param {string|RegExp|null} pattern - Pattern to match, or null to clear filter
|
|
240
|
+
*/
|
|
241
|
+
filter(pattern) {
|
|
242
|
+
globalFilter = pattern;
|
|
243
|
+
if (pattern === null) {
|
|
244
|
+
console.log('%c[lume-debug]%c Filter cleared', 'color: #888; font-weight: bold', 'color: inherit');
|
|
245
|
+
} else {
|
|
246
|
+
console.log(`%c[lume-debug]%c Filter set: ${pattern}`, 'color: #888; font-weight: bold', 'color: inherit');
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get current filter pattern
|
|
252
|
+
* @returns {string|RegExp|null}
|
|
253
|
+
*/
|
|
254
|
+
getFilter() {
|
|
255
|
+
return globalFilter;
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get statistics data (silent - no console output)
|
|
260
|
+
* Use logStats() if you want to see stats in console.
|
|
261
|
+
* @returns {object} Stats object for programmatic access
|
|
262
|
+
*/
|
|
263
|
+
stats() {
|
|
264
|
+
const result = {};
|
|
265
|
+
|
|
266
|
+
for (const [label, data] of stats) {
|
|
267
|
+
result[label] = {
|
|
268
|
+
gets: Object.fromEntries(data.gets),
|
|
269
|
+
sets: Object.fromEntries(data.sets),
|
|
270
|
+
notifies: Object.fromEntries(data.notifies)
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return result;
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Log statistics summary to console (with formatting)
|
|
279
|
+
* @returns {object} Stats object for programmatic access
|
|
280
|
+
*/
|
|
281
|
+
logStats() {
|
|
282
|
+
const result = this.stats();
|
|
283
|
+
|
|
284
|
+
if (Object.keys(result).length === 0) {
|
|
285
|
+
console.log('%c[lume-debug]%c No stats collected yet', 'color: #888; font-weight: bold', 'color: inherit');
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
console.group('%c[lume-debug] Statistics', 'color: #888; font-weight: bold');
|
|
290
|
+
|
|
291
|
+
for (const [label, data] of Object.entries(result)) {
|
|
292
|
+
console.group(`%c${label}`, 'color: #2196F3; font-weight: bold');
|
|
293
|
+
|
|
294
|
+
// Use console.table for better formatted output
|
|
295
|
+
const tableData = [];
|
|
296
|
+
const allKeys = new Set([
|
|
297
|
+
...Object.keys(data.gets),
|
|
298
|
+
...Object.keys(data.sets),
|
|
299
|
+
...Object.keys(data.notifies)
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
for (const key of allKeys) {
|
|
303
|
+
tableData.push({
|
|
304
|
+
key,
|
|
305
|
+
gets: data.gets[key] || 0,
|
|
306
|
+
sets: data.sets[key] || 0,
|
|
307
|
+
notifies: data.notifies[key] || 0
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (tableData.length > 0) {
|
|
312
|
+
console.table(tableData);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
console.groupEnd();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
console.groupEnd();
|
|
319
|
+
|
|
320
|
+
return result;
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Reset all collected statistics
|
|
325
|
+
*/
|
|
326
|
+
resetStats() {
|
|
327
|
+
stats.clear();
|
|
328
|
+
console.log('%c[lume-debug]%c Stats reset', 'color: #888; font-weight: bold', 'color: inherit');
|
|
329
|
+
}
|
|
330
|
+
};
|
package/src/addons/index.d.ts
CHANGED
|
@@ -115,19 +115,19 @@ export type ScrollPreservation = (container: HTMLElement, context?: Preservation
|
|
|
115
115
|
export interface RepeatOptions<T> {
|
|
116
116
|
/** Function to extract unique key from item */
|
|
117
117
|
key: (item: T) => string | number;
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
/** Function to render/update an item's element */
|
|
120
120
|
render: (item: T, element: HTMLElement, index: number) => void;
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
/** Element tag name or factory function (default: 'div') */
|
|
123
123
|
element?: string | (() => HTMLElement);
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
/**
|
|
126
126
|
* Focus preservation strategy (default: defaultFocusPreservation)
|
|
127
127
|
* Set to null to disable focus preservation
|
|
128
128
|
*/
|
|
129
129
|
preserveFocus?: FocusPreservation | null;
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
/**
|
|
132
132
|
* Scroll preservation strategy (default: defaultScrollPreservation)
|
|
133
133
|
* Set to null to disable scroll preservation
|
|
@@ -256,3 +256,97 @@ export function repeat<T>(
|
|
|
256
256
|
arrayKey: string,
|
|
257
257
|
options: RepeatOptions<T>
|
|
258
258
|
): Unsubscribe;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Options for createDebugPlugin
|
|
262
|
+
*/
|
|
263
|
+
export interface DebugPluginOptions {
|
|
264
|
+
/** Label for log messages (default: 'store') */
|
|
265
|
+
label?: string;
|
|
266
|
+
/** Log property reads - can be noisy (default: false) */
|
|
267
|
+
logGet?: boolean;
|
|
268
|
+
/** Log property writes (default: true) */
|
|
269
|
+
logSet?: boolean;
|
|
270
|
+
/** Log subscriber notifications (default: true) */
|
|
271
|
+
logNotify?: boolean;
|
|
272
|
+
/** Show stack trace for SET operations - helps find where state changes originate (default: false) */
|
|
273
|
+
trace?: boolean;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* State plugin with lifecycle hooks for debugging
|
|
278
|
+
*/
|
|
279
|
+
export interface DebugPlugin {
|
|
280
|
+
name: string;
|
|
281
|
+
onInit?: () => void;
|
|
282
|
+
onGet?: (key: string, value: any) => any;
|
|
283
|
+
onSet?: (key: string, newValue: any, oldValue: any) => any;
|
|
284
|
+
onSubscribe?: (key: string) => void;
|
|
285
|
+
onNotify?: (key: string, value: any) => void;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Create a debug plugin instance for a reactive state store.
|
|
290
|
+
* Logs state operations to the console with colored output.
|
|
291
|
+
*
|
|
292
|
+
* @param options - Configuration options
|
|
293
|
+
* @returns Plugin object for state()
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* import { state } from 'lume-js';
|
|
298
|
+
* import { createDebugPlugin } from 'lume-js/addons';
|
|
299
|
+
*
|
|
300
|
+
* const store = state({ count: 0 }, {
|
|
301
|
+
* plugins: [createDebugPlugin({ label: 'counter' })]
|
|
302
|
+
* });
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
export function createDebugPlugin(options?: DebugPluginOptions): DebugPlugin;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Statistics for a single store
|
|
309
|
+
*/
|
|
310
|
+
export interface DebugStats {
|
|
311
|
+
gets: Record<string, number>;
|
|
312
|
+
sets: Record<string, number>;
|
|
313
|
+
notifies: Record<string, number>;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Global debug controls for Lume.js
|
|
318
|
+
*/
|
|
319
|
+
export interface Debug {
|
|
320
|
+
/** Enable debug logging globally */
|
|
321
|
+
enable(): void;
|
|
322
|
+
/** Disable debug logging globally */
|
|
323
|
+
disable(): void;
|
|
324
|
+
/** Check if debug logging is enabled */
|
|
325
|
+
isEnabled(): boolean;
|
|
326
|
+
/** Filter logs by key pattern (string, RegExp, or null to clear) */
|
|
327
|
+
filter(pattern: string | RegExp | null): void;
|
|
328
|
+
/** Get current filter pattern */
|
|
329
|
+
getFilter(): string | RegExp | null;
|
|
330
|
+
/** Get statistics data (silent - no console output) */
|
|
331
|
+
stats(): Record<string, DebugStats>;
|
|
332
|
+
/** Log statistics to console with table formatting */
|
|
333
|
+
logStats(): Record<string, DebugStats>;
|
|
334
|
+
/** Reset all collected statistics */
|
|
335
|
+
resetStats(): void;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Global debug controls
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* import { debug } from 'lume-js/addons';
|
|
344
|
+
*
|
|
345
|
+
* debug.enable(); // Enable logging
|
|
346
|
+
* debug.filter('count'); // Only log keys containing 'count'
|
|
347
|
+
* debug.stats(); // Show statistics
|
|
348
|
+
* debug.disable(); // Disable logging
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
export const debug: Debug;
|
|
352
|
+
|
package/src/addons/index.js
CHANGED