Rpc integration + type fixes #3
@ -1,2 +1,5 @@
 | 
				
			|||||||
export const EVENT_LISTENER = '__rpc:listener'
 | 
					export const EVENT_LISTENER = '__rpc:listener'
 | 
				
			||||||
export const EVENT_RESPONSE = '__rpc:response'
 | 
					export const EVENT_RESPONSE = '__rpc:response'
 | 
				
			||||||
 | 
					export const CEF_EVENT_LISTENER = '__rpc:cef_listener'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CLIENT_ROUTER_LISTENER = '__rpc:clientRouter'
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ import { EVENT_LISTENER } from './events'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { client } from './modules/client'
 | 
					import { client } from './modules/client'
 | 
				
			||||||
import { server } from './modules/server'
 | 
					import { server } from './modules/server'
 | 
				
			||||||
 | 
					import { cef } from './modules/cef'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const environment = utils.getEnvironment()
 | 
					const environment = utils.getEnvironment()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,6 +24,11 @@ class rpc {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    await client.listenEvent(request)
 | 
					                    await client.listenEvent(request)
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                case Environment.CEF:
 | 
				
			||||||
 | 
					                    request = player
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await cef
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -50,9 +56,18 @@ class rpc {
 | 
				
			|||||||
        eventName: string,
 | 
					        eventName: string,
 | 
				
			||||||
        ...args: Args
 | 
					        ...args: Args
 | 
				
			||||||
    ): Promise<Return | unknown> {
 | 
					    ): Promise<Return | unknown> {
 | 
				
			||||||
        if (environment === Environment.UNKNOWN) return
 | 
					        switch (environment) {
 | 
				
			||||||
        if (environment === Environment.CLIENT) {
 | 
					            case Environment.UNKNOWN:
 | 
				
			||||||
            return server.executeServer(eventName, args)
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case Environment.SERVER:
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case Environment.CEF:
 | 
				
			||||||
 | 
					                return client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case Environment.CLIENT:
 | 
				
			||||||
 | 
					                return server.executeServer(eventName, args)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										68
									
								
								rpc/src/modules/cef.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								rpc/src/modules/cef.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					import { Wrapper } from './wrapper'
 | 
				
			||||||
 | 
					import { Environment, RPCState } from '../utils'
 | 
				
			||||||
 | 
					import { CEF_EVENT_LISTENER } from '../events'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Cef extends Wrapper {
 | 
				
			||||||
 | 
					    public async callClient<Args extends any[] = unknown[], Return = unknown>(
 | 
				
			||||||
 | 
					        eventName: string,
 | 
				
			||||||
 | 
					        ...args: Args
 | 
				
			||||||
 | 
					    ): Promise<Return | unknown> {
 | 
				
			||||||
 | 
					        return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					            const uuid = this._utils.generateUUID()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const data: RPCState = {
 | 
				
			||||||
 | 
					                uuid,
 | 
				
			||||||
 | 
					                eventName,
 | 
				
			||||||
 | 
					                calledFrom: Environment.CEF,
 | 
				
			||||||
 | 
					                calledTo: Environment.CLIENT,
 | 
				
			||||||
 | 
					                data: args,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            mp.trigger(CEF_EVENT_LISTENER, this._utils.prepareForTransfer(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.handleReturn(uuid, resolve, reject)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public async callServer<Args extends any[] = unknown[], Return = unknown>(
 | 
				
			||||||
 | 
					        eventName: string,
 | 
				
			||||||
 | 
					        ...args: Args
 | 
				
			||||||
 | 
					    ): Promise<Return | unknown> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async handleReturn(
 | 
				
			||||||
 | 
					        uuid: string,
 | 
				
			||||||
 | 
					        resolve: (value: unknown) => void,
 | 
				
			||||||
 | 
					        reject: (reason?: unknown) => void,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        const responseEvent = this._utils.generateResponseEventName(uuid)
 | 
				
			||||||
 | 
					        const timeoutDuration = 1000 * 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const timeoutID = setTimeout(() => {
 | 
				
			||||||
 | 
					            reject(new Error('Timeout ended'))
 | 
				
			||||||
 | 
					            mp.events.remove(responseEvent)
 | 
				
			||||||
 | 
					        }, timeoutDuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const handler = (response: string) => {
 | 
				
			||||||
 | 
					            const { knownError, data } = this._utils.prepareForExecute(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (knownError) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    clearTimeout(timeoutID)
 | 
				
			||||||
 | 
					                    reject(knownError)
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					                } catch (e) {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            resolve(data)
 | 
				
			||||||
 | 
					            mp.events.remove(responseEvent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                clearTimeout(timeoutID)
 | 
				
			||||||
 | 
					            } catch (e) {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mp.events.add(responseEvent, handler)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const cef = new Cef()
 | 
				
			||||||
@ -1,94 +1,156 @@
 | 
				
			|||||||
import { EVENT_LISTENER } from '../events'
 | 
					import { CLIENT_ROUTER_LISTENER, EVENT_LISTENER } from '../events'
 | 
				
			||||||
import { Wrapper } from './wrapper'
 | 
					import { Wrapper } from './wrapper'
 | 
				
			||||||
import { RPCState } from '../utils'
 | 
					import { Environment, RPCState } from '../utils'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Client extends Wrapper {
 | 
					class Client extends Wrapper {
 | 
				
			||||||
    private sendResponseToServer(data: RPCState) {
 | 
					    constructor() {
 | 
				
			||||||
        const eventName = this._utils.generateResponseEventName(data.uuid)
 | 
					        super()
 | 
				
			||||||
        const preparedData = this._utils.prepareForTransfer(data)
 | 
					 | 
				
			||||||
        mp.events.callRemote(eventName, preparedData)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async listenEvent(data: string) {
 | 
					        mp.events.add(CLIENT_ROUTER_LISTENER, (data: string) => {
 | 
				
			||||||
        const rpcData = this._verifyEvent(data)
 | 
					            const parsedData = this._utils.prepareForExecute(data)
 | 
				
			||||||
 | 
					            const environment = this._environment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (rpcData.knownError) {
 | 
					            if (environment === Environment.CLIENT) {
 | 
				
			||||||
            this._triggerError(rpcData)
 | 
					                switch (parsedData.calledTo) {
 | 
				
			||||||
            return
 | 
					                    case Environment.SERVER:
 | 
				
			||||||
        }
 | 
					                        // route to server listener
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					                    case Environment.CEF:
 | 
				
			||||||
            const fnResponse = await this._state[rpcData.eventName](
 | 
					                        // route to cef listener
 | 
				
			||||||
                ...rpcData.data,
 | 
					                        break
 | 
				
			||||||
            )
 | 
					                }
 | 
				
			||||||
            const response = {
 | 
					 | 
				
			||||||
                ...rpcData,
 | 
					 | 
				
			||||||
                data: fnResponse,
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.sendResponseToServer(response)
 | 
					 | 
				
			||||||
        } catch (e) {
 | 
					 | 
				
			||||||
            this._triggerError(rpcData, e)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private handleClientServerReturn(
 | 
					 | 
				
			||||||
        uuid: string,
 | 
					 | 
				
			||||||
        resolve: (value: unknown) => void,
 | 
					 | 
				
			||||||
        reject: (reason?: any) => void,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        const responseEvent = this._utils.generateResponseEventName(uuid)
 | 
					 | 
				
			||||||
        const timeoutDuration = 1000 * 10
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const timeoutID = setTimeout(() => {
 | 
					 | 
				
			||||||
            reject(new Error('Timeout ended'))
 | 
					 | 
				
			||||||
            mp.events.remove(responseEvent)
 | 
					 | 
				
			||||||
        }, timeoutDuration)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const handler = (_: any, response: string) => {
 | 
					 | 
				
			||||||
            const { knownError, data } = this._utils.prepareForExecute(response)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (knownError)
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    clearTimeout(timeoutID)
 | 
					 | 
				
			||||||
                    reject(knownError)
 | 
					 | 
				
			||||||
                    return
 | 
					 | 
				
			||||||
                } catch (e) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            resolve(data)
 | 
					 | 
				
			||||||
            mp.events.remove(responseEvent)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                clearTimeout(timeoutID)
 | 
					 | 
				
			||||||
            } catch (e) {}
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        mp.events.add(responseEvent, handler)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public async executeClient<
 | 
					 | 
				
			||||||
        Args extends any[] = unknown[],
 | 
					 | 
				
			||||||
        Return = unknown,
 | 
					 | 
				
			||||||
    >(
 | 
					 | 
				
			||||||
        player: any,
 | 
					 | 
				
			||||||
        eventName: string,
 | 
					 | 
				
			||||||
        ...args: Args
 | 
					 | 
				
			||||||
    ): Promise<Return | unknown> {
 | 
					 | 
				
			||||||
        return new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
            const uuid = this._utils.generateUUID()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const data: RPCState = {
 | 
					 | 
				
			||||||
                uuid,
 | 
					 | 
				
			||||||
                eventName,
 | 
					 | 
				
			||||||
                calledFrom: this._environment,
 | 
					 | 
				
			||||||
                data: args,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            player.call(EVENT_LISTENER, [this._utils.prepareForTransfer(data)])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.handleClientServerReturn(uuid, resolve, reject)
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async createCallbackListener(uuid: string) {
 | 
				
			||||||
 | 
					        const eventName = this._utils.generateResponseEventName(uuid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const handler = async (data: string) => {
 | 
				
			||||||
 | 
					            mp.events.remove(eventName)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mp.events.add(eventName, handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return eventName
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async requestToServer(data: string) {
 | 
				
			||||||
 | 
					        const { uuid } = this._utils.prepareForExecute(data)
 | 
				
			||||||
 | 
					        const callbackEventName = await this.createCallbackListener(uuid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mp.events.callRemote(callbackEventName, data)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async requestToBrowser(data: string) {
 | 
				
			||||||
 | 
					        const { uuid } = this._utils.prepareForExecute(data)
 | 
				
			||||||
 | 
					        const callbackEventName = await this.createCallbackListener(uuid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mp.browsers.at(0).call()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // private sendResponseToServer(data: RPCState) {
 | 
				
			||||||
 | 
					    //     const eventName = this._utils.generateResponseEventName(data.uuid)
 | 
				
			||||||
 | 
					    //     const preparedData = this._utils.prepareForTransfer(data)
 | 
				
			||||||
 | 
					    //     mp.events.callRemote(eventName, preparedData)
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // private sendEventToServer(data: RPCState) {
 | 
				
			||||||
 | 
					    //     const eventName = this._utils.generateResponseEventName(data.uuid)
 | 
				
			||||||
 | 
					    //     const preparedData = this._utils.prepareForTransfer(data)
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // public async listenEvent(data: string) {
 | 
				
			||||||
 | 
					    //     const rpcData = this._verifyEvent(data)
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     if (rpcData.knownError) {
 | 
				
			||||||
 | 
					    //         this._triggerError(rpcData)
 | 
				
			||||||
 | 
					    //         return
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     await this.navigateTo(rpcData.calledTo, rpcData)
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     // try {
 | 
				
			||||||
 | 
					    //     //     const fnResponse = await this._state[rpcData.eventName](
 | 
				
			||||||
 | 
					    //     //         ...rpcData.data,
 | 
				
			||||||
 | 
					    //     //     )
 | 
				
			||||||
 | 
					    //     //     const response = {
 | 
				
			||||||
 | 
					    //     //         ...rpcData,
 | 
				
			||||||
 | 
					    //     //         data: fnResponse,
 | 
				
			||||||
 | 
					    //     //     }
 | 
				
			||||||
 | 
					    //     //
 | 
				
			||||||
 | 
					    //     //     this.sendResponseToServer(response)
 | 
				
			||||||
 | 
					    //     // } catch (e) {
 | 
				
			||||||
 | 
					    //     //     this._triggerError(rpcData, e)
 | 
				
			||||||
 | 
					    //     // }
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // private async navigateTo(toEnvironment: Environment, data: RPCState) {
 | 
				
			||||||
 | 
					    //     switch (toEnvironment) {
 | 
				
			||||||
 | 
					    //         case Environment.SERVER:
 | 
				
			||||||
 | 
					    //             this.sendEventToServer()
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // private handleClientServerReturn(
 | 
				
			||||||
 | 
					    //     uuid: string,
 | 
				
			||||||
 | 
					    //     resolve: (value: unknown) => void,
 | 
				
			||||||
 | 
					    //     reject: (reason?: any) => void,
 | 
				
			||||||
 | 
					    // ) {
 | 
				
			||||||
 | 
					    //     const responseEvent = this._utils.generateResponseEventName(uuid)
 | 
				
			||||||
 | 
					    //     const timeoutDuration = 1000 * 10
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     const timeoutID = setTimeout(() => {
 | 
				
			||||||
 | 
					    //         reject(new Error('Timeout ended'))
 | 
				
			||||||
 | 
					    //         mp.events.remove(responseEvent)
 | 
				
			||||||
 | 
					    //     }, timeoutDuration)
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     const handler = (_: any, response: string) => {
 | 
				
			||||||
 | 
					    //         const { knownError, data } = this._utils.prepareForExecute(response)
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //         if (knownError)
 | 
				
			||||||
 | 
					    //             try {
 | 
				
			||||||
 | 
					    //                 clearTimeout(timeoutID)
 | 
				
			||||||
 | 
					    //                 reject(knownError)
 | 
				
			||||||
 | 
					    //                 return
 | 
				
			||||||
 | 
					    //             } catch (e) {}
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //         resolve(data)
 | 
				
			||||||
 | 
					    //         mp.events.remove(responseEvent)
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //         try {
 | 
				
			||||||
 | 
					    //             clearTimeout(timeoutID)
 | 
				
			||||||
 | 
					    //         } catch (e) {}
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     mp.events.add(responseEvent, handler)
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // public async executeClient<
 | 
				
			||||||
 | 
					    //     Args extends any[] = unknown[],
 | 
				
			||||||
 | 
					    //     Return = unknown,
 | 
				
			||||||
 | 
					    // >(
 | 
				
			||||||
 | 
					    //     player: any,
 | 
				
			||||||
 | 
					    //     eventName: string,
 | 
				
			||||||
 | 
					    //     ...args: Args
 | 
				
			||||||
 | 
					    // ): Promise<Return | unknown> {
 | 
				
			||||||
 | 
					    //     return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					    //         const uuid = this._utils.generateUUID()
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //         const data: RPCState = {
 | 
				
			||||||
 | 
					    //             uuid,
 | 
				
			||||||
 | 
					    //             eventName,
 | 
				
			||||||
 | 
					    //             calledFrom: this._environment,
 | 
				
			||||||
 | 
					    //             calledTo: Environment.CLIENT,
 | 
				
			||||||
 | 
					    //             data: args,
 | 
				
			||||||
 | 
					    //         }
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //         player.call(EVENT_LISTENER, [this._utils.prepareForTransfer(data)])
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //         this.handleClientServerReturn(uuid, resolve, reject)
 | 
				
			||||||
 | 
					    //     })
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const client = new Client()
 | 
					export const client = new Client()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { Wrapper } from './wrapper'
 | 
					import { Wrapper } from './wrapper'
 | 
				
			||||||
import { RPCState, utils } from '../utils'
 | 
					import { Environment, RPCState } from '../utils'
 | 
				
			||||||
import { EVENT_LISTENER } from '../events'
 | 
					import { EVENT_LISTENER } from '../events'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Server extends Wrapper {
 | 
					class Server extends Wrapper {
 | 
				
			||||||
@ -79,6 +79,7 @@ class Server extends Wrapper {
 | 
				
			|||||||
                uuid,
 | 
					                uuid,
 | 
				
			||||||
                eventName,
 | 
					                eventName,
 | 
				
			||||||
                calledFrom: this._environment,
 | 
					                calledFrom: this._environment,
 | 
				
			||||||
 | 
					                calledTo: Environment.SERVER,
 | 
				
			||||||
                data: args,
 | 
					                data: args,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@ export type RPCState = {
 | 
				
			|||||||
    knownError?: string
 | 
					    knownError?: string
 | 
				
			||||||
    data?: any
 | 
					    data?: any
 | 
				
			||||||
    calledFrom: Environment
 | 
					    calledFrom: Environment
 | 
				
			||||||
 | 
					    calledTo: Environment
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Utils {
 | 
					class Utils {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user