@coherent.js/nextjs 1.0.0-beta.2 → 1.0.0-beta.5
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 +521 -0
- package/dist/index.cjs +75 -2038
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +75 -2028
- package/dist/index.js.map +4 -4
- package/dist/nextjs/coherent-nextjs.js +24 -24
- package/dist/nextjs/index.js +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ function createPerformanceMonitor(options = {}) {
|
|
|
23
23
|
},
|
|
24
24
|
alerts: {
|
|
25
25
|
enabled: true,
|
|
26
|
+
debounceMs: 5e3,
|
|
26
27
|
rules: []
|
|
27
28
|
},
|
|
28
29
|
resources: {
|
|
@@ -184,7 +185,7 @@ function createPerformanceMonitor(options = {}) {
|
|
|
184
185
|
const alertKey = `${rule.metric}-${rule.condition}-${rule.threshold}`;
|
|
185
186
|
const lastTriggered = alertState.triggered.get(alertKey);
|
|
186
187
|
const now = Date.now();
|
|
187
|
-
if (!lastTriggered || now - lastTriggered >
|
|
188
|
+
if (!lastTriggered || now - lastTriggered > opts.alerts.debounceMs) {
|
|
188
189
|
alertState.triggered.set(alertKey, now);
|
|
189
190
|
alertState.history.push({
|
|
190
191
|
rule,
|
|
@@ -474,1951 +475,98 @@ function createPerformanceMonitor(options = {}) {
|
|
|
474
475
|
var performanceMonitor2 = createPerformanceMonitor();
|
|
475
476
|
|
|
476
477
|
// ../core/src/core/object-utils.js
|
|
477
|
-
function
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
if (seen.has(obj)) {
|
|
482
|
-
return seen.get(obj);
|
|
483
|
-
}
|
|
484
|
-
if (obj instanceof Date) {
|
|
485
|
-
return new Date(obj.getTime());
|
|
486
|
-
}
|
|
487
|
-
if (obj instanceof RegExp) {
|
|
488
|
-
return new RegExp(obj.source, obj.flags);
|
|
489
|
-
}
|
|
490
|
-
if (Array.isArray(obj)) {
|
|
491
|
-
const clonedArray = [];
|
|
492
|
-
seen.set(obj, clonedArray);
|
|
493
|
-
for (let i = 0; i < obj.length; i++) {
|
|
494
|
-
clonedArray[i] = deepClone(obj[i], seen);
|
|
495
|
-
}
|
|
496
|
-
return clonedArray;
|
|
497
|
-
}
|
|
498
|
-
if (typeof obj === "function") {
|
|
499
|
-
return obj;
|
|
500
|
-
}
|
|
501
|
-
if (obj instanceof Map) {
|
|
502
|
-
const clonedMap = /* @__PURE__ */ new Map();
|
|
503
|
-
seen.set(obj, clonedMap);
|
|
504
|
-
for (const [key, value] of obj) {
|
|
505
|
-
clonedMap.set(deepClone(key, seen), deepClone(value, seen));
|
|
506
|
-
}
|
|
507
|
-
return clonedMap;
|
|
508
|
-
}
|
|
509
|
-
if (obj instanceof Set) {
|
|
510
|
-
const clonedSet = /* @__PURE__ */ new Set();
|
|
511
|
-
seen.set(obj, clonedSet);
|
|
512
|
-
for (const value of obj) {
|
|
513
|
-
clonedSet.add(deepClone(value, seen));
|
|
514
|
-
}
|
|
515
|
-
return clonedSet;
|
|
516
|
-
}
|
|
517
|
-
if (obj instanceof WeakMap) {
|
|
518
|
-
return /* @__PURE__ */ new WeakMap();
|
|
519
|
-
}
|
|
520
|
-
if (obj instanceof WeakSet) {
|
|
521
|
-
return /* @__PURE__ */ new WeakSet();
|
|
522
|
-
}
|
|
523
|
-
const clonedObj = {};
|
|
524
|
-
seen.set(obj, clonedObj);
|
|
525
|
-
if (obj.constructor && obj.constructor !== Object) {
|
|
526
|
-
try {
|
|
527
|
-
clonedObj.__proto__ = obj.__proto__;
|
|
528
|
-
} catch {
|
|
529
|
-
Object.setPrototypeOf(clonedObj, Object.getPrototypeOf(obj));
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
for (const key in obj) {
|
|
533
|
-
if (obj.hasOwnProperty(key)) {
|
|
534
|
-
clonedObj[key] = deepClone(obj[key], seen);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
const descriptors = Object.getOwnPropertyDescriptors(obj);
|
|
538
|
-
for (const key of Object.keys(descriptors)) {
|
|
539
|
-
if (!descriptors[key].enumerable && descriptors[key].configurable) {
|
|
540
|
-
try {
|
|
541
|
-
Object.defineProperty(clonedObj, key, {
|
|
542
|
-
...descriptors[key],
|
|
543
|
-
value: deepClone(descriptors[key].value, seen)
|
|
544
|
-
});
|
|
545
|
-
} catch {
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
return clonedObj;
|
|
550
|
-
}
|
|
551
|
-
function validateComponent(component, path2 = "root") {
|
|
552
|
-
if (component === null || component === void 0) {
|
|
553
|
-
throw new Error(`Invalid component at ${path2}: null or undefined`);
|
|
554
|
-
}
|
|
555
|
-
if (["string", "number", "boolean"].includes(typeof component)) {
|
|
556
|
-
return true;
|
|
557
|
-
}
|
|
558
|
-
if (typeof component === "function") {
|
|
559
|
-
return true;
|
|
560
|
-
}
|
|
561
|
-
if (Array.isArray(component)) {
|
|
562
|
-
component.forEach((child, index) => {
|
|
563
|
-
validateComponent(child, `${path2}[${index}]`);
|
|
564
|
-
});
|
|
565
|
-
return true;
|
|
566
|
-
}
|
|
567
|
-
if (typeof component === "object") {
|
|
568
|
-
const keys = Object.keys(component);
|
|
569
|
-
if (keys.length === 0) {
|
|
570
|
-
throw new Error(`Empty object at ${path2}`);
|
|
571
|
-
}
|
|
572
|
-
keys.forEach((key) => {
|
|
573
|
-
const value = component[key];
|
|
574
|
-
if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(key) && key !== "text") {
|
|
575
|
-
console.warn(`Potentially invalid tag name at ${path2}: ${key}`);
|
|
576
|
-
}
|
|
577
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
578
|
-
if (value.children) {
|
|
579
|
-
validateComponent(value.children, `${path2}.${key}.children`);
|
|
580
|
-
}
|
|
581
|
-
} else if (value && typeof value !== "string" && typeof value !== "number" && typeof value !== "function") {
|
|
582
|
-
throw new Error(`Invalid value type at ${path2}.${key}: ${typeof value}`);
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
return true;
|
|
586
|
-
}
|
|
587
|
-
throw new Error(`Invalid component type at ${path2}: ${typeof component}`);
|
|
588
|
-
}
|
|
589
|
-
function isCoherentObject(obj) {
|
|
590
|
-
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
591
|
-
return false;
|
|
592
|
-
}
|
|
593
|
-
const keys = Object.keys(obj);
|
|
594
|
-
if (keys.length === 0) {
|
|
595
|
-
return false;
|
|
596
|
-
}
|
|
597
|
-
return keys.every((key) => {
|
|
598
|
-
if (key === "text") return true;
|
|
599
|
-
return /^[a-zA-Z][a-zA-Z0-9-]*$/.test(key);
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
function extractProps(coherentObj) {
|
|
603
|
-
if (!isCoherentObject(coherentObj)) {
|
|
604
|
-
return {};
|
|
605
|
-
}
|
|
606
|
-
const props = {};
|
|
607
|
-
const keys = Object.keys(coherentObj);
|
|
608
|
-
keys.forEach((tag) => {
|
|
609
|
-
const value = coherentObj[tag];
|
|
610
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
611
|
-
props[tag] = { ...value };
|
|
612
|
-
} else {
|
|
613
|
-
props[tag] = { text: value };
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
return props;
|
|
617
|
-
}
|
|
618
|
-
function hasChildren(component) {
|
|
619
|
-
if (Array.isArray(component)) {
|
|
620
|
-
return component.length > 0;
|
|
621
|
-
}
|
|
622
|
-
if (isCoherentObject(component)) {
|
|
623
|
-
if (component.children !== void 0 && component.children !== null) {
|
|
624
|
-
return Array.isArray(component.children) ? component.children.length > 0 : true;
|
|
625
|
-
}
|
|
626
|
-
const keys = Object.keys(component);
|
|
627
|
-
return keys.some((key) => {
|
|
628
|
-
const value = component[key];
|
|
629
|
-
return value && typeof value === "object" && value.children;
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
return false;
|
|
633
|
-
}
|
|
634
|
-
function normalizeChildren(children) {
|
|
635
|
-
if (children === null || children === void 0) {
|
|
636
|
-
return [];
|
|
637
|
-
}
|
|
638
|
-
if (Array.isArray(children)) {
|
|
639
|
-
return children.flat().filter((child) => child !== null && child !== void 0);
|
|
640
|
-
}
|
|
641
|
-
return [children];
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// ../core/src/components/component-system.js
|
|
645
|
-
var COMPONENT_METADATA = /* @__PURE__ */ new WeakMap();
|
|
646
|
-
var ComponentState = class {
|
|
647
|
-
constructor(initialState = {}) {
|
|
648
|
-
this.state = { ...initialState };
|
|
649
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
650
|
-
this.isUpdating = false;
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Get state value by key or entire state
|
|
654
|
-
*
|
|
655
|
-
* @param {string} [key] - State key to retrieve
|
|
656
|
-
* @returns {*} State value or entire state object
|
|
657
|
-
*/
|
|
658
|
-
get(key) {
|
|
659
|
-
return key ? this.state[key] : { ...this.state };
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Update state with new values
|
|
663
|
-
*
|
|
664
|
-
* @param {Object} updates - State updates to apply
|
|
665
|
-
* @returns {ComponentState} This instance for chaining
|
|
666
|
-
*/
|
|
667
|
-
set(updates) {
|
|
668
|
-
if (this.isUpdating) return this;
|
|
669
|
-
const oldState = { ...this.state };
|
|
670
|
-
if (typeof updates === "function") {
|
|
671
|
-
updates = updates(oldState);
|
|
672
|
-
}
|
|
673
|
-
this.state = { ...this.state, ...updates };
|
|
674
|
-
this.notifyListeners(oldState, this.state);
|
|
675
|
-
return this;
|
|
676
|
-
}
|
|
677
|
-
subscribe(listener) {
|
|
678
|
-
this.listeners.add(listener);
|
|
679
|
-
return () => this.listeners.delete(listener);
|
|
680
|
-
}
|
|
681
|
-
notifyListeners(oldState, newState) {
|
|
682
|
-
if (this.listeners.size === 0) return;
|
|
683
|
-
this.isUpdating = true;
|
|
684
|
-
this.listeners.forEach((listener) => {
|
|
685
|
-
try {
|
|
686
|
-
listener(newState, oldState);
|
|
687
|
-
} catch (_error) {
|
|
688
|
-
console.error("State listener _error:", _error);
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
this.isUpdating = false;
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
var Component = class _Component {
|
|
695
|
-
constructor(definition = {}) {
|
|
696
|
-
this.definition = definition;
|
|
697
|
-
this.name = definition.name || "AnonymousComponent";
|
|
698
|
-
this.props = {};
|
|
699
|
-
this.state = new ComponentState(definition.state || {});
|
|
700
|
-
this.children = [];
|
|
701
|
-
this.parent = null;
|
|
702
|
-
this.rendered = null;
|
|
703
|
-
this.isMounted = false;
|
|
704
|
-
this.isDestroyed = false;
|
|
705
|
-
this.hooks = {
|
|
706
|
-
beforeCreate: definition.beforeCreate || (() => {
|
|
707
|
-
}),
|
|
708
|
-
created: definition.created || (() => {
|
|
709
|
-
}),
|
|
710
|
-
beforeMount: definition.beforeMount || (() => {
|
|
711
|
-
}),
|
|
712
|
-
mounted: definition.mounted || (() => {
|
|
713
|
-
}),
|
|
714
|
-
beforeUpdate: definition.beforeUpdate || (() => {
|
|
715
|
-
}),
|
|
716
|
-
updated: definition.updated || (() => {
|
|
717
|
-
}),
|
|
718
|
-
beforeDestroy: definition.beforeDestroy || (() => {
|
|
719
|
-
}),
|
|
720
|
-
destroyed: definition.destroyed || (() => {
|
|
721
|
-
}),
|
|
722
|
-
errorCaptured: definition.errorCaptured || (() => {
|
|
723
|
-
})
|
|
724
|
-
};
|
|
725
|
-
this.methods = definition.methods || {};
|
|
726
|
-
Object.keys(this.methods).forEach((methodName) => {
|
|
727
|
-
if (typeof this.methods[methodName] === "function") {
|
|
728
|
-
this[methodName] = this.methods[methodName].bind(this);
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
this.computed = definition.computed || {};
|
|
732
|
-
this.computedCache = /* @__PURE__ */ new Map();
|
|
733
|
-
this.watchers = definition.watch || {};
|
|
734
|
-
this.setupWatchers();
|
|
735
|
-
COMPONENT_METADATA.set(this, {
|
|
736
|
-
createdAt: Date.now(),
|
|
737
|
-
updateCount: 0,
|
|
738
|
-
renderCount: 0
|
|
739
|
-
});
|
|
740
|
-
this.callHook("beforeCreate");
|
|
741
|
-
this.initialize();
|
|
742
|
-
this.callHook("created");
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Initialize component
|
|
746
|
-
*/
|
|
747
|
-
initialize() {
|
|
748
|
-
this.unsubscribeState = this.state.subscribe((newState, oldState) => {
|
|
749
|
-
this.onStateChange(newState, oldState);
|
|
750
|
-
});
|
|
751
|
-
this.initializeComputed();
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* Set up watchers for reactive data
|
|
755
|
-
*/
|
|
756
|
-
setupWatchers() {
|
|
757
|
-
Object.keys(this.watchers).forEach((key) => {
|
|
758
|
-
const handler = this.watchers[key];
|
|
759
|
-
this.state.subscribe((newState, oldState) => {
|
|
760
|
-
if (newState[key] !== oldState[key]) {
|
|
761
|
-
handler.call(this, newState[key], oldState[key]);
|
|
762
|
-
}
|
|
763
|
-
});
|
|
764
|
-
});
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Initialize computed properties
|
|
768
|
-
*/
|
|
769
|
-
initializeComputed() {
|
|
770
|
-
Object.keys(this.computed).forEach((key) => {
|
|
771
|
-
Object.defineProperty(this, key, {
|
|
772
|
-
get: () => {
|
|
773
|
-
if (!this.computedCache.has(key)) {
|
|
774
|
-
const value = this.computed[key].call(this);
|
|
775
|
-
this.computedCache.set(key, value);
|
|
776
|
-
}
|
|
777
|
-
return this.computedCache.get(key);
|
|
778
|
-
},
|
|
779
|
-
enumerable: true
|
|
780
|
-
});
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* Handle state changes
|
|
785
|
-
*/
|
|
786
|
-
onStateChange() {
|
|
787
|
-
if (this.isDestroyed) return;
|
|
788
|
-
this.computedCache.clear();
|
|
789
|
-
if (this.isMounted) {
|
|
790
|
-
this.update();
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* Call lifecycle hook
|
|
795
|
-
*/
|
|
796
|
-
callHook(hookName, ...args) {
|
|
797
|
-
try {
|
|
798
|
-
if (this.hooks[hookName]) {
|
|
799
|
-
return this.hooks[hookName].call(this, ...args);
|
|
800
|
-
}
|
|
801
|
-
} catch (_error) {
|
|
802
|
-
this.handleError(_error, `${hookName} hook`);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
/**
|
|
806
|
-
* Handle component errors
|
|
807
|
-
*/
|
|
808
|
-
handleError(_error) {
|
|
809
|
-
console.error(`Component Error in ${this.name}:`, _error);
|
|
810
|
-
this.callHook("errorCaptured", _error);
|
|
811
|
-
if (this.parent && this.parent.handleError) {
|
|
812
|
-
this.parent.handleError(_error, `${this.name} -> ${context}`);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
/**
|
|
816
|
-
* Render the component
|
|
817
|
-
*/
|
|
818
|
-
render(props = {}) {
|
|
819
|
-
if (this.isDestroyed) {
|
|
820
|
-
console.warn(`Attempting to render destroyed component: ${this.name}`);
|
|
821
|
-
return null;
|
|
822
|
-
}
|
|
823
|
-
try {
|
|
824
|
-
const metadata = COMPONENT_METADATA.get(this);
|
|
825
|
-
if (metadata) {
|
|
826
|
-
metadata.renderCount++;
|
|
827
|
-
}
|
|
828
|
-
this.props = { ...props };
|
|
829
|
-
if (typeof this.definition.render === "function") {
|
|
830
|
-
this.rendered = this.definition.render.call(this, this.props, this.state.get());
|
|
831
|
-
} else if (typeof this.definition.template !== "undefined") {
|
|
832
|
-
this.rendered = this.processTemplate(this.definition.template, this.props, this.state.get());
|
|
833
|
-
} else {
|
|
834
|
-
throw new Error(`Component ${this.name} must have either render method or template`);
|
|
835
|
-
}
|
|
836
|
-
if (this.rendered !== null) {
|
|
837
|
-
validateComponent(this.rendered, this.name);
|
|
838
|
-
}
|
|
839
|
-
return this.rendered;
|
|
840
|
-
} catch (_error) {
|
|
841
|
-
this.handleError(_error);
|
|
842
|
-
return { div: { className: "component-_error", text: `Error in ${this.name}` } };
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Process template with data
|
|
847
|
-
*/
|
|
848
|
-
processTemplate(template, props, state) {
|
|
849
|
-
if (typeof template === "function") {
|
|
850
|
-
return template.call(this, props, state);
|
|
851
|
-
}
|
|
852
|
-
if (typeof template === "string") {
|
|
853
|
-
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
854
|
-
return props[key] || state[key] || "";
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
const processed = deepClone(template);
|
|
858
|
-
this.interpolateObject(processed, { ...props, ...state });
|
|
859
|
-
return processed;
|
|
860
|
-
}
|
|
861
|
-
/**
|
|
862
|
-
* Interpolate object with data
|
|
863
|
-
*/
|
|
864
|
-
interpolateObject(obj, data) {
|
|
865
|
-
if (typeof obj === "string") {
|
|
866
|
-
return obj.replace(/\{\{(\w+)\}\}/g, (match, key) => data[key] || "");
|
|
867
|
-
}
|
|
868
|
-
if (Array.isArray(obj)) {
|
|
869
|
-
return obj.map((item) => this.interpolateObject(item, data));
|
|
870
|
-
}
|
|
871
|
-
if (obj && typeof obj === "object") {
|
|
872
|
-
Object.keys(obj).forEach((key) => {
|
|
873
|
-
obj[key] = this.interpolateObject(obj[key], data);
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
return obj;
|
|
877
|
-
}
|
|
878
|
-
/**
|
|
879
|
-
* Mount the component
|
|
880
|
-
*/
|
|
881
|
-
mount() {
|
|
882
|
-
if (this.isMounted || this.isDestroyed) return this;
|
|
883
|
-
this.callHook("beforeMount");
|
|
884
|
-
this.isMounted = true;
|
|
885
|
-
this.callHook("mounted");
|
|
886
|
-
return this;
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* Update the component
|
|
890
|
-
*/
|
|
891
|
-
update() {
|
|
892
|
-
if (!this.isMounted || this.isDestroyed) return this;
|
|
893
|
-
const metadata = COMPONENT_METADATA.get(this);
|
|
894
|
-
if (metadata) {
|
|
895
|
-
metadata.updateCount++;
|
|
896
|
-
}
|
|
897
|
-
this.callHook("beforeUpdate");
|
|
898
|
-
this.callHook("updated");
|
|
899
|
-
return this;
|
|
900
|
-
}
|
|
901
|
-
/**
|
|
902
|
-
* Destroy the component
|
|
903
|
-
*/
|
|
904
|
-
destroy() {
|
|
905
|
-
if (this.isDestroyed) return this;
|
|
906
|
-
this.callHook("beforeDestroy");
|
|
907
|
-
if (this.unsubscribeState) {
|
|
908
|
-
this.unsubscribeState();
|
|
909
|
-
}
|
|
910
|
-
this.children.forEach((child) => {
|
|
911
|
-
if (child.destroy) {
|
|
912
|
-
child.destroy();
|
|
913
|
-
}
|
|
914
|
-
});
|
|
915
|
-
this.isMounted = false;
|
|
916
|
-
this.isDestroyed = true;
|
|
917
|
-
this.children = [];
|
|
918
|
-
this.parent = null;
|
|
919
|
-
this.callHook("destroyed");
|
|
920
|
-
return this;
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* Get component metadata
|
|
924
|
-
*/
|
|
925
|
-
getMetadata() {
|
|
926
|
-
return COMPONENT_METADATA.get(this) || {};
|
|
927
|
-
}
|
|
928
|
-
/**
|
|
929
|
-
* Clone component with new props/state
|
|
930
|
-
*/
|
|
931
|
-
clone(overrides = {}) {
|
|
932
|
-
const newDefinition = { ...this.definition, ...overrides };
|
|
933
|
-
return new _Component(newDefinition);
|
|
934
|
-
}
|
|
935
|
-
};
|
|
936
|
-
if (performanceMonitor2) {
|
|
937
|
-
const originalRender = Component.prototype.render;
|
|
938
|
-
Component.prototype.render = function(...args) {
|
|
939
|
-
const start = performance.now();
|
|
940
|
-
const result = originalRender.apply(this, args);
|
|
941
|
-
const duration = performance.now() - start;
|
|
942
|
-
performanceMonitor2.recordMetric("renderTime", duration, {
|
|
943
|
-
type: "component",
|
|
944
|
-
name: this.name,
|
|
945
|
-
propsSize: JSON.stringify(this.props || {}).length,
|
|
946
|
-
hasState: Object.keys(this.state?.get() || {}).length > 0
|
|
947
|
-
});
|
|
948
|
-
return result;
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
// ../core/src/utils/error-handler.js
|
|
953
|
-
var CoherentError = class _CoherentError extends Error {
|
|
954
|
-
constructor(message, options = {}) {
|
|
955
|
-
super(message);
|
|
956
|
-
this.name = "CoherentError";
|
|
957
|
-
this.type = options.type || "generic";
|
|
958
|
-
this.component = options.component;
|
|
959
|
-
this.context = options.context;
|
|
960
|
-
this.suggestions = options.suggestions || [];
|
|
961
|
-
this.timestamp = Date.now();
|
|
962
|
-
if (Error.captureStackTrace) {
|
|
963
|
-
Error.captureStackTrace(this, _CoherentError);
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
toJSON() {
|
|
967
|
-
return {
|
|
968
|
-
name: this.name,
|
|
969
|
-
message: this.message,
|
|
970
|
-
type: this.type,
|
|
971
|
-
component: this.component,
|
|
972
|
-
context: this.context,
|
|
973
|
-
suggestions: this.suggestions,
|
|
974
|
-
timestamp: this.timestamp,
|
|
975
|
-
stack: this.stack
|
|
976
|
-
};
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
|
-
var ComponentValidationError = class extends CoherentError {
|
|
980
|
-
constructor(message, component, suggestions = []) {
|
|
981
|
-
super(message, {
|
|
982
|
-
type: "validation",
|
|
983
|
-
component,
|
|
984
|
-
suggestions: [
|
|
985
|
-
"Check component structure and syntax",
|
|
986
|
-
"Ensure all required properties are present",
|
|
987
|
-
"Validate prop types and values",
|
|
988
|
-
...suggestions
|
|
989
|
-
]
|
|
990
|
-
});
|
|
991
|
-
this.name = "ComponentValidationError";
|
|
992
|
-
}
|
|
993
|
-
};
|
|
994
|
-
var RenderingError = class extends CoherentError {
|
|
995
|
-
constructor(message, component, context2, suggestions = []) {
|
|
996
|
-
super(message, {
|
|
997
|
-
type: "rendering",
|
|
998
|
-
component,
|
|
999
|
-
context: context2,
|
|
1000
|
-
suggestions: [
|
|
1001
|
-
"Check for circular references",
|
|
1002
|
-
"Validate component depth",
|
|
1003
|
-
"Ensure all functions return valid components",
|
|
1004
|
-
...suggestions
|
|
1005
|
-
]
|
|
1006
|
-
});
|
|
1007
|
-
this.name = "RenderingError";
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
var PerformanceError = class extends CoherentError {
|
|
1011
|
-
constructor(message, metrics, suggestions = []) {
|
|
1012
|
-
super(message, {
|
|
1013
|
-
type: "performance",
|
|
1014
|
-
context: metrics,
|
|
1015
|
-
suggestions: [
|
|
1016
|
-
"Consider component memoization",
|
|
1017
|
-
"Reduce component complexity",
|
|
1018
|
-
"Enable caching",
|
|
1019
|
-
...suggestions
|
|
1020
|
-
]
|
|
1021
|
-
});
|
|
1022
|
-
this.name = "PerformanceError";
|
|
1023
|
-
}
|
|
1024
|
-
};
|
|
1025
|
-
var StateError = class extends CoherentError {
|
|
1026
|
-
constructor(message, state, suggestions = []) {
|
|
1027
|
-
super(message, {
|
|
1028
|
-
type: "state",
|
|
1029
|
-
context: state,
|
|
1030
|
-
suggestions: [
|
|
1031
|
-
"Check state mutations",
|
|
1032
|
-
"Ensure proper state initialization",
|
|
1033
|
-
"Validate state transitions",
|
|
1034
|
-
...suggestions
|
|
1035
|
-
]
|
|
1036
|
-
});
|
|
1037
|
-
this.name = "StateError";
|
|
1038
|
-
}
|
|
1039
|
-
};
|
|
1040
|
-
var ErrorHandler = class {
|
|
1041
|
-
constructor(options = {}) {
|
|
1042
|
-
this.options = {
|
|
1043
|
-
enableStackTrace: options.enableStackTrace !== false,
|
|
1044
|
-
enableSuggestions: options.enableSuggestions !== false,
|
|
1045
|
-
enableLogging: options.enableLogging !== false,
|
|
1046
|
-
logLevel: options.logLevel || "_error",
|
|
1047
|
-
maxErrorHistory: options.maxErrorHistory || 100,
|
|
1048
|
-
...options
|
|
1049
|
-
};
|
|
1050
|
-
this.errorHistory = [];
|
|
1051
|
-
this.errorCounts = /* @__PURE__ */ new Map();
|
|
1052
|
-
this.suppressedErrors = /* @__PURE__ */ new Set();
|
|
1053
|
-
}
|
|
1054
|
-
/**
|
|
1055
|
-
* Handle and report errors with detailed context
|
|
1056
|
-
*/
|
|
1057
|
-
handle(_error, context2 = {}) {
|
|
1058
|
-
const enhancedError = this.enhanceError(_error, context2);
|
|
1059
|
-
this.addToHistory(enhancedError);
|
|
1060
|
-
if (this.options.enableLogging) {
|
|
1061
|
-
this.logError(enhancedError);
|
|
1062
|
-
}
|
|
1063
|
-
return enhancedError;
|
|
1064
|
-
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Enhance existing errors with more context
|
|
1067
|
-
*/
|
|
1068
|
-
enhanceError(_error, context2 = {}) {
|
|
1069
|
-
if (_error instanceof CoherentError) {
|
|
1070
|
-
return _error;
|
|
1071
|
-
}
|
|
1072
|
-
const errorType = this.classifyError(_error, context2);
|
|
1073
|
-
switch (errorType) {
|
|
1074
|
-
case "validation":
|
|
1075
|
-
return new ComponentValidationError(
|
|
1076
|
-
_error.message,
|
|
1077
|
-
context2.component,
|
|
1078
|
-
this.generateSuggestions(_error, context2)
|
|
1079
|
-
);
|
|
1080
|
-
case "rendering":
|
|
1081
|
-
return new RenderingError(
|
|
1082
|
-
_error.message,
|
|
1083
|
-
context2.component,
|
|
1084
|
-
context2.renderContext,
|
|
1085
|
-
this.generateSuggestions(_error, context2)
|
|
1086
|
-
);
|
|
1087
|
-
case "performance":
|
|
1088
|
-
return new PerformanceError(
|
|
1089
|
-
_error.message,
|
|
1090
|
-
context2.metrics,
|
|
1091
|
-
this.generateSuggestions(_error, context2)
|
|
1092
|
-
);
|
|
1093
|
-
case "state":
|
|
1094
|
-
return new StateError(
|
|
1095
|
-
_error.message,
|
|
1096
|
-
context2.state,
|
|
1097
|
-
this.generateSuggestions(_error, context2)
|
|
1098
|
-
);
|
|
1099
|
-
default:
|
|
1100
|
-
return new CoherentError(_error.message, {
|
|
1101
|
-
type: errorType,
|
|
1102
|
-
component: context2.component,
|
|
1103
|
-
context: context2.context,
|
|
1104
|
-
suggestions: this.generateSuggestions(_error, context2)
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Classify _error type based on message and context
|
|
1110
|
-
*/
|
|
1111
|
-
classifyError(_error, context2) {
|
|
1112
|
-
const message = _error.message.toLowerCase();
|
|
1113
|
-
if (message.includes("invalid") || message.includes("validation") || message.includes("required") || message.includes("type")) {
|
|
1114
|
-
return "validation";
|
|
1115
|
-
}
|
|
1116
|
-
if (message.includes("render") || message.includes("circular") || message.includes("depth") || message.includes("cannot render")) {
|
|
1117
|
-
return "rendering";
|
|
1118
|
-
}
|
|
1119
|
-
if (message.includes("slow") || message.includes("memory") || message.includes("performance") || message.includes("timeout")) {
|
|
1120
|
-
return "performance";
|
|
1121
|
-
}
|
|
1122
|
-
if (message.includes("state") || message.includes("mutation") || message.includes("store") || context2.state) {
|
|
1123
|
-
return "state";
|
|
1124
|
-
}
|
|
1125
|
-
if (context2.component) return "validation";
|
|
1126
|
-
if (context2.renderContext) return "rendering";
|
|
1127
|
-
if (context2.metrics) return "performance";
|
|
1128
|
-
return "generic";
|
|
1129
|
-
}
|
|
1130
|
-
/**
|
|
1131
|
-
* Generate helpful suggestions based on _error
|
|
1132
|
-
*/
|
|
1133
|
-
generateSuggestions(_error, context2 = {}) {
|
|
1134
|
-
const suggestions = [];
|
|
1135
|
-
const message = _error.message.toLowerCase();
|
|
1136
|
-
const patterns = [
|
|
1137
|
-
{
|
|
1138
|
-
pattern: /cannot render|render.*failed/,
|
|
1139
|
-
suggestions: [
|
|
1140
|
-
"Check if component returns a valid object structure",
|
|
1141
|
-
"Ensure all properties are properly defined",
|
|
1142
|
-
"Look for undefined variables or null references"
|
|
1143
|
-
]
|
|
1144
|
-
},
|
|
1145
|
-
{
|
|
1146
|
-
pattern: /circular.*reference/,
|
|
1147
|
-
suggestions: [
|
|
1148
|
-
"Remove circular references between components",
|
|
1149
|
-
"Use lazy loading or memoization to break cycles",
|
|
1150
|
-
"Check for self-referencing components"
|
|
1151
|
-
]
|
|
1152
|
-
},
|
|
1153
|
-
{
|
|
1154
|
-
pattern: /maximum.*depth/,
|
|
1155
|
-
suggestions: [
|
|
1156
|
-
"Reduce component nesting depth",
|
|
1157
|
-
"Break complex components into smaller parts",
|
|
1158
|
-
"Check for infinite recursion in component functions"
|
|
1159
|
-
]
|
|
1160
|
-
},
|
|
1161
|
-
{
|
|
1162
|
-
pattern: /invalid.*component/,
|
|
1163
|
-
suggestions: [
|
|
1164
|
-
"Ensure component follows the expected object structure",
|
|
1165
|
-
"Check property names and values for typos",
|
|
1166
|
-
"Verify component is not null or undefined"
|
|
1167
|
-
]
|
|
1168
|
-
},
|
|
1169
|
-
{
|
|
1170
|
-
pattern: /performance|slow|timeout/,
|
|
1171
|
-
suggestions: [
|
|
1172
|
-
"Enable component caching",
|
|
1173
|
-
"Use memoization for expensive operations",
|
|
1174
|
-
"Reduce component complexity",
|
|
1175
|
-
"Consider lazy loading for large components"
|
|
1176
|
-
]
|
|
1177
|
-
}
|
|
1178
|
-
];
|
|
1179
|
-
patterns.forEach(({ pattern, suggestions: patternSuggestions }) => {
|
|
1180
|
-
if (pattern.test(message)) {
|
|
1181
|
-
suggestions.push(...patternSuggestions);
|
|
1182
|
-
}
|
|
1183
|
-
});
|
|
1184
|
-
if (context2.component) {
|
|
1185
|
-
const componentType = typeof context2.component;
|
|
1186
|
-
if (componentType === "function") {
|
|
1187
|
-
suggestions.push("Check function component return value");
|
|
1188
|
-
} else if (componentType === "object" && context2.component === null) {
|
|
1189
|
-
suggestions.push("Component is null - ensure proper initialization");
|
|
1190
|
-
} else if (Array.isArray(context2.component)) {
|
|
1191
|
-
suggestions.push("Arrays should contain valid component objects");
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
if (suggestions.length === 0) {
|
|
1195
|
-
suggestions.push(
|
|
1196
|
-
"Enable development tools for more detailed debugging",
|
|
1197
|
-
"Check browser console for additional _error details",
|
|
1198
|
-
"Use component validation tools to identify issues"
|
|
1199
|
-
);
|
|
1200
|
-
}
|
|
1201
|
-
return [...new Set(suggestions)];
|
|
1202
|
-
}
|
|
1203
|
-
/**
|
|
1204
|
-
* Add _error to history with deduplication
|
|
1205
|
-
*/
|
|
1206
|
-
addToHistory(_error) {
|
|
1207
|
-
const errorKey = `${_error.name}:${_error.message}`;
|
|
1208
|
-
this.errorCounts.set(errorKey, (this.errorCounts.get(errorKey) || 0) + 1);
|
|
1209
|
-
const historyEntry = {
|
|
1210
|
-
..._error.toJSON(),
|
|
1211
|
-
count: this.errorCounts.get(errorKey),
|
|
1212
|
-
firstSeen: this.errorHistory.find((e) => e.key === errorKey)?.firstSeen || _error.timestamp,
|
|
1213
|
-
key: errorKey
|
|
1214
|
-
};
|
|
1215
|
-
this.errorHistory = this.errorHistory.filter((e) => e.key !== errorKey);
|
|
1216
|
-
this.errorHistory.unshift(historyEntry);
|
|
1217
|
-
if (this.errorHistory.length > this.options.maxErrorHistory) {
|
|
1218
|
-
this.errorHistory = this.errorHistory.slice(0, this.options.maxErrorHistory);
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
/**
|
|
1222
|
-
* Log _error with enhanced formatting
|
|
1223
|
-
*/
|
|
1224
|
-
logError(_error) {
|
|
1225
|
-
if (this.suppressedErrors.has(`${_error.name}:${_error.message}`)) {
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
const isRepeated = this.errorCounts.get(`${_error.name}:${_error.message}`) > 1;
|
|
1229
|
-
const errorGroup = `\u{1F6A8} ${_error.name}${isRepeated ? ` (\xD7${this.errorCounts.get(`${_error.name}:${_error.message}`)})` : ""}`;
|
|
1230
|
-
console.group(errorGroup);
|
|
1231
|
-
console.error(`\u274C ${_error.message}`);
|
|
1232
|
-
if (_error.component) {
|
|
1233
|
-
console.log("\u{1F50D} Component:", this.formatComponent(_error.component));
|
|
1234
|
-
}
|
|
1235
|
-
if (_error.context) {
|
|
1236
|
-
console.log("\u{1F4CB} Context:", _error.context);
|
|
1237
|
-
}
|
|
1238
|
-
if (this.options.enableSuggestions && _error.suggestions.length > 0) {
|
|
1239
|
-
console.group("\u{1F4A1} Suggestions:");
|
|
1240
|
-
_error.suggestions.forEach((suggestion, index) => {
|
|
1241
|
-
console.log(`${index + 1}. ${suggestion}`);
|
|
1242
|
-
});
|
|
1243
|
-
console.groupEnd();
|
|
1244
|
-
}
|
|
1245
|
-
if (this.options.enableStackTrace && _error.stack) {
|
|
1246
|
-
console.log("\u{1F4DA} Stack trace:", _error.stack);
|
|
1247
|
-
}
|
|
1248
|
-
console.groupEnd();
|
|
1249
|
-
}
|
|
1250
|
-
/**
|
|
1251
|
-
* Format component for logging
|
|
1252
|
-
*/
|
|
1253
|
-
formatComponent(component, maxDepth = 2, currentDepth = 0) {
|
|
1254
|
-
if (currentDepth > maxDepth) {
|
|
1255
|
-
return "[...deep]";
|
|
1256
|
-
}
|
|
1257
|
-
if (typeof component === "function") {
|
|
1258
|
-
return `[Function: ${component.name || "anonymous"}]`;
|
|
1259
|
-
}
|
|
1260
|
-
if (Array.isArray(component)) {
|
|
1261
|
-
return component.slice(0, 3).map(
|
|
1262
|
-
(item) => this.formatComponent(item, maxDepth, currentDepth + 1)
|
|
1263
|
-
);
|
|
1264
|
-
}
|
|
1265
|
-
if (component && typeof component === "object") {
|
|
1266
|
-
const formatted = {};
|
|
1267
|
-
const keys = Object.keys(component).slice(0, 5);
|
|
1268
|
-
for (const key of keys) {
|
|
1269
|
-
if (key === "children" && component[key]) {
|
|
1270
|
-
formatted[key] = this.formatComponent(component[key], maxDepth, currentDepth + 1);
|
|
1271
|
-
} else {
|
|
1272
|
-
formatted[key] = component[key];
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
if (Object.keys(component).length > 5) {
|
|
1276
|
-
formatted["..."] = `(${Object.keys(component).length - 5} more)`;
|
|
1277
|
-
}
|
|
1278
|
-
return formatted;
|
|
1279
|
-
}
|
|
1280
|
-
return component;
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Suppress specific _error types
|
|
1284
|
-
*/
|
|
1285
|
-
suppress(errorPattern) {
|
|
1286
|
-
this.suppressedErrors.add(errorPattern);
|
|
1287
|
-
}
|
|
1288
|
-
/**
|
|
1289
|
-
* Clear _error history
|
|
1290
|
-
*/
|
|
1291
|
-
clearHistory() {
|
|
1292
|
-
this.errorHistory = [];
|
|
1293
|
-
this.errorCounts.clear();
|
|
1294
|
-
}
|
|
1295
|
-
/**
|
|
1296
|
-
* Get _error statistics
|
|
1297
|
-
*/
|
|
1298
|
-
getStats() {
|
|
1299
|
-
const errorsByType = {};
|
|
1300
|
-
const errorsByTime = {};
|
|
1301
|
-
this.errorHistory.forEach((_error) => {
|
|
1302
|
-
errorsByType[_error.type] = (errorsByType[_error.type] || 0) + _error.count;
|
|
1303
|
-
const hour = new Date(_error.timestamp).toISOString().slice(0, 13);
|
|
1304
|
-
errorsByTime[hour] = (errorsByTime[hour] || 0) + _error.count;
|
|
1305
|
-
});
|
|
1306
|
-
return {
|
|
1307
|
-
totalErrors: this.errorHistory.reduce((sum, e) => sum + e.count, 0),
|
|
1308
|
-
uniqueErrors: this.errorHistory.length,
|
|
1309
|
-
errorsByType,
|
|
1310
|
-
errorsByTime,
|
|
1311
|
-
mostCommonErrors: this.getMostCommonErrors(5),
|
|
1312
|
-
recentErrors: this.errorHistory.slice(0, 10)
|
|
1313
|
-
};
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* Get most common errors
|
|
1317
|
-
*/
|
|
1318
|
-
getMostCommonErrors(limit = 10) {
|
|
1319
|
-
return this.errorHistory.sort((a, b) => b.count - a.count).slice(0, limit).map(({ name, message, count, type }) => ({
|
|
1320
|
-
name,
|
|
1321
|
-
message,
|
|
1322
|
-
count,
|
|
1323
|
-
type
|
|
1324
|
-
}));
|
|
1325
|
-
}
|
|
1326
|
-
};
|
|
1327
|
-
var globalErrorHandler = new ErrorHandler();
|
|
1328
|
-
|
|
1329
|
-
// ../core/src/components/lifecycle.js
|
|
1330
|
-
var LIFECYCLE_PHASES = {
|
|
1331
|
-
BEFORE_CREATE: "beforeCreate",
|
|
1332
|
-
CREATED: "created",
|
|
1333
|
-
BEFORE_MOUNT: "beforeMount",
|
|
1334
|
-
MOUNTED: "mounted",
|
|
1335
|
-
BEFORE_UPDATE: "beforeUpdate",
|
|
1336
|
-
UPDATED: "updated",
|
|
1337
|
-
BEFORE_UNMOUNT: "beforeUnmount",
|
|
1338
|
-
UNMOUNTED: "unmounted",
|
|
1339
|
-
ERROR: "_error"
|
|
1340
|
-
};
|
|
1341
|
-
var componentInstances = /* @__PURE__ */ new WeakMap();
|
|
1342
|
-
var ComponentEventSystem = class {
|
|
1343
|
-
constructor() {
|
|
1344
|
-
this.events = /* @__PURE__ */ new Map();
|
|
1345
|
-
this.globalHandlers = /* @__PURE__ */ new Map();
|
|
1346
|
-
}
|
|
1347
|
-
/**
|
|
1348
|
-
* Emit event to component or globally
|
|
1349
|
-
*/
|
|
1350
|
-
emit(eventName, data = {}, target = null) {
|
|
1351
|
-
const event = {
|
|
1352
|
-
name: eventName,
|
|
1353
|
-
data,
|
|
1354
|
-
target,
|
|
1355
|
-
timestamp: Date.now(),
|
|
1356
|
-
stopped: false,
|
|
1357
|
-
preventDefault: false
|
|
1358
|
-
};
|
|
1359
|
-
if (target) {
|
|
1360
|
-
const instance = componentInstances.get(target);
|
|
1361
|
-
if (instance) {
|
|
1362
|
-
this._notifyHandlers(instance.id, event);
|
|
1363
|
-
}
|
|
1364
|
-
} else {
|
|
1365
|
-
this._notifyGlobalHandlers(event);
|
|
1366
|
-
}
|
|
1367
|
-
return event;
|
|
1368
|
-
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Listen for events on component or globally
|
|
1371
|
-
*/
|
|
1372
|
-
on(eventName, handler, componentId = null) {
|
|
1373
|
-
if (componentId) {
|
|
1374
|
-
if (!this.events.has(componentId)) {
|
|
1375
|
-
this.events.set(componentId, /* @__PURE__ */ new Map());
|
|
1376
|
-
}
|
|
1377
|
-
const componentEvents = this.events.get(componentId);
|
|
1378
|
-
if (!componentEvents.has(eventName)) {
|
|
1379
|
-
componentEvents.set(eventName, /* @__PURE__ */ new Set());
|
|
1380
|
-
}
|
|
1381
|
-
componentEvents.get(eventName).add(handler);
|
|
1382
|
-
} else {
|
|
1383
|
-
if (!this.globalHandlers.has(eventName)) {
|
|
1384
|
-
this.globalHandlers.set(eventName, /* @__PURE__ */ new Set());
|
|
1385
|
-
}
|
|
1386
|
-
this.globalHandlers.get(eventName).add(handler);
|
|
1387
|
-
}
|
|
1388
|
-
return () => this.off(eventName, handler, componentId);
|
|
1389
|
-
}
|
|
1390
|
-
/**
|
|
1391
|
-
* Remove event handler
|
|
1392
|
-
*/
|
|
1393
|
-
off(eventName, handler, componentId = null) {
|
|
1394
|
-
if (componentId) {
|
|
1395
|
-
const componentEvents = this.events.get(componentId);
|
|
1396
|
-
if (componentEvents && componentEvents.has(eventName)) {
|
|
1397
|
-
componentEvents.get(eventName).delete(handler);
|
|
1398
|
-
if (componentEvents.get(eventName).size === 0) {
|
|
1399
|
-
componentEvents.delete(eventName);
|
|
1400
|
-
if (componentEvents.size === 0) {
|
|
1401
|
-
this.events.delete(componentId);
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
} else {
|
|
1406
|
-
const handlers = this.globalHandlers.get(eventName);
|
|
1407
|
-
if (handlers) {
|
|
1408
|
-
handlers.delete(handler);
|
|
1409
|
-
if (handlers.size === 0) {
|
|
1410
|
-
this.globalHandlers.delete(eventName);
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
/**
|
|
1416
|
-
* Listen once
|
|
1417
|
-
*/
|
|
1418
|
-
once(eventName, handler, componentId = null) {
|
|
1419
|
-
const onceHandler = (event) => {
|
|
1420
|
-
handler(event);
|
|
1421
|
-
this.off(eventName, onceHandler, componentId);
|
|
1422
|
-
};
|
|
1423
|
-
return this.on(eventName, onceHandler, componentId);
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Notify component handlers
|
|
1427
|
-
*/
|
|
1428
|
-
_notifyHandlers(componentId, event) {
|
|
1429
|
-
const componentEvents = this.events.get(componentId);
|
|
1430
|
-
if (componentEvents && componentEvents.has(event.name)) {
|
|
1431
|
-
const handlers = componentEvents.get(event.name);
|
|
1432
|
-
for (const handler of handlers) {
|
|
1433
|
-
if (event.stopped) break;
|
|
1434
|
-
try {
|
|
1435
|
-
handler(event);
|
|
1436
|
-
} catch (_error) {
|
|
1437
|
-
globalErrorHandler.handle(_error, {
|
|
1438
|
-
type: "event-handler-_error",
|
|
1439
|
-
context: { event, handler: handler.toString() }
|
|
1440
|
-
});
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
/**
|
|
1446
|
-
* Notify global handlers
|
|
1447
|
-
*/
|
|
1448
|
-
_notifyGlobalHandlers(event) {
|
|
1449
|
-
const handlers = this.globalHandlers.get(event.name);
|
|
1450
|
-
if (handlers) {
|
|
1451
|
-
for (const handler of handlers) {
|
|
1452
|
-
if (event.stopped) break;
|
|
1453
|
-
try {
|
|
1454
|
-
handler(event);
|
|
1455
|
-
} catch (_error) {
|
|
1456
|
-
globalErrorHandler.handle(_error, {
|
|
1457
|
-
type: "global-event-handler-_error",
|
|
1458
|
-
context: { event, handler: handler.toString() }
|
|
1459
|
-
});
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
/**
|
|
1465
|
-
* Clean up events for a component
|
|
1466
|
-
*/
|
|
1467
|
-
cleanup(componentId) {
|
|
1468
|
-
this.events.delete(componentId);
|
|
1469
|
-
}
|
|
1470
|
-
/**
|
|
1471
|
-
* Get event statistics
|
|
1472
|
-
*/
|
|
1473
|
-
getStats() {
|
|
1474
|
-
return {
|
|
1475
|
-
componentEvents: this.events.size,
|
|
1476
|
-
globalEvents: this.globalHandlers.size,
|
|
1477
|
-
totalHandlers: Array.from(this.events.values()).reduce((sum, events) => {
|
|
1478
|
-
return sum + Array.from(events.values()).reduce((eventSum, handlers) => {
|
|
1479
|
-
return eventSum + handlers.size;
|
|
1480
|
-
}, 0);
|
|
1481
|
-
}, 0) + Array.from(this.globalHandlers.values()).reduce((sum, handlers) => {
|
|
1482
|
-
return sum + handlers.size;
|
|
1483
|
-
}, 0)
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
};
|
|
1487
|
-
var eventSystem = new ComponentEventSystem();
|
|
1488
|
-
function createLifecycleHooks() {
|
|
1489
|
-
const hooks = {};
|
|
1490
|
-
Object.values(LIFECYCLE_PHASES).forEach((phase) => {
|
|
1491
|
-
hooks[phase] = (callback) => {
|
|
1492
|
-
const instance = getCurrentInstance();
|
|
1493
|
-
if (instance) {
|
|
1494
|
-
instance.hook(phase, callback);
|
|
1495
|
-
}
|
|
1496
|
-
};
|
|
1497
|
-
});
|
|
1498
|
-
return hooks;
|
|
1499
|
-
}
|
|
1500
|
-
var currentInstance = null;
|
|
1501
|
-
function getCurrentInstance() {
|
|
1502
|
-
return currentInstance;
|
|
1503
|
-
}
|
|
1504
|
-
var useHooks = createLifecycleHooks();
|
|
1505
|
-
|
|
1506
|
-
// ../core/src/events/event-bus.js
|
|
1507
|
-
function throttle(func, delay) {
|
|
1508
|
-
let lastCall = 0;
|
|
1509
|
-
let timeoutId = null;
|
|
1510
|
-
return function throttled(...args) {
|
|
1511
|
-
const now = Date.now();
|
|
1512
|
-
const timeSinceLastCall = now - lastCall;
|
|
1513
|
-
if (timeSinceLastCall >= delay) {
|
|
1514
|
-
lastCall = now;
|
|
1515
|
-
return func.apply(this, args);
|
|
1516
|
-
} else {
|
|
1517
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
1518
|
-
timeoutId = setTimeout(() => {
|
|
1519
|
-
lastCall = Date.now();
|
|
1520
|
-
func.apply(this, args);
|
|
1521
|
-
}, delay - timeSinceLastCall);
|
|
1522
|
-
}
|
|
1523
|
-
};
|
|
1524
|
-
}
|
|
1525
|
-
var EventBus = class {
|
|
1526
|
-
constructor(options = {}) {
|
|
1527
|
-
this.listeners = /* @__PURE__ */ new Map();
|
|
1528
|
-
this.handlers = /* @__PURE__ */ new Map();
|
|
1529
|
-
this.actionHandlers = /* @__PURE__ */ new Map();
|
|
1530
|
-
this.middleware = [];
|
|
1531
|
-
this.throttledEmitters = /* @__PURE__ */ new Map();
|
|
1532
|
-
this.debouncedEmitters = /* @__PURE__ */ new Map();
|
|
1533
|
-
this.options = {
|
|
1534
|
-
debug: false,
|
|
1535
|
-
performance: true,
|
|
1536
|
-
maxListeners: 100,
|
|
1537
|
-
enableWildcards: true,
|
|
1538
|
-
enableAsync: true,
|
|
1539
|
-
wildcardSeparator: ":",
|
|
1540
|
-
enablePriority: true,
|
|
1541
|
-
defaultPriority: 0,
|
|
1542
|
-
errorHandler: null,
|
|
1543
|
-
filters: {
|
|
1544
|
-
allowList: null,
|
|
1545
|
-
// null means allow all
|
|
1546
|
-
blockList: []
|
|
1547
|
-
},
|
|
1548
|
-
throttle: {
|
|
1549
|
-
enabled: false,
|
|
1550
|
-
defaultDelay: 100,
|
|
1551
|
-
events: {}
|
|
1552
|
-
},
|
|
1553
|
-
batching: {
|
|
1554
|
-
enabled: false,
|
|
1555
|
-
maxBatchSize: 10,
|
|
1556
|
-
flushInterval: 16
|
|
1557
|
-
},
|
|
1558
|
-
...options
|
|
1559
|
-
};
|
|
1560
|
-
this.stats = {
|
|
1561
|
-
eventsEmitted: 0,
|
|
1562
|
-
listenersExecuted: 0,
|
|
1563
|
-
errorsOccurred: 0,
|
|
1564
|
-
averageEmitTime: 0,
|
|
1565
|
-
throttledEvents: 0,
|
|
1566
|
-
filteredEvents: 0
|
|
1567
|
-
};
|
|
1568
|
-
if (this.options.batching.enabled) {
|
|
1569
|
-
this.batchQueue = [];
|
|
1570
|
-
this.batchTimer = null;
|
|
1571
|
-
}
|
|
1572
|
-
if (this.options.debug) {
|
|
1573
|
-
this.use((event, data, next) => {
|
|
1574
|
-
console.log(`[EventBus] ${event}:`, data);
|
|
1575
|
-
next();
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
/**
|
|
1580
|
-
* Add middleware
|
|
1581
|
-
*/
|
|
1582
|
-
use(middleware) {
|
|
1583
|
-
if (typeof middleware !== "function") {
|
|
1584
|
-
throw new Error("Middleware must be a function");
|
|
1585
|
-
}
|
|
1586
|
-
this.middleware.push(middleware);
|
|
1587
|
-
return this;
|
|
1588
|
-
}
|
|
1589
|
-
/**
|
|
1590
|
-
* Check if event passes filters
|
|
1591
|
-
*/
|
|
1592
|
-
passesFilters(event) {
|
|
1593
|
-
const { allowList, blockList } = this.options.filters;
|
|
1594
|
-
if (blockList && blockList.length > 0) {
|
|
1595
|
-
for (const pattern of blockList) {
|
|
1596
|
-
if (this.matchPattern(pattern, event)) {
|
|
1597
|
-
this.stats.filteredEvents++;
|
|
1598
|
-
return false;
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
if (allowList && allowList.length > 0) {
|
|
1603
|
-
for (const pattern of allowList) {
|
|
1604
|
-
if (this.matchPattern(pattern, event)) {
|
|
1605
|
-
return true;
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
this.stats.filteredEvents++;
|
|
1609
|
-
return false;
|
|
1610
|
-
}
|
|
1611
|
-
return true;
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Match event against pattern
|
|
1615
|
-
*/
|
|
1616
|
-
matchPattern(pattern, event) {
|
|
1617
|
-
const sep = this.options.wildcardSeparator;
|
|
1618
|
-
const patternParts = pattern.split(sep);
|
|
1619
|
-
const eventParts = event.split(sep);
|
|
1620
|
-
if (pattern.includes("*")) {
|
|
1621
|
-
if (patternParts.length !== eventParts.length) {
|
|
1622
|
-
return false;
|
|
1623
|
-
}
|
|
1624
|
-
return patternParts.every((part, i) => part === "*" || part === eventParts[i]);
|
|
1625
|
-
}
|
|
1626
|
-
return pattern === event;
|
|
1627
|
-
}
|
|
1628
|
-
/**
|
|
1629
|
-
* Emit an event
|
|
1630
|
-
*/
|
|
1631
|
-
async emit(event, data = null) {
|
|
1632
|
-
if (!this.passesFilters(event)) {
|
|
1633
|
-
if (this.options.debug) {
|
|
1634
|
-
console.warn(`[EventBus] Event filtered: ${event}`);
|
|
1635
|
-
}
|
|
1636
|
-
return;
|
|
1637
|
-
}
|
|
1638
|
-
if (this.options.batching.enabled) {
|
|
1639
|
-
return this.addToBatch(event, data);
|
|
1640
|
-
}
|
|
1641
|
-
if (this.options.throttle.enabled) {
|
|
1642
|
-
const throttleDelay = this.options.throttle.events && this.options.throttle.events[event] || this.options.throttle.defaultDelay;
|
|
1643
|
-
if (throttleDelay > 0) {
|
|
1644
|
-
return this.emitThrottled(event, data, throttleDelay);
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
return this.emitImmediate(event, data);
|
|
1648
|
-
}
|
|
1649
|
-
/**
|
|
1650
|
-
* Emit immediately without throttling
|
|
1651
|
-
*/
|
|
1652
|
-
async emitImmediate(event, data = null) {
|
|
1653
|
-
const startTime = this.options.performance ? performance.now() : 0;
|
|
1654
|
-
try {
|
|
1655
|
-
await this.runMiddleware(event, data);
|
|
1656
|
-
const listeners = this.getEventListeners(event);
|
|
1657
|
-
if (listeners.length === 0) {
|
|
1658
|
-
if (this.options.debug) {
|
|
1659
|
-
console.warn(`[EventBus] No listeners for event: ${event}`);
|
|
1660
|
-
}
|
|
1661
|
-
return;
|
|
1662
|
-
}
|
|
1663
|
-
const promises = listeners.map(
|
|
1664
|
-
(listenerObj) => this.executeListener(listenerObj.listener, event, data, listenerObj.options)
|
|
1665
|
-
);
|
|
1666
|
-
if (this.options.enableAsync) {
|
|
1667
|
-
await Promise.allSettled(promises);
|
|
1668
|
-
} else {
|
|
1669
|
-
for (const promise of promises) {
|
|
1670
|
-
await promise;
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
this.stats.eventsEmitted++;
|
|
1674
|
-
this.stats.listenersExecuted += listeners.length;
|
|
1675
|
-
} catch (error) {
|
|
1676
|
-
this.stats.errorsOccurred++;
|
|
1677
|
-
this.handleError(error, event, data);
|
|
1678
|
-
} finally {
|
|
1679
|
-
if (this.options.performance) {
|
|
1680
|
-
const duration = performance.now() - startTime;
|
|
1681
|
-
this.updatePerformanceStats(duration);
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
/**
|
|
1686
|
-
* Emit with throttling
|
|
1687
|
-
*/
|
|
1688
|
-
emitThrottled(event, data, delay) {
|
|
1689
|
-
if (!this.throttledEmitters.has(event)) {
|
|
1690
|
-
const throttled = throttle((evt, d) => this.emitImmediate(evt, d), delay);
|
|
1691
|
-
this.throttledEmitters.set(event, throttled);
|
|
1692
|
-
}
|
|
1693
|
-
this.stats.throttledEvents++;
|
|
1694
|
-
return this.throttledEmitters.get(event)(event, data);
|
|
1695
|
-
}
|
|
1696
|
-
/**
|
|
1697
|
-
* Add event to batch queue
|
|
1698
|
-
*/
|
|
1699
|
-
addToBatch(event, data) {
|
|
1700
|
-
this.batchQueue.push({ event, data, timestamp: Date.now() });
|
|
1701
|
-
if (this.batchQueue.length >= this.options.batching.maxBatchSize) {
|
|
1702
|
-
this.flushBatch();
|
|
1703
|
-
} else if (!this.batchTimer) {
|
|
1704
|
-
this.batchTimer = setTimeout(() => {
|
|
1705
|
-
this.flushBatch();
|
|
1706
|
-
}, this.options.batching.flushInterval);
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
/**
|
|
1710
|
-
* Flush batch queue
|
|
1711
|
-
*/
|
|
1712
|
-
async flushBatch() {
|
|
1713
|
-
if (this.batchTimer) {
|
|
1714
|
-
clearTimeout(this.batchTimer);
|
|
1715
|
-
this.batchTimer = null;
|
|
1716
|
-
}
|
|
1717
|
-
const batch = this.batchQueue.splice(0);
|
|
1718
|
-
for (const { event, data } of batch) {
|
|
1719
|
-
await this.emitImmediate(event, data);
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
/**
|
|
1723
|
-
* Register event listener with options
|
|
1724
|
-
*/
|
|
1725
|
-
on(event, listener, options = {}) {
|
|
1726
|
-
if (typeof listener !== "function") {
|
|
1727
|
-
throw new Error("Listener must be a function");
|
|
1728
|
-
}
|
|
1729
|
-
if (!this.listeners.has(event)) {
|
|
1730
|
-
this.listeners.set(event, []);
|
|
1731
|
-
}
|
|
1732
|
-
const listeners = this.listeners.get(event);
|
|
1733
|
-
if (listeners.length >= this.options.maxListeners) {
|
|
1734
|
-
console.warn(`[EventBus] Max listeners (${this.options.maxListeners}) reached for event: ${event}`);
|
|
1735
|
-
}
|
|
1736
|
-
const listenerId = this.generateListenerId(event);
|
|
1737
|
-
const listenerObj = {
|
|
1738
|
-
listener,
|
|
1739
|
-
listenerId,
|
|
1740
|
-
priority: options.priority !== void 0 ? options.priority : this.options.defaultPriority,
|
|
1741
|
-
condition: options.condition || null,
|
|
1742
|
-
timeout: options.timeout || null,
|
|
1743
|
-
options
|
|
1744
|
-
};
|
|
1745
|
-
listener.__listenerId = listenerId;
|
|
1746
|
-
listener.__event = event;
|
|
1747
|
-
if (this.options.enablePriority) {
|
|
1748
|
-
const insertIndex = listeners.findIndex((l) => l.priority < listenerObj.priority);
|
|
1749
|
-
if (insertIndex === -1) {
|
|
1750
|
-
listeners.push(listenerObj);
|
|
1751
|
-
} else {
|
|
1752
|
-
listeners.splice(insertIndex, 0, listenerObj);
|
|
1753
|
-
}
|
|
1754
|
-
} else {
|
|
1755
|
-
listeners.push(listenerObj);
|
|
1756
|
-
}
|
|
1757
|
-
return listenerId;
|
|
1758
|
-
}
|
|
1759
|
-
/**
|
|
1760
|
-
* Register one-time listener
|
|
1761
|
-
*/
|
|
1762
|
-
once(event, listener, options = {}) {
|
|
1763
|
-
const onceListener = (...args) => {
|
|
1764
|
-
this.off(event, onceListener.__listenerId);
|
|
1765
|
-
return listener.call(this, ...args);
|
|
1766
|
-
};
|
|
1767
|
-
if (options.timeout) {
|
|
1768
|
-
const timeoutId = setTimeout(() => {
|
|
1769
|
-
this.off(event, onceListener.__listenerId);
|
|
1770
|
-
if (this.options.debug) {
|
|
1771
|
-
console.warn(`[EventBus] Listener timeout for event: ${event}`);
|
|
1772
|
-
}
|
|
1773
|
-
}, options.timeout);
|
|
1774
|
-
onceListener.__cleanup = () => clearTimeout(timeoutId);
|
|
1775
|
-
}
|
|
1776
|
-
return this.on(event, onceListener, options);
|
|
1777
|
-
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Remove listener
|
|
1780
|
-
*/
|
|
1781
|
-
off(event, listenerId) {
|
|
1782
|
-
if (!this.listeners.has(event)) {
|
|
1783
|
-
return false;
|
|
1784
|
-
}
|
|
1785
|
-
const listeners = this.listeners.get(event);
|
|
1786
|
-
const index = listeners.findIndex((l) => l.listenerId === listenerId);
|
|
1787
|
-
if (index !== -1) {
|
|
1788
|
-
const listenerObj = listeners[index];
|
|
1789
|
-
if (listenerObj.listener.__cleanup) {
|
|
1790
|
-
listenerObj.listener.__cleanup();
|
|
1791
|
-
}
|
|
1792
|
-
listeners.splice(index, 1);
|
|
1793
|
-
if (listeners.length === 0) {
|
|
1794
|
-
this.listeners.delete(event);
|
|
1795
|
-
}
|
|
1796
|
-
return true;
|
|
1797
|
-
}
|
|
1798
|
-
return false;
|
|
1799
|
-
}
|
|
1800
|
-
/**
|
|
1801
|
-
* Remove all listeners for an event
|
|
1802
|
-
*/
|
|
1803
|
-
removeAllListeners(event) {
|
|
1804
|
-
if (event) {
|
|
1805
|
-
this.listeners.delete(event);
|
|
1806
|
-
} else {
|
|
1807
|
-
this.listeners.clear();
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
/**
|
|
1811
|
-
* Get event listeners with wildcard support
|
|
1812
|
-
*/
|
|
1813
|
-
getEventListeners(event) {
|
|
1814
|
-
const listeners = [];
|
|
1815
|
-
if (this.listeners.has(event)) {
|
|
1816
|
-
listeners.push(...this.listeners.get(event));
|
|
1817
|
-
}
|
|
1818
|
-
if (this.options.enableWildcards) {
|
|
1819
|
-
for (const [pattern, patternListeners] of this.listeners) {
|
|
1820
|
-
if (pattern.includes("*") && this.matchPattern(pattern, event)) {
|
|
1821
|
-
listeners.push(...patternListeners);
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
if (this.options.enablePriority) {
|
|
1826
|
-
listeners.sort((a, b) => b.priority - a.priority);
|
|
1827
|
-
}
|
|
1828
|
-
return listeners;
|
|
1829
|
-
}
|
|
1830
|
-
/**
|
|
1831
|
-
* Execute listener with options
|
|
1832
|
-
*/
|
|
1833
|
-
async executeListener(listener, event, data, options = {}) {
|
|
1834
|
-
try {
|
|
1835
|
-
if (options.condition && !options.condition(data)) {
|
|
1836
|
-
return;
|
|
1837
|
-
}
|
|
1838
|
-
const result = listener.call(this, data, event);
|
|
1839
|
-
if (result && typeof result.then === "function") {
|
|
1840
|
-
await result;
|
|
1841
|
-
}
|
|
1842
|
-
return result;
|
|
1843
|
-
} catch (error) {
|
|
1844
|
-
this.handleError(error, event, data);
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
/**
|
|
1848
|
-
* Run middleware chain
|
|
1849
|
-
*/
|
|
1850
|
-
async runMiddleware(event, data) {
|
|
1851
|
-
if (this.middleware.length === 0) return;
|
|
1852
|
-
let index = 0;
|
|
1853
|
-
const next = async () => {
|
|
1854
|
-
if (index < this.middleware.length) {
|
|
1855
|
-
const middleware = this.middleware[index++];
|
|
1856
|
-
await middleware(event, data, next);
|
|
1857
|
-
}
|
|
1858
|
-
};
|
|
1859
|
-
await next();
|
|
1860
|
-
}
|
|
1861
|
-
/**
|
|
1862
|
-
* Handle errors
|
|
1863
|
-
*/
|
|
1864
|
-
handleError(error, event, data) {
|
|
1865
|
-
if (this.options.errorHandler) {
|
|
1866
|
-
this.options.errorHandler(error, event, data);
|
|
1867
|
-
} else if (this.options.debug) {
|
|
1868
|
-
console.error(`[EventBus] Error in event ${event}:`, error, data);
|
|
1869
|
-
}
|
|
1870
|
-
this.emitSync("eventbus:error", { error, event, data });
|
|
478
|
+
function validateComponent(component, path = "root") {
|
|
479
|
+
if (component === null || component === void 0) {
|
|
480
|
+
throw new Error(`Invalid component at ${path}: null or undefined`);
|
|
1871
481
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
*/
|
|
1875
|
-
emitSync(event, data = null) {
|
|
1876
|
-
try {
|
|
1877
|
-
const listeners = this.getEventListeners(event);
|
|
1878
|
-
listeners.forEach((listenerObj) => {
|
|
1879
|
-
try {
|
|
1880
|
-
if (!listenerObj.options.condition || listenerObj.options.condition(data)) {
|
|
1881
|
-
listenerObj.listener.call(this, data, event);
|
|
1882
|
-
}
|
|
1883
|
-
} catch (error) {
|
|
1884
|
-
this.handleError(error, event, data);
|
|
1885
|
-
}
|
|
1886
|
-
});
|
|
1887
|
-
this.stats.eventsEmitted++;
|
|
1888
|
-
this.stats.listenersExecuted += listeners.length;
|
|
1889
|
-
} catch (error) {
|
|
1890
|
-
this.stats.errorsOccurred++;
|
|
1891
|
-
this.handleError(error, event, data);
|
|
1892
|
-
}
|
|
482
|
+
if (["string", "number", "boolean"].includes(typeof component)) {
|
|
483
|
+
return true;
|
|
1893
484
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
*/
|
|
1897
|
-
registerAction(action, handler) {
|
|
1898
|
-
if (typeof handler !== "function") {
|
|
1899
|
-
throw new Error("Action handler must be a function");
|
|
1900
|
-
}
|
|
1901
|
-
this.actionHandlers.set(action, handler);
|
|
1902
|
-
if (this.options.debug) {
|
|
1903
|
-
console.log(`[EventBus] Registered action: ${action}`);
|
|
1904
|
-
}
|
|
485
|
+
if (typeof component === "function") {
|
|
486
|
+
return true;
|
|
1905
487
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
registerActions(actions) {
|
|
1910
|
-
Object.entries(actions).forEach(([action, handler]) => {
|
|
1911
|
-
this.registerAction(action, handler);
|
|
488
|
+
if (Array.isArray(component)) {
|
|
489
|
+
component.forEach((child, index) => {
|
|
490
|
+
validateComponent(child, `${path}[${index}]`);
|
|
1912
491
|
});
|
|
492
|
+
return true;
|
|
1913
493
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
return Array.from(this.actionHandlers.keys());
|
|
1919
|
-
}
|
|
1920
|
-
/**
|
|
1921
|
-
* Handle action event (called by DOM integration)
|
|
1922
|
-
*/
|
|
1923
|
-
handleAction(action, element, event, data) {
|
|
1924
|
-
const handler = this.actionHandlers.get(action);
|
|
1925
|
-
if (!handler) {
|
|
1926
|
-
if (this.options.debug) {
|
|
1927
|
-
console.warn(`[EventBus] No handler registered for action: ${action}`);
|
|
1928
|
-
}
|
|
1929
|
-
return;
|
|
1930
|
-
}
|
|
1931
|
-
try {
|
|
1932
|
-
handler.call(element, {
|
|
1933
|
-
element,
|
|
1934
|
-
event,
|
|
1935
|
-
data,
|
|
1936
|
-
emit: this.emit.bind(this),
|
|
1937
|
-
emitSync: this.emitSync.bind(this)
|
|
1938
|
-
});
|
|
1939
|
-
} catch (error) {
|
|
1940
|
-
this.handleError(error, `action:${action}`, { element, event, data });
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
/**
|
|
1944
|
-
* Generate unique listener ID
|
|
1945
|
-
*/
|
|
1946
|
-
generateListenerId(event) {
|
|
1947
|
-
return `${event}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1948
|
-
}
|
|
1949
|
-
/**
|
|
1950
|
-
* Update performance stats
|
|
1951
|
-
*/
|
|
1952
|
-
updatePerformanceStats(duration) {
|
|
1953
|
-
const count = this.stats.eventsEmitted;
|
|
1954
|
-
this.stats.averageEmitTime = (this.stats.averageEmitTime * (count - 1) + duration) / count;
|
|
1955
|
-
}
|
|
1956
|
-
/**
|
|
1957
|
-
* Get statistics
|
|
1958
|
-
*/
|
|
1959
|
-
getStats() {
|
|
1960
|
-
return { ...this.stats };
|
|
1961
|
-
}
|
|
1962
|
-
/**
|
|
1963
|
-
* Reset statistics
|
|
1964
|
-
*/
|
|
1965
|
-
resetStats() {
|
|
1966
|
-
this.stats = {
|
|
1967
|
-
eventsEmitted: 0,
|
|
1968
|
-
listenersExecuted: 0,
|
|
1969
|
-
errorsOccurred: 0,
|
|
1970
|
-
averageEmitTime: 0,
|
|
1971
|
-
throttledEvents: 0,
|
|
1972
|
-
filteredEvents: 0
|
|
1973
|
-
};
|
|
1974
|
-
}
|
|
1975
|
-
/**
|
|
1976
|
-
* Destroy event bus
|
|
1977
|
-
*/
|
|
1978
|
-
destroy() {
|
|
1979
|
-
this.removeAllListeners();
|
|
1980
|
-
this.actionHandlers.clear();
|
|
1981
|
-
this.handlers.clear();
|
|
1982
|
-
this.middleware = [];
|
|
1983
|
-
this.throttledEmitters.clear();
|
|
1984
|
-
this.debouncedEmitters.clear();
|
|
1985
|
-
if (this.batchTimer) {
|
|
1986
|
-
clearTimeout(this.batchTimer);
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
};
|
|
1990
|
-
function createEventBus(options = {}) {
|
|
1991
|
-
return new EventBus(options);
|
|
1992
|
-
}
|
|
1993
|
-
var globalEventBus = createEventBus();
|
|
1994
|
-
var emit = globalEventBus.emit.bind(globalEventBus);
|
|
1995
|
-
var emitSync = globalEventBus.emitSync.bind(globalEventBus);
|
|
1996
|
-
var on = globalEventBus.on.bind(globalEventBus);
|
|
1997
|
-
var once = globalEventBus.once.bind(globalEventBus);
|
|
1998
|
-
var off = globalEventBus.off.bind(globalEventBus);
|
|
1999
|
-
var registerAction = globalEventBus.registerAction.bind(globalEventBus);
|
|
2000
|
-
var handleAction = globalEventBus.handleAction.bind(globalEventBus);
|
|
2001
|
-
|
|
2002
|
-
// ../core/src/events/dom-integration.js
|
|
2003
|
-
var DOMEventIntegration = class {
|
|
2004
|
-
constructor(eventBus = globalEventBus, options = {}) {
|
|
2005
|
-
this.eventBus = eventBus;
|
|
2006
|
-
this.options = {
|
|
2007
|
-
debug: false,
|
|
2008
|
-
debounceDelay: 150,
|
|
2009
|
-
throttleDelay: 100,
|
|
2010
|
-
enableDelegation: true,
|
|
2011
|
-
enableDebounce: true,
|
|
2012
|
-
enableThrottle: false,
|
|
2013
|
-
...options
|
|
2014
|
-
};
|
|
2015
|
-
this.boundHandlers = /* @__PURE__ */ new Map();
|
|
2016
|
-
this.activeElement = null;
|
|
2017
|
-
this.isInitialized = false;
|
|
2018
|
-
this.handleClick = this.handleClick.bind(this);
|
|
2019
|
-
this.handleChange = this.handleChange.bind(this);
|
|
2020
|
-
this.handleInput = this.handleInput.bind(this);
|
|
2021
|
-
this.handleSubmit = this.handleSubmit.bind(this);
|
|
2022
|
-
this.handleKeydown = this.handleKeydown.bind(this);
|
|
2023
|
-
this.handleFocus = this.handleFocus.bind(this);
|
|
2024
|
-
this.handleBlur = this.handleBlur.bind(this);
|
|
2025
|
-
}
|
|
2026
|
-
/**
|
|
2027
|
-
* Initialize DOM event listeners
|
|
2028
|
-
* @param {HTMLElement} rootElement - Root element to attach listeners to (default: document)
|
|
2029
|
-
*/
|
|
2030
|
-
initialize(rootElement = document) {
|
|
2031
|
-
if (this.isInitialized) {
|
|
2032
|
-
console.warn("[DOMEventIntegration] Already initialized");
|
|
2033
|
-
return;
|
|
2034
|
-
}
|
|
2035
|
-
if (typeof window === "undefined" || !rootElement) {
|
|
2036
|
-
console.warn("[DOMEventIntegration] Cannot initialize: no DOM environment");
|
|
2037
|
-
return;
|
|
2038
|
-
}
|
|
2039
|
-
this.rootElement = rootElement;
|
|
2040
|
-
this.setupDOMEventListeners();
|
|
2041
|
-
this.isInitialized = true;
|
|
2042
|
-
if (this.options.debug) {
|
|
2043
|
-
console.log("[DOMEventIntegration] Initialized with options:", this.options);
|
|
494
|
+
if (typeof component === "object") {
|
|
495
|
+
const keys = Object.keys(component);
|
|
496
|
+
if (keys.length === 0) {
|
|
497
|
+
throw new Error(`Empty object at ${path}`);
|
|
2044
498
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
*/
|
|
2050
|
-
setupDOMEventListeners() {
|
|
2051
|
-
const clickHandler = this.createDelegatedHandler("click", this.handleClick);
|
|
2052
|
-
this.rootElement.addEventListener("click", clickHandler, { passive: false });
|
|
2053
|
-
this.boundHandlers.set("click", clickHandler);
|
|
2054
|
-
const changeHandler = this.options.enableDebounce ? this.debounce(this.createDelegatedHandler("change", this.handleChange), this.options.debounceDelay) : this.createDelegatedHandler("change", this.handleChange);
|
|
2055
|
-
this.rootElement.addEventListener("change", changeHandler, { passive: true });
|
|
2056
|
-
this.boundHandlers.set("change", changeHandler);
|
|
2057
|
-
const inputHandler = this.options.enableDebounce ? this.debounce(this.createDelegatedHandler("input", this.handleInput), this.options.debounceDelay) : this.createDelegatedHandler("input", this.handleInput);
|
|
2058
|
-
this.rootElement.addEventListener("input", inputHandler, { passive: true });
|
|
2059
|
-
this.boundHandlers.set("input", inputHandler);
|
|
2060
|
-
const submitHandler = this.createDelegatedHandler("submit", this.handleSubmit);
|
|
2061
|
-
this.rootElement.addEventListener("submit", submitHandler, { passive: false });
|
|
2062
|
-
this.boundHandlers.set("submit", submitHandler);
|
|
2063
|
-
const keydownHandler = this.createDelegatedHandler("keydown", this.handleKeydown);
|
|
2064
|
-
this.rootElement.addEventListener("keydown", keydownHandler, { passive: false });
|
|
2065
|
-
this.boundHandlers.set("keydown", keydownHandler);
|
|
2066
|
-
const focusHandler = this.createDelegatedHandler("focus", this.handleFocus);
|
|
2067
|
-
this.rootElement.addEventListener("focus", focusHandler, { passive: true, capture: true });
|
|
2068
|
-
this.boundHandlers.set("focus", focusHandler);
|
|
2069
|
-
const blurHandler = this.createDelegatedHandler("blur", this.handleBlur);
|
|
2070
|
-
this.rootElement.addEventListener("blur", blurHandler, { passive: true, capture: true });
|
|
2071
|
-
this.boundHandlers.set("blur", blurHandler);
|
|
2072
|
-
}
|
|
2073
|
-
/**
|
|
2074
|
-
* Create a delegated event handler
|
|
2075
|
-
* @private
|
|
2076
|
-
*/
|
|
2077
|
-
createDelegatedHandler(eventType, handler) {
|
|
2078
|
-
return (event) => {
|
|
2079
|
-
const target = event.target;
|
|
2080
|
-
if (!target) return;
|
|
2081
|
-
const actionElement = this.options.enableDelegation ? target.closest("[data-action]") : target.hasAttribute?.("data-action") ? target : null;
|
|
2082
|
-
if (actionElement) {
|
|
2083
|
-
handler(actionElement, event);
|
|
2084
|
-
} else {
|
|
2085
|
-
handler(target, event);
|
|
499
|
+
keys.forEach((key) => {
|
|
500
|
+
const value = component[key];
|
|
501
|
+
if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(key) && key !== "text") {
|
|
502
|
+
console.warn(`Potentially invalid tag name at ${path}: ${key}`);
|
|
2086
503
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
* Handle click events
|
|
2091
|
-
* @private
|
|
2092
|
-
*/
|
|
2093
|
-
handleClick(element, event) {
|
|
2094
|
-
const action = element.getAttribute?.("data-action");
|
|
2095
|
-
if (action) {
|
|
2096
|
-
this.handleDataAction(element, event, action);
|
|
2097
|
-
}
|
|
2098
|
-
this.eventBus.emitSync("dom:click", {
|
|
2099
|
-
element,
|
|
2100
|
-
event,
|
|
2101
|
-
action,
|
|
2102
|
-
data: this.parseDataAttributes(element)
|
|
2103
|
-
});
|
|
2104
|
-
}
|
|
2105
|
-
/**
|
|
2106
|
-
* Handle change events
|
|
2107
|
-
* @private
|
|
2108
|
-
*/
|
|
2109
|
-
handleChange(element, event) {
|
|
2110
|
-
const action = element.getAttribute?.("data-action");
|
|
2111
|
-
if (action) {
|
|
2112
|
-
this.handleDataAction(element, event, action);
|
|
2113
|
-
}
|
|
2114
|
-
this.eventBus.emitSync("dom:change", {
|
|
2115
|
-
element,
|
|
2116
|
-
event,
|
|
2117
|
-
value: element.value,
|
|
2118
|
-
action,
|
|
2119
|
-
data: this.parseDataAttributes(element)
|
|
2120
|
-
});
|
|
2121
|
-
}
|
|
2122
|
-
/**
|
|
2123
|
-
* Handle input events
|
|
2124
|
-
* @private
|
|
2125
|
-
*/
|
|
2126
|
-
handleInput(element, event) {
|
|
2127
|
-
const action = element.getAttribute?.("data-action");
|
|
2128
|
-
if (action) {
|
|
2129
|
-
this.handleDataAction(element, event, action);
|
|
2130
|
-
}
|
|
2131
|
-
this.eventBus.emitSync("dom:input", {
|
|
2132
|
-
element,
|
|
2133
|
-
event,
|
|
2134
|
-
value: element.value,
|
|
2135
|
-
action,
|
|
2136
|
-
data: this.parseDataAttributes(element)
|
|
2137
|
-
});
|
|
2138
|
-
}
|
|
2139
|
-
/**
|
|
2140
|
-
* Handle submit events
|
|
2141
|
-
* @private
|
|
2142
|
-
*/
|
|
2143
|
-
handleSubmit(element, event) {
|
|
2144
|
-
const action = element.getAttribute?.("data-action");
|
|
2145
|
-
if (action) {
|
|
2146
|
-
event.preventDefault();
|
|
2147
|
-
this.handleDataAction(element, event, action);
|
|
2148
|
-
}
|
|
2149
|
-
this.eventBus.emitSync("dom:submit", {
|
|
2150
|
-
element,
|
|
2151
|
-
event,
|
|
2152
|
-
action,
|
|
2153
|
-
formData: this.extractFormData(element),
|
|
2154
|
-
data: this.parseDataAttributes(element)
|
|
2155
|
-
});
|
|
2156
|
-
}
|
|
2157
|
-
/**
|
|
2158
|
-
* Handle keydown events
|
|
2159
|
-
* @private
|
|
2160
|
-
*/
|
|
2161
|
-
handleKeydown(element, event) {
|
|
2162
|
-
const action = element.getAttribute?.("data-action");
|
|
2163
|
-
const keyAction = element.getAttribute?.(`data-key-${event.key.toLowerCase()}`);
|
|
2164
|
-
if (action && this.shouldTriggerKeyAction(event)) {
|
|
2165
|
-
this.handleDataAction(element, event, action);
|
|
2166
|
-
}
|
|
2167
|
-
if (keyAction) {
|
|
2168
|
-
this.handleDataAction(element, event, keyAction);
|
|
2169
|
-
}
|
|
2170
|
-
this.eventBus.emitSync("dom:keydown", {
|
|
2171
|
-
element,
|
|
2172
|
-
event,
|
|
2173
|
-
key: event.key,
|
|
2174
|
-
code: event.code,
|
|
2175
|
-
action,
|
|
2176
|
-
keyAction,
|
|
2177
|
-
data: this.parseDataAttributes(element)
|
|
2178
|
-
});
|
|
2179
|
-
}
|
|
2180
|
-
/**
|
|
2181
|
-
* Handle focus events
|
|
2182
|
-
* @private
|
|
2183
|
-
*/
|
|
2184
|
-
handleFocus(element, event) {
|
|
2185
|
-
this.activeElement = element;
|
|
2186
|
-
this.eventBus.emitSync("dom:focus", {
|
|
2187
|
-
element,
|
|
2188
|
-
event,
|
|
2189
|
-
data: this.parseDataAttributes(element)
|
|
2190
|
-
});
|
|
2191
|
-
}
|
|
2192
|
-
/**
|
|
2193
|
-
* Handle blur events
|
|
2194
|
-
* @private
|
|
2195
|
-
*/
|
|
2196
|
-
handleBlur(element, event) {
|
|
2197
|
-
if (this.activeElement === element) {
|
|
2198
|
-
this.activeElement = null;
|
|
2199
|
-
}
|
|
2200
|
-
this.eventBus.emitSync("dom:blur", {
|
|
2201
|
-
element,
|
|
2202
|
-
event,
|
|
2203
|
-
data: this.parseDataAttributes(element)
|
|
2204
|
-
});
|
|
2205
|
-
}
|
|
2206
|
-
/**
|
|
2207
|
-
* Handle data-action attributes
|
|
2208
|
-
* @private
|
|
2209
|
-
*/
|
|
2210
|
-
handleDataAction(element, event, action) {
|
|
2211
|
-
if (!action) return;
|
|
2212
|
-
const data = this.parseDataAttributes(element);
|
|
2213
|
-
this.eventBus.emitSync("dom:action", {
|
|
2214
|
-
action,
|
|
2215
|
-
element,
|
|
2216
|
-
event,
|
|
2217
|
-
data
|
|
2218
|
-
});
|
|
2219
|
-
this.eventBus.handleAction(action, element, event, data);
|
|
2220
|
-
if (this.options.debug) {
|
|
2221
|
-
console.log(`[DOMEventIntegration] Action triggered: ${action}`, {
|
|
2222
|
-
element,
|
|
2223
|
-
event: event.type,
|
|
2224
|
-
data
|
|
2225
|
-
});
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
/**
|
|
2229
|
-
* Parse data attributes from an element
|
|
2230
|
-
* @private
|
|
2231
|
-
*/
|
|
2232
|
-
parseDataAttributes(element) {
|
|
2233
|
-
if (!element?.attributes) return {};
|
|
2234
|
-
const data = {};
|
|
2235
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
2236
|
-
if (attr.name.startsWith("data-") && attr.name !== "data-action") {
|
|
2237
|
-
const key = attr.name.slice(5).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
2238
|
-
let value = attr.value;
|
|
2239
|
-
try {
|
|
2240
|
-
if (value === "true") value = true;
|
|
2241
|
-
else if (value === "false") value = false;
|
|
2242
|
-
else if (value === "null") value = null;
|
|
2243
|
-
else if (value === "undefined") value = void 0;
|
|
2244
|
-
else if (/^\d+$/.test(value)) value = parseInt(value, 10);
|
|
2245
|
-
else if (/^\d*\.\d+$/.test(value)) value = parseFloat(value);
|
|
2246
|
-
else if (value.startsWith("{") && value.endsWith("}") || value.startsWith("[") && value.endsWith("]")) {
|
|
2247
|
-
value = JSON.parse(value);
|
|
2248
|
-
}
|
|
2249
|
-
} catch {
|
|
504
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
505
|
+
if (value.children) {
|
|
506
|
+
validateComponent(value.children, `${path}.${key}.children`);
|
|
2250
507
|
}
|
|
2251
|
-
|
|
508
|
+
} else if (value && typeof value !== "string" && typeof value !== "number" && typeof value !== "function") {
|
|
509
|
+
throw new Error(`Invalid value type at ${path}.${key}: ${typeof value}`);
|
|
2252
510
|
}
|
|
2253
511
|
});
|
|
2254
|
-
return
|
|
2255
|
-
}
|
|
2256
|
-
/**
|
|
2257
|
-
* Extract form data from a form element
|
|
2258
|
-
* @private
|
|
2259
|
-
*/
|
|
2260
|
-
extractFormData(formElement) {
|
|
2261
|
-
if (!formElement || formElement.tagName !== "FORM") {
|
|
2262
|
-
return {};
|
|
2263
|
-
}
|
|
2264
|
-
const formData = new FormData(formElement);
|
|
2265
|
-
const data = {};
|
|
2266
|
-
for (const [key, value] of formData.entries()) {
|
|
2267
|
-
if (data[key]) {
|
|
2268
|
-
if (Array.isArray(data[key])) {
|
|
2269
|
-
data[key].push(value);
|
|
2270
|
-
} else {
|
|
2271
|
-
data[key] = [data[key], value];
|
|
2272
|
-
}
|
|
2273
|
-
} else {
|
|
2274
|
-
data[key] = value;
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
return data;
|
|
2278
|
-
}
|
|
2279
|
-
/**
|
|
2280
|
-
* Check if a key event should trigger an action
|
|
2281
|
-
* @private
|
|
2282
|
-
*/
|
|
2283
|
-
shouldTriggerKeyAction(event) {
|
|
2284
|
-
const triggerKeys = ["Enter", "Space", "Escape"];
|
|
2285
|
-
return triggerKeys.includes(event.key);
|
|
512
|
+
return true;
|
|
2286
513
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
let timeout;
|
|
2293
|
-
return function executedFunction(...args) {
|
|
2294
|
-
const later = () => {
|
|
2295
|
-
clearTimeout(timeout);
|
|
2296
|
-
func.apply(this, args);
|
|
2297
|
-
};
|
|
2298
|
-
clearTimeout(timeout);
|
|
2299
|
-
timeout = setTimeout(later, wait);
|
|
2300
|
-
};
|
|
514
|
+
throw new Error(`Invalid component type at ${path}: ${typeof component}`);
|
|
515
|
+
}
|
|
516
|
+
function isCoherentObject(obj) {
|
|
517
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
518
|
+
return false;
|
|
2301
519
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
*/
|
|
2306
|
-
throttle(func, limit) {
|
|
2307
|
-
let inThrottle;
|
|
2308
|
-
return function executedFunction(...args) {
|
|
2309
|
-
if (!inThrottle) {
|
|
2310
|
-
func.apply(this, args);
|
|
2311
|
-
inThrottle = true;
|
|
2312
|
-
setTimeout(() => inThrottle = false, limit);
|
|
2313
|
-
}
|
|
2314
|
-
};
|
|
520
|
+
const keys = Object.keys(obj);
|
|
521
|
+
if (keys.length === 0) {
|
|
522
|
+
return false;
|
|
2315
523
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
this.rootElement.addEventListener(eventType, wrappedHandler, options);
|
|
2325
|
-
this.boundHandlers.set(`custom:${eventType}`, wrappedHandler);
|
|
524
|
+
return keys.every((key) => {
|
|
525
|
+
if (key === "text") return true;
|
|
526
|
+
return /^[a-zA-Z][a-zA-Z0-9-]*$/.test(key);
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
function extractProps(coherentObj) {
|
|
530
|
+
if (!isCoherentObject(coherentObj)) {
|
|
531
|
+
return {};
|
|
2326
532
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
this.boundHandlers.delete(`custom:${eventType}`);
|
|
533
|
+
const props = {};
|
|
534
|
+
const keys = Object.keys(coherentObj);
|
|
535
|
+
keys.forEach((tag) => {
|
|
536
|
+
const value = coherentObj[tag];
|
|
537
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
538
|
+
props[tag] = { ...value };
|
|
539
|
+
} else {
|
|
540
|
+
props[tag] = { text: value };
|
|
2336
541
|
}
|
|
542
|
+
});
|
|
543
|
+
return props;
|
|
544
|
+
}
|
|
545
|
+
function hasChildren(component) {
|
|
546
|
+
if (Array.isArray(component)) {
|
|
547
|
+
return component.length > 0;
|
|
2337
548
|
}
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
*/
|
|
2342
|
-
registerActions(actions) {
|
|
2343
|
-
this.eventBus.registerActions(actions);
|
|
2344
|
-
}
|
|
2345
|
-
/**
|
|
2346
|
-
* Get the currently active (focused) element
|
|
2347
|
-
* @returns {HTMLElement|null}
|
|
2348
|
-
*/
|
|
2349
|
-
getActiveElement() {
|
|
2350
|
-
return this.activeElement;
|
|
2351
|
-
}
|
|
2352
|
-
/**
|
|
2353
|
-
* Trigger an action programmatically
|
|
2354
|
-
* @param {string} action - Action name
|
|
2355
|
-
* @param {HTMLElement} element - Target element
|
|
2356
|
-
* @param {Object} data - Additional data
|
|
2357
|
-
*/
|
|
2358
|
-
triggerAction(action, element, data = {}) {
|
|
2359
|
-
const syntheticEvent = new CustomEvent("synthetic", {
|
|
2360
|
-
bubbles: true,
|
|
2361
|
-
cancelable: true,
|
|
2362
|
-
detail: data
|
|
2363
|
-
});
|
|
2364
|
-
this.eventBus.handleAction(action, element, syntheticEvent, data);
|
|
2365
|
-
}
|
|
2366
|
-
/**
|
|
2367
|
-
* Clean up event listeners
|
|
2368
|
-
*/
|
|
2369
|
-
destroy() {
|
|
2370
|
-
if (!this.isInitialized) return;
|
|
2371
|
-
this.boundHandlers.forEach((handler, eventType) => {
|
|
2372
|
-
this.rootElement.removeEventListener(
|
|
2373
|
-
eventType.replace("custom:", ""),
|
|
2374
|
-
handler
|
|
2375
|
-
);
|
|
2376
|
-
});
|
|
2377
|
-
this.boundHandlers.clear();
|
|
2378
|
-
this.activeElement = null;
|
|
2379
|
-
this.isInitialized = false;
|
|
2380
|
-
if (this.options.debug) {
|
|
2381
|
-
console.log("[DOMEventIntegration] Destroyed");
|
|
549
|
+
if (isCoherentObject(component)) {
|
|
550
|
+
if (component.children !== void 0 && component.children !== null) {
|
|
551
|
+
return Array.isArray(component.children) ? component.children.length > 0 : true;
|
|
2382
552
|
}
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
});
|
|
2388
|
-
if (typeof window !== "undefined") {
|
|
2389
|
-
if (document.readyState === "loading") {
|
|
2390
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
2391
|
-
globalDOMIntegration.initialize();
|
|
553
|
+
const keys = Object.keys(component);
|
|
554
|
+
return keys.some((key) => {
|
|
555
|
+
const value = component[key];
|
|
556
|
+
return value && typeof value === "object" && value.children;
|
|
2392
557
|
});
|
|
2393
|
-
} else {
|
|
2394
|
-
globalDOMIntegration.initialize();
|
|
2395
558
|
}
|
|
559
|
+
return false;
|
|
2396
560
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
// Core bus
|
|
2401
|
-
bus: globalEventBus,
|
|
2402
|
-
dom: globalDOMIntegration,
|
|
2403
|
-
// Quick access methods
|
|
2404
|
-
emit: globalEventBus.emit.bind(globalEventBus),
|
|
2405
|
-
emitSync: globalEventBus.emitSync.bind(globalEventBus),
|
|
2406
|
-
on: globalEventBus.on.bind(globalEventBus),
|
|
2407
|
-
once: globalEventBus.once.bind(globalEventBus),
|
|
2408
|
-
off: globalEventBus.off.bind(globalEventBus),
|
|
2409
|
-
// Action methods
|
|
2410
|
-
registerAction: globalEventBus.registerAction.bind(globalEventBus),
|
|
2411
|
-
registerActions: globalEventBus.registerActions.bind(globalEventBus),
|
|
2412
|
-
handleAction: globalEventBus.handleAction.bind(globalEventBus),
|
|
2413
|
-
// Statistics and debugging
|
|
2414
|
-
getStats: globalEventBus.getStats.bind(globalEventBus),
|
|
2415
|
-
resetStats: globalEventBus.resetStats.bind(globalEventBus),
|
|
2416
|
-
// Lifecycle
|
|
2417
|
-
destroy() {
|
|
2418
|
-
globalEventBus.destroy();
|
|
2419
|
-
globalDOMIntegration.destroy();
|
|
561
|
+
function normalizeChildren(children) {
|
|
562
|
+
if (children === null || children === void 0) {
|
|
563
|
+
return [];
|
|
2420
564
|
}
|
|
2421
|
-
|
|
565
|
+
if (Array.isArray(children)) {
|
|
566
|
+
return children.flat().filter((child) => child !== null && child !== void 0);
|
|
567
|
+
}
|
|
568
|
+
return [children];
|
|
569
|
+
}
|
|
2422
570
|
|
|
2423
571
|
// ../core/src/index.js
|
|
2424
572
|
var scopeCounter = { value: 0 };
|
|
@@ -3103,10 +1251,10 @@ function createCacheManager(options = {}) {
|
|
|
3103
1251
|
cleanupInterval.unref();
|
|
3104
1252
|
}
|
|
3105
1253
|
}
|
|
3106
|
-
function generateCacheKey(component, props = {},
|
|
1254
|
+
function generateCacheKey(component, props = {}, context = {}) {
|
|
3107
1255
|
const componentStr = typeof component === "function" ? component.name || component.toString() : JSON.stringify(component);
|
|
3108
1256
|
const propsStr = JSON.stringify(props, Object.keys(props).sort());
|
|
3109
|
-
const contextStr = JSON.stringify(
|
|
1257
|
+
const contextStr = JSON.stringify(context);
|
|
3110
1258
|
const hash = simpleHash(componentStr + propsStr + contextStr);
|
|
3111
1259
|
return `${extractComponentName(component)}_${hash}`;
|
|
3112
1260
|
}
|
|
@@ -3286,107 +1434,6 @@ function createCacheManager(options = {}) {
|
|
|
3286
1434
|
}
|
|
3287
1435
|
var cacheManager = createCacheManager();
|
|
3288
1436
|
|
|
3289
|
-
// ../core/src/rendering/css-manager.js
|
|
3290
|
-
import fs from "node:fs/promises";
|
|
3291
|
-
import path from "node:path";
|
|
3292
|
-
var CSSManager = class {
|
|
3293
|
-
constructor(options = {}) {
|
|
3294
|
-
this.options = {
|
|
3295
|
-
basePath: process.cwd(),
|
|
3296
|
-
minify: false,
|
|
3297
|
-
cache: true,
|
|
3298
|
-
autoprefixer: false,
|
|
3299
|
-
...options
|
|
3300
|
-
};
|
|
3301
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
3302
|
-
this.loadedFiles = /* @__PURE__ */ new Set();
|
|
3303
|
-
}
|
|
3304
|
-
/**
|
|
3305
|
-
* Load CSS file content
|
|
3306
|
-
*/
|
|
3307
|
-
async loadCSSFile(filePath) {
|
|
3308
|
-
const fullPath = path.resolve(this.options.basePath, filePath);
|
|
3309
|
-
const cacheKey = fullPath;
|
|
3310
|
-
if (this.options.cache && this.cache.has(cacheKey)) {
|
|
3311
|
-
return this.cache.get(cacheKey);
|
|
3312
|
-
}
|
|
3313
|
-
try {
|
|
3314
|
-
let content = await fs.readFile(fullPath, "utf8");
|
|
3315
|
-
if (this.options.minify) {
|
|
3316
|
-
content = this.minifyCSS(content);
|
|
3317
|
-
}
|
|
3318
|
-
if (this.options.cache) {
|
|
3319
|
-
this.cache.set(cacheKey, content);
|
|
3320
|
-
}
|
|
3321
|
-
this.loadedFiles.add(filePath);
|
|
3322
|
-
return content;
|
|
3323
|
-
} catch (_error) {
|
|
3324
|
-
console.warn(`Failed to load CSS file: ${filePath}`, _error.message);
|
|
3325
|
-
return "";
|
|
3326
|
-
}
|
|
3327
|
-
}
|
|
3328
|
-
/**
|
|
3329
|
-
* Load multiple CSS files
|
|
3330
|
-
*/
|
|
3331
|
-
async loadCSSFiles(filePaths) {
|
|
3332
|
-
if (!Array.isArray(filePaths)) {
|
|
3333
|
-
filePaths = [filePaths];
|
|
3334
|
-
}
|
|
3335
|
-
const cssContents = await Promise.all(
|
|
3336
|
-
filePaths.map((filePath) => this.loadCSSFile(filePath))
|
|
3337
|
-
);
|
|
3338
|
-
return cssContents.join("\n");
|
|
3339
|
-
}
|
|
3340
|
-
/**
|
|
3341
|
-
* Generate CSS link tags for external files
|
|
3342
|
-
*/
|
|
3343
|
-
generateCSSLinks(filePaths, baseUrl = "/") {
|
|
3344
|
-
if (!Array.isArray(filePaths)) {
|
|
3345
|
-
filePaths = [filePaths];
|
|
3346
|
-
}
|
|
3347
|
-
return filePaths.map((filePath) => {
|
|
3348
|
-
const href = filePath.startsWith("http") ? filePath : `${baseUrl}${filePath}`.replace(/\/+/g, "/");
|
|
3349
|
-
return `<link rel="stylesheet" href="${this.escapeHtml(href)}" />`;
|
|
3350
|
-
}).join("\n");
|
|
3351
|
-
}
|
|
3352
|
-
/**
|
|
3353
|
-
* Generate inline style tag with CSS content
|
|
3354
|
-
*/
|
|
3355
|
-
generateInlineStyles(cssContent) {
|
|
3356
|
-
if (!cssContent) return "";
|
|
3357
|
-
return `<style type="text/css">
|
|
3358
|
-
${cssContent}
|
|
3359
|
-
</style>`;
|
|
3360
|
-
}
|
|
3361
|
-
/**
|
|
3362
|
-
* Basic CSS minification
|
|
3363
|
-
*/
|
|
3364
|
-
minifyCSS(css) {
|
|
3365
|
-
return css.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/;\s*}/g, "}").replace(/{\s+/g, "{").replace(/;\s+/g, ";").trim();
|
|
3366
|
-
}
|
|
3367
|
-
/**
|
|
3368
|
-
* Escape HTML entities
|
|
3369
|
-
*/
|
|
3370
|
-
escapeHtml(text) {
|
|
3371
|
-
const div = { textContent: text };
|
|
3372
|
-
return div.innerHTML || text;
|
|
3373
|
-
}
|
|
3374
|
-
/**
|
|
3375
|
-
* Clear cache
|
|
3376
|
-
*/
|
|
3377
|
-
clearCache() {
|
|
3378
|
-
this.cache.clear();
|
|
3379
|
-
this.loadedFiles.clear();
|
|
3380
|
-
}
|
|
3381
|
-
/**
|
|
3382
|
-
* Get loaded file list
|
|
3383
|
-
*/
|
|
3384
|
-
getLoadedFiles() {
|
|
3385
|
-
return Array.from(this.loadedFiles);
|
|
3386
|
-
}
|
|
3387
|
-
};
|
|
3388
|
-
var defaultCSSManager = new CSSManager();
|
|
3389
|
-
|
|
3390
1437
|
// ../core/src/rendering/html-renderer.js
|
|
3391
1438
|
var rendererCache = createCacheManager({
|
|
3392
1439
|
maxSize: 1e3,
|