/// declare type VaultOperation = 'add' | 'set' | 'del' | 'touch'; import * as commander from 'commander'; import * as config from './lib/config'; import MageError from './lib/mage/MageError'; /** * Abstracted data store access interface * * In general, you will be accessing an archivist instance * through a state object: * * ```javascript * state.archivist.set('player', { userId: userId }, { name: 'someone' }); * ``` */ declare class Archivist { /** * Check whether a given exists in any of our vaults * * @param {string} topicName * @param {ArchivistIndex} index * @param {ArchivistExistsCallback} callback * * @memberOf Archivist */ exists(topicName: string, index: mage.archivist.IArchivistIndex, callback: mage.archivist.ArchivistExistsCallback): void; /** * Retrieve a value * * This method is a wrapper around `getValue` which directly returns ArchivistValue.value; * in most cases, you will want to call `get` instead to get the actual data. * * @param {string} topicName * @param {ArchivistIndex} index * @param {ArchivistOptions} options * @param {Callback} callback * * @memberOf Archivist */ get(topicName: string, index: mage.archivist.IArchivistIndex, options: mage.archivist.IArchivistGetOptions | undefined, callback: mage.archivist.ArchivistGetCallback): void; get(topicName: string, index: mage.archivist.IArchivistIndex, callback: mage.archivist.ArchivistGetCallback): void; /** * Retrieve the VaultValue object for a given key * * In most cases, you will want to call `get` instead to get the actual data. * * @param {string} topicName * @param {ArchivistIndex} index * @param {ArchivistOptions} [options] * @param {Function} callback * * @memberOf Archivist */ getValue(topicName: string, index: mage.archivist.IArchivistIndex, options: mage.archivist.IArchivistGetOptions | undefined, callback: mage.archivist.ArchivistGetCallback): void; getValue(topicName: string, index: mage.archivist.IArchivistIndex, callback: mage.archivist.ArchivistGetCallback): void; /** * Retrieve multiple values * * This method is a wrapper around `mgetValue` which directly returns an array of ArchivistValue.value; * in most cases, you will want to call `mget` instead to get the actual data. * * @param {ArchivistQuery[]} queries * @param {ArchivistOptions} [options] * @param {Function} callback * * @memberOf Archivist */ mget(queries: mage.archivist.IArchivistQuery[], options: mage.archivist.IArchivistGetOptions | undefined, callback: mage.archivist.ArchivistMGetCallback): void; mget(queries: mage.archivist.INamedArchivistQuery, options: mage.archivist.IArchivistGetOptions | undefined, callback: mage.archivist.ArchivistNamedMGetCallback): void; mget(queries: mage.archivist.IArchivistQuery[], callback: mage.archivist.ArchivistMGetCallback): void; mget(queries: mage.archivist.INamedArchivistQuery, callback: mage.archivist.ArchivistNamedMGetCallback): void; /** * Retrieve multiple VaultValue objects * * In most cases, you will want to call `mget` instead to get the actual data. * * @param {ArchivistQuery[]} queries * @param {ArchivistOptions} [options] * @param {Function} callback * * @memberOf Archivist */ mgetValues(queries: mage.archivist.IArchivistQuery[], options: mage.archivist.IArchivistGetOptions | undefined, callback: mage.archivist.ArchivistMGetCallback): void; mgetValues(queries: mage.archivist.IArchivistQuery[], callback: mage.archivist.ArchivistMGetCallback): void; /** * Search the backend vault for matching indexes, and return them * * In this case, the index can be partial; for instance, `{ userId: 1 }`, would match * all the following indexes: * * ```json * { userId: 1, somethingElse: 'hi'} * { userId: 1, somethingElse: 'hello'} * ``` * * Note that this API returns the list of matching *indexes*, not the data they hold; * please see `scan()` if you wish to retrieve the data instead. * * @param {string} topicName * @param {ArchivistIndex} partialIndex * @param {ArchivistOptions} [options] * @param {Function} callback * * @memberOf Archivist */ list(topicName: string, partialIndex: mage.archivist.IArchivistIndex, options: mage.archivist.IArchivistListOptions | undefined, callback: mage.archivist.ArchivistListCallback): void; list(topicName: string, partialIndex: mage.archivist.IArchivistIndex, callback: mage.archivist.ArchivistListCallback): void; /** * Scan the backend vault for entries matching a given partial index. * * In this case, the index can be partial; for instance, `{ userId: 1 }`, would match * all the following indexes: * * ```json * { userId: 1, somethingElse: 'hi'} * { userId: 1, somethingElse: 'hello'} * ``` * * @param {string} topicName * @param {ArchivistIndex} partialIndex * @param {ArchivistScanOptions} [options] * @param {Function} callback * * @memberOf Archivist */ scan(topicName: string, partialIndex: mage.archivist.IArchivistIndex, options: mage.archivist.ArchivistScanOptions | undefined, callback: mage.archivist.ArchivistMGetCallback): void; scan(topicName: string, partialIndex: mage.archivist.IArchivistIndex, callback: mage.archivist.ArchivistMGetCallback): void; /** * Add a new topic value by index. * * Note that if the index already exists, this call will return an error. Use * `set` instead if you wish to write the value regardless of whether * it already exists. * * @param {string} topicName * @param {ArchivistIndex} index * @param {*} data * @param {ArchivistMediaType} mediaType * @param {ArchivistEncoding} encoding * @param {number} expirationTime * * @memberOf Archivist */ add(topicName: string, index: mage.archivist.IArchivistIndex, data: T, mediaType?: mage.archivist.ArchivistMediaType, encoding?: mage.archivist.ArchivistEncoding, expirationTime?: number): void; /** * Set the value for an existing index. * * @param {string} topicName * @param {ArchivistIndex} index * @param {*} data * @param {ArchivistMediaType} mediaType * @param {ArchivistEncoding} encoding * @param {number} expirationTime * * @memberOf Archivist */ set(topicName: string, index: mage.archivist.IArchivistIndex, data: T, mediaType?: mage.archivist.ArchivistMediaType, encoding?: mage.archivist.ArchivistEncoding, expirationTime?: number): void; /** * Delete a topic by index. * * @param {string} topicName * @param {ArchivistIndex} index * * @memberOf Archivist */ del(topicName: string, index: mage.archivist.IArchivistIndex): void; /** * Touch a topic * * Used to reset the expiration timer. * * @param {string} topicName * @param {ArchivistIndex} index * @param {number} expirationTime * * @memberOf Archivist */ touch(topicName: string, index: mage.archivist.IArchivistIndex, expirationTime?: number): void; /** * Commit all current changes to their respective vault backend(s) * * @param {*} [options] * @param {(preDistributionErrors: Error[], distributionErrors: Error[]) => void} callback * * @memberOf Archivist */ distribute(callback: mage.archivist.ArchivistDistributeCallback): void; /** * Clear all the loaded entries from this archivist instance * * Note that this will not clear mutated entries; please use * `reset()` if you wish to clear all data instead. * * @memberOf Archivist */ clearCache(): void; /** * Reset the instance; remove all operations that are * currently scheduled on the instance * * @memberOf Archivist */ reset(): void; } /** * Auth callbacks */ declare type AuthenticateCallback = (error: Error|null, userId: string|number, acl: string[]) => void; declare type LoginCallback = (error: Error|null, session: Session) => void; declare type RegisterCallback = (error: Error|null, userId: string) => void; declare type ChangePasswordCallback = (error: Error|null) => void; declare type BanCallback = (error: Error|null) => void; declare type UnbanCallback = (error: Error|null) => void; declare interface ILog extends Function { /** * String(s) to log */ (...content: string[]): void; /** * Append additional data to the log entry * * Note that this can be called multiple times on the same log entry; in such * cases, each key-value entry is merged with the previous one. * * You **must** call `.log()` a the end of the chain for the log entry * to be recorded. * * ```javascript * logger.debug.data({ hello: 'world' }).log(); * ``` * * @param {{[id: string]: any}} data Key-value of data to add to your log entry * @returns {this} * * @memberOf Log */ data(data: {[id: string]: any}): this; /** * Append additional text information to your log entry * * This is useful for cases where you would need to explain in more * details to the operator of your game server what has happened, and, in the * case of an error, how to correct the situation. * * You **must** call `.log()` a the end of the chain for the log entry * to be recorded. * * ```javascript * logger.debug.data({ hello: 'world' }).log(); * ``` * * @param {...string[]} content * @returns {this} * * @memberOf Log */ details(...content: string[]): this; /** * Record the log entry * * The following are essentially equivalent: * * ```javascript * logger.debug.log('hi') * logger.debug('hi') * ``` * * Use `log` whenever you have previously chained `data` or `details` * method calls: * * ```javascript * logger.error * .data({hello: 'world'}) * .details('Something awful has occured. Here is how you can fix the situation') * .log('error. error.error') * ``` * * @param {...string[]} content * * @memberOf Log */ log(...content: string[]): void; } declare class Logger { /** * Add contexts to the current logger instance. * * Logging contexts should be used to help with the triage and filtering * of logs; for instance, you will often want to create one logger context * per module. * * This adds contexts to the current instance. You will not normally want * to use this on the global logger instance: instead, you will normally * want to use this on a logger instance returned by `mage.logger.context` * or something similar. * * ```javascript * import * as mage from mage; * const logger = mage.logger.context('mymodule') * logger.addContexts('some other context to add') * logger.debug('this will be contextualized to mymodule') * ``` * * In most cases, simply using `mage.logger.context` is sufficient. * * @param {...string[]} contexts * @returns {Logger} * * @memberof Logger */ addContexts(...contexts: string[]): void; /** * Return a contextualized logger instance * * Logging contexts should be used to help with the triage and filtering * of logs; for instance, you will often want to create one logger context * per module. * * Contrarily to `mage.logger.addContexts`, this method returns * a new logger with the given context, instead of setting the context * on the current logger instance. This can be useful to create a localized logger; * * ```javascript * import * as mage from mage; * const logger = mage.logger.context('mymodule') * logger.debug('this will be contextualized to mymodule') * ``` * * @summary Append a logging context. * @param {...string[]} contexts * @returns {Logger} * * @memberOf Logger */ context(...contexts: string[]): Logger; /** * Disable a log channel * * This will in effect take all logs sent to this channel, * and ignore them; logs sent on this channel will no * longer be forwarded to any logger backend. * * @param {string} channelName The name of the channel to disable * @memberof Logger */ disableChannel(channelName: string): void; /** * Create a channel handler * * You should only call this if you want to programatically * enable a channel which is not currently enabled or if you * want to re-enable a channel which was disabled using `disableChannel`. * * @param {string} channelName The channel to enable * @memberof Logger */ enableChannel(channelName: string): void; /** * Low-level debug information (I/O details, etc). Reserved to MAGE internals * * @type {ILog} * @memberOf Logger */ verbose: ILog; /** * Game server debugging information * * @type {ILog} * @memberOf Logger */ debug: ILog; /** * User command request information * * @type {ILog} * @memberOf Logger */ info: ILog; /** * Services state change notification (example: third-party services and databases) * * @type {ILog} * @memberOf Logger */ notice: ILog; /** * An unusual situation occurred, which requires analysis * * @type {ILog} * @memberOf Logger */ warning: ILog; /** * A user request caused an error. The user should still be able to continue using the services * * @type {ILog} * @memberOf Logger */ error: ILog; /** * A user is now stuck in a broken state which the system cannot naturally repair * * @type {ILog} * @memberOf Logger */ critical: ILog; /** * Internal services (data store API calls failed, etc) or external services are failing * * @type {ILog} * @memberOf Logger */ alert: ILog; /** * The app cannot boot or stopped unexpectedly * * @type {ILog} * @memberOf Logger */ emergency: ILog; } /** * There is currently only 1 flag, namely ‘TRACK_ROUTE’. * * When this flag is active, the returnRoute must be kept * around in the envelope as it travels across the network. */ declare type MmrpEnvelopeFlag = 'NONE' | 'TRACK_ROUTE' | number /** * Data which may be put into an MMRP envelope */ declare type MmrpEnvelopeMessage = string | Buffer /** * */ declare type MmrpEnvelopeRoute = string[] /** * MMRP Envelopes are used to encapsulate data to * be sent between MAGE nodes. * * @class MmrpEnvelope */ declare class MmrpEnvelope { constructor(eventName: string, messages: MmrpEnvelopeMessage[], route?: MmrpEnvelopeRoute, returnRoute?: MmrpEnvelopeRoute, flags?: MmrpEnvelopeFlag | MmrpEnvelopeFlag[]); /** * Return route * * This will be set on envelopes received by `.on(`delivery`)` * if `envelope.setFlag('TRACK_ROUTE')` was called before * sending the envelope. * * The returnRoute can then be used after reception * to send back a reply if needed. */ public returnRoute?: MmrpEnvelopeRoute /** * List of messages * * @type {MmrpEnvelopeMessage[]} * @memberof MmrpEnvelope */ public messages: MmrpEnvelopeMessage[] /** * Sets the message part(s) of the envelope * * The event name is what one will listen to, but prefixed * with 'delivery.'. Example: * * ```javascript * mmrpNode.on('delivery.MyEventName', (...args) => console.log(...args)) * ``` * * @param {string} eventName * @param {MmrpEnvelopeMessage} message * @param {Function} callback * * @memberof MmrpEnvelope */ setMessage(eventName: string, message: MmrpEnvelopeMessage): void; /** * Appends a message part to the envelope * * @param {MmrpEnvelopeMessage} message * * @memberof MmrpEnvelope */ addMessage(message: MmrpEnvelopeMessage): void; /** * Sets the route that the envelope needs to take to reach its destination * * @param {MmrpEnvelopeRoute} route * * @memberof MmrpEnvelope */ setRoute(route: MmrpEnvelopeRoute): void; /** * Prepends a route to the existing envelope route * * @param {MmrpEnvelopeRoute} route * * @memberof MmrpEnvelope */ injectRoute(route: MmrpEnvelopeRoute): void; /** * Removes a route part from the start of the route, if the value matches, * or empties the entire route if the given identity is undefined * * @param {string} [identity] * * @memberof MmrpEnvelope */ consumeRoute(identity?: string): void; /** * Returns true if the route has not been fully consumed, false otherwise * * @returns {boolean} * * @memberof MmrpEnvelope */ routeRemains(): boolean; /** * Returns the final address in the route * * @returns {string} * * @memberof MmrpEnvelope */ getFinalDestination(): string; /** * Returns true if this envelope tracks and contains the sender's route, false otherwise * * @returns {boolean} * * @memberof MmrpEnvelope */ hasReturnRoute(): boolean; /** * Set the return route for this message * * Used whenever a reply is required * * @param {(string | string[])} route * * @memberof MmrpEnvelope */ setReturnRoute(route: MmrpEnvelopeRoute): void; /** * Prepends a route to the existing return route * * @param {string} route * * @memberof MmrpEnvelope */ injectSender(route: string): void; /** * Returns the final portion of the return route * * This identifies the node on which the original sender * resides. * * @returns {string} * * @memberof MmrpEnvelope */ getInitialSource(): string; /** * * * @param {*} flags * * @memberof MmrpEnvelope */ setMeta(flags: MmrpEnvelopeFlag | MmrpEnvelopeFlag[]): void; /** * True if a flag is set * * @returns {boolean} * * @memberof MmrpEnvelope */ isFlagged(): boolean; /** * Retrieves the flags's string representation * * @returns {string[]} * * @memberof MmrpEnvelope */ getFlags(): string[]; /** * Set a flag value * * @param {*} flag * @returns {boolean} * * @memberof MmrpEnvelope */ setFlag(flag: MmrpEnvelopeFlag): boolean; } /** * MMRP Nodes are created to transmit data encapsulated * into MmrpEnvelopes across MAGE servers within a cluster * * @class MmrpNode */ declare class MmrpNode { constructor(role: 'relay' | 'client' | 'both', cfg: any, clusterId: string); /** * Connects the dealer to a particular URI (if not already connected) and sends it a handshake. * * @param {string} uri * @param {string} clusterId * @param {Function} [callback] */ connect(uri: string, clusterId: string, callback?: Function): void; /** * Disconnects the dealer from a given URI (if connected) * * @param {string} uri */ disconnect(uri: string): void; /** * Announce a relay as available. It should be connected to if appropriate. * * @param {string} uri * @param {string} clusterId * @param {Function} [callback] */ relayUp(uri: string, clusterId: string, callback?: Function): void; /** * Announce a relay as no longer available. It will disconnect from this relay if connected. * * @param {string} uri */ relayDown(uri: string): void; /** * Sends a message along a route of identities * * @param {MmrpEnvelope} envelope The envelope to send * @param {number} [attempts] Number of times to try resending of the route does not currently exist * @param {Function} cb Callback that may receive an error if routing failed * @return {number} Number of bytes sent */ send(envelope: MmrpEnvelope, attempts: number | null, callback: Function): number; /** * Broadcasts an envelope across the entire mesh of relays and clients. * * @param {Envelope} envelope * @param {string} [routingStyle] "*" (to all relays and clients), "*:c" (to all clients), "*:r" (to all peer relays) * @return {number} Number of bytes sent */ broadcast(envelope: MmrpEnvelope, routingStyle?: string): number; /** * Closes all sockets on this node and removes all event listeners as we won't be emitting anymore. */ close(): void; on(eventName: string, onEvent: (envelope: MmrpEnvelope) => void): void; once(eventName: string, onEvent: (envelope: MmrpEnvelope) => void): void; removeAllListeners(eventName: string): void; removeListener(eventName: string, callback: Function): void; } /** * Service discovery service events */ declare type ServiceEventName = 'up' | 'down' | 'error' /** * Service discovery callback function for 'up' and 'down' events */ declare type ServiceEventHandler = (serviceNode: mage.core.IServiceNode) => void /** * Service discovery callback function for 'error' events */ declare type ServiceErrorEventHandler = (error: Error) => void /** * MAGE session class * * @class Session */ declare class Session { /** * Key/value meta data object to store with the session * * @type {Object} * @memberOf Session */ meta: { [id: string] : string; }; /** * An actor ID to associate with this session * * @type {string} * @memberOf Session */ actorId: string; /** * The language of the user * * @type {string} * @memberOf Session */ language: string; /** * The session key * * @type {string} * @memberOf Session */ key: string; /** * The clusterId associated with this session (for mmrp) * * @type {string} * @memberOf Session */ clusterId: string; /** * Unix timestamp of the creation time of this session * * @type {number} * @memberOf Session */ creationTime: number; /** * The game version at the time of registration * * @type {string} * @memberOf Session */ version: string; /** * Expires the session and communicates it * to the client passing the given reason * * @param state * @param reason */ expire(state: mage.core.IState, reason: string): void /** * Recalculates a new expiration time for * the session and saves it * * @param {mage.core.IState} state * @memberof Session */ extend(state: mage.core.IState): void /** * Returns data for the given key * * @param {string} key * @returns {*} * @memberof Session */ getData(key: string): any /** * Sets data at a given key * * @param {string} key * @param {*} value * @memberof Session */ setData(key: string, value: any): void /** * Deletes the data at a given key * * @param {string} key * @memberof Session */ delData(key: string): void } /** * */ declare type TimeConfig = { /** * By how much time, in milliseconds, should time be offset? * * @type {number} */ offset: number; /** * How fast or slow should time fast? Values smaller than 1 slow down time, * and values higher than 1 accelerate time. * * @type {number} */ accelerationFactor: number; /** * From which point in time should we consider time accelerated? * * @type {number} */ startAt: number; } declare interface IMageCore { /** * State class * * The state library exposes a constructor that constructs objects which form an interface * between an actor, a session and the archivist. Virtually any command that involves reading * and modification of data should be managed by a state object. * * When you’re done using the state class, always make sure to clean it up by calling close() on it. * MAGE’s module and command center systems that bridge the communication between client * and server use the State object for API responses and server-sent events. * * @type {{ new(): mage.core.IState }} */ State: { new(actorId?: string, session?: Session, options?: mage.core.IStateOptions): mage.core.IState }; /** * Archivist core module * * This module can be used to access things such as topic * configuration and vault backends. */ archivist: { /** * Retrieve an object containing all the * existing vault backend instances * * @returns {{ [vaultName: string]: any }} */ getPersistentVaults(): { [vaultName: string]: any } /** * Used to confirm the abilities of the topic on this configured system * * Not all vaults can implement the full spectrum of internals required * by the higher level APIs; this method allows developers to verify * whether a given vault backend support the functionalities it needs. * * @param {string} topic The topic to test. * @param {string[]} [index] The index signature this topic should conform to. * @param {string[]} [operations] The operations that every vault associated with this topic must * support. Values: 'list', 'get', 'add', 'set', 'touch', 'del' * @param {boolean} [indexIsPartial] True if the given index signature is allowed to be incomplete. */ assertTopicAbilities(topicName: string, index?: string[], requiredOperations?: string[], isIndexPartial?: boolean): any /** * Close all vaults instances * * @returns {*} */ closeVaults(): any /** * Check if a topic is defined * * @param {string} topicName * @returns {boolean} */ topicExists(topicName: string): boolean /** * Retrieve a map of all existing topics * * @returns {{ [topicName: string]: mage.archivist.ITopic }} */ getTopics(): { [topicName: string]: mage.archivist.ITopic } /** * * * @param {string} topicName * @returns {*} */ getTopicApi(topicName: string, vaultName: string): mage.archivist.ITopicApi | null /** * Migrate all current vaults to a given version * * This will look at the list of available migration scripts * and execute them if needed. * * @returns {*} */ migrateToVersion(targetVersion: string, callback: (error: Error | null) => void): any } /** * Configuration core module */ config: typeof config, /** * HTTP Server * * This API can be used to add your own custom routes if needed, as well * as to serve files. This can be useful during the development of an HTML5 * game. */ httpServer: { /** * Register a route (a string or a regular expression) on the HTTP server * * Incoming requests will be expected to be handled by the handler function you pass. * Based on the type you specify, your handler function will receive different arguments. * * #### "simple": handler(req, res, path, query, urlInfo) * * * req: the IncomingMessage object. * * res: the ServerResponse object. * * path: the path part of the URL. * * query: the parsed query string. * * urlInfo: all information that came out of `url.parse()`. * * #### "callback": handler(req, path, query, callback) * * * req: the IncomingMessage object. * * path: the path part of the URL. * * query: the parsed query string. * * callback: call this when you've constructed your HTTP response. * * The callback accepts the following arguments in order: * * * httpCode: the HTTP status code you want to return. * * out: a string or buffer that you want to send to the client. * * headers: an object with headers. * * #### "websocket": handler(client, urlInfo) * * * client: a WebSocket client connection object. See the [`ws` documentation](https://npmjs.org/package/ws). * * urlInfo: all information that came out of `url.parse()`. * * #### "proxy": endpoint handler(req, urlInfo) * * * req: the IncomingMessage object. * * urlInfo: all information that came out of `url.parse()`. * * Your handler function *must* return an endpoint object to connect to. * For syntax, please read the [`net.createConnection()`](https://nodejs.org/docs/latest/api/net.html#net_net_createconnection) * documentation. */ addRoute(pathMatch: string | RegExp, handlerFunction: (...args: any[]) => any, type: 'simple' | 'callback' | 'websocket' | 'proxy'): void; /** * Removes the handler function registered on the given route. */ delRoute(pathMatch: string | RegExp): void; /** * Registers a route to lead directly to a folder on disk * * If you want to be notified when a request finishes, you may pass * an `onFinish` function. It may receive an error as its first argument. * If you decide to pass this function, logging the * error will be your responsibility. * * Example: * * ```javascript * mage.core.httpServer.serveFolder('/source', './lib'); * ``` * * If you provide a `defaultFile` file name argument, serving up a * folder by its name will serve up a default file if it * exists. * * Example: * * ```javascript * mage.core.httpServer.serveFolder('/source', './lib', 'index.html'); * ``` */ serveFolder(route: string | RegExp, folderPath:string, defaultFile?: string, onFinish?: (error?: Error) => void): void; /** * Registers a route to lead directly to a file on disk * * If you want to be notified when a request finishes, you may pass * an `onFinish` function. It may receive an error as its first argument. * * If you decide to pass this function, logging the error will be your responsibility. */ serveFile(route: string | RegExp, filePath:string, onFinish?: (error?: Error) => void): void; /** * Registers a route "/favicon.ico" and serves the given buffer as content * * The mime type defaults to `image/x-icon` and may be overridden. */ setFavicon(buffer: Buffer, mimetype?: string): void; } /** * The core logger is the logger instance used internally * by MAGE to log different events; for your application, * you should most likely use `mage.logger` instead */ logger: Logger; /** * Message Server * * The message server is used for state propagation across * multiple MAGE servers in a cluster; it can also be used directly * by MAGE developer to transfer data across servers. * * @memberof Mage */ msgServer: { /** * Message Server */ mmrp: { Envelope: typeof MmrpEnvelope; MmrpNode: typeof MmrpNode; } /** * Check whether the Message Server is enabled * * See https://mage.github.io/mage/api.html#subsystems * for mor details on how to enable message server. * * @returns {boolean} */ isEnabled(): boolean; /** * Retrieve the underlying MMRP Node instance * used by this Message Server instance. * * @returns {MmrpNode} */ getMmrpNode(): MmrpNode; /** * Retrieve the unique cluster identifier * * @returns {string} */ getClusterId(): string; /** * Retrieve the configuration used by this Message * Server instance * * @returns {*} */ getPublicConfig(): any; /** * Send a message to a remote Message Server instance * * @param {string} address * @param {string} clusterId * @param {MmrpEnvelope} message */ send(address: string, clusterId: string, message: MmrpEnvelope): void; /** * Broadcast a message to all connected Message Server instance * * @param {MmrpEnvelope} message */ broadcast(message: MmrpEnvelope): void; /** * Send a confirmation message * * @param {string} address * @param {string} clusterId * @param {string[]} msgIds */ confirm(address: string, clusterId: string, msgIds: string[]): void; /** * Mark a given address as connected * * @param {string} address * @param {string} clusterId * @param {('never' | 'always' | 'ondelivery')} [disconnects] */ connect(address: string, clusterId: string, disconnects?: 'never' | 'always' | 'ondelivery'): void; /** * Mark a given address as disconnected * * @param {string} address * @param {string} clusterId */ disconnect(address: string, clusterId: string): void; /** * Close the network connection for this Message Server */ close(): void; } /** * Sampler core module * * Used for keeping tracks of local server metrics. This is useful * for when you wish to expose some information about your server * in production (for Zabbix, Nagios, Grafana, etc). * * See https://mage.github.io/mage/#metrics for more details. */ sampler: { /** * Set the value of a given metric * * @param {string[]} path * @param {string} id * @param {number} value */ set(path: string[], id: string, value: number): void; /** * Increment a given metric * * @param {string[]} path * @param {string} id * @param {number} increment */ inc(path: string[], id: string, increment: number): void; /** * Keep track of a value * * When accessing the savvy HTTP interface to collect the data point * created by this method, you will have access to: * * - max * - min * - average * - standard deviation * * See https://www.npmjs.com/package/panopticon#panopticonsamplepath-id-n * * @param {string[]} path * @param {string} id * @param {number} value */ sample(path: string[], id: string, value: number): void; /** * Keep track of a value over a period of time * * Works similarly to `mage.sampler.sample`, but let the user pass * a time delta value. * * See https://www.npmjs.com/package/panopticon#panopticontimedsamplepath-id-dt * * @param {string[]} path * @param {string} id * @param {number} delta */ timedSample(path: string[], id: string, delta: number): void; } /** * The Service Discovery library is a library that allows you to announce and discover services * on the local network, through different types of engines. * * To use service discovery, configuration is mandatory; * See https://mage.github.io/mage/#service-discovery to learn how to configure service discovery. * * @memberof Mage */ serviceDiscovery: { /** * Create a service instance * * Service instances can be used to announce services as well * as listen for service instance appearing and disappearing. * * @param {string} name * @param {('tcp' | 'udp')} type * @returns {mage.core.IService} */ createService(name: string, type: 'tcp' | 'udp'): mage.core.IService } } declare class Mage extends NodeJS.EventEmitter { MageError: typeof MageError; /** * Check if a file is considered like a source code file in MAGE * * Example: * * ```javascript * mage.isCodeFileExtension('.js'); * ``` * * @param {string} ext File extension * @returns {boolean} True if the extension is for a source code file */ isCodeFileExtension(ext: string): boolean; /** * The worker ID of the currently worker process * * Will return false if the process is the master process, or * if MAGE is running in single mode. * * This differs from cluster.worker.id because it will remain consistent * if restarted or reloaded. */ workerId?: number; /** * The current task to execute. Internal value. * @memberof Mage */ task: { /** * The name of the task to execute * * @type {string} */ name: string; /** * Options passed to the task * * @type {*} */ options: any; } /** * The current version of MAGE * * @type {string} * @memberof Mage */ version: string; /** * Contains information from MAGE’s `package.json` file: * * - name: “mage” * - version: The version of MAGE. * - path: The path to MAGE on disk. * - package: The parsed contents of MAGE’s package.json file. * * @type {any} * @memberof Mage */ magePackage: any; /** * Contains information from your project's `package.json` file: * * - name: “mage” * - version: The version of MAGE. * - path: The path to MAGE on disk. * - package: The parsed contents of MAGE’s package.json file. * * @type {any} * @memberof Mage */ rootPackage: any; /** * Requiring MAGE's dependencies into games * * This should only be used to load the tomes module (when used). * * @param {string} packageName * @returns {*} * * @memberOf Mage */ require(packageName: string): any; /** * Return MAGE's current run state * * @returns {string} * * @memberOf Mage */ getRunState(): 'setup' | 'running' | 'quitting'; /** * Set MAGE's current run state * * @param {('setup' | 'running' | 'quitting')} state * @returns {string} * * @memberOf Mage */ setRunState(state: 'setup' | 'running' | 'quitting'): string; /** * Set which MAGE task to execute. * * @param {string} name * @param {*} options * * @memberOf Mage */ setTask(name: string, options: any): void; /** * Get the task to be executed * * @returns {mage.core.ITask} * * @memberOf Mage */ getTask(): mage.core.ITask; /** * Quit MAGE * * This will stop ALL MAGE processes regardless of where it * is called from. To stop only the local process (similarly * to what `process.exit` would do, please see `mage.exit`) * * @param {number} [exitCode] exit code to use * @param {boolean} [hard] If true, exit immediately (exit code will be ignored and set to 1) */ quit(exitCode?: number, hard?: boolean) : never; /** * Shut down MAGE * * When setting `hard` to true, MAGE will not try to complete current I/O * operations and exit immediately; you should avoid using `hard` unless there * are no other options available to you. * * Note that this will behave similarly to `process.exit`; only the *current* * process will be stopped, not the entire server. To stop the entire server, * see `mage.quit` * * @param {number} [exitCode] exit code to use * @param {boolean} [hard] If true, exit immediately (exit code will be ignored and set to 1) * * @memberOf Mage */ exit(exitCode?: number, hard?: boolean) : never; // deprecated // fatalError(...args: any[]): never; /** * Add a new lookup path when attempting to load modules * * @param {string} path * * @memberOf Mage */ addModulesPath(path: string) : void; /** * Define which modules to load at runtime * * @param {string[]} moduleNames * @returns {this} * * @memberOf Mage */ useModules(moduleNames: string[]): this; /** * Tell MAGE to load all your application's modules * * MAGE will then load all modules found under your project's * `./lib/modules` folder. * * @returns {this} * * @memberOf Mage */ useApplicationModules(): this; /** * Return the path from which a given module was loaded from * * @param {string} name * @returns {string} * * @memberOf Mage */ getModulePath(name: string): string; /** * List all loaded modules * * @returns {string[]} * * @memberOf Mage */ listModules(): string[]; /** * Setup MAGE modules. * * You should not need to call this manually * unless you are trying to manually set up MAGE * for some special use case. * * @param {Function} cb * * @memberof Mage */ setupModules(cb: (error: Error|null) => void): void; /** * Setup MAGE * * You should not need to call this manually * unless you are trying to manually set up MAGE * for some special use case. * * @param {Function} [cb] * @returns {IMage} * * @memberof Mage */ setup(cb?: (error: Error|null) => void): Mage; /** * Start MAGE * * You should not need to call this manually * unless you are trying to manually set up MAGE * for some special use case. * * @param {Function} [cb] * @returns {IMage} * * @memberof Mage */ start(cb?: (error: Error|null) => void): Mage; /** * Boot the MAGE server * * You normally will not have to call this manually; the * `mage` binary referred to in your project's `package.json` will * call this for you. * * However, you will need to call this in cases * where you wish to create your own binary entry point (when creating a debug console * to run MAGE under special conditions, for instance). * * @param {Function} [callback] * * @memberof Mage */ boot(allback?: (error: Error|null) => void): void; /** * Verify if development mode is currently activated, or * if a given development mode feature is activated * * @param {string} [feature] * @returns {boolean} * * @memberOf Mage */ isDevelopmentMode(feature?: string): boolean; /** * Retrieve a given app's configuration * * @param {string} appName * @param {string} baseUrl * @returns {*} * * @memberof Mage */ getClientConfig(appName: string, baseUrl: string): any; /** * auth module * * The auth module can be used to register and authenticate users * * @memberof Mage */ auth: { /** * Authenticate a user. * * In general, you will want to create a session for a user upon authentication; * therefore you will generally want to use `login` instead. */ authenticate(state: mage.core.IState, username: string, password: string, callback: AuthenticateCallback): void; /** * User login */ login(state: mage.core.IState, username: string, password: string, callback: LoginCallback): void; /** * Login a user as an anonymous user * * This allows for user-to-user events to be emitted, even if a given user * does not exist. */ loginAnonymous(state: mage.core.IState, options: mage.auth.IAuthOptions): Session; /** * Register a new user */ register(state: mage.core.IState, username: string, password: string, options: mage.auth.IAuthOptions, callback: RegisterCallback): void; /** * Change a user's password. */ changePassword(state: mage.core.IState, username: string, newPassword: string, callback: ChangePasswordCallback): void; /** * Ban the user */ ban(state: mage.core.IState, username: string, callback: BanCallback): void; /** * Unban the user */ unban(state: mage.core.IState, username: string, callback: UnbanCallback): void; } /** * Command-line interface API * * The cli property is a library that lets you extend and boot up the command-line * argument parser. * * @type {MageCore} * @memberOf Mage */ cli: { /** * Parse the process arguments obtained via process.argv * * @returns {void} */ run(): void /** * Commander object which allows you to extend the provided CLI * * ```javascript * var cli = require('mage').cli; * cli.program.option('--clown', 'Enables clown mode'); * cli.run(); * ``` * * With the previous code, you should obtain the following: * * ```shell * $ ./game --verbose --help * * Usage: game [options] [command] * ... * Options: * ... * --clown Enables clown mode * ``` */ program: commander.CommanderStatic } /** * Core modules * * @type {MageCore} * @memberOf Mage */ core: IMageCore /** * Logger module * * The logger module should be used to capture log entries; it can be configured to * send logs to one or multiple logging backend (syslog, file, etc). * * See https://mage.github.io/mage/#logging for more details. * * @type {Logger} * @memberOf Mage */ logger: Logger; /** * Session module * * The session module can be used to create a session * object, which will then normally be accessible through * the `state.session` key on the state object received * through user commands. * * @memberOf Mage */ session: { /** * Register a new session * * @param {State} state * @param {string} actorId * @param {string} language * @param {*} meta */ register(state: mage.core.IState, actorId: string, language: string, meta: any): void; /** * Transfer a session and its content to another user * * Mostly used for testing purposes. * * @param {State} state * @param {string} fromActorId * @param {string} toActorId * @param {Function} callback */ reassign(state: mage.core.IState, fromActorId: string, toActorId: string, callback: Function): void; } /** * Time module * * The time module can be used to affect how time is perceived from the MAGE * process' perspective. This is mostly useful when either writing tests (like unit tests) * or simulations (run an instance of your game server faster to, for instance, play a full * week of your game within a few hours). * * See https://mage.github.io/mage/#time-manipulation for more details. * @memberOf Mage */ time: { /** * Change the current time, and how quickly time advances * * You may use the offset to specify how far back or forward in time you wish to * go, and the acceleration factor to decide how fast (or slow) should time pass. * * The `startAt` attribute might be a bit confusing; in essence, the only time you should * need to set it is when you wish to specify from which time in the past timestamps should be * translated based on the acceleration factor. Therefore, unless you use an acceleration factor * other than 1, setting this will have no effect. * * In short, you should almost never have to use the `startAt` attribute. * * See https://github.com/Wizcorp/timer.js/blob/master/index.js#L37 for more details. * * @param {number} offset in milliseconds (default: 0) * @param {number} accelerationFactor How fast should time move forward. (default: 1) * @param {number} startAt From which point in time do we consider time to be accelerated (default: Date.now()) */ bend(offset: number, accelerationFactor: number, startAt: number): void; /** * Return the current time configuration (offset, acceleration factor and acceleration start time) * * @returns {TimeConfig} */ getConfig(): TimeConfig; /** * Get the current time according to MAGE, in milliseconds * * You can use this to instanciate Date objects: * * ```javascript * var date = Date(mage.time.msec()); * ``` * * @returns {number} */ msec(): number; /** * Get the current time according to MAGE, in seconds * * @returns {number} */ sec(): number; /** * Translate a given timestamp * * Sometimes, you will need to take a given timestamp and calculate * its relative representation according to MAGE time settings. In general, * you will only need to use this when using `mage.time.bend`; however, * using it when time is not bent will simply have no effect on the passed value. * * @param {number} timestamp The timestamp to translate * @param {boolean} [msecOut] True if the timestamp passed is in milliseconds, false or null if in seconds * @returns {number} The translated timestamp according to the current time rules enforced by `mage.time.bend` */ translate(timestamp: number, msecOut?: boolean): number; /** * Remove the effect of `mage.time.bend` * * This effectively resets the clock to the current time. */ unbend(): void; } } declare var mage: Mage; declare namespace mage { namespace archivist { /** * The media type defines how the data will be deserialized when read from a topic, and * how it will be serialized before it is written to a topic. */ type ArchivistMediaType = 'application/json' | 'application/octet-stream' | 'application/x-tome' | 'text/plain' | string; /** * Defines to what type of data structure a topic instance will be deserialized into. It is also used * in some cases to specify to a serialization method what is the data type of the data we are feeding in. */ type ArchivistEncoding = 'utf8' | 'buffer' | 'live'; /** * Callback functions */ type ArchivistExistsCallback = (error: Error|null, doesValueExist: boolean) => void; type ArchivistGetCallback = (error: Error|null, value: T) => void; type ArchivistMGetCallback = (error: Error|null, value: T[]) => void; type ArchivistNamedMGetCallback = (error: Error|null, value: { [name: string]: T }) => void; type ArchivistListCallback = (error: Error|null, indexes: mage.archivist.IArchivistIndex[]) => void; type ArchivistDistributeCallback = (preDistributionErrors: Error[], distributionErrors: Error[]) => void; interface IArchivistGetOptions { /** * Is the value optional? * * Default is false; when false, `get` will trigger an error * if the value being queried does not exist. * * @type {boolean} */ optional?: boolean; /** * Media type * * @type {ArchivistMediaType[]} */ mediaTypes?: ArchivistMediaType[]; /** * Encoding * * @type {ArchivistEncoding[]} */ encodings?: ArchivistEncoding[]; } interface IArchivistListSortOption { /** * Name of the index field to sort on * * @type {string} */ name: string; /** * Direction in which to order by * * @type {('asc' | 'desc')} */ direction?: 'asc' | 'desc'; } interface IArchivistListOptions { /** * List of sort options * * @type {ArchivistListSortOption[]} */ sort?: IArchivistListSortOption[]; /** * Pagination option * * The array needs to be of the following structure: * * [start, length] * * Where: * * * start: is the entry to start from * * length: how many entries to return from the starting point * * @type {number[]} */ chunk: number[]; } /** * Scans can set options for both listing and getting. */ type ArchivistScanOptions = IArchivistGetOptions & IArchivistListOptions; /** * Key-value map of index field names and their expected value. */ interface IArchivistIndex { [id: string] : string; } /** * An IArchivistQuery is a combination of a topic name, and * an ArchivistIndex. When making query, it is possible to give only a * partial index, that is an index which only defines a value for * some (not all) the index's fields. */ interface IArchivistQuery { topic: string; index: IArchivistIndex; options?: IArchivistGetOptions; } /** * An INamedArchivistQuery can be used with mget * to retrieve multiple values at once in a key-value map. * * Note you can also pass IArchivistIndex[] to mget * instead if you wish to receive back an interatable array. */ interface INamedArchivistQuery { [name: string]: IArchivistQuery } /** * Register what operations are allowed. * * Note that this is executed directly at configuration time; * it is not something which gets executed as a validation function * at verification time. * * To limit what a given user has access to at runtime, please * see `ArchivistTopicVaultConfiguration.shard`. * * @interface AclTest * @extends {Function} */ interface IAclTest extends Function { (acl: string[], operation: VaultOperation | 'get' | '*', options?: { shard: boolean }): void; } /** * Define how data will be read, serialized and deserialized. * * Other configuration attributes may be available depending on * the vault backend being used by the parent topic. * * Please see https://mage.github.io/mage/api.html#vaults, and look for the * paragraph called "Required topic API" for your current vault; any functions * defined there can be overridden for your given topic. */ interface IArchivistTopicVaultConfiguration { shard?(value: IVaultValue): T, acl?: (test: IAclTest) => void } /** * Define the topic's configuration. * * @interface ITopic */ interface ITopic { index: string[], vaults: {[name: string]: IArchivistTopicVaultConfiguration}, readOptions?: mage.archivist.IArchivistGetOptions, afterLoad?: () => void, beforeDistribute?: () => void } /** * Contains the index fields, and methods for * processing a given topics on a given vault backend * * @interface ITopicApi */ interface ITopicApi { /** * List of index fields * * @type {string[]} * @memberof ITopicApi */ index: string[] /** * Serialize the data * * @memberof ITopicApi */ serialize?: (value: any) => {mediaType: mage.archivist.ArchivistMediaType, data: any, encoding: string } /** * Deserialize the data * * @memberof ITopicApi */ deserialize?: (mediaType: mage.archivist.ArchivistMediaType, data: any, encoding: string) => any /** * Generate a storage key from the topic name and the index information * * @memberof ITopicApi */ createKey?: (topicName: string, index: mage.archivist.IArchivistIndex) => any, /** * Shard function * * * @memberof ITopicApi */ shard?: (value: mage.archivist.IVaultValue) => string | number, /** * Object containing the ACL information, per level * * @type {*} * @memberof ITopicApi */ acl?: { [level: string]: { ops: VaultOperation | 'get' | '*', shard?: boolean } } } interface IVaultValue { /** * The topic of origin * * @type {string} * @memberOf VaultValue */ topic: string; /** * The index by which this value can be accessed * * @type {string[]} * @memberOf VaultValue */ index: mage.archivist.IArchivistIndex; /** * Expiration timeout (unix timestamp, in seconds) * * If undefined, this value will never expire. * * @type {(number | undefined)} * @memberOf VaultValue */ expirationTime?: number; /** * Archivist media type * * @type {ArchivistMediaType} * @memberOf VaultValue */ mediaType: ArchivistMediaType; /** * Is the value going to be deleted from our vault backends? * * @type {boolean} * @memberof VaultValue */ didExist: boolean; /** * Will the value be created in our vault backends? * * @type {boolean} * @memberof VaultValue */ willExist: boolean; /** * The data stored in this entry * * @type {*} * @memberOf VaultValue */ data: any; /** * Delete the currently scheduled operation for this vault value */ resetOperation(): void; /** * Check if an operation has been scheduled on this vault value * * @returns {boolean} * * @memberof VaultValue */ hasOperation(): boolean; /** * Return the operation scheduled for execution on this vault value * * @param {string} vault * @returns {(VaultOperation | null)} * * @memberof VaultValue */ getOperationForVault(vault: string): VaultOperation | null; /** * Register a read miss * * You should not have to call this manually in most cases. * * @param {string} vault * * @memberof VaultValue */ registerReadMiss(vault: string): void; /** * Schedule a data set on the different vault backends * * @param {string} mediaType * @param {*} data * @param {string} encoding * * @memberof VaultValue */ set(mediaType: string, data: any, encoding: string): void; /** * Schedule the set of the expiration time for the vault value (on supported vault backends) * * @param {number} expirationTime * * @memberof VaultValue */ touch(expirationTime: number): void; /** * Mark the vault value for deletion in the different vault backends * * @memberof VaultValue */ del(): void; /** * Retrieve the data diff for this vault value. * * This will only works on values with a mediaType supporting diffs (so currently, only tomes) * * @returns {(object[]|null)} * * @memberof VaultValue */ getDiff(): object[]|null; /** * Apply a diff to the vault value. * * This will only works on values with a mediaType supporting diffs (so currently, only tomes) * * @param {*} diff * * @memberof VaultValue */ applyDiff(diff: any): void; } } namespace auth { interface IAuthOptions { userId?: string|number acl: string[] } } namespace core { /** * Tasks are what `mage.cli` will execute for the duration * of this MAGE's run cycle. * * @interface ITask */ interface ITask { setup?(cb: (error: Error|null) => void): void; start?(cb: (error: Error|null) => void): void; shutdown?(cb: (error: Error|null) => void): void; } /** * Defines what lifecycle methods can be exposed by any MAGE modules * * @interface IModule */ interface IModule { setup?(state: IState, cb: (error: Error|null) => void): void; } /** * Defines what user commands may be consisted of * * @interface IUserCommand */ interface IUserCommand { /** * What user levels are allowed to access * this user command */ acl?: string[]; /** * Timeout for the user command * * By default, the user command will be allowed * to run forever. */ timeout?: number; /** * The code of the user command itself */ execute(state: IState, ...args: any[]): Promise; } /** * Logger interface * * This is useful when you wish, for instance, to add a logger * instance to a class. * * ```typescript * class MyClass { * private _logger: mage.core.ILogger * * constructor() { * this._logger = mage.logger.context('MyClass') * } * } * ``` * * @interface ILogger * @extends {Logger} */ interface ILogger extends Logger {} /** * IService interface * * IService is an interface describing the service instance * returned by `mage.core.serviceDiscovery.createService` * * For some examples of how you can use this API, see * https://mage.github.io/mage/api.html#examples * * @export * @interface IService */ export interface IService { /** * Announce the service as a new available service instance * * @param {number} port * @param {*} metadata * @param {(error?: Error) => void} callback * @memberof IService */ announce(port: number, metadata: any, callback: (error?: Error) => void): void; /** * Start listening for the presence of service instances * * @memberof IService */ discover(): void; /** * Event listener * * @param {ServiceEventName} eventName * @param {(ServiceEventHandler | ServiceErrorEventHandler)} handler * @memberof IService */ on(eventName: ServiceEventName, handler: ServiceEventHandler | ServiceErrorEventHandler): void; /** * Event listener * * @param {ServiceEventName} eventName * @param {(ServiceEventHandler | ServiceErrorEventHandler)} handler * @memberof IService */ once(eventName: ServiceEventName, handler: ServiceEventHandler | ServiceErrorEventHandler): void; /** * Remove all instances of an event listener * * @param {ServiceEventName} eventName * @param {(ServiceEventHandler | ServiceErrorEventHandler)} handler * @memberof IService */ removeAllListeners(eventName: ServiceEventName): void; /** * Remove an instance of an event listener * * @param {ServiceEventName} eventName * @param {(ServiceEventHandler | ServiceErrorEventHandler)} handler * @memberof IService */ removeListener(eventName: ServiceEventName, handler: ServiceEventHandler | ServiceErrorEventHandler): void; } /** * IServiceNode interface * * IServiceNode are received through the `on` and `once` event listeners; * they normally contain the network information and metadata necessary for * you to be able to connect and use this service instance. * * @export * @interface IServiceNode */ export interface IServiceNode { /** * Host name * * @type {string} * @memberof IServiceNode */ host: string; /** * Port number * * @type {number} * @memberof IServiceNode */ port: number; /** * List of available addresses * * Some services may be running on machines with more than one * network interface; here, you will find a list of all announced * IP addresses to connect through those different interfaces. * * @type {string[]} * @memberof IServiceNode */ addresses: string[]; /** * Metadata * * This data is the same as the one that gets registered * through `IService.announce`. * * @type {*} * @memberof IServiceNode */ data: any; /** * Retrieve an IP from the addresses list * * The `network` parameter is an array containing the network list * where your service is. The CIDR notation is used to represent the networks. * * This method may return `null` if no addresses could be found within * the list of provided networks, or if the node provides no networks. * * @param {(4 | 6)} version * @param {string[]} [networks] * @memberof IServiceNode */ getIp(version: 4 | 6, networks?: string[]): string | null; } /** * Options you can pass when you manually create a state object */ export interface IStateOptions { /** * The name of the application this state was created in */ appName?: string; /** * A description of what this state is being used for */ description?: string; /** * State metadata * * This data can be used to carry data around through the execution path. */ data?: Object } export interface IState { /** * Actor ID * * Depending on how the state object was created, the actor ID * may hold different meaning; however, it will in most cases * hold the session ID of the user currently executing the user command. * This will only apply to users authenticated through the auth module. * * @type {string} * @memberof IState */ actorId: string | null; /** * Archivist instance * * When a state is received through a user command, use this archvist instance * to make your data transactions. The state will automatically call `distribute` * upon a successful user command execution. * * @type {Archivist} * @memberOf State */ archivist: Archivist; /** * Session instance * * @type {Session|null} * @memberOf State */ session: Session | null; /** * State metadata * * This data can be used to carry data around through the execution path. */ data?: Object /** * Reply to the user * * This will generally be called in synchronouse user commands to specify * what value to return to the end-user. * * Note that asynchronous user commands as defined in * https://mage.github.io/mage/#using-async-await-node-7-6 * do not require you to call `state.respond`; instead, simply `return` * the data you wish to send to the end-user. * * @param {*} data * * @memberOf State */ respond(data: any): void; /** * Return an error to the user * * This will generally be called in synchronouse user commands to specify * that an error has occured, and that no response data is to be expected. * * Note that asynchronous user commands as defined in * https://mage.github.io/mage/#using-async-await-node-7-6 * do not require you to call `state.error`; instead, you can simply * throw an Error. If the Error instance has a `code` attribute, the code * will then also be returned as part of the response to the end-user. * * * @param {(string | number)} code * @param {Error} error * @param {Function} callback * * @memberOf State */ error(code: string | number, error: Error, callback: Function): void; /** * Parse a list of actorIds, and verify who is online */ findActors(actorIds: string[], callback: (error: Error | null, actors: { online: string[], offline: string[] }) => void): void; /** * Send an event to the user * * Note that the event will be blackholed if an user with a given actorId * is not currently connected. * * @param {(string | string[])} actorId * @param {string | number} eventName * @param {*} data * * @memberOf State */ emit(actorId: string | string[], eventName: string | number, data: T, configuration?: mage.core.IStateEmitConfig): void; /** * Broadcast an event to all connected users * * @param {string | number} eventName * @param {*} data * * @memberOf State */ broadcast(eventName: string | number, data: T, configuration?: mage.core.IStateEmitConfig): void; /** * Distribute both events and archivist operations * currently stacked on this state object. * * @memberOf State */ distribute(callback: (error?: Error) => void): void; /** * Distribute all events currently stacked on * this state object. * * If you wish to also distribute archivist mutations stacked * on this state, please have a look at the `distribute` method * instead. * * @memberOf State */ distributeEvents(callback: (error?: Error) => void): void; /** * Close this state object * * **Note**: You should never really have to use this method, it is * only documented for informational purposes. * * This will trigger the same thing as `distribute`, but will * return the response value set on the state and the * events to be returned directly to the actor this state refers * to. * * @memberOf State */ close(callback: (error: Error | null, data: { response: any, events: any }) => void): void; /** * Register a session on the user * * @param {*} session * * @memberOf State */ registerSession(session: any): void; /** * Forget the current session for the current user * * * @memberOf State */ unregisterSession(): void; /** * Verify the current user's credentials * * @param {*} acl * @returns {boolean} * * @memberOf State */ canAccess(acl: any): boolean; /** * Define a timeout on the state * * Once the timeout is reached, the state will automatically * report an error to the end-user. * * Note that you do not have to call `clearTimeout` manually when * receiving a state in a user command; this will be done automatically for you. * * @param {number} timeout * * @memberOf State */ setTimeout(timeout: number): void; /** * Clear a given state's timeout * * * This will do nothing if a timeout is not set on the state * * @memberOf State */ clearTimeout(): void; } /** * State emit/broadcast optional configuration */ export interface IStateEmitConfig { /** * Set if the data to emit has already been stringified * * In some cases, you might want to emit data that has already been * serialized to a JSON string; to do so, set this option to true. */ isJson?: boolean; /** * Always emit, even on error * * By default, events will only be emitted if the related call * is successful; however, you might want to make sure an event * will always be emitted, even on error. Set this configuration * entry to `true` to make sure the event will always be emitted. */ alwaysEmit?: boolean; } } } export = mage;