juxscript 1.1.28 → 1.1.30

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.
@@ -0,0 +1,200 @@
1
+ /**
2
+ * State History Tracker
3
+ * Tracks all state mutations for time-travel debugging and undo/redo
4
+ */
5
+
6
+ export interface StateSnapshot<T = any> {
7
+ timestamp: number;
8
+ componentId: string;
9
+ property: string;
10
+ oldValue: T;
11
+ newValue: T;
12
+ stackTrace?: string;
13
+ }
14
+
15
+ export interface EventSnapshot {
16
+ timestamp: number;
17
+ componentId: string;
18
+ eventType: 'bind' | 'sync' | 'callback' | 'trigger';
19
+ eventName: string;
20
+ details: Record<string, any>;
21
+ }
22
+
23
+ export class StateHistory {
24
+ private stateSnapshots: StateSnapshot[] = [];
25
+ private eventSnapshots: EventSnapshot[] = [];
26
+ private maxHistory: number = 100;
27
+ private currentIndex: number = -1;
28
+ private isReplaying: boolean = false;
29
+
30
+ /**
31
+ * Record a state change
32
+ */
33
+ recordStateChange<T>(
34
+ componentId: string,
35
+ property: string,
36
+ oldValue: T,
37
+ newValue: T
38
+ ): void {
39
+ if (this.isReplaying) return; // Don't record during replay
40
+
41
+ const snapshot: StateSnapshot<T> = {
42
+ timestamp: Date.now(),
43
+ componentId,
44
+ property,
45
+ oldValue,
46
+ newValue,
47
+ stackTrace: new Error().stack
48
+ };
49
+
50
+ // Trim future history if we're not at the end
51
+ if (this.currentIndex < this.stateSnapshots.length - 1) {
52
+ this.stateSnapshots = this.stateSnapshots.slice(0, this.currentIndex + 1);
53
+ }
54
+
55
+ this.stateSnapshots.push(snapshot);
56
+ this.currentIndex = this.stateSnapshots.length - 1;
57
+
58
+ // Trim if exceeds max
59
+ if (this.stateSnapshots.length > this.maxHistory) {
60
+ this.stateSnapshots.shift();
61
+ this.currentIndex--;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Record an event (bind, sync, callback, trigger)
67
+ */
68
+ recordEvent(
69
+ componentId: string,
70
+ eventType: 'bind' | 'sync' | 'callback' | 'trigger',
71
+ eventName: string,
72
+ details: Record<string, any> = {}
73
+ ): void {
74
+ const snapshot: EventSnapshot = {
75
+ timestamp: Date.now(),
76
+ componentId,
77
+ eventType,
78
+ eventName,
79
+ details
80
+ };
81
+
82
+ this.eventSnapshots.push(snapshot);
83
+
84
+ // Trim if exceeds max
85
+ if (this.eventSnapshots.length > this.maxHistory) {
86
+ this.eventSnapshots.shift();
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Get timeline of all changes and events
92
+ */
93
+ getTimeline(): Array<StateSnapshot | EventSnapshot> {
94
+ return [...this.stateSnapshots, ...this.eventSnapshots]
95
+ .sort((a, b) => a.timestamp - b.timestamp);
96
+ }
97
+
98
+ /**
99
+ * Get state snapshots for a specific component
100
+ */
101
+ getComponentHistory(componentId: string): StateSnapshot[] {
102
+ return this.stateSnapshots.filter(s => s.componentId === componentId);
103
+ }
104
+
105
+ /**
106
+ * Get event snapshots for a specific component
107
+ */
108
+ getComponentEvents(componentId: string): EventSnapshot[] {
109
+ return this.eventSnapshots.filter(s => s.componentId === componentId);
110
+ }
111
+
112
+ /**
113
+ * Check if we can roll back
114
+ */
115
+ canRollback(): boolean {
116
+ return this.currentIndex > 0;
117
+ }
118
+
119
+ /**
120
+ * Check if we can roll forward
121
+ */
122
+ canRollforward(): boolean {
123
+ return this.currentIndex < this.stateSnapshots.length - 1;
124
+ }
125
+
126
+ /**
127
+ * Get the current snapshot (for rollback)
128
+ */
129
+ getCurrentSnapshot(): StateSnapshot | null {
130
+ if (this.currentIndex >= 0 && this.currentIndex < this.stateSnapshots.length) {
131
+ return this.stateSnapshots[this.currentIndex];
132
+ }
133
+ return null;
134
+ }
135
+
136
+ /**
137
+ * Move index for rollback
138
+ */
139
+ moveBack(): StateSnapshot | null {
140
+ if (this.canRollback()) {
141
+ this.currentIndex--;
142
+ return this.stateSnapshots[this.currentIndex];
143
+ }
144
+ return null;
145
+ }
146
+
147
+ /**
148
+ * Move index for rollforward
149
+ */
150
+ moveForward(): StateSnapshot | null {
151
+ if (this.canRollforward()) {
152
+ this.currentIndex++;
153
+ return this.stateSnapshots[this.currentIndex];
154
+ }
155
+ return null;
156
+ }
157
+
158
+ /**
159
+ * Mark as replaying to prevent recursive recording
160
+ */
161
+ startReplay(): void {
162
+ this.isReplaying = true;
163
+ }
164
+
165
+ /**
166
+ * End replay mode
167
+ */
168
+ endReplay(): void {
169
+ this.isReplaying = false;
170
+ }
171
+
172
+ /**
173
+ * Clear all history
174
+ */
175
+ clear(): void {
176
+ this.stateSnapshots = [];
177
+ this.eventSnapshots = [];
178
+ this.currentIndex = -1;
179
+ }
180
+
181
+ /**
182
+ * Get summary statistics
183
+ */
184
+ getStats() {
185
+ return {
186
+ totalStateChanges: this.stateSnapshots.length,
187
+ totalEvents: this.eventSnapshots.length,
188
+ currentIndex: this.currentIndex,
189
+ canRollback: this.canRollback(),
190
+ canRollforward: this.canRollforward(),
191
+ components: new Set([
192
+ ...this.stateSnapshots.map(s => s.componentId),
193
+ ...this.eventSnapshots.map(s => s.componentId)
194
+ ]).size
195
+ };
196
+ }
197
+ }
198
+
199
+ // Global singleton
200
+ export const stateHistory = new StateHistory();
@@ -1,59 +1,33 @@
1
1
  /**
2
- * Global component registry for debugging and console access
3
- * All rendered components are automatically registered here
4
- *
5
- * Usage in console:
6
- * __jux.get('hero-1').state.title = 'New Title'
7
- * __jux.list()
8
- * __jux.find('hero')
9
- * __jux.create('button', 'btn-1', { label: 'Click Me' }).render()
2
+ * Simplified JUX Component Registry
3
+ * Lean console debugging interface
10
4
  */
11
5
  import { BaseComponent } from './base/BaseComponent.js';
12
6
  declare class ComponentRegistry {
13
7
  private components;
14
- /**
15
- * Register a component instance
16
- */
17
8
  register(component: BaseComponent<any>): void;
18
- /**
19
- * Unregister a component
20
- */
21
9
  unregister(id: string): void;
22
10
  /**
23
- * Get a component by ID
24
- */
25
- get<T extends BaseComponent<any>>(id: string): T | undefined;
26
- /**
27
- * Check if component exists
11
+ * Get component by index or ID
28
12
  */
29
- has(id: string): boolean;
13
+ get(indexOrId: number | string): BaseComponent<any> | undefined;
30
14
  /**
31
- * List all registered component IDs
15
+ * Print indexed list to console
32
16
  */
33
- list(): string[];
17
+ list(): void;
34
18
  /**
35
- * Find components by partial ID match
19
+ * Show component tree with state, history, events
36
20
  */
37
- find(pattern: string): BaseComponent<any>[];
21
+ tree(indexOrId?: number | string): void;
22
+ private _printTree;
38
23
  /**
39
24
  * Get all components
40
25
  */
41
26
  all(): BaseComponent<any>[];
42
27
  /**
43
- * Get components by type (constructor name)
44
- */
45
- byType(typeName: string): BaseComponent<any>[];
46
- /**
47
- * Clear all registered components
28
+ * Clear registry
48
29
  */
49
30
  clear(): void;
50
- /**
51
- * Get registry stats
52
- */
53
- stats(): {
54
- total: number;
55
- types: Record<string, number>;
56
- };
57
31
  }
58
32
  export declare const registry: ComponentRegistry;
59
33
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,cAAM,iBAAiB;IACnB,OAAO,CAAC,UAAU,CAA8C;IAEhE;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAK7C;;OAEG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK5B;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI5D;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;IAIhB;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE;IAM3C;;OAEG;IACH,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE;IAI3B;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE;IAK9C;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,KAAK,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;CAa5D;AAGD,eAAO,MAAM,QAAQ,mBAA0B,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,cAAM,iBAAiB;IACnB,OAAO,CAAC,UAAU,CAA4B;IAE9C,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAO7C,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI5B;;OAEG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;IAO/D;;OAEG;IACH,IAAI,IAAI,IAAI;IAaZ;;OAEG;IACH,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAkBvC,OAAO,CAAC,UAAU;IAkClB;;OAEG;IACH,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE;IAI3B;;OAEG;IACH,KAAK,IAAI,IAAI;CAIhB;AAED,eAAO,MAAM,QAAQ,mBAA0B,CAAC"}
@@ -1,146 +1,163 @@
1
1
  /**
2
- * Global component registry for debugging and console access
3
- * All rendered components are automatically registered here
4
- *
5
- * Usage in console:
6
- * __jux.get('hero-1').state.title = 'New Title'
7
- * __jux.list()
8
- * __jux.find('hero')
9
- * __jux.create('button', 'btn-1', { label: 'Click Me' }).render()
2
+ * Simplified JUX Component Registry
3
+ * Lean console debugging interface
10
4
  */
5
+ import { stateHistory } from './history/StateHistory.js';
11
6
  class ComponentRegistry {
12
7
  constructor() {
13
- this.components = new Map();
8
+ this.components = [];
14
9
  }
15
- /**
16
- * Register a component instance
17
- */
18
10
  register(component) {
19
- this.components.set(component._id, component);
20
- console.debug(`[Jux Registry] Registered: ${component._id}`);
11
+ // Avoid duplicates
12
+ if (!this.components.find(c => c._id === component._id)) {
13
+ this.components.push(component);
14
+ }
21
15
  }
22
- /**
23
- * Unregister a component
24
- */
25
16
  unregister(id) {
26
- this.components.delete(id);
27
- console.debug(`[Jux Registry] Unregistered: ${id}`);
17
+ this.components = this.components.filter(c => c._id !== id);
28
18
  }
29
19
  /**
30
- * Get a component by ID
20
+ * Get component by index or ID
31
21
  */
32
- get(id) {
33
- return this.components.get(id);
34
- }
35
- /**
36
- * Check if component exists
37
- */
38
- has(id) {
39
- return this.components.has(id);
22
+ get(indexOrId) {
23
+ if (typeof indexOrId === 'number') {
24
+ return this.components[indexOrId];
25
+ }
26
+ return this.components.find(c => c._id === indexOrId);
40
27
  }
41
28
  /**
42
- * List all registered component IDs
29
+ * Print indexed list to console
43
30
  */
44
31
  list() {
45
- return Array.from(this.components.keys());
32
+ if (this.components.length === 0) {
33
+ console.log('📭 No components registered');
34
+ return;
35
+ }
36
+ console.log('\n📋 Registered Components:\n');
37
+ this.components.forEach((comp, idx) => {
38
+ console.log(` [${idx}] ${comp._id} → ${comp.constructor.name}`);
39
+ });
40
+ console.log(`\n💡 Access via: _jux.get(${0}) or _jux.get('${this.components[0]._id}')\n`);
46
41
  }
47
42
  /**
48
- * Find components by partial ID match
43
+ * Show component tree with state, history, events
49
44
  */
50
- find(pattern) {
51
- const regex = new RegExp(pattern, 'i');
52
- return Array.from(this.components.values())
53
- .filter(comp => regex.test(comp._id));
45
+ tree(indexOrId) {
46
+ let comp;
47
+ if (indexOrId === undefined) {
48
+ // Show all components
49
+ this.components.forEach((c, idx) => this._printTree(c, idx));
50
+ return;
51
+ }
52
+ comp = this.get(indexOrId);
53
+ if (!comp) {
54
+ console.error(`❌ Component not found: ${indexOrId}`);
55
+ return;
56
+ }
57
+ this._printTree(comp, typeof indexOrId === 'number' ? indexOrId : undefined);
58
+ }
59
+ _printTree(comp, index) {
60
+ const idx = index !== undefined ? `[${index}]` : '';
61
+ const stateChanges = stateHistory.getComponentHistory(comp._id);
62
+ const events = stateHistory.getComponentEvents(comp._id);
63
+ const syncCount = comp._syncBindings?.length || 0;
64
+ const bindCount = comp._bindings?.length || 0;
65
+ console.group(`🌳 ${idx} ${comp._id} (${comp.constructor.name})`);
66
+ console.log('📊 State:', comp.state);
67
+ if (stateChanges.length > 0) {
68
+ console.log(`📜 History: ${stateChanges.length} changes`);
69
+ console.log(' Last 3:', stateChanges.slice(-3).map(s => `${s.property}: ${s.oldValue} → ${s.newValue}`));
70
+ }
71
+ if (syncCount > 0) {
72
+ console.log(`🔗 Syncs: ${syncCount} active`);
73
+ }
74
+ if (bindCount > 0 || events.length > 0) {
75
+ console.log(`⚡ Events: ${bindCount} binds, ${events.length} fired`);
76
+ }
77
+ console.log(`\n💡 Commands:`);
78
+ console.log(` _jux.get(${index ?? `'${comp._id}'`}).state.prop = value`);
79
+ console.log(` _jux.get(${index ?? `'${comp._id}'`}).rollback()`);
80
+ console.log(` _jux.get(${index ?? `'${comp._id}'`}).timeline()`);
81
+ console.groupEnd();
54
82
  }
55
83
  /**
56
84
  * Get all components
57
85
  */
58
86
  all() {
59
- return Array.from(this.components.values());
60
- }
61
- /**
62
- * Get components by type (constructor name)
63
- */
64
- byType(typeName) {
65
- return Array.from(this.components.values())
66
- .filter(comp => comp.constructor.name === typeName);
87
+ return this.components;
67
88
  }
68
89
  /**
69
- * Clear all registered components
90
+ * Clear registry
70
91
  */
71
92
  clear() {
72
- this.components.clear();
73
- console.debug('[Jux Registry] Cleared all components');
74
- }
75
- /**
76
- * Get registry stats
77
- */
78
- stats() {
79
- const types = {};
80
- this.components.forEach(comp => {
81
- const type = comp.constructor.name;
82
- types[type] = (types[type] || 0) + 1;
83
- });
84
- return {
85
- total: this.components.size,
86
- types
87
- };
93
+ this.components = [];
94
+ stateHistory.clear();
88
95
  }
89
96
  }
90
- // Global singleton instance
91
97
  export const registry = new ComponentRegistry();
92
- // Expose to window for console access
98
+ // Expose lean console interface
93
99
  if (typeof window !== 'undefined') {
94
- window.__jux = {
95
- get: (id) => registry.get(id),
100
+ window._jux = {
101
+ /**
102
+ * Get component by index or ID
103
+ * @example
104
+ * const h = _jux.get(0) // Get first component
105
+ * const h = _jux.get('hero-1') // Get by ID
106
+ * h.state.title = 'New Title' // Direct mutation
107
+ * h.rollback() // Undo last change
108
+ */
109
+ get: (indexOrId) => registry.get(indexOrId),
110
+ /**
111
+ * List all components with indices
112
+ * @example
113
+ * _jux.list()
114
+ * // [0] hero-1 → Hero
115
+ * // [1] nav-main → Nav
116
+ */
96
117
  list: () => registry.list(),
97
- find: (pattern) => registry.find(pattern),
118
+ /**
119
+ * Show component tree (state, history, events, syncs)
120
+ * @example
121
+ * _jux.tree() // Show all
122
+ * _jux.tree(0) // Show first component
123
+ * _jux.tree('hero-1') // Show specific component
124
+ */
125
+ tree: (indexOrId) => registry.tree(indexOrId),
126
+ /**
127
+ * Get all components
128
+ */
98
129
  all: () => registry.all(),
99
- byType: (type) => registry.byType(type),
100
- stats: () => registry.stats(),
130
+ /**
131
+ * Clear all components and history
132
+ */
101
133
  clear: () => registry.clear(),
102
- // Factory helpers for creating components from console
103
- create: (type, id, options) => {
104
- const jux = window.jux;
105
- if (!jux || !jux[type]) {
106
- console.error(`Component type "${type}" not found`);
107
- return null;
108
- }
109
- return jux[type](id, options);
110
- },
111
- // Debug helper
112
- debug: (id) => {
113
- const comp = registry.get(id);
114
- if (!comp) {
115
- console.error(`Component "${id}" not found`);
116
- return;
117
- }
118
- console.group(`🔍 Component: ${id}`);
119
- console.log('Type:', comp.constructor.name);
120
- console.log('State:', comp.state);
121
- console.log('Props:', comp.props);
122
- console.log('Container:', comp.container);
123
- console.log('Instance:', comp);
124
- console.groupEnd();
134
+ /**
135
+ * Access global state history
136
+ */
137
+ history: {
138
+ stats: () => stateHistory.getStats(),
139
+ timeline: () => stateHistory.getTimeline(),
140
+ clear: () => stateHistory.clear()
125
141
  }
126
142
  };
127
143
  console.log(`
128
- 🎨 Jux DevTools Available!
144
+ 🎨 JUX DevTools Ready!
145
+
146
+ Quick Commands:
147
+ _jux.list() List all components with indices
148
+ _jux.tree() Show component tree (state, history, events)
149
+ _jux.get(0) Get first component
150
+ _jux.get('hero-1') Get component by ID
129
151
 
130
- Available commands:
131
- __jux.get('id') Get component instance
132
- __jux.list() List all component IDs
133
- __jux.find('pattern') Search components by ID
134
- __jux.all() Get all components
135
- __jux.byType('Hero') Get components by type
136
- __jux.stats() Show registry statistics
137
- __jux.debug('id') Debug component details
138
- __jux.create('hero', 'h1', {...}) Create component from console
152
+ Direct Manipulation:
153
+ const h = _jux.get(0)
154
+ h.state.title = 'New!' Mutate state directly
155
+ h.rollback() Undo last change
156
+ h.rollforward() Redo change
157
+ h.timeline() View full history
139
158
 
140
- Example:
141
- const h = __jux.get('hero-1')
142
- h.state.title = 'Changed from console!'
143
- h.hide()
144
- h.show()
159
+ History:
160
+ _jux.history.stats() Global statistics
161
+ _jux.history.timeline() Complete timeline
145
162
  `);
146
163
  }