@uistate/core 4.0.0 → 4.1.1
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/LICENSE +21 -0
- package/README.md +50 -88
- package/package.json +22 -22
- package/src/stateInspector.js +0 -140
- package/src/telemetryPlugin.js +0 -638
- /package/{src/cssState.js → cssState.js} +0 -0
- /package/{src/eventState.js → eventState.js} +0 -0
- /package/{src/index.js → index.js} +0 -0
- /package/{src/stateSerializer.js → stateSerializer.js} +0 -0
- /package/{src/templateManager.js → templateManager.js} +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Imsirovic Ajdin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,37 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# UIstate
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
**maintainer**: uistate <ajdika.i@gmail.com> (npm)
|
|
3
|
+
A revolutionary approach to UI state management using CSS custom properties and DOM attributes, featuring Attribute-Driven State Inheritance (ADSI).
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
**Current Version**: 4.1.1
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- 🛠️ Added `setStates()` method for batch state updates
|
|
11
|
-
- 🧩 Added `registerSpecialHandler()` and `registerEventBinding()` for extensibility
|
|
12
|
-
- 🏷️ New `setupObservers()` for automatic state-to-DOM binding
|
|
13
|
-
- ⚡ Improved `setupStateActions()` with customizable event handling
|
|
14
|
-
- 💾 Default event handlers for clicks and inputs with declarative binding
|
|
15
|
-
- 🗡️ Enhanced attribute serialization with specialized handlers
|
|
16
|
-
|
|
17
|
-
## What's New in v3.0.0
|
|
18
|
-
|
|
19
|
-
- 🔄 Fully declarative state management approach
|
|
20
|
-
- 🧩 Enhanced template system with CSS-based templates
|
|
21
|
-
- 🚀 Improved performance through optimized state propagation
|
|
22
|
-
- 📦 New state serialization and inspection capabilities
|
|
23
|
-
- 🔍 Telemetry plugin for better debugging
|
|
24
|
-
|
|
25
|
-
## Features
|
|
26
|
-
|
|
27
|
-
- 🚀 O(1) state updates using CSS custom properties
|
|
28
|
-
- 📉 Significant memory savings compared to virtual DOM approaches
|
|
29
|
-
- 🎯 Zero configuration
|
|
30
|
-
- 🔄 Automatic reactivity through CSS cascade
|
|
31
|
-
- 🎨 Framework agnostic
|
|
32
|
-
- 📦 Tiny bundle size (~2KB)
|
|
33
|
-
- 🧩 Modular architecture with dedicated modules for CSS state and templates
|
|
34
|
-
- 📝 Declarative HTML-in-CSS templates
|
|
7
|
+
**Author**: Ajdin Imsirovic <ajdika@live.com> (GitHub)
|
|
8
|
+
**Maintainer**: uistate <ajdika.i@gmail.com> (npm)
|
|
35
9
|
|
|
36
10
|
## Installation
|
|
37
11
|
|
|
@@ -39,85 +13,73 @@ High-performance UI state management using CSS custom properties and ADSI (Attri
|
|
|
39
13
|
npm install @uistate/core
|
|
40
14
|
```
|
|
41
15
|
|
|
42
|
-
##
|
|
43
|
-
|
|
44
|
-
### Performance
|
|
16
|
+
## Quick Start
|
|
45
17
|
|
|
46
|
-
|
|
47
|
-
- **DOM as Source of Truth**: Efficient state storage using CSS custom properties and data attributes
|
|
48
|
-
- **Minimal Overhead**: No virtual DOM diffing or shadow DOM needed
|
|
18
|
+
UIstate v4.1.1 provides four core modules that can be imported individually:
|
|
49
19
|
|
|
50
|
-
|
|
20
|
+
```javascript
|
|
21
|
+
import { createCssState, createEventState, stateSerializer, createTemplateManager } from '@uistate/core';
|
|
51
22
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- **Zero Config**: No store setup, no reducers, no actions
|
|
55
|
-
- **CSS-Native**: Leverages the power of CSS selectors and the cascade
|
|
23
|
+
// Create CSS-based state management
|
|
24
|
+
const cssState = createCssState();
|
|
56
25
|
|
|
57
|
-
|
|
26
|
+
// Create event-based state management
|
|
27
|
+
const eventState = createEventState();
|
|
58
28
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- **Automatic State Propagation**: State changes automatically update the UI
|
|
29
|
+
// Use state serialization utilities
|
|
30
|
+
const serialized = stateSerializer.serialize(myState);
|
|
62
31
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
@uistate/core/
|
|
67
|
-
├── src/ # Core library
|
|
68
|
-
│ ├── index.js # Main entry
|
|
69
|
-
│ ├── cssState.js # CSS variables management
|
|
70
|
-
│ ├── templateManager.js # Declarative template management
|
|
71
|
-
│ ├── stateInspector.js # State inspection tools
|
|
72
|
-
│ └── stateSerializer.js # State serialization
|
|
73
|
-
└── examples/ # Example applications
|
|
74
|
-
└── 001-.../ # Progressive examples from simpler to more complex
|
|
32
|
+
// Create template manager for declarative UI
|
|
33
|
+
const templateManager = createTemplateManager();
|
|
75
34
|
```
|
|
76
35
|
|
|
77
|
-
##
|
|
36
|
+
## Core Modules
|
|
78
37
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
- Safari 10.1+
|
|
82
|
-
- Edge 79+
|
|
83
|
-
|
|
84
|
-
# Core Ideas Behind UIstate
|
|
38
|
+
### `createCssState`
|
|
39
|
+
Manages state using CSS custom properties for optimal performance and automatic reactivity.
|
|
85
40
|
|
|
86
|
-
|
|
41
|
+
### `createEventState`
|
|
42
|
+
Provides event-driven state management with pub/sub patterns.
|
|
87
43
|
|
|
88
|
-
|
|
44
|
+
### `stateSerializer`
|
|
45
|
+
Utilities for serializing and deserializing state data.
|
|
89
46
|
|
|
90
|
-
###
|
|
47
|
+
### `createTemplateManager`
|
|
48
|
+
Declarative template management system for building UIs with CSS-based templates.
|
|
91
49
|
|
|
92
|
-
|
|
50
|
+
## Key Features
|
|
93
51
|
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
52
|
+
- Potentially O(1) state updates
|
|
53
|
+
- Significant memory savings compared to virtual DOM approaches
|
|
54
|
+
- DOM as the single source of truth
|
|
55
|
+
- CSS-driven state derivation
|
|
56
|
+
- Framework agnostic
|
|
57
|
+
- Tiny bundle size (~30KB uncompressed, ~8-10KB gzipped)
|
|
58
|
+
- Zero dependencies
|
|
59
|
+
- Modular architecture - import only what you need
|
|
99
60
|
|
|
100
|
-
|
|
61
|
+
## Browser Support
|
|
101
62
|
|
|
102
|
-
|
|
63
|
+
- Chrome 60+
|
|
64
|
+
- Firefox 54+
|
|
65
|
+
- Safari 10.1+
|
|
66
|
+
- Edge 79+
|
|
103
67
|
|
|
104
|
-
|
|
105
|
-
- **registerActions()**: Register event handlers for UI components
|
|
106
|
-
- **attachDelegation()**: Set up event delegation for efficient event handling
|
|
68
|
+
## Philosophy
|
|
107
69
|
|
|
108
|
-
|
|
70
|
+
UIstate challenges traditional assumptions in web development by using the DOM as the source of truth for state, leveraging CSS variables and data attributes for state storage, and using the CSS cascade for state inheritance and derivation.
|
|
109
71
|
|
|
110
|
-
1.
|
|
111
|
-
2. Provides a clear separation between state storage (CSS) and behavior (JavaScript)
|
|
112
|
-
3. Implements a pub/sub pattern for reactive updates
|
|
113
|
-
4. Leverages CSS templates for declarative UI definition
|
|
72
|
+
The v4.1.0 release focuses on simplicity and modularity - providing clean, individual modules that can be composed together as needed, without the complexity of a unified framework.
|
|
114
73
|
|
|
115
|
-
|
|
74
|
+
## Examples
|
|
116
75
|
|
|
117
|
-
|
|
76
|
+
Explore our documentation and examples to see UIstate in action:
|
|
118
77
|
|
|
119
|
-
|
|
78
|
+
- Range sliders with different state derivation approaches
|
|
79
|
+
- Button toggles with CSS state projection
|
|
80
|
+
- Font adjusters with domain-based state management
|
|
81
|
+
- And more!
|
|
120
82
|
|
|
121
83
|
## License
|
|
122
84
|
|
|
123
|
-
MIT
|
|
85
|
+
MIT Ajdin Imsirovic
|
package/package.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uistate/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.1",
|
|
4
|
+
"description": "Revolutionary DOM-based state management using CSS custom properties - zero dependencies, potential O(1) updates",
|
|
4
5
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./cssState": "./cssState.js",
|
|
10
|
+
"./eventState": "./eventState.js",
|
|
11
|
+
"./stateSerializer": "./stateSerializer.js",
|
|
12
|
+
"./templateManager": "./templateManager.js"
|
|
13
|
+
},
|
|
8
14
|
"files": [
|
|
9
|
-
"
|
|
15
|
+
"index.js",
|
|
16
|
+
"cssState.js",
|
|
17
|
+
"eventState.js",
|
|
18
|
+
"stateSerializer.js",
|
|
19
|
+
"templateManager.js"
|
|
10
20
|
],
|
|
11
|
-
"scripts": {},
|
|
12
21
|
"keywords": [
|
|
13
22
|
"state-management",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"vanilla-js"
|
|
19
|
-
],
|
|
20
|
-
"author": "Ajdin Imsirovic <ajdika@live.com> (GitHub)",
|
|
21
|
-
"contributors": [
|
|
22
|
-
"uistate <ajdika.i@gmail.com> (npm)"
|
|
23
|
+
"css-variables",
|
|
24
|
+
"dom-events",
|
|
25
|
+
"zero-dependency",
|
|
26
|
+
"modular"
|
|
23
27
|
],
|
|
28
|
+
"author": "Ajdin Imsirovic",
|
|
24
29
|
"license": "MIT",
|
|
25
30
|
"repository": {
|
|
26
31
|
"type": "git",
|
|
27
|
-
"url": "
|
|
28
|
-
}
|
|
29
|
-
"bugs": {
|
|
30
|
-
"url": "https://github.com/ImsirovicAjdin/uistate/issues"
|
|
31
|
-
},
|
|
32
|
-
"homepage": "https://github.com/ImsirovicAjdin/uistate#readme",
|
|
33
|
-
"sideEffects": false
|
|
32
|
+
"url": "https://github.com/ImsirovicAjdin/uistate"
|
|
33
|
+
}
|
|
34
34
|
}
|
package/src/stateInspector.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* StateInspector - Real-time state inspection panel for UIstate
|
|
3
|
-
*
|
|
4
|
-
* This module provides a configurable UI panel that displays and allows manipulation
|
|
5
|
-
* of CSS variables and state values in UIstate applications.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Toggle visibility with a dedicated button
|
|
9
|
-
* - Real-time updates of CSS variable values
|
|
10
|
-
* - Organized display of state by categories
|
|
11
|
-
* - Ability to filter state variables
|
|
12
|
-
* - Direct manipulation of state values
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Create a configured state inspector instance
|
|
17
|
-
* @param {Object} config - Configuration options
|
|
18
|
-
* @returns {Object} - StateInspector instance
|
|
19
|
-
*/
|
|
20
|
-
function createStateInspector(config = {}) {
|
|
21
|
-
// Default configuration
|
|
22
|
-
const defaultConfig = {
|
|
23
|
-
target: document.body,
|
|
24
|
-
initiallyVisible: false,
|
|
25
|
-
categories: ['app', 'ui', 'data'],
|
|
26
|
-
position: 'bottom-right',
|
|
27
|
-
maxHeight: '300px',
|
|
28
|
-
stateManager: null, // Optional reference to UIstate or CssState instance
|
|
29
|
-
theme: 'light'
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Merge provided config with defaults
|
|
33
|
-
const options = { ...defaultConfig, ...config };
|
|
34
|
-
|
|
35
|
-
// Private state
|
|
36
|
-
let isVisible = options.initiallyVisible;
|
|
37
|
-
let panel = null;
|
|
38
|
-
let toggleButton = null;
|
|
39
|
-
let filterInput = null;
|
|
40
|
-
let stateContainer = null;
|
|
41
|
-
let currentFilter = '';
|
|
42
|
-
|
|
43
|
-
// StateInspector instance
|
|
44
|
-
const inspector = {
|
|
45
|
-
// Current configuration
|
|
46
|
-
config: options,
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Update configuration
|
|
50
|
-
* @param {Object} newConfig - New configuration options
|
|
51
|
-
*/
|
|
52
|
-
configure(newConfig) {
|
|
53
|
-
Object.assign(this.config, newConfig);
|
|
54
|
-
this.refresh();
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Attach the inspector panel to the target element
|
|
59
|
-
* @param {Element} element - Target element to attach to (defaults to config.target)
|
|
60
|
-
* @returns {Object} - The inspector instance for chaining
|
|
61
|
-
*/
|
|
62
|
-
attach(element = this.config.target) {
|
|
63
|
-
// Implementation will create and attach the panel
|
|
64
|
-
// For now, this is a placeholder
|
|
65
|
-
console.log('StateInspector: Panel would be attached to', element);
|
|
66
|
-
return this;
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Show the inspector panel
|
|
71
|
-
* @returns {Object} - The inspector instance for chaining
|
|
72
|
-
*/
|
|
73
|
-
show() {
|
|
74
|
-
isVisible = true;
|
|
75
|
-
if (panel) panel.style.display = 'block';
|
|
76
|
-
return this;
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Hide the inspector panel
|
|
81
|
-
* @returns {Object} - The inspector instance for chaining
|
|
82
|
-
*/
|
|
83
|
-
hide() {
|
|
84
|
-
isVisible = false;
|
|
85
|
-
if (panel) panel.style.display = 'none';
|
|
86
|
-
return this;
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Toggle the visibility of the inspector panel
|
|
91
|
-
* @returns {Object} - The inspector instance for chaining
|
|
92
|
-
*/
|
|
93
|
-
toggle() {
|
|
94
|
-
return isVisible ? this.hide() : this.show();
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Refresh the state display
|
|
99
|
-
* @returns {Object} - The inspector instance for chaining
|
|
100
|
-
*/
|
|
101
|
-
refresh() {
|
|
102
|
-
// Implementation will update the displayed state values
|
|
103
|
-
// For now, this is a placeholder
|
|
104
|
-
console.log('StateInspector: State display would be refreshed');
|
|
105
|
-
return this;
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Filter the displayed state variables
|
|
110
|
-
* @param {string} filterText - Text to filter by
|
|
111
|
-
* @returns {Object} - The inspector instance for chaining
|
|
112
|
-
*/
|
|
113
|
-
filter(filterText) {
|
|
114
|
-
currentFilter = filterText;
|
|
115
|
-
// Implementation will filter the displayed variables
|
|
116
|
-
return this.refresh();
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Clean up resources used by the inspector
|
|
121
|
-
*/
|
|
122
|
-
destroy() {
|
|
123
|
-
if (panel && panel.parentNode) {
|
|
124
|
-
panel.parentNode.removeChild(panel);
|
|
125
|
-
}
|
|
126
|
-
panel = null;
|
|
127
|
-
toggleButton = null;
|
|
128
|
-
filterInput = null;
|
|
129
|
-
stateContainer = null;
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
return inspector;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Create a default instance
|
|
137
|
-
const StateInspector = createStateInspector();
|
|
138
|
-
|
|
139
|
-
export default StateInspector;
|
|
140
|
-
export { createStateInspector };
|
package/src/telemetryPlugin.js
DELETED
|
@@ -1,638 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UIstate Telemetry Plugin
|
|
3
|
-
*
|
|
4
|
-
* A plugin for tracking and analyzing usage patterns in UIstate applications.
|
|
5
|
-
* Provides insights into method calls, state changes, and performance metrics.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Automatic tracking of API method calls
|
|
9
|
-
* - State change monitoring
|
|
10
|
-
* - Performance timing
|
|
11
|
-
* - Usage statistics and reporting
|
|
12
|
-
* - Visual dashboard integration
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Create a telemetry plugin instance
|
|
17
|
-
* @param {Object} config - Configuration options
|
|
18
|
-
* @returns {Object} - Configured telemetry plugin
|
|
19
|
-
*/
|
|
20
|
-
const createTelemetryPlugin = (config = {}) => {
|
|
21
|
-
// Default configuration
|
|
22
|
-
const defaultConfig = {
|
|
23
|
-
enabled: true,
|
|
24
|
-
trackStateChanges: true,
|
|
25
|
-
trackMethodCalls: true,
|
|
26
|
-
trackPerformance: true,
|
|
27
|
-
maxEntries: 1000,
|
|
28
|
-
logToConsole: false,
|
|
29
|
-
samplingRate: 1.0, // 1.0 = track everything, 0.5 = track 50% of events
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Merge provided config with defaults
|
|
33
|
-
const options = { ...defaultConfig, ...config };
|
|
34
|
-
|
|
35
|
-
// Telemetry data storage
|
|
36
|
-
const data = {
|
|
37
|
-
startTime: Date.now(),
|
|
38
|
-
methodCalls: new Map(),
|
|
39
|
-
stateChanges: new Map(),
|
|
40
|
-
performanceMetrics: new Map(),
|
|
41
|
-
unusedMethods: new Set(),
|
|
42
|
-
activeTimers: new Map()
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Reference to the UIstate instance
|
|
46
|
-
let uistateRef = null;
|
|
47
|
-
|
|
48
|
-
// Original method references
|
|
49
|
-
const originalMethods = new Map();
|
|
50
|
-
|
|
51
|
-
// Methods to track (will be populated during initialization)
|
|
52
|
-
const methodsToTrack = [
|
|
53
|
-
'getState', 'setState', 'subscribe', 'observe',
|
|
54
|
-
'configureSerializer', 'getSerializerConfig', 'setSerializationMode',
|
|
55
|
-
'onAction', 'registerActions', 'attachDelegation', 'mount',
|
|
56
|
-
'renderTemplateFromCss', 'createComponent', 'applyClassesFromState'
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
// Helper function to check if we should sample this event
|
|
60
|
-
const shouldSample = () => {
|
|
61
|
-
return options.samplingRate >= 1.0 || Math.random() <= options.samplingRate;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Helper function to track a method call
|
|
65
|
-
const trackMethodCall = (methodName, args = [], result = undefined, error = null, duration = 0) => {
|
|
66
|
-
if (!options.enabled || !options.trackMethodCalls || !shouldSample()) return;
|
|
67
|
-
|
|
68
|
-
if (!data.methodCalls.has(methodName)) {
|
|
69
|
-
data.methodCalls.set(methodName, []);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const calls = data.methodCalls.get(methodName);
|
|
73
|
-
|
|
74
|
-
// Limit the number of entries to prevent memory issues
|
|
75
|
-
if (calls.length >= options.maxEntries) {
|
|
76
|
-
calls.shift(); // Remove oldest entry
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Create a safe copy of arguments (avoiding circular references)
|
|
80
|
-
const safeArgs = Array.from(args).map(arg => {
|
|
81
|
-
try {
|
|
82
|
-
// For simple types, return as is
|
|
83
|
-
if (arg === null || arg === undefined ||
|
|
84
|
-
typeof arg === 'string' ||
|
|
85
|
-
typeof arg === 'number' ||
|
|
86
|
-
typeof arg === 'boolean') {
|
|
87
|
-
return arg;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// For functions, just return the function name or "[Function]"
|
|
91
|
-
if (typeof arg === 'function') {
|
|
92
|
-
return arg.name || "[Function]";
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// For objects, create a simplified representation
|
|
96
|
-
if (typeof arg === 'object') {
|
|
97
|
-
if (Array.isArray(arg)) {
|
|
98
|
-
return `[Array(${arg.length})]`;
|
|
99
|
-
}
|
|
100
|
-
return Object.keys(arg).length > 0
|
|
101
|
-
? `[Object: ${Object.keys(arg).join(', ')}]`
|
|
102
|
-
: '[Object]';
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return String(arg);
|
|
106
|
-
} catch (e) {
|
|
107
|
-
return "[Complex Value]";
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Create a safe copy of the result
|
|
112
|
-
let safeResult;
|
|
113
|
-
try {
|
|
114
|
-
if (result === null || result === undefined ||
|
|
115
|
-
typeof result === 'string' ||
|
|
116
|
-
typeof result === 'number' ||
|
|
117
|
-
typeof result === 'boolean') {
|
|
118
|
-
safeResult = result;
|
|
119
|
-
} else if (typeof result === 'function') {
|
|
120
|
-
safeResult = result.name || "[Function]";
|
|
121
|
-
} else if (typeof result === 'object') {
|
|
122
|
-
if (Array.isArray(result)) {
|
|
123
|
-
safeResult = `[Array(${result.length})]`;
|
|
124
|
-
} else {
|
|
125
|
-
safeResult = Object.keys(result).length > 0
|
|
126
|
-
? `[Object: ${Object.keys(result).join(', ')}]`
|
|
127
|
-
: '[Object]';
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
safeResult = String(result);
|
|
131
|
-
}
|
|
132
|
-
} catch (e) {
|
|
133
|
-
safeResult = "[Complex Value]";
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Record the call
|
|
137
|
-
calls.push({
|
|
138
|
-
timestamp: Date.now(),
|
|
139
|
-
relativeTime: Date.now() - data.startTime,
|
|
140
|
-
args: safeArgs,
|
|
141
|
-
result: safeResult,
|
|
142
|
-
error: error ? error.message : null,
|
|
143
|
-
duration
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// Log to console if enabled
|
|
147
|
-
if (options.logToConsole) {
|
|
148
|
-
console.log(`[Telemetry] ${methodName}`, {
|
|
149
|
-
args: safeArgs,
|
|
150
|
-
result: safeResult,
|
|
151
|
-
error: error ? error.message : null,
|
|
152
|
-
duration
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Helper function to track state changes
|
|
158
|
-
const trackStateChange = (path, value, previousValue) => {
|
|
159
|
-
if (!options.enabled || !options.trackStateChanges || !shouldSample()) return;
|
|
160
|
-
|
|
161
|
-
if (!data.stateChanges.has(path)) {
|
|
162
|
-
data.stateChanges.set(path, []);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const changes = data.stateChanges.get(path);
|
|
166
|
-
|
|
167
|
-
// Limit the number of entries to prevent memory issues
|
|
168
|
-
if (changes.length >= options.maxEntries) {
|
|
169
|
-
changes.shift(); // Remove oldest entry
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Create safe copies of values
|
|
173
|
-
let safeValue, safePreviousValue;
|
|
174
|
-
try {
|
|
175
|
-
safeValue = JSON.stringify(value);
|
|
176
|
-
} catch (e) {
|
|
177
|
-
safeValue = String(value);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
safePreviousValue = JSON.stringify(previousValue);
|
|
182
|
-
} catch (e) {
|
|
183
|
-
safePreviousValue = String(previousValue);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Record the change
|
|
187
|
-
changes.push({
|
|
188
|
-
timestamp: Date.now(),
|
|
189
|
-
relativeTime: Date.now() - data.startTime,
|
|
190
|
-
value: safeValue,
|
|
191
|
-
previousValue: safePreviousValue
|
|
192
|
-
});
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
// Helper function to start a performance timer
|
|
196
|
-
const startTimer = (label) => {
|
|
197
|
-
if (!options.enabled || !options.trackPerformance) return;
|
|
198
|
-
|
|
199
|
-
data.activeTimers.set(label, performance.now());
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
// Helper function to end a performance timer
|
|
203
|
-
const endTimer = (label) => {
|
|
204
|
-
if (!options.enabled || !options.trackPerformance) return 0;
|
|
205
|
-
|
|
206
|
-
if (!data.activeTimers.has(label)) return 0;
|
|
207
|
-
|
|
208
|
-
const startTime = data.activeTimers.get(label);
|
|
209
|
-
const duration = performance.now() - startTime;
|
|
210
|
-
|
|
211
|
-
data.activeTimers.delete(label);
|
|
212
|
-
|
|
213
|
-
if (!data.performanceMetrics.has(label)) {
|
|
214
|
-
data.performanceMetrics.set(label, {
|
|
215
|
-
count: 0,
|
|
216
|
-
totalDuration: 0,
|
|
217
|
-
minDuration: Infinity,
|
|
218
|
-
maxDuration: 0,
|
|
219
|
-
samples: []
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const metrics = data.performanceMetrics.get(label);
|
|
224
|
-
metrics.count++;
|
|
225
|
-
metrics.totalDuration += duration;
|
|
226
|
-
metrics.minDuration = Math.min(metrics.minDuration, duration);
|
|
227
|
-
metrics.maxDuration = Math.max(metrics.maxDuration, duration);
|
|
228
|
-
|
|
229
|
-
// Store sample data (limited to prevent memory issues)
|
|
230
|
-
if (metrics.samples.length >= options.maxEntries) {
|
|
231
|
-
metrics.samples.shift();
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
metrics.samples.push({
|
|
235
|
-
timestamp: Date.now(),
|
|
236
|
-
relativeTime: Date.now() - data.startTime,
|
|
237
|
-
duration
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
return duration;
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
// Method proxying function
|
|
244
|
-
const createMethodProxy = (obj, methodName) => {
|
|
245
|
-
// Store original method
|
|
246
|
-
const originalMethod = obj[methodName];
|
|
247
|
-
originalMethods.set(methodName, originalMethod);
|
|
248
|
-
|
|
249
|
-
// Create proxy
|
|
250
|
-
return function(...args) {
|
|
251
|
-
if (!options.enabled) {
|
|
252
|
-
return originalMethod.apply(this, args);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
let result, error;
|
|
256
|
-
|
|
257
|
-
startTimer(`method.${methodName}`);
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
// Call original method
|
|
261
|
-
result = originalMethod.apply(this, args);
|
|
262
|
-
|
|
263
|
-
// Handle promises
|
|
264
|
-
if (result instanceof Promise) {
|
|
265
|
-
return result
|
|
266
|
-
.then(asyncResult => {
|
|
267
|
-
const duration = endTimer(`method.${methodName}`);
|
|
268
|
-
trackMethodCall(methodName, args, asyncResult, null, duration);
|
|
269
|
-
return asyncResult;
|
|
270
|
-
})
|
|
271
|
-
.catch(asyncError => {
|
|
272
|
-
const duration = endTimer(`method.${methodName}`);
|
|
273
|
-
trackMethodCall(methodName, args, undefined, asyncError, duration);
|
|
274
|
-
throw asyncError;
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const duration = endTimer(`method.${methodName}`);
|
|
279
|
-
trackMethodCall(methodName, args, result, null, duration);
|
|
280
|
-
return result;
|
|
281
|
-
|
|
282
|
-
} catch (e) {
|
|
283
|
-
error = e;
|
|
284
|
-
const duration = endTimer(`method.${methodName}`);
|
|
285
|
-
trackMethodCall(methodName, args, undefined, error, duration);
|
|
286
|
-
throw error;
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
// Create the telemetry plugin
|
|
292
|
-
const telemetryPlugin = {
|
|
293
|
-
name: 'telemetry',
|
|
294
|
-
|
|
295
|
-
// Initialize the plugin
|
|
296
|
-
init(api) {
|
|
297
|
-
if (!options.enabled) return this;
|
|
298
|
-
|
|
299
|
-
uistateRef = api;
|
|
300
|
-
|
|
301
|
-
// Proxy methods for tracking
|
|
302
|
-
if (options.trackMethodCalls) {
|
|
303
|
-
methodsToTrack.forEach(methodName => {
|
|
304
|
-
if (typeof api[methodName] === 'function') {
|
|
305
|
-
// Add to list of methods to check for usage
|
|
306
|
-
data.unusedMethods.add(methodName);
|
|
307
|
-
|
|
308
|
-
// Create proxy
|
|
309
|
-
api[methodName] = createMethodProxy(api, methodName);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return this;
|
|
315
|
-
},
|
|
316
|
-
|
|
317
|
-
// Middleware for state changes
|
|
318
|
-
middlewares: [
|
|
319
|
-
(path, value, getState) => {
|
|
320
|
-
if (options.enabled && options.trackStateChanges) {
|
|
321
|
-
const previousValue = getState(path);
|
|
322
|
-
startTimer(`stateChange.${path}`);
|
|
323
|
-
|
|
324
|
-
// Return a proxy that will track after the state change
|
|
325
|
-
return {
|
|
326
|
-
__telemetryValue: true,
|
|
327
|
-
value,
|
|
328
|
-
path,
|
|
329
|
-
previousValue,
|
|
330
|
-
finalize() {
|
|
331
|
-
const duration = endTimer(`stateChange.${path}`);
|
|
332
|
-
trackStateChange(path, value, previousValue);
|
|
333
|
-
return value;
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
return value;
|
|
338
|
-
}
|
|
339
|
-
],
|
|
340
|
-
|
|
341
|
-
// Lifecycle hooks
|
|
342
|
-
hooks: {
|
|
343
|
-
beforeStateChange: (path, value) => {
|
|
344
|
-
// If this is our proxy value, do nothing
|
|
345
|
-
if (value && value.__telemetryValue) {
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
|
|
350
|
-
afterStateChange: (path, value) => {
|
|
351
|
-
// If this is our proxy value, finalize it
|
|
352
|
-
if (value && value.__telemetryValue) {
|
|
353
|
-
return value.finalize();
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
|
|
358
|
-
// Plugin methods
|
|
359
|
-
methods: {
|
|
360
|
-
// Enable or disable telemetry
|
|
361
|
-
setEnabled(enabled) {
|
|
362
|
-
options.enabled = enabled;
|
|
363
|
-
return this;
|
|
364
|
-
},
|
|
365
|
-
|
|
366
|
-
// Get current configuration
|
|
367
|
-
getConfig() {
|
|
368
|
-
return { ...options };
|
|
369
|
-
},
|
|
370
|
-
|
|
371
|
-
// Update configuration
|
|
372
|
-
configure(newConfig) {
|
|
373
|
-
Object.assign(options, newConfig);
|
|
374
|
-
return this;
|
|
375
|
-
},
|
|
376
|
-
|
|
377
|
-
// Reset telemetry data
|
|
378
|
-
reset() {
|
|
379
|
-
data.startTime = Date.now();
|
|
380
|
-
data.methodCalls.clear();
|
|
381
|
-
data.stateChanges.clear();
|
|
382
|
-
data.performanceMetrics.clear();
|
|
383
|
-
data.activeTimers.clear();
|
|
384
|
-
return this;
|
|
385
|
-
},
|
|
386
|
-
|
|
387
|
-
// Get method call statistics
|
|
388
|
-
getMethodStats() {
|
|
389
|
-
const stats = {};
|
|
390
|
-
|
|
391
|
-
data.methodCalls.forEach((calls, methodName) => {
|
|
392
|
-
// Mark method as used
|
|
393
|
-
data.unusedMethods.delete(methodName);
|
|
394
|
-
|
|
395
|
-
stats[methodName] = {
|
|
396
|
-
count: calls.length,
|
|
397
|
-
averageDuration: calls.reduce((sum, call) => sum + (call.duration || 0), 0) / calls.length,
|
|
398
|
-
lastCall: calls[calls.length - 1],
|
|
399
|
-
firstCall: calls[0]
|
|
400
|
-
};
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
return stats;
|
|
404
|
-
},
|
|
405
|
-
|
|
406
|
-
// Get detailed method call data
|
|
407
|
-
getMethodCalls(methodName) {
|
|
408
|
-
if (!methodName) {
|
|
409
|
-
const result = {};
|
|
410
|
-
data.methodCalls.forEach((calls, method) => {
|
|
411
|
-
result[method] = [...calls];
|
|
412
|
-
});
|
|
413
|
-
return result;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return data.methodCalls.has(methodName)
|
|
417
|
-
? [...data.methodCalls.get(methodName)]
|
|
418
|
-
: [];
|
|
419
|
-
},
|
|
420
|
-
|
|
421
|
-
// Get state change statistics
|
|
422
|
-
getStateStats() {
|
|
423
|
-
const stats = {};
|
|
424
|
-
|
|
425
|
-
data.stateChanges.forEach((changes, path) => {
|
|
426
|
-
stats[path] = {
|
|
427
|
-
count: changes.length,
|
|
428
|
-
lastChange: changes[changes.length - 1],
|
|
429
|
-
firstChange: changes[0]
|
|
430
|
-
};
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
return stats;
|
|
434
|
-
},
|
|
435
|
-
|
|
436
|
-
// Get detailed state change data
|
|
437
|
-
getStateChanges(path) {
|
|
438
|
-
if (!path) {
|
|
439
|
-
const result = {};
|
|
440
|
-
data.stateChanges.forEach((changes, statePath) => {
|
|
441
|
-
result[statePath] = [...changes];
|
|
442
|
-
});
|
|
443
|
-
return result;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return data.stateChanges.has(path)
|
|
447
|
-
? [...data.stateChanges.get(path)]
|
|
448
|
-
: [];
|
|
449
|
-
},
|
|
450
|
-
|
|
451
|
-
// Get performance metrics
|
|
452
|
-
getPerformanceMetrics() {
|
|
453
|
-
const metrics = {};
|
|
454
|
-
|
|
455
|
-
data.performanceMetrics.forEach((data, label) => {
|
|
456
|
-
metrics[label] = {
|
|
457
|
-
count: data.count,
|
|
458
|
-
totalDuration: data.totalDuration,
|
|
459
|
-
averageDuration: data.totalDuration / data.count,
|
|
460
|
-
minDuration: data.minDuration,
|
|
461
|
-
maxDuration: data.maxDuration
|
|
462
|
-
};
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
return metrics;
|
|
466
|
-
},
|
|
467
|
-
|
|
468
|
-
// Get unused methods
|
|
469
|
-
getUnusedMethods() {
|
|
470
|
-
return [...data.unusedMethods];
|
|
471
|
-
},
|
|
472
|
-
|
|
473
|
-
// Get comprehensive telemetry report
|
|
474
|
-
getReport() {
|
|
475
|
-
return {
|
|
476
|
-
startTime: data.startTime,
|
|
477
|
-
duration: Date.now() - data.startTime,
|
|
478
|
-
methodStats: this.getMethodStats(),
|
|
479
|
-
stateStats: this.getStateStats(),
|
|
480
|
-
performanceMetrics: this.getPerformanceMetrics(),
|
|
481
|
-
unusedMethods: this.getUnusedMethods(),
|
|
482
|
-
config: this.getConfig(),
|
|
483
|
-
// Add detailed logs for download
|
|
484
|
-
detailedLogs: {
|
|
485
|
-
methodCalls: this.getMethodCalls(),
|
|
486
|
-
stateChanges: this.getStateChanges()
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
},
|
|
490
|
-
|
|
491
|
-
// Download telemetry logs as a JSON file
|
|
492
|
-
downloadLogs() {
|
|
493
|
-
const report = this.getReport();
|
|
494
|
-
const data = JSON.stringify(report, null, 2);
|
|
495
|
-
const blob = new Blob([data], { type: 'application/json' });
|
|
496
|
-
const url = URL.createObjectURL(blob);
|
|
497
|
-
|
|
498
|
-
const a = document.createElement('a');
|
|
499
|
-
a.href = url;
|
|
500
|
-
a.download = `uistate-telemetry-${new Date().toISOString().replace(/:/g, '-')}.json`;
|
|
501
|
-
document.body.appendChild(a);
|
|
502
|
-
a.click();
|
|
503
|
-
document.body.removeChild(a);
|
|
504
|
-
URL.revokeObjectURL(url);
|
|
505
|
-
|
|
506
|
-
return true;
|
|
507
|
-
},
|
|
508
|
-
|
|
509
|
-
// Create a visual dashboard
|
|
510
|
-
createDashboard(container = document.body) {
|
|
511
|
-
// Create dashboard element
|
|
512
|
-
const dashboard = document.createElement('div');
|
|
513
|
-
dashboard.className = 'uistate-telemetry-dashboard';
|
|
514
|
-
dashboard.style.cssText = `
|
|
515
|
-
position: fixed;
|
|
516
|
-
bottom: 10px;
|
|
517
|
-
right: 10px;
|
|
518
|
-
width: 300px;
|
|
519
|
-
max-height: 400px;
|
|
520
|
-
overflow: auto;
|
|
521
|
-
background: #fff;
|
|
522
|
-
border: 1px solid #ccc;
|
|
523
|
-
border-radius: 4px;
|
|
524
|
-
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
525
|
-
padding: 10px;
|
|
526
|
-
font-family: monospace;
|
|
527
|
-
font-size: 12px;
|
|
528
|
-
z-index: 9999;
|
|
529
|
-
`;
|
|
530
|
-
|
|
531
|
-
// Update function
|
|
532
|
-
const updateDashboard = () => {
|
|
533
|
-
const report = this.getReport();
|
|
534
|
-
|
|
535
|
-
dashboard.innerHTML = `
|
|
536
|
-
<h3 style="margin: 0 0 10px; font-size: 14px;">UIstate Telemetry</h3>
|
|
537
|
-
<div style="margin-bottom: 5px;">
|
|
538
|
-
<button id="telemetry-refresh">Refresh</button>
|
|
539
|
-
<button id="telemetry-reset">Reset</button>
|
|
540
|
-
<button id="telemetry-close">Close</button>
|
|
541
|
-
</div>
|
|
542
|
-
<div style="margin: 10px 0;">
|
|
543
|
-
<strong>Duration:</strong> ${Math.round((Date.now() - report.startTime) / 1000)}s
|
|
544
|
-
</div>
|
|
545
|
-
|
|
546
|
-
<h4 style="margin: 10px 0 5px; font-size: 13px;">Method Calls</h4>
|
|
547
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
548
|
-
<tr>
|
|
549
|
-
<th style="text-align: left; border-bottom: 1px solid #eee;">Method</th>
|
|
550
|
-
<th style="text-align: right; border-bottom: 1px solid #eee;">Count</th>
|
|
551
|
-
<th style="text-align: right; border-bottom: 1px solid #eee;">Avg Time</th>
|
|
552
|
-
</tr>
|
|
553
|
-
${Object.entries(report.methodStats)
|
|
554
|
-
.sort((a, b) => b[1].count - a[1].count)
|
|
555
|
-
.map(([method, stats]) => `
|
|
556
|
-
<tr>
|
|
557
|
-
<td style="padding: 2px 0; border-bottom: 1px solid #eee;">${method}</td>
|
|
558
|
-
<td style="text-align: right; padding: 2px 0; border-bottom: 1px solid #eee;">${stats.count}</td>
|
|
559
|
-
<td style="text-align: right; padding: 2px 0; border-bottom: 1px solid #eee;">${stats.averageDuration ? stats.averageDuration.toFixed(2) + 'ms' : 'n/a'}</td>
|
|
560
|
-
</tr>
|
|
561
|
-
`).join('')}
|
|
562
|
-
</table>
|
|
563
|
-
|
|
564
|
-
<h4 style="margin: 10px 0 5px; font-size: 13px;">State Changes</h4>
|
|
565
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
566
|
-
<tr>
|
|
567
|
-
<th style="text-align: left; border-bottom: 1px solid #eee;">Path</th>
|
|
568
|
-
<th style="text-align: right; border-bottom: 1px solid #eee;">Count</th>
|
|
569
|
-
</tr>
|
|
570
|
-
${Object.entries(report.stateStats)
|
|
571
|
-
.sort((a, b) => b[1].count - a[1].count)
|
|
572
|
-
.map(([path, stats]) => `
|
|
573
|
-
<tr>
|
|
574
|
-
<td style="padding: 2px 0; border-bottom: 1px solid #eee;">${path}</td>
|
|
575
|
-
<td style="text-align: right; padding: 2px 0; border-bottom: 1px solid #eee;">${stats.count}</td>
|
|
576
|
-
</tr>
|
|
577
|
-
`).join('')}
|
|
578
|
-
</table>
|
|
579
|
-
|
|
580
|
-
${report.unusedMethods.length > 0 ? `
|
|
581
|
-
<h4 style="margin: 10px 0 5px; font-size: 13px;">Unused Methods</h4>
|
|
582
|
-
<div style="color: #666;">
|
|
583
|
-
${report.unusedMethods.join(', ')}
|
|
584
|
-
</div>
|
|
585
|
-
` : ''}
|
|
586
|
-
`;
|
|
587
|
-
|
|
588
|
-
// Add event listeners
|
|
589
|
-
dashboard.querySelector('#telemetry-refresh').addEventListener('click', updateDashboard);
|
|
590
|
-
dashboard.querySelector('#telemetry-reset').addEventListener('click', () => {
|
|
591
|
-
this.reset();
|
|
592
|
-
updateDashboard();
|
|
593
|
-
});
|
|
594
|
-
dashboard.querySelector('#telemetry-close').addEventListener('click', () => {
|
|
595
|
-
dashboard.remove();
|
|
596
|
-
});
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
// Initial update
|
|
600
|
-
updateDashboard();
|
|
601
|
-
|
|
602
|
-
// Add to container
|
|
603
|
-
container.appendChild(dashboard);
|
|
604
|
-
|
|
605
|
-
return dashboard;
|
|
606
|
-
}
|
|
607
|
-
},
|
|
608
|
-
|
|
609
|
-
// Clean up
|
|
610
|
-
destroy() {
|
|
611
|
-
// Restore original methods
|
|
612
|
-
if (uistateRef) {
|
|
613
|
-
originalMethods.forEach((originalMethod, methodName) => {
|
|
614
|
-
if (uistateRef[methodName]) {
|
|
615
|
-
uistateRef[methodName] = originalMethod;
|
|
616
|
-
}
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// Clear data
|
|
621
|
-
data.methodCalls.clear();
|
|
622
|
-
data.stateChanges.clear();
|
|
623
|
-
data.performanceMetrics.clear();
|
|
624
|
-
data.activeTimers.clear();
|
|
625
|
-
data.unusedMethods.clear();
|
|
626
|
-
|
|
627
|
-
uistateRef = null;
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
|
|
631
|
-
return telemetryPlugin;
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
// Create a default instance with standard configuration
|
|
635
|
-
const telemetryPlugin = createTelemetryPlugin();
|
|
636
|
-
|
|
637
|
-
export default telemetryPlugin;
|
|
638
|
-
export { createTelemetryPlugin };
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|