11import { ItemRegistrationCallback } from "./types" ;
2+ import { onPageLoaded , onPageUnloaded } from "./pageLifecycle" ;
3+ import { getContextAndRailsContext } from "./context" ;
24
35export default class CallbackRegistry < T > {
6+ private readonly registryType : string ;
47 private registeredItems = new Map < string , T > ( ) ;
5- private callbacks = new Map < string , Array < ItemRegistrationCallback < T > > > ( ) ;
8+ private callbacks = new Map < string , Array < {
9+ resolve : ItemRegistrationCallback < T > ;
10+ reject : ( error : Error ) => void ;
11+ } > > ( ) ;
12+
13+ private timoutEventsInitialized = false ;
14+ private timedout = false ;
15+
16+ constructor ( registryType : string ) {
17+ this . registryType = registryType ;
18+ this . initializeTimeoutEvents ( ) ;
19+ }
20+
21+ private initializeTimeoutEvents ( ) {
22+ if ( ! this . timoutEventsInitialized ) {
23+ this . timoutEventsInitialized = true ;
24+ }
25+
26+ let timeoutId : NodeJS . Timeout ;
27+ const triggerTimeout = ( ) => {
28+ this . timedout = true ;
29+ this . callbacks . forEach ( ( itemCallbacks , itemName ) => {
30+ itemCallbacks . forEach ( ( callback ) => {
31+ callback . reject ( this . createNotFoundError ( itemName ) ) ;
32+ } ) ;
33+ } ) ;
34+ } ;
35+
36+ onPageLoaded ( ( ) => {
37+ const registryTimeout = getContextAndRailsContext ( ) . railsContext ?. componentRegistryTimeout ;
38+ if ( ! registryTimeout ) return ;
39+
40+ timeoutId = setTimeout ( triggerTimeout , registryTimeout ) ;
41+ } ) ;
42+
43+ onPageUnloaded ( ( ) => {
44+ this . callbacks . clear ( ) ;
45+ this . timedout = false ;
46+ clearTimeout ( timeoutId ) ;
47+ } ) ;
48+ }
649
750 set ( name : string , item : T ) : void {
851 this . registeredItems . set ( name , item ) ;
952
1053 const callbacks = this . callbacks . get ( name ) || [ ] ;
11- callbacks . forEach ( callback => {
12- setTimeout ( ( ) => callback ( item ) , 0 ) ;
13- } ) ;
54+ callbacks . forEach ( callback => callback . resolve ( item ) ) ;
1455 this . callbacks . delete ( name ) ;
1556 }
1657
17- get ( name : string ) : T | undefined {
18- return this . registeredItems . get ( name ) ;
58+ get ( name : string ) : T {
59+ const item = this . registeredItems . get ( name ) ;
60+ if ( item !== undefined ) return item ;
61+
62+ throw this . createNotFoundError ( name ) ;
1963 }
2064
2165 has ( name : string ) : boolean {
@@ -30,21 +74,28 @@ export default class CallbackRegistry<T> {
3074 return this . registeredItems ;
3175 }
3276
33- onItemRegistered ( name : string , callback : ItemRegistrationCallback < T > ) : void {
34- const existingItem = this . registeredItems . get ( name ) ;
35- if ( existingItem ) {
36- setTimeout ( ( ) => callback ( existingItem ) , 0 ) ;
37- return ;
38- }
77+ getOrWaitForItem ( name : string ) : Promise < T > {
78+ return new Promise ( ( resolve , reject ) => {
79+ try {
80+ resolve ( this . get ( name ) ) ;
81+ } catch ( error ) {
82+ if ( this . timedout ) {
83+ throw error ;
84+ }
3985
40- const itemCallbacks = this . callbacks . get ( name ) || [ ] ;
41- itemCallbacks . push ( callback ) ;
42- this . callbacks . set ( name , itemCallbacks ) ;
86+ const itemCallbacks = this . callbacks . get ( name ) || [ ] ;
87+ itemCallbacks . push ( { resolve, reject } ) ;
88+ this . callbacks . set ( name , itemCallbacks ) ;
89+ }
90+ } ) ;
4391 }
4492
45- getOrWaitForItem ( name : string ) : Promise < T > {
46- return new Promise ( ( resolve ) => {
47- this . onItemRegistered ( name , resolve ) ;
48- } ) ;
93+ private createNotFoundError ( itemName : string ) : Error {
94+ const keys = Array . from ( this . registeredItems . keys ( ) ) . join ( ', ' ) ;
95+ return new Error (
96+ `Could not find ${ this . registryType } registered with name ${ itemName } . ` +
97+ `Registered ${ this . registryType } names include [ ${ keys } ]. ` +
98+ `Maybe you forgot to register the ${ this . registryType } ?`
99+ ) ;
49100 }
50101}
0 commit comments