@coherent.js/core 1.0.0-rc.6 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +291 -182
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/types/index.d.ts +711 -9
package/types/index.d.ts
CHANGED
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* @version 1.0.0-beta.1
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// Re-export strict element types
|
|
8
|
+
// Re-export strict element types. `export *` re-exports without binding the
|
|
9
|
+
// names locally, so CoherentNode below needs its own import.
|
|
9
10
|
export * from './elements';
|
|
11
|
+
import type { StrictCoherentElement } from './elements';
|
|
10
12
|
|
|
11
13
|
// ============================================================================
|
|
12
14
|
// Basic Types
|
|
@@ -595,8 +597,44 @@ export function evaluateLazy<T>(obj: T, ...args: any[]): T;
|
|
|
595
597
|
// Component System Classes and Functions
|
|
596
598
|
// ============================================================================
|
|
597
599
|
|
|
598
|
-
/**
|
|
599
|
-
export
|
|
600
|
+
/** Stateful component class backing createComponent() */
|
|
601
|
+
export class Component implements ComponentInstance {
|
|
602
|
+
constructor(definition?: ComponentDefinition);
|
|
603
|
+
|
|
604
|
+
name: string;
|
|
605
|
+
props: ComponentProps;
|
|
606
|
+
state: ComponentStateManager;
|
|
607
|
+
children: ComponentInstance[];
|
|
608
|
+
parent: ComponentInstance | null;
|
|
609
|
+
rendered: CoherentNode | null;
|
|
610
|
+
isMounted: boolean;
|
|
611
|
+
isDestroyed: boolean;
|
|
612
|
+
definition: ComponentDefinition;
|
|
613
|
+
hooks: Required<ComponentLifecycleHooks>;
|
|
614
|
+
methods: ComponentMethods;
|
|
615
|
+
computed: ComputedProperties;
|
|
616
|
+
computedCache: Map<string, any>;
|
|
617
|
+
watchers: ComponentWatchers;
|
|
618
|
+
|
|
619
|
+
render(props?: ComponentProps): CoherentNode;
|
|
620
|
+
mount(): ComponentInstance;
|
|
621
|
+
update(): ComponentInstance;
|
|
622
|
+
destroy(): ComponentInstance;
|
|
623
|
+
clone(overrides?: Partial<ComponentDefinition>): ComponentInstance;
|
|
624
|
+
getMetadata(): ComponentMetadata;
|
|
625
|
+
callHook(hookName: keyof ComponentLifecycleHooks, ...args: any[]): any;
|
|
626
|
+
handleError(error: Error, context?: string): void;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Create a component.
|
|
631
|
+
*
|
|
632
|
+
* The result is callable -- `render(Counter({ count: 2 }))` -- and also carries
|
|
633
|
+
* the full Component instance API (`render`, `mount`, `state`, ...).
|
|
634
|
+
*/
|
|
635
|
+
export function createComponent<P extends ComponentProps = ComponentProps>(
|
|
636
|
+
definition: ComponentDefinition | CoherentComponent
|
|
637
|
+
): CoherentComponent<P> & ComponentInstance;
|
|
600
638
|
|
|
601
639
|
/** Define a component factory */
|
|
602
640
|
export function defineComponent<P extends ComponentProps>(
|
|
@@ -615,6 +653,29 @@ export function getComponent<P extends ComponentProps>(name: string): CoherentCo
|
|
|
615
653
|
/** Get all registered components */
|
|
616
654
|
export function getRegisteredComponents(): Map<string, CoherentComponent>;
|
|
617
655
|
|
|
656
|
+
/** Create a higher-order component */
|
|
657
|
+
export function createHOC<P extends ComponentProps = ComponentProps>(
|
|
658
|
+
enhancer: (component: CoherentComponent<P>, props: P) => CoherentNode
|
|
659
|
+
): (component: CoherentComponent<P>) => CoherentComponent<P>;
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Options for memoComponent().
|
|
663
|
+
*
|
|
664
|
+
* Distinct from MemoOptions: memoComponent compares props and state rather
|
|
665
|
+
* than raw memo arguments.
|
|
666
|
+
*/
|
|
667
|
+
export interface MemoComponentOptions<P extends ComponentProps = ComponentProps> {
|
|
668
|
+
propsEqual?: (a: P, b: P) => boolean;
|
|
669
|
+
stateEqual?: (a: ComponentState, b: ComponentState) => boolean;
|
|
670
|
+
name?: string;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/** Memoize a component */
|
|
674
|
+
export function memoComponent<P extends ComponentProps = ComponentProps>(
|
|
675
|
+
component: CoherentComponent<P>,
|
|
676
|
+
options?: MemoComponentOptions<P>
|
|
677
|
+
): MemoizedFunction<CoherentComponent<P>>;
|
|
678
|
+
|
|
618
679
|
// ============================================================================
|
|
619
680
|
// State Management Functions
|
|
620
681
|
// ============================================================================
|
|
@@ -649,6 +710,22 @@ export interface VDOMPatch {
|
|
|
649
710
|
// ============================================================================
|
|
650
711
|
|
|
651
712
|
/** Global performance monitor instance */
|
|
713
|
+
/** Performance monitor surface, as implemented by performance/monitor.js */
|
|
714
|
+
export interface PerformanceMonitor {
|
|
715
|
+
startRender(componentName?: string): string;
|
|
716
|
+
endRender(renderId: string): number;
|
|
717
|
+
recordMetric(name: string, value: number, tags?: Record<string, any>): void;
|
|
718
|
+
addMetric(name: string, value: number, tags?: Record<string, any>): void;
|
|
719
|
+
measure<T>(name: string, fn: () => T): T;
|
|
720
|
+
measureAsync<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
721
|
+
addAlertRule(rule: Record<string, any>): void;
|
|
722
|
+
generateReport(): Record<string, any>;
|
|
723
|
+
getStats(): Record<string, any>;
|
|
724
|
+
reset(): void;
|
|
725
|
+
start(): void;
|
|
726
|
+
stop(): void;
|
|
727
|
+
}
|
|
728
|
+
|
|
652
729
|
export const performanceMonitor: PerformanceMonitor;
|
|
653
730
|
|
|
654
731
|
// ============================================================================
|
|
@@ -673,6 +750,12 @@ export interface CacheManager {
|
|
|
673
750
|
prune(): void;
|
|
674
751
|
}
|
|
675
752
|
|
|
753
|
+
/** Shared cache manager instance */
|
|
754
|
+
export const cacheManager: CacheManager;
|
|
755
|
+
|
|
756
|
+
/** Create a cache manager */
|
|
757
|
+
export function createCacheManager(options?: CacheManagerOptions): CacheManager;
|
|
758
|
+
|
|
676
759
|
// ============================================================================
|
|
677
760
|
// Bundle Optimization (Additional)
|
|
678
761
|
// ============================================================================
|
|
@@ -681,16 +764,141 @@ export interface CacheManager {
|
|
|
681
764
|
// Component Cache (Additional)
|
|
682
765
|
// ============================================================================
|
|
683
766
|
|
|
684
|
-
/**
|
|
767
|
+
/** Options for {@link ComponentCache} */
|
|
768
|
+
export interface ComponentCacheOptions {
|
|
769
|
+
/** Entries retained before least-used eviction; defaults to `1000` */
|
|
770
|
+
maxSize?: number;
|
|
771
|
+
/** Entry lifetime in ms; defaults to `300000` */
|
|
772
|
+
defaultTTL?: number;
|
|
773
|
+
/** Expiry sweep interval in ms, or `0` to disable; defaults to `60000` */
|
|
774
|
+
cleanupInterval?: number;
|
|
775
|
+
/** Track hits, misses and evictions; defaults to `true` */
|
|
776
|
+
enableStats?: boolean;
|
|
777
|
+
[option: string]: unknown;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/** Per-entry overrides for {@link ComponentCache.set} */
|
|
781
|
+
export interface ComponentCacheEntryOptions {
|
|
782
|
+
/** Keys that invalidate this entry */
|
|
783
|
+
dependencies?: string[];
|
|
784
|
+
/** Lifetime in ms; defaults to the cache's `defaultTTL` */
|
|
785
|
+
ttl?: number;
|
|
786
|
+
/** Exempt from expiry */
|
|
787
|
+
persistent?: boolean;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
export interface ComponentCacheStats {
|
|
791
|
+
size: number;
|
|
792
|
+
maxSize: number;
|
|
793
|
+
hits: number;
|
|
794
|
+
misses: number;
|
|
795
|
+
/** Percentage, as a fixed-2 string, or `0` before any request */
|
|
796
|
+
hitRate: string | number;
|
|
797
|
+
evictions: number;
|
|
798
|
+
cleanups: number;
|
|
799
|
+
invalidations: number;
|
|
800
|
+
dependencies: number;
|
|
801
|
+
/** Rough estimate in KB */
|
|
802
|
+
memoryUsage: number;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Caches rendered components with TTL, least-used eviction and
|
|
807
|
+
* dependency-based invalidation.
|
|
808
|
+
*
|
|
809
|
+
* `cache` is the backing Map, not a method — write through `set()`.
|
|
810
|
+
*/
|
|
685
811
|
export class ComponentCache {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
812
|
+
constructor(options?: ComponentCacheOptions);
|
|
813
|
+
|
|
814
|
+
options: ComponentCacheOptions;
|
|
815
|
+
/** Backing store, keyed by cache key */
|
|
816
|
+
cache: Map<string, unknown>;
|
|
817
|
+
/** Dependency key to the cache keys that depend on it */
|
|
818
|
+
dependencies: Map<string, Set<string>>;
|
|
819
|
+
|
|
820
|
+
/** Derive a stable key from a component and its props */
|
|
821
|
+
generateKey(
|
|
822
|
+
component: unknown,
|
|
823
|
+
props?: Record<string, unknown>,
|
|
824
|
+
context?: Record<string, unknown>
|
|
825
|
+
): string;
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Read an entry, recording a hit or miss. Returns a deep clone, or `null`
|
|
829
|
+
* when absent or expired. Any `dependencies` passed are added to the entry.
|
|
830
|
+
*/
|
|
831
|
+
get(key: string, dependencies?: string[]): CoherentNode | null;
|
|
832
|
+
|
|
833
|
+
/** Store an entry, evicting the least-used one when the cache is full */
|
|
834
|
+
set(key: string, component: CoherentNode, options?: ComponentCacheEntryOptions): boolean;
|
|
835
|
+
|
|
836
|
+
/** Whether a live, unexpired entry exists */
|
|
837
|
+
has(key: string): boolean;
|
|
838
|
+
|
|
839
|
+
/** Drop every entry depending on `dependency`; returns how many */
|
|
840
|
+
invalidate(dependency: string): number;
|
|
841
|
+
|
|
842
|
+
/** Drop every entry depending on any of `dependencies`; returns how many */
|
|
843
|
+
invalidateMultiple(dependencies: string[]): number;
|
|
844
|
+
|
|
845
|
+
/** Drop every entry */
|
|
846
|
+
clear(): void;
|
|
847
|
+
|
|
848
|
+
/** Drop expired entries; returns how many */
|
|
849
|
+
cleanup(): number;
|
|
850
|
+
|
|
851
|
+
getStats(): ComponentCacheStats;
|
|
852
|
+
|
|
853
|
+
/** Rough memory estimate in KB */
|
|
854
|
+
estimateMemoryUsage(): number;
|
|
855
|
+
|
|
856
|
+
/** Most-accessed entries, hottest first */
|
|
857
|
+
getHotComponents(limit?: number): Array<{
|
|
858
|
+
key: string;
|
|
859
|
+
accessCount: number;
|
|
860
|
+
component: CoherentNode;
|
|
861
|
+
dependencies: string[];
|
|
862
|
+
}>;
|
|
863
|
+
|
|
864
|
+
/** Tuning suggestions derived from the current stats */
|
|
865
|
+
getRecommendations(): Array<{
|
|
866
|
+
type: string;
|
|
867
|
+
message: string;
|
|
868
|
+
priority: 'HIGH' | 'MEDIUM' | 'LOW';
|
|
869
|
+
}>;
|
|
870
|
+
|
|
871
|
+
/** Stop the cleanup timer and drop every entry */
|
|
872
|
+
destroy(): void;
|
|
690
873
|
}
|
|
691
874
|
|
|
692
875
|
/** Create component cache */
|
|
693
|
-
export function createComponentCache(options?:
|
|
876
|
+
export function createComponentCache(options?: ComponentCacheOptions): ComponentCache;
|
|
877
|
+
|
|
878
|
+
// ============================================================================
|
|
879
|
+
// HTML Utilities
|
|
880
|
+
// ============================================================================
|
|
881
|
+
|
|
882
|
+
/** Escape HTML special characters in text */
|
|
883
|
+
export function escapeHtml(text: string): string;
|
|
884
|
+
|
|
885
|
+
/** Check whether a tag is a void element */
|
|
886
|
+
export function isVoidElement(tagName: string): boolean;
|
|
887
|
+
|
|
888
|
+
/** Serialize props into an HTML attribute string */
|
|
889
|
+
export function formatAttributes(props: Record<string, any>): string;
|
|
890
|
+
|
|
891
|
+
/** Mark content as trusted so it is emitted without escaping */
|
|
892
|
+
export function dangerouslySetInnerContent(content: string): TrustedContent;
|
|
893
|
+
|
|
894
|
+
/** Content marked trusted by dangerouslySetInnerContent() */
|
|
895
|
+
export interface TrustedContent {
|
|
896
|
+
__html: string;
|
|
897
|
+
__trusted: true;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/** Detect content marked by dangerouslySetInnerContent() */
|
|
901
|
+
export function isTrustedContent(value: unknown): value is TrustedContent;
|
|
694
902
|
|
|
695
903
|
// ============================================================================
|
|
696
904
|
// Utility Types and Constants
|
|
@@ -715,3 +923,497 @@ declare const coherent: {
|
|
|
715
923
|
};
|
|
716
924
|
|
|
717
925
|
export default coherent;
|
|
926
|
+
|
|
927
|
+
// ============================================================================
|
|
928
|
+
// Object Factory
|
|
929
|
+
// ============================================================================
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Build a single-element node.
|
|
933
|
+
*
|
|
934
|
+
* @throws when `tag` is not a known HTML element.
|
|
935
|
+
*/
|
|
936
|
+
export function createElement(tag: string, props?: Record<string, unknown>): CoherentNode;
|
|
937
|
+
|
|
938
|
+
/** Build a text node from any value. */
|
|
939
|
+
export function createTextNode(text: unknown): CoherentNode;
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Shorthand element factories: `h.div({ text: 'hi' })` is
|
|
943
|
+
* `createElement('div', { text: 'hi' })`.
|
|
944
|
+
*/
|
|
945
|
+
export const h: Record<string, (props?: Record<string, unknown>) => CoherentNode>;
|
|
946
|
+
|
|
947
|
+
// ============================================================================
|
|
948
|
+
// HTML Nesting Validation
|
|
949
|
+
// ============================================================================
|
|
950
|
+
|
|
951
|
+
/** Children the HTML spec forbids, keyed by parent tag. */
|
|
952
|
+
export const FORBIDDEN_CHILDREN: Record<string, Set<string>>;
|
|
953
|
+
|
|
954
|
+
/** Raised by {@link validateNesting} when `throwOnError` is set. */
|
|
955
|
+
export class HTMLNestingError extends Error {
|
|
956
|
+
constructor(message: string, context?: { parent?: string; child?: string; path?: string });
|
|
957
|
+
parent?: string;
|
|
958
|
+
child?: string;
|
|
959
|
+
path?: string;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Check one parent/child pair against {@link FORBIDDEN_CHILDREN}.
|
|
964
|
+
*
|
|
965
|
+
* Returns `true` when the nesting is legal. Otherwise warns (outside
|
|
966
|
+
* production) and returns `false`, or throws {@link HTMLNestingError} when
|
|
967
|
+
* `throwOnError` is set. Browsers silently reparent invalid nesting, which
|
|
968
|
+
* shows up later as a hydration mismatch.
|
|
969
|
+
*/
|
|
970
|
+
export function validateNesting(
|
|
971
|
+
parentTag: string,
|
|
972
|
+
childTag: string,
|
|
973
|
+
path?: string,
|
|
974
|
+
options?: { warn?: boolean; throwOnError?: boolean }
|
|
975
|
+
): boolean;
|
|
976
|
+
|
|
977
|
+
// ============================================================================
|
|
978
|
+
// Component Lifecycle
|
|
979
|
+
// ============================================================================
|
|
980
|
+
|
|
981
|
+
/** Lifecycle phase names. */
|
|
982
|
+
export const LIFECYCLE_PHASES: {
|
|
983
|
+
readonly BEFORE_CREATE: 'beforeCreate';
|
|
984
|
+
readonly CREATED: 'created';
|
|
985
|
+
readonly BEFORE_MOUNT: 'beforeMount';
|
|
986
|
+
readonly MOUNTED: 'mounted';
|
|
987
|
+
readonly BEFORE_UPDATE: 'beforeUpdate';
|
|
988
|
+
readonly UPDATED: 'updated';
|
|
989
|
+
readonly BEFORE_UNMOUNT: 'beforeUnmount';
|
|
990
|
+
readonly UNMOUNTED: 'unmounted';
|
|
991
|
+
readonly ERROR: '_error';
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
/** One of the {@link LIFECYCLE_PHASES} values. */
|
|
995
|
+
export type LifecyclePhase =
|
|
996
|
+
| 'beforeCreate'
|
|
997
|
+
| 'created'
|
|
998
|
+
| 'beforeMount'
|
|
999
|
+
| 'mounted'
|
|
1000
|
+
| 'beforeUpdate'
|
|
1001
|
+
| 'updated'
|
|
1002
|
+
| 'beforeUnmount'
|
|
1003
|
+
| 'unmounted'
|
|
1004
|
+
| '_error';
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Tracks one component's phase, hooks, state and cleanup.
|
|
1008
|
+
*
|
|
1009
|
+
* Timers, listeners and subscriptions registered through the instance are
|
|
1010
|
+
* released on unmount, so prefer them over the globals.
|
|
1011
|
+
*/
|
|
1012
|
+
export class ComponentLifecycle {
|
|
1013
|
+
constructor(component: unknown, options?: Record<string, unknown>);
|
|
1014
|
+
|
|
1015
|
+
component: unknown;
|
|
1016
|
+
id: string;
|
|
1017
|
+
options: Record<string, unknown>;
|
|
1018
|
+
phase: LifecyclePhase | null;
|
|
1019
|
+
hooks: Map<string, Array<(...args: never[]) => unknown>>;
|
|
1020
|
+
state: Map<string, unknown>;
|
|
1021
|
+
props: Record<string, unknown>;
|
|
1022
|
+
context: Record<string, unknown>;
|
|
1023
|
+
isMounted: boolean;
|
|
1024
|
+
isDestroyed: boolean;
|
|
1025
|
+
children: Set<ComponentLifecycle>;
|
|
1026
|
+
parent: ComponentLifecycle | null;
|
|
1027
|
+
|
|
1028
|
+
/** Register a callback for a phase */
|
|
1029
|
+
hook(phase: LifecyclePhase, callback: (...args: never[]) => unknown): this;
|
|
1030
|
+
|
|
1031
|
+
addChild(child: ComponentLifecycle): void;
|
|
1032
|
+
removeChild(child: ComponentLifecycle): void;
|
|
1033
|
+
|
|
1034
|
+
/** Add a listener released on unmount */
|
|
1035
|
+
addEventListener(
|
|
1036
|
+
element: unknown,
|
|
1037
|
+
event: string,
|
|
1038
|
+
listener: (event: unknown) => void,
|
|
1039
|
+
options?: Record<string, unknown>
|
|
1040
|
+
): void;
|
|
1041
|
+
|
|
1042
|
+
/** Register an unsubscribe called on unmount */
|
|
1043
|
+
addSubscription(unsubscribe: () => void): void;
|
|
1044
|
+
|
|
1045
|
+
/** `setTimeout` cleared on unmount */
|
|
1046
|
+
setTimeout(callback: () => void, delay: number): unknown;
|
|
1047
|
+
/** `setInterval` cleared on unmount */
|
|
1048
|
+
setInterval(callback: () => void, interval: number): unknown;
|
|
1049
|
+
|
|
1050
|
+
/** Counts of hooks, children, timers and subscriptions */
|
|
1051
|
+
getStats(): Record<string, unknown>;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Component-scoped event emitter used by the lifecycle system. Reachable
|
|
1056
|
+
* through {@link eventSystem}; the class itself is not exported.
|
|
1057
|
+
*/
|
|
1058
|
+
declare class ComponentEventSystem {
|
|
1059
|
+
constructor();
|
|
1060
|
+
emit(eventName: string, data?: unknown, target?: unknown): unknown;
|
|
1061
|
+
on(eventName: string, handler: (event: unknown) => void, componentId?: string | null): () => void;
|
|
1062
|
+
off(eventName: string, handler: (event: unknown) => void, componentId?: string | null): void;
|
|
1063
|
+
once(eventName: string, handler: (event: unknown) => void, componentId?: string | null): () => void;
|
|
1064
|
+
/** Release every handler for a component */
|
|
1065
|
+
cleanup(componentId: string): void;
|
|
1066
|
+
getStats(): Record<string, unknown>;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/** Process-wide {@link ComponentEventSystem}. */
|
|
1070
|
+
export const eventSystem: ComponentEventSystem;
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Build a hook registrar per lifecycle phase. Each registers against the
|
|
1074
|
+
* instance currently being created, and is a no-op outside that window.
|
|
1075
|
+
*/
|
|
1076
|
+
export function createLifecycleHooks(): Record<LifecyclePhase, (callback: (...args: never[]) => unknown) => void>;
|
|
1077
|
+
|
|
1078
|
+
/** Shared {@link createLifecycleHooks} result. */
|
|
1079
|
+
export const useHooks: Record<LifecyclePhase, (callback: (...args: never[]) => unknown) => void>;
|
|
1080
|
+
|
|
1081
|
+
/** Helpers for reaching lifecycle instances from a component. */
|
|
1082
|
+
export const lifecycleUtils: {
|
|
1083
|
+
/** The instance attached to a component, if any */
|
|
1084
|
+
getLifecycle(component: unknown): ComponentLifecycle | undefined;
|
|
1085
|
+
/** Wrap a component with a lifecycle and expose its mount hooks */
|
|
1086
|
+
createWithLifecycle(
|
|
1087
|
+
component: unknown,
|
|
1088
|
+
options?: Record<string, unknown>
|
|
1089
|
+
): {
|
|
1090
|
+
component: unknown;
|
|
1091
|
+
lifecycle: ComponentLifecycle;
|
|
1092
|
+
mount: (...args: never[]) => unknown;
|
|
1093
|
+
unmount: (...args: never[]) => unknown;
|
|
1094
|
+
update: (...args: never[]) => unknown;
|
|
1095
|
+
};
|
|
1096
|
+
/** Every live instance */
|
|
1097
|
+
getAllInstances(): ComponentLifecycle[];
|
|
1098
|
+
findById(id: string): ComponentLifecycle | undefined;
|
|
1099
|
+
/** Emit through {@link eventSystem}, targeting a component */
|
|
1100
|
+
emit(component: unknown, eventName: string, data?: unknown): unknown;
|
|
1101
|
+
/** Listen for a component's events; returns an unsubscribe function */
|
|
1102
|
+
listen(component: unknown, eventName: string, handler: (event: unknown) => void): () => void;
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
/** Wrap a component so a {@link ComponentLifecycle} is created around renders. */
|
|
1106
|
+
export function withLifecycle(
|
|
1107
|
+
component: CoherentComponent | CoherentNode,
|
|
1108
|
+
options?: Record<string, unknown>
|
|
1109
|
+
): (props?: Record<string, unknown>) => CoherentNode;
|
|
1110
|
+
|
|
1111
|
+
// ============================================================================
|
|
1112
|
+
// Error Boundaries
|
|
1113
|
+
// ============================================================================
|
|
1114
|
+
|
|
1115
|
+
export interface ErrorBoundaryOptions {
|
|
1116
|
+
/** Rendered instead of the children when a render throws */
|
|
1117
|
+
fallback?: CoherentNode | ((error: Error, reset: () => void) => CoherentNode);
|
|
1118
|
+
/** Notified when a render throws */
|
|
1119
|
+
onError?: (error: Error, errorInfo: Record<string, unknown>) => void;
|
|
1120
|
+
/** Clear the error when any of these values change */
|
|
1121
|
+
resetKeys?: unknown[];
|
|
1122
|
+
[option: string]: unknown;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/** Wrap children so a render error shows a fallback instead of propagating. */
|
|
1126
|
+
export function createErrorBoundary(options?: ErrorBoundaryOptions): CoherentComponent;
|
|
1127
|
+
|
|
1128
|
+
/** Build a fallback node for an error boundary. */
|
|
1129
|
+
export function createErrorFallback(options?: Record<string, unknown>): CoherentNode;
|
|
1130
|
+
|
|
1131
|
+
/** Wrap components in an error boundary. */
|
|
1132
|
+
export function withErrorBoundary(
|
|
1133
|
+
options: ErrorBoundaryOptions,
|
|
1134
|
+
components: CoherentComponent | CoherentComponent[]
|
|
1135
|
+
): CoherentComponent;
|
|
1136
|
+
|
|
1137
|
+
/** An error boundary that also catches rejections from async children. */
|
|
1138
|
+
export function createAsyncErrorBoundary(options?: ErrorBoundaryOptions): CoherentComponent;
|
|
1139
|
+
|
|
1140
|
+
export interface GlobalErrorHandlerOptions {
|
|
1141
|
+
/** Cap on retained errors */
|
|
1142
|
+
maxErrors?: number;
|
|
1143
|
+
/** Notified for every captured error */
|
|
1144
|
+
onError?: (error: Error, context: Record<string, unknown>) => void;
|
|
1145
|
+
[option: string]: unknown;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/** Collects errors that escaped every boundary. */
|
|
1149
|
+
export class GlobalErrorHandler {
|
|
1150
|
+
constructor(options?: GlobalErrorHandlerOptions);
|
|
1151
|
+
|
|
1152
|
+
/** Record an error with context */
|
|
1153
|
+
captureError(error: Error, context?: Record<string, unknown>): void;
|
|
1154
|
+
/** Retained errors */
|
|
1155
|
+
getErrors(): Array<{ error: Error; context: Record<string, unknown>; timestamp: number }>;
|
|
1156
|
+
clearErrors(): void;
|
|
1157
|
+
/** Counts by type and recency */
|
|
1158
|
+
getStats(): Record<string, unknown>;
|
|
1159
|
+
enable(): void;
|
|
1160
|
+
disable(): void;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
/** Create a {@link GlobalErrorHandler}. */
|
|
1164
|
+
export function createGlobalErrorHandler(options?: GlobalErrorHandlerOptions): GlobalErrorHandler;
|
|
1165
|
+
|
|
1166
|
+
// ============================================================================
|
|
1167
|
+
// Event Bus
|
|
1168
|
+
// ============================================================================
|
|
1169
|
+
|
|
1170
|
+
/** Called when a subscribed event fires. */
|
|
1171
|
+
export type EventListener = (data: unknown, event: string) => unknown;
|
|
1172
|
+
|
|
1173
|
+
export interface EventListenerOptions {
|
|
1174
|
+
/** Higher runs first when `enablePriority` is set */
|
|
1175
|
+
priority?: number;
|
|
1176
|
+
/** Skip the listener when this returns false */
|
|
1177
|
+
condition?: (data: unknown) => boolean;
|
|
1178
|
+
[option: string]: unknown;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
export interface EventBusOptions {
|
|
1182
|
+
/** Log every emit; defaults to `false` */
|
|
1183
|
+
debug?: boolean;
|
|
1184
|
+
/** Track emit timings; defaults to `true` */
|
|
1185
|
+
performance?: boolean;
|
|
1186
|
+
/** Listeners per event before warning; defaults to `100` */
|
|
1187
|
+
maxListeners?: number;
|
|
1188
|
+
/** Allow `a:*` patterns; defaults to `true` */
|
|
1189
|
+
enableWildcards?: boolean;
|
|
1190
|
+
/** Allow async listeners; defaults to `true` */
|
|
1191
|
+
enableAsync?: boolean;
|
|
1192
|
+
/** Wildcard segment separator; defaults to `':'` */
|
|
1193
|
+
wildcardSeparator?: string;
|
|
1194
|
+
/** Honor listener priority; defaults to `true` */
|
|
1195
|
+
enablePriority?: boolean;
|
|
1196
|
+
defaultPriority?: number;
|
|
1197
|
+
errorHandler?: ((error: Error, event: string, data: unknown) => void) | null;
|
|
1198
|
+
filters?: {
|
|
1199
|
+
/** Only these events pass; `null` allows all */
|
|
1200
|
+
allowList?: string[] | null;
|
|
1201
|
+
blockList?: string[];
|
|
1202
|
+
};
|
|
1203
|
+
throttle?: {
|
|
1204
|
+
enabled?: boolean;
|
|
1205
|
+
defaultDelay?: number;
|
|
1206
|
+
events?: Record<string, number>;
|
|
1207
|
+
};
|
|
1208
|
+
batching?: {
|
|
1209
|
+
enabled?: boolean;
|
|
1210
|
+
maxBatchSize?: number;
|
|
1211
|
+
flushInterval?: number;
|
|
1212
|
+
};
|
|
1213
|
+
[option: string]: unknown;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
export interface EventBusStats {
|
|
1217
|
+
eventsEmitted: number;
|
|
1218
|
+
listenersExecuted: number;
|
|
1219
|
+
errorsOccurred: number;
|
|
1220
|
+
averageEmitTime: number;
|
|
1221
|
+
throttledEvents: number;
|
|
1222
|
+
filteredEvents: number;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Pub/sub bus with wildcards, priorities, middleware, throttling and
|
|
1227
|
+
* batching, plus a named-action registry for DOM handlers.
|
|
1228
|
+
*/
|
|
1229
|
+
export class EventBus {
|
|
1230
|
+
constructor(options?: EventBusOptions);
|
|
1231
|
+
|
|
1232
|
+
options: EventBusOptions;
|
|
1233
|
+
listeners: Map<string, unknown[]>;
|
|
1234
|
+
actionHandlers: Map<string, (...args: never[]) => unknown>;
|
|
1235
|
+
middleware: Array<(event: string, data: unknown, next: () => void) => void>;
|
|
1236
|
+
|
|
1237
|
+
/** Add middleware run before listeners */
|
|
1238
|
+
use(middleware: (event: string, data: unknown, next: () => void) => void): this;
|
|
1239
|
+
|
|
1240
|
+
/** Emit, honoring batching and throttling */
|
|
1241
|
+
emit(event: string, data?: unknown): Promise<unknown>;
|
|
1242
|
+
|
|
1243
|
+
/** Emit immediately, ignoring batching and throttling */
|
|
1244
|
+
emitSync(event: string, data?: unknown): void;
|
|
1245
|
+
|
|
1246
|
+
/** Subscribe; returns the listener id for {@link EventBus.off} */
|
|
1247
|
+
on(event: string, listener: EventListener, options?: EventListenerOptions): string;
|
|
1248
|
+
|
|
1249
|
+
/** Subscribe for one emit */
|
|
1250
|
+
once(event: string, listener: EventListener, options?: EventListenerOptions): string;
|
|
1251
|
+
|
|
1252
|
+
/** Unsubscribe by listener id; `false` when not found */
|
|
1253
|
+
off(event: string, listenerId: string): boolean;
|
|
1254
|
+
|
|
1255
|
+
/** Drop every listener for an event, or for all events */
|
|
1256
|
+
removeAllListeners(event?: string): void;
|
|
1257
|
+
|
|
1258
|
+
/** Listeners matching an event, wildcards included */
|
|
1259
|
+
getEventListeners(event: string): unknown[];
|
|
1260
|
+
|
|
1261
|
+
/** Register a named action for DOM handlers */
|
|
1262
|
+
registerAction(action: string, handler: (...args: never[]) => unknown): void;
|
|
1263
|
+
/** Register several named actions */
|
|
1264
|
+
registerActions(actions: Record<string, (...args: never[]) => unknown>): void;
|
|
1265
|
+
getRegisteredActions(): string[];
|
|
1266
|
+
/** Invoke a registered action */
|
|
1267
|
+
handleAction(action: string, element?: unknown, event?: unknown, data?: unknown): unknown;
|
|
1268
|
+
|
|
1269
|
+
getStats(): EventBusStats;
|
|
1270
|
+
resetStats(): void;
|
|
1271
|
+
|
|
1272
|
+
/** Drop every listener, action and timer */
|
|
1273
|
+
destroy(): void;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/** Create an {@link EventBus}. */
|
|
1277
|
+
export function createEventBus(options?: EventBusOptions): EventBus;
|
|
1278
|
+
|
|
1279
|
+
/** Process-wide {@link EventBus} backing the module-level helpers. */
|
|
1280
|
+
export const globalEventBus: EventBus;
|
|
1281
|
+
|
|
1282
|
+
/** {@link EventBus.emit} on {@link globalEventBus}. */
|
|
1283
|
+
export const emit: EventBus['emit'];
|
|
1284
|
+
/** {@link EventBus.emitSync} on {@link globalEventBus}. */
|
|
1285
|
+
export const emitSync: EventBus['emitSync'];
|
|
1286
|
+
/** {@link EventBus.on} on {@link globalEventBus}. */
|
|
1287
|
+
export const on: EventBus['on'];
|
|
1288
|
+
/** {@link EventBus.once} on {@link globalEventBus}. */
|
|
1289
|
+
export const once: EventBus['once'];
|
|
1290
|
+
/** {@link EventBus.off} on {@link globalEventBus}. */
|
|
1291
|
+
export const off: EventBus['off'];
|
|
1292
|
+
/** {@link EventBus.registerAction} on {@link globalEventBus}. */
|
|
1293
|
+
export const registerAction: EventBus['registerAction'];
|
|
1294
|
+
/** {@link EventBus.handleAction} on {@link globalEventBus}. */
|
|
1295
|
+
export const handleAction: EventBus['handleAction'];
|
|
1296
|
+
|
|
1297
|
+
// ============================================================================
|
|
1298
|
+
// Event System Integration
|
|
1299
|
+
// ============================================================================
|
|
1300
|
+
|
|
1301
|
+
/** Wire a component to an event bus. */
|
|
1302
|
+
export function withEventBus(
|
|
1303
|
+
options?: Record<string, unknown>
|
|
1304
|
+
): (component: CoherentComponent) => CoherentComponent;
|
|
1305
|
+
|
|
1306
|
+
/** Give a component state that updates in response to bus events. */
|
|
1307
|
+
export function withEventState(
|
|
1308
|
+
initialState?: Record<string, unknown>,
|
|
1309
|
+
options?: Record<string, unknown>
|
|
1310
|
+
): (component: CoherentComponent) => CoherentComponent;
|
|
1311
|
+
|
|
1312
|
+
/** Factories for the common data-action handler shapes. */
|
|
1313
|
+
export const createActionHandlers: Record<string, (...args: never[]) => unknown>;
|
|
1314
|
+
|
|
1315
|
+
/** Factories for the common DOM event handler shapes. */
|
|
1316
|
+
export const createEventHandlers: Record<string, (...args: never[]) => unknown>;
|
|
1317
|
+
|
|
1318
|
+
/** Wrap a component so its declared events are wired to the bus. */
|
|
1319
|
+
export function createEventComponent(
|
|
1320
|
+
component: CoherentComponent,
|
|
1321
|
+
options?: Record<string, unknown>
|
|
1322
|
+
): CoherentComponent;
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Bridges DOM events to an {@link EventBus} through delegated listeners.
|
|
1326
|
+
*
|
|
1327
|
+
* Browser-only: constructing it outside a document is inert.
|
|
1328
|
+
*/
|
|
1329
|
+
export class DOMEventIntegration {
|
|
1330
|
+
constructor(eventBus?: EventBus, options?: Record<string, unknown>);
|
|
1331
|
+
|
|
1332
|
+
/** Attach delegated listeners */
|
|
1333
|
+
initialize(root?: unknown): void;
|
|
1334
|
+
/** Detach every listener */
|
|
1335
|
+
destroy(): void;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/** Process-wide {@link DOMEventIntegration} bound to {@link globalEventBus}. */
|
|
1339
|
+
export const globalDOMIntegration: DOMEventIntegration;
|
|
1340
|
+
|
|
1341
|
+
/** Initialize {@link globalDOMIntegration}. */
|
|
1342
|
+
export function initializeDOMIntegration(options?: Record<string, unknown>): DOMEventIntegration;
|
|
1343
|
+
|
|
1344
|
+
// ============================================================================
|
|
1345
|
+
// State Management
|
|
1346
|
+
// ============================================================================
|
|
1347
|
+
|
|
1348
|
+
export interface StateManagerConfig {
|
|
1349
|
+
initialState?: Record<string, unknown>;
|
|
1350
|
+
/** Reducers combined into a root reducer, keyed by state slice */
|
|
1351
|
+
reducers?: Record<string, (state: unknown, action: unknown) => unknown>;
|
|
1352
|
+
actions?: Record<string, (...args: never[]) => unknown>;
|
|
1353
|
+
middleware?: Array<(...args: never[]) => unknown>;
|
|
1354
|
+
/** Applied to the config before the manager is built */
|
|
1355
|
+
plugins?: Array<(config: Record<string, unknown>) => Record<string, unknown>>;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/** Build a reducer-based state manager from slice reducers and actions. */
|
|
1359
|
+
export function createStateManager(config: StateManagerConfig): Record<string, unknown>;
|
|
1360
|
+
|
|
1361
|
+
/** Presets over `withState` for the common state shapes. */
|
|
1362
|
+
export const withStateUtils: {
|
|
1363
|
+
/** Component-local state */
|
|
1364
|
+
local(initialState: Record<string, unknown>): ReturnType<typeof withState>;
|
|
1365
|
+
/** State mirrored to localStorage under `key` */
|
|
1366
|
+
persistent(initialState: Record<string, unknown>, key: string): ReturnType<typeof withState>;
|
|
1367
|
+
/** State driven by a reducer */
|
|
1368
|
+
reducer(
|
|
1369
|
+
initialState: Record<string, unknown>,
|
|
1370
|
+
reducer: (state: unknown, action: unknown) => unknown,
|
|
1371
|
+
actions?: Record<string, (...args: never[]) => unknown>
|
|
1372
|
+
): ReturnType<typeof withState>;
|
|
1373
|
+
[preset: string]: (...args: never[]) => unknown;
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// ============================================================================
|
|
1377
|
+
// Composition and Utilities
|
|
1378
|
+
// ============================================================================
|
|
1379
|
+
|
|
1380
|
+
/** Higher-order component factories. */
|
|
1381
|
+
export const hoc: {
|
|
1382
|
+
/** Merge extra props into a component */
|
|
1383
|
+
withProps(additionalProps: Record<string, unknown>): (component: CoherentComponent) => CoherentComponent;
|
|
1384
|
+
/** Render only when `condition` holds */
|
|
1385
|
+
withCondition(condition: (props: Record<string, unknown>) => boolean): (component: CoherentComponent) => CoherentComponent;
|
|
1386
|
+
/** Show a placeholder while `props.loading` is set */
|
|
1387
|
+
withLoading(loadingComponent: CoherentNode): (component: CoherentComponent) => CoherentComponent;
|
|
1388
|
+
/** Show a fallback when `props.error` is set */
|
|
1389
|
+
withError(errorComponent: CoherentNode): (component: CoherentComponent) => CoherentComponent;
|
|
1390
|
+
/** Cache renders by a derived key */
|
|
1391
|
+
withMemo(getMemoKey: (props: Record<string, unknown>) => string): (component: CoherentComponent) => CoherentComponent;
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1394
|
+
/** Functional helpers. */
|
|
1395
|
+
export const fp: {
|
|
1396
|
+
/** Curried map: `fp.map(fn)(array)` */
|
|
1397
|
+
map<T, R>(fn: (value: T, index: number, array: T[]) => R): (array: T[]) => R[];
|
|
1398
|
+
};
|
|
1399
|
+
|
|
1400
|
+
/** Cache a component's renders, keyed by `keyGenerator`. */
|
|
1401
|
+
export function memoize<C extends CoherentComponent>(
|
|
1402
|
+
component: C,
|
|
1403
|
+
keyGenerator?: (props: Record<string, unknown>) => string,
|
|
1404
|
+
options?: ComponentCacheEntryOptions
|
|
1405
|
+
): C;
|
|
1406
|
+
|
|
1407
|
+
/** Mark a component as an interactive island for client-side hydration. */
|
|
1408
|
+
export function Island(componentFn: CoherentComponent): CoherentComponent;
|
|
1409
|
+
|
|
1410
|
+
/** Shadow DOM helpers; browser-only. */
|
|
1411
|
+
export const shadowDOM: {
|
|
1412
|
+
isShadowDOMSupported(): boolean;
|
|
1413
|
+
createShadowComponent(
|
|
1414
|
+
element: unknown,
|
|
1415
|
+
componentDef: CoherentNode,
|
|
1416
|
+
options?: Record<string, unknown>
|
|
1417
|
+
): unknown;
|
|
1418
|
+
renderWithBestEncapsulation(componentDef: CoherentNode, containerElement?: unknown): unknown;
|
|
1419
|
+
};
|