{"version":3,"sources":["../src/types/request.ts","../src/core/app.core.ts","../src/helpers/constants.ts","../src/gateways/base.gateway.ts","../src/models/cache.model.ts","../src/models/ussd_state.model.ts","../src/gateways/wigal.gateway.ts","../src/sessions/base.session.ts","../src/sessions/redis.session.ts","../src/sessions/postgresql.session.ts","../src/sessions/memcache.session.ts","../src/sessions/mysql.session.ts","../src/types/config_options.type.ts","../src/gateways/emergent_technology.gateway.ts","../src/config.ts","../src/menus/action.menu.ts","../src/menus/base.menu.ts","../src/menus/dynamic_menu.menu.ts","../src/menus/index.ts","../src/helpers/menu.helper.ts","../src/helpers/index.ts","../src/core/form_handler.ts","../src/core/pagination_handler.ts","../src/core/request_handler.ts","../src/japa-plugin/index.ts"],"sourcesContent":["import { State } from \"@src/models\";\nimport { IncomingHttpHeaders, IncomingMessage, ServerResponse } from \"http\";\nimport { Url } from \"url\";\nimport { Session } from \".\";\n\nexport class Request {\n\tmethod:\n\t\t| \"GET\"\n\t\t| \"POST\"\n\t\t| \"PUT\"\n\t\t| \"DELETE\"\n\t\t| \"PATCH\"\n\t\t| \"HEAD\"\n\t\t| \"OPTIONS\"\n\t\t| \"CONNECT\"\n\t\t| \"TRACE\";\n\n\tpath?: string | null;\n\n\turl: string;\n\n\t/**\n\t * The msisdn of the user\n\t */\n\tmsisdn?: string;\n\n\t/**\n\t * USSD service short code. Set only for Emergent Technology USSD\n\t */\n\tserviceCode?: string;\n\n\t/**\n\t * The input from the user\n\t */\n\tinput?: string;\n\n\theaders: IncomingHttpHeaders;\n\n\tbody: any;\n\n\tquery?: Record<string, string>;\n\n\t/**\n\t * Current USSD state. Null until the request is processed by the middlewares\n\t */\n\tstate: State;\n\n\t/**\n\t * Current session. Null until the request is processed by the middlewares\n\t */\n\tsession: Session;\n\n\tconstructor(_url: Url, req: IncomingMessage) {\n\t\tthis.method = req.method as any;\n\t\tthis.path = _url.pathname;\n\t\tthis.url = _url.href;\n\t\tthis.headers = req.headers;\n\t\tthis.query = _url.query as Record<string, string>;\n\t}\n}\n\nexport class Response extends ServerResponse {\n\tdata: Record<string, any> | any;\n}\n","import { Request, type Response } from \"@src/types/request\";\nimport type { ServerResponse, IncomingMessage } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport { parse } from \"node:url\";\nimport type { Menus } from \"@src/menus\";\nimport { Config, type ConfigOptions } from \"@src/config\";\nimport { RequestHandler } from \"./request_handler\";\n\n// @ts-ignore\nimport type {\n\tRequest as ExpressRequest,\n\tResponse as ExpressResponse,\n} from \"express\";\n\nexport class Ananse {\n\tprivate router: Menus;\n\n\tconfigure(opts: ConfigOptions) {\n\t\tconst instance = Config.getInstance();\n\t\tinstance.init(opts);\n\n\t\treturn this;\n\t}\n\n\tlisten(port?: number, hostname?: string, listeningListener?: () => void) {\n\t\treturn createServer((req, res) => this.requestListener(req, res)).listen(\n\t\t\tport,\n\t\t\thostname,\n\t\t\tlisteningListener,\n\t\t);\n\t}\n\n\tprivate async requestListener(req: IncomingMessage, res: ServerResponse) {\n\t\tconst request = new Request(parse(req.url!, true), req);\n\n\t\tif (req.method == \"POST\" || req.method == \"PUT\" || req.method == \"PATCH\") {\n\t\t\tlet data = \"\";\n\n\t\t\treq.on(\"data\", (chunk) => {\n\t\t\t\tdata += chunk;\n\t\t\t});\n\n\t\t\treq.on(\"end\", () => {\n\t\t\t\ttry {\n\t\t\t\t\tif (req.headers[\"content-type\"] == \"application/json\") {\n\t\t\t\t\t\trequest.body = JSON.parse(data);\n\t\t\t\t\t}\n\t\t\t\t\t// TODO: parse other content types\n\t\t\t\t} catch (error) {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\terror: \"Invalid JSON format in the request body\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tconst handler = new RequestHandler(request, res as Response, this.router);\n\t\tawait handler.processRequest();\n\t}\n\n\tasync express(req: ExpressRequest, res: ExpressResponse) {\n\t\tconst request = new Request(parse(req.url!, true), req);\n\n\t\tif (req.method === \"POST\" || req.method === \"PUT\" || req.method === \"PATCH\") {\n\t\t\tconst data = req.body;\n\n\t\t\ttry {\n\t\t\t\tif (req.headers[\"content-type\"] != null && req.headers[\"content-type\"]?.indexOf(\"application/json\") > -1) {\n\t\t\t\t\trequest.body = data;\n\t\t\t\t}\n\t\t\t\t// TODO: parse other content types\n\t\t\t} catch (error) {\n\t\t\t\tres\n\t\t\t\t\t.status(400)\n\t\t\t\t\t.json({ error: \"Invalid JSON format in the request body\" });\n\t\t\t}\n\t\t}\n\n\t\tconst handler = new RequestHandler(\n\t\t\trequest,\n\t\t\tres as unknown as Response,\n\t\t\tthis.router,\n\t\t);\n\t\treturn await handler.processRequest();\n\t}\n}\n","/**\n * List of USSD gateways currently supported by default.\n */\nexport enum SupportedGateway {\n\twigal = \"wigal\",\n\temergent_technology = \"emergent_technology\",\n}\n\nexport const MAXIMUM_CHARACTERS = 182;\n","import { Request, Response } from \"@src/types/request\";\nimport { State } from \"@src/models\";\nimport { Config } from \"@src/config\";\nimport { BaseSession } from \"@src/sessions\";\n\nexport abstract class Gateway {\n\tconstructor(\n\t\tprotected readonly request: Request,\n\t\tprotected readonly response: Response,\n\t) {}\n\n\tget state(): Promise<State | undefined> {\n\t\treturn Config.getInstance().session!.getState(this.sessionId);\n\t}\n\n\tget session(): BaseSession {\n\t\treturn Config.getInstance().session!;\n\t}\n\n\tabstract get sessionId(): string;\n\n\t// TODO: add helper to load session/prev state from redis/cache\n\n\t// # extract ussd params from request body/parameters/json/form-data\n\t// # extract session from redis\n\tabstract handleRequest(\n\t\treq: Request,\n\t\tresp: Response,\n\t): Promise<State | undefined>;\n\n\tabstract handleResponse(req: Request, resp: Response): Promise<void>;\n\t// # pick data from session, eg. req.session\n\t// # AND\n\t// # return response based on the expected format of the ussd gateway\n}\n\n// TODO: add africstalking, etc\n","import { BaseMenu } from \"@src/menus/base.menu\";\nimport { DynamicMenu } from \"@src/menus/dynamic_menu.menu\";\nimport { Type } from \"@src/types\";\nimport { PaginationItem } from \"@src/types/pagination.type\";\n\nexport const MENU_CACHE: {\n\t[menuId: string]: { paginated: boolean; menu: Type<BaseMenu> | DynamicMenu };\n} = {};\n\n// export const PAGINATION_CACHE: {\n// \t[menuId: string]: { [page: number | string]: PaginationItem };\n// } = {};\n","import type { PaginationItem } from \"@src/types/pagination.type\";\nimport type { MenuAction } from \"../menus\";\n\nexport enum StateMode {\n\tstart = \"start\",\n\tmore = \"more\",\n\tend = \"end\",\n}\n\nexport class State {\n\tsessionId: string;\n\tmode: StateMode;\n\tmsisdn: string;\n\tuserData: string;\n\taction?: MenuAction | undefined;\n\tprevious?: State | undefined;\n\tform?:\n\t\t| {\n\t\t\t\tid: string;\n\t\t\t\t/**\n\t\t\t\t * Tracks submitted inputs. Key is the input name, and value must be `true`.\n\t\t\t\t * If an input is submitted, it is added to this object.\n\t\t\t\t * If the input is revisited, it is first removed from this object and\n\t\t\t\t * then added back when it is submitted again.\n\t\t\t\t *\n\t\t\t\t */\n\t\t\t\tsubmitted: Record<string, true>; // Can be array but a map for O(1) lookup\n\t\t\t\tnextInput: string | undefined;\n\t\t\t\t// TODO: track submitted inputs\n\t\t  }\n\t\t| undefined;\n\n\t/**\n\t * Tracks visited menus/next to be visited menus.\n\t */\n\tmenu?:\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * Tracks visited menus.\n\t\t\t\t *\n\t\t\t\t * Key is the menu name, and value must be `true`.\n\t\t\t\t * If a menu is visited, it is added to this object. If the menu is to\n\t\t\t\t * be revisited, it is first removed from this object and then added back\n\t\t\t\t * after input validation.\n\t\t\t\t *\n\t\t\t\t */\n\t\t\t\tvisited: Record<string, true>; // Can be array but a map for O(1) lookup\n\t\t\t\tnextMenu: string | undefined;\n\t\t\t\t// TODO: track submitted inputs\n\t\t  }\n\t\t| undefined;\n\n\tpagination: {\n\t\t[menuId: string]: {\n\t\t\tcurrentPage: PaginationItem | undefined;\n\t\t\tpages: PaginationItem[];\n\t\t};\n\t} = {};\n\n\tget isStart(): boolean {\n\t\treturn this.mode == StateMode.start;\n\t}\n\n\tget isEnd(): boolean {\n\t\treturn this.mode == StateMode.end;\n\t}\n\n\t/**\n\t * Sets mode to \"end\"\n\t */\n\tend(): void {\n\t\tthis.mode = StateMode.end;\n\t}\n\n\tstatic fromJSON(json: Record<string, any>): State {\n\t\treturn Object.assign(new State(), json);\n\t}\n\n\ttoJSON(): Record<string, any> {\n\t\treturn {\n\t\t\tsessionId: this.sessionId,\n\t\t\tmode: this.mode,\n\t\t\tmsisdn: this.msisdn,\n\t\t\tuserData: this.userData,\n\t\t\t// nextMenu: this.nextMenu,\n\t\t\tmenu: this.menu,\n\t\t\taction: this.action,\n\t\t\tprevious: this.previous?.toJSON(),\n\t\t\t// formInputId: this.formInputId,\n\t\t\tform: this.form,\n\t\t\tpagination: this.pagination,\n\t\t};\n\t}\n}\n","import { Request, Response } from \"@src/types/request\";\nimport { Gateway } from \"./base.gateway\";\nimport { State } from \"@src/models\";\n\nexport class WigalGateway extends Gateway {\n\tget sessionId(): string {\n\t\treturn this.request.query?.sessionid!;\n\t}\n\n\tasync handleRequest(): Promise<State | undefined> {\n\t\tlet _state = await this.state;\n\n\t\t_state ??= new State();\n\n\t\t_state.mode = this.request.query?.mode as any; //todo: validate\n\t\t_state.msisdn = this.request.query?.msisdn!;\n\t\t_state.sessionId = this.request.query?.sessionid!;\n\t\t_state.userData = this.request.query?.userdata!;\n\n\t\t// await this.session.setState(this.sessionId, _state);\n\t\tthis.request.state = _state;\n\t\tthis.request.input = this.request.query?.userdata!;\n\t\tthis.request.msisdn = _state.msisdn;\n\n\t\treturn _state;\n\t}\n\n\tasync handleResponse(_req: Request, res: Response): Promise<void> {\n\t\tres.writeHead(200, { \"Content-Type\": \"text/plain\" });\n\t\tres.end(await this.wigalResponse());\n\t\treturn;\n\t}\n\n\tprivate async wigalResponse(): Promise<string> {\n\t\tconst data = (await this.state)!;\n\t\treturn `${this.request.query?.network}|${data?.mode}|${data?.msisdn}|${\n\t\t\tdata?.sessionId\n\t\t}|${this.response.data?.replace(/\\n/g, \"^\") ?? \"\"}|${\n\t\t\tthis.request.query?.username\n\t\t}|${this.request.query?.trafficid}|${data?.menu?.nextMenu || \"\"}`;\n\t}\n}\n","import { State } from \"@src/models\";\nimport { SessionOptions } from \"@src/types\";\n\nexport abstract class BaseSession {\n  protected readonly states: { [sessionId: string]: State } = {};\n  protected readonly data: { [sessionId: string]: Record<string, any> } = {};\n\n  // TODO: change this to a proper configuration based on the session type\n  async configure(options?: SessionOptions): Promise<void> {\n    // throw new Error(\"Method not implemented.\");\n  }\n\n  abstract setState(id: string, state: State): Promise<State>;\n\n  abstract getState(id: string): Promise<State | undefined>;\n\n  abstract clear(id: string): State;\n\n  // TODO: add delete for data\n\n  /**\n   * Set a key value pair in the session\n   *\n   * @param   {string}         sessionId  The session ID, must be unique for each user\n   * @param   {string}         key        The key to store the value\n   * @param   {any}            value     The value to store\n   */\n  abstract set(sessionId: string, key: string, value: any): Promise<void>;\n\n  /**\n   * Remove a key value pair from the session\n   *\n   * @param   {string}         sessionId  The session ID, must be unique for each user\n   * @param   {string}         key        The key to remove along with its value from the session\n   */\n  abstract remove(sessionId: string, key: string): Promise<void>;\n\n  abstract get<T>(\n    sessionId: string,\n    key: string,\n    defaultValue?: T,\n  ): Promise<T | undefined>;\n\n  abstract getAll<T = unknown>(sessionId: string): Promise<T | undefined>;\n}\n","import { State } from \"@src/models\";\nimport { BaseSession } from \"./base.session\";\n\n// @ts-ignore\nimport { createClient, type RedisClientType } from \"redis\";\nimport type { RedisSessionOptions } from \"@src/types\";\n\nexport class RedisSession extends BaseSession {\n  private static instance: RedisSession;\n\n  private CLIENT: RedisClientType;\n  private config: RedisSessionOptions;\n\n  private constructor() {\n    super();\n  }\n\n  public static getInstance(): RedisSession {\n    if (!RedisSession.instance) {\n      RedisSession.instance = new RedisSession();\n    }\n\n    return RedisSession.instance;\n  }\n\n  async configure(options?: RedisSessionOptions): Promise<void> {\n    if (options == null) {\n      throw new Error(\"Redis session configuration is required!\");\n    }\n\n    this.config = options!;\n    this.config.keyPrefix = options?.keyPrefix || \"\";\n\n    await this.redisClient();\n\n    // TODO: How data is loaded from redis should be optimized. Idealy, the keys\n    // should be the session ids, and the values should be the states and data\n    // combined. This way, we can simply load the keys and values, and assign them\n    // to the states and data properties respectively. ie. { sessionId: { state, data }}\n    // instead of having to loop through the keys and values to get the states (VERY INEFFICIENT!!)\n    //\n\n    // Load all the states from redis\n    // const keys = await this.CLIENT.keys(`${this.keyPrefix}*`);\n    // const states = await this.CLIENT.mGet(keys);\n\n    // states.forEach((state) => {\n    //   console.log(state);\n\n    //   const _state = JSON.parse(state!);\n    //   this.states[_state.sessionId] = _state;\n    // });\n\n    // // Load all the data from redis\n    // const data = await this.CLIENT.mGet(keys);\n    // data.forEach((data) => {\n    //   console.log(data);\n    //   const _data = JSON.parse(data!);\n    //   this.data[_data.key] = _data.value;\n    // });\n  }\n\n  async setState(sessionId: string, state: State) {\n    this.states[sessionId] = state;\n\n    await this.redisClient().then((client) =>\n      client.set(`${sessionId}:state`, JSON.stringify(state.toJSON())),\n    );\n    return state;\n  }\n\n  async getState(sessionId: string) {\n    await this.redisClient();\n    const val = await this.CLIENT.get(`${sessionId}:state`);\n\n    return val == null ? undefined : State.fromJSON(JSON.parse(val));\n  }\n\n  clear(sessionId: string): State {\n    const _state = this.states[sessionId];\n    delete this.states[sessionId];\n    delete this.data[sessionId];\n\n    this.redisClient().then((client) => client.del(`${sessionId}:*`));\n\n    return _state;\n  }\n\n  async set(sessionId: string, key: string, value: any): Promise<void> {\n    await this.redisClient();\n    const val = await this.CLIENT.get(`${sessionId}:data`);\n\n    const data = JSON.parse(val || \"{}\");\n    data[key] = value;\n\n    await this.redisClient().then((client) =>\n      client.set(`${sessionId}:data`, JSON.stringify(data)),\n    );\n  }\n\n  async remove(sessionId: string, key: string): Promise<void> {\n    await this.redisClient();\n    const val = await this.CLIENT.get(`${sessionId}:data`);\n\n    const data = JSON.parse(val || \"{}\");\n    delete data[key]\n\n    await this.redisClient().then((client) =>\n      client.set(`${sessionId}:data`, JSON.stringify(data)),\n    );\n  }\n\n  async get<T>(\n    sessionId: string,\n    key: string,\n    defaultValue?: T,\n  ): Promise<T | undefined> {\n    await this.redisClient();\n    const val = await this.CLIENT.get(`${sessionId}:data`);\n\n    if (val == null) {\n      return defaultValue;\n    }\n\n    return (JSON.parse(val)[key] || defaultValue) as T;\n  }\n\n  async getAll<T>(sessionId: string): Promise<T | undefined> {\n    const val = await this.redisClient().then((client) =>\n      client.get(`${sessionId}:data`),\n    );\n\n    if (val == null) {\n      return undefined;\n    }\n\n    return JSON.parse(val) as T;\n  }\n\n  private async redisClient() {\n    if (this.CLIENT == null) {\n      if (this.config.url != null) {\n        this.CLIENT = createClient({\n          url: this.config.url,\n        });\n      } else {\n        this.CLIENT = createClient({\n          username: this.config.username!,\n          socket: {\n            host: this.config.host || \"localhost\",\n            port: this.config.port || 6379,\n          },\n          database: this.config.database as number,\n          password: this.config.password!,\n        });\n      }\n    }\n    if (!this.CLIENT?.isOpen) {\n      await this.CLIENT.connect();\n    }\n\n    return this.CLIENT;\n  }\n}\n","import { State } from \"@src/models\";\nimport { BaseSession } from \"./base.session\";\nimport type { SQLSessionOptions } from \"@src/types\";\n\n/**\n * PostgreSQL session manager\n * A session manager that uses postgres as the session store\n *\n * It is assumed that database has a session table with following schema:\n * ```sql\n * CREATE TABLE ussd_sessions (\n *   id UUID PRIMARY KEY DEFAULT uuid_generate_v5(),\n *   session_id VARCHAR(255),\n *  state JSONB DEFAULT '{}',\n *   data JSONB DEFAULT '{}',\n *   created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,\n *   updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,\n *   deleted_at TIMESTAMP WITH TIME ZONE DEFAULT NULL\n * );\n * -- comment this out (and use the next code) if you want to use soft delete\n * CREATE UNIQUE INDEX session_uniq_key ON ussd_sessions (session_id);\n *\n * -- use this index, if soft delete is enabled\n * -- CREATE UNIQUE INDEX session_uniq_key ON ussd_session (session_id, deleted_at);\n * ```\n */\nexport class PostgresSession extends BaseSession {\n\tprivate static instance: PostgresSession;\n\n\tprivate config: SQLSessionOptions;\n\tprivate db: any;\n\n\tprivate constructor() {\n\t\tsuper();\n\t}\n\n\tpublic static getInstance(): PostgresSession {\n\t\tif (!PostgresSession.instance) {\n\t\t\tPostgresSession.instance = new PostgresSession();\n\t\t}\n\n\t\treturn PostgresSession.instance;\n\t}\n\n\tasync configure(options: SQLSessionOptions): Promise<void> {\n\t\tif (options == null) {\n\t\t\tthrow new Error(\"Postgres session configuration is required!\");\n\t\t}\n\t\tthis.config = options;\n\t\tthis.config.tableName ??= \"ussd_sessions\";\n\t\tthis.config.schema ??= options?.schema ?? \"public\";\n\n\t\tlet pgPromise;\n\n\t\ttry {\n\t\t\tpgPromise = await import(\"pg-promise\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t\"'pg-promise' module is required for postgres session. Please install it using 'npm install pg-promise' or 'yarn add pg-promise'\",\n\t\t\t);\n\t\t}\n\n\t\tconst pgp = pgPromise.default({\n\t\t\tcapSQL: true, // capitalize all generated SQL\n\t\t\tschema: [this.config.schema],\n\t\t});\n\n\t\tthis.db = pgp({\n\t\t\thost: options?.host || \"localhost\",\n\t\t\tport: options?.port || 5432,\n\t\t\tdatabase: options.database,\n\t\t\tuser: options.username || \"postgres\",\n\t\t\tpassword: options.password as any,\n\t\t});\n\t}\n\n\tprivate get softDeleteQuery() {\n\t\tif (this.config.softDelete == false || this.config.softDelete == null)\n\t\t\treturn \"\";\n\n\t\treturn \"AND deleted_at IS NULL\";\n\t}\n\n\tasync setState(sessionId: string, state: State) {\n\t\tthis.states[sessionId] = state;\n\n\t\t// Write postgres query to insert or update state\n\t\tawait this.db.none(\n\t\t\t`INSERT INTO $1~.$2~ (session_id, state, created_at, updated_at, deleted_at) VALUES ($3, $4::jsonb, $5, $5, NULL)\n     ON CONFLICT (session_id) DO UPDATE SET state = $4::jsonb, updated_at = $5 WHERE $1~.$2~.session_id = $3 ${this.softDeleteQuery}`,\n\t\t\t[\n\t\t\t\tthis.config.schema,\n\t\t\t\tthis.config.tableName,\n\t\t\t\tsessionId,\n\t\t\t\tJSON.stringify(state.toJSON()),\n\t\t\t\tnew Date().toISOString(),\n\t\t\t],\n\t\t);\n\t\treturn state;\n\t}\n\n\tasync getState(sessionId: string) {\n\t\tconst [val] = await this.db.any(\n\t\t\t`SELECT state FROM $1~.$2~ WHERE session_id = $3 ${this.softDeleteQuery} LIMIT 1`,\n\t\t\t[this.config.schema, this.config.tableName, sessionId],\n\t\t);\n\n\t\tif (val == null) return undefined;\n\n\t\tif (typeof val === \"object\") return State.fromJSON(val.state);\n\n\t\treturn State.fromJSON(JSON.parse(val.state));\n\t}\n\n\tclear(sessionId: string): State {\n\t\tconst _state = this.states[sessionId];\n\t\tdelete this.states[sessionId];\n\t\tdelete this.data[sessionId];\n\n\t\tif (this.config.softDelete === false || this.config.softDelete == null) {\n\t\t\tthis.db\n\t\t\t\t.none(\"DELETE FROM $1~.$2~ WHERE session_id = $3\", [\n\t\t\t\t\tthis.config.schema,\n\t\t\t\t\tthis.config.tableName,\n\t\t\t\t\tsessionId,\n\t\t\t\t])\n\t\t\t\t.catch((error: Error) => {\n\t\t\t\t\tthrow error;\n\t\t\t\t});\n\t\t} else {\n\t\t\tthis.db\n\t\t\t\t.none(\n\t\t\t\t\t`UPDATE $1~.$2~ SET updated_at = $3, deleted_at = $3 WHERE session_id = $4 ${this.softDeleteQuery}`,\n\t\t\t\t\t[\n\t\t\t\t\t\tthis.config.schema,\n\t\t\t\t\t\tthis.config.tableName,\n\t\t\t\t\t\tnew Date().toISOString(),\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t],\n\t\t\t\t)\n\t\t\t\t.catch((error: Error) => {\n\t\t\t\t\tthrow error;\n\t\t\t\t});\n\t\t}\n\n\t\treturn _state;\n\t}\n\n\tasync set(sessionId: string, key: string, value: any): Promise<void> {\n\t\tconst val = await this.db.one(\n\t\t\t`UPDATE $1~.$2~ SET data = jsonb_set(data, '{$3~}', $4::jsonb), updated_at = $5 WHERE session_id = $6 ${this.softDeleteQuery} RETURNING *`,\n\t\t\t[\n\t\t\t\tthis.config.schema,\n\t\t\t\tthis.config.tableName,\n\t\t\t\tkey,\n\t\t\t\tJSON.stringify(value),\n\t\t\t\tnew Date().toISOString(),\n\t\t\t\tsessionId,\n\t\t\t],\n\t\t);\n\t\treturn val;\n\t}\n\n\tasync remove(sessionId: string, key: string): Promise<void> {\n\t\tconst val = await this.db.one(\n\t\t\t`UPDATE $1~.$2~ SET data = data - '{$3}', updated_at = $4 WHERE session_id = $5 ${this.softDeleteQuery} RETURNING *`,\n\t\t\t[\n\t\t\t\tthis.config.schema,\n\t\t\t\tthis.config.tableName,\n\t\t\t\tkey,\n\t\t\t\tnew Date().toISOString(),\n\t\t\t\tsessionId,\n\t\t\t],\n\t\t);\n\t\treturn val;\n\t}\n\n\tasync get<T>(\n\t\tsessionId: string,\n\t\tkey: string,\n\t\tdefaultValue?: T,\n\t): Promise<T | undefined> {\n\t\tconst [val] = await this.db.any(\n\t\t\t`SELECT data FROM $1~.$2~ WHERE session_id = $3 ${this.softDeleteQuery} LIMIT 1`,\n\t\t\t[this.config.schema, this.config.tableName, sessionId],\n\t\t);\n\n\t\tif (val == null) {\n\t\t\treturn defaultValue;\n\t\t}\n\n\t\tif (typeof val === \"object\") {\n\t\t\treturn val[key] || defaultValue;\n\t\t}\n\n\t\treturn (JSON.parse(val)[key] || defaultValue) as T;\n\t}\n\n\tasync getAll<T>(sessionId: string): Promise<T | undefined> {\n\t\tconst [val] = await this.db.one(\n\t\t\t`SELECT data FROM $1~.$2~ WHERE session_id = $3 ${this.softDeleteQuery} LIMIT 1`,\n\t\t\t[this.config.schema, this.config.tableName, sessionId],\n\t\t);\n\n\t\tif (val == null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn JSON.parse(val) as T;\n\t}\n}\n","import { State } from \"@src/models\";\nimport { BaseSession } from \"./base.session\";\n\nexport class MemcacheSession extends BaseSession {\n  private static instance: MemcacheSession;\n\n  private constructor() {\n    super();\n  }\n\n  public static getInstance(): MemcacheSession {\n    if (!MemcacheSession.instance) {\n      MemcacheSession.instance = new MemcacheSession();\n    }\n\n    return MemcacheSession.instance;\n  }\n\n  async setState(id: string, state: State): Promise<State> {\n    this.states[id] = state;\n    return state;\n  }\n\n  async getState(id: string): Promise<State | undefined> {\n    return this.states[id];\n  }\n\n  clear(id: string): State {\n    const _state = this.states[id];\n    delete this.states[id];\n    delete this.data[id];\n    return _state;\n  }\n\n  async set(sessionId: string, key: string, value: any) {\n    if (this.data[sessionId] == null) {\n      this.data[sessionId] = {};\n    }\n\n    this.data[sessionId][key] = value;\n  }\n\n  async remove(sessionId: string, key: string) {\n    this.data[sessionId] ??= {};\n    delete this.data[sessionId][key]\n  }\n\n  async get<T = unknown>(\n    sessionId: string,\n    key: string,\n    defaultValue?: T,\n  ): Promise<T | undefined> {\n    if (this.data[sessionId] == null) {\n      return defaultValue;\n    }\n\n    return (this.data[sessionId][key] || defaultValue) as T;\n  }\n\n  async getAll<T>(sessionId: string): Promise<T | undefined> {\n    return this.data[sessionId] as T;\n  }\n}\n","import { State } from \"@src/models\";\nimport { BaseSession } from \"./base.session\";\nimport { SQLSessionOptions } from \"@src/types\";\n\n/**\n * MySQL session manager\n * A session manager that uses mysql as the session store\n *\n * It is assumed that database has a session table with following schema:\n * ```sql\n * CREATE TABLE ussd_sessions (\n *   id INTEGER PRIMARY KEY AUTO_INCREMENT,\n *   session_id VARCHAR(255),\n *   state LONGTEXT DEFAULT '{}',\n *   data LONGTEXT DEFAULT '{}',\n *   created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n *    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n *   deleted_at TIMESTAMP NULL DEFAULT NULL,\n *   UNIQUE KEY session_uniq_key (session_id)\n *   -- or uncomment the this for soft delete constraint\n *   -- UNIQUE KEY session_uniq_key (session_id, deleted_at)\n * );\n * ```\n */\nexport class MySQLSession extends BaseSession {\n  private static instance: MySQLSession;\n\n  private config: SQLSessionOptions;\n  private db: any;\n\n  private constructor() {\n    super();\n  }\n\n  public static getInstance(): MySQLSession {\n    if (!MySQLSession.instance) {\n      MySQLSession.instance = new MySQLSession();\n    }\n\n    return MySQLSession.instance;\n  }\n\n  async configure(options: SQLSessionOptions): Promise<void> {\n    if (options == null) {\n      throw new Error(\"Postgres session configuration is required!\");\n    }\n    this.config = options;\n    this.config.tableName ??= \"ussd_sessions\";\n\n    let mysql;\n\n    try {\n      mysql = await import(\"mysql2/promise\");\n    } catch (error) {\n      throw new Error(\n        \"'mysql2/promise' module is required for postgres session. Please install it using 'npm install mysql2/promise' or 'yarn add mysql2/promise'\",\n      );\n    }\n\n    this.db = await mysql.createConnection({\n      host: this.config?.host || \"localhost\",\n      user: this.config.username || \"root\",\n      database: this.config.database,\n      password: this.config.password as string,\n    });\n  }\n\n  private get softDeleteQuery() {\n    if (this.config.softDelete == false || this.config.softDelete == null)\n      return \"\";\n\n    return \"AND deleted_at IS NULL\";\n  }\n\n  private get tableName() {\n    return this.config.tableName!;\n  }\n\n  async setState(sessionId: string, state: State) {\n    this.states[sessionId] = state;\n\n    // Write postgres query to insert or update state\n    await this.db.query(\n      `INSERT INTO ${this.tableName} (session_id, state, created_at, updated_at) VALUES (?, ?, NOW(), NOW())\n     ON DUPLICATE KEY UPDATE state = ?`,\n      [\n        sessionId,\n        JSON.stringify(state.toJSON()),\n        JSON.stringify(state.toJSON()),\n        sessionId,\n      ],\n    );\n    return state;\n  }\n\n  async getState(sessionId: string) {\n    const [resp, _fields] = await this.db.query(\n      `SELECT state, data FROM ${this.tableName} WHERE session_id = ? ${this.softDeleteQuery} LIMIT 1`,\n      [sessionId],\n    );\n\n    if (resp.length === 0) return undefined;\n\n    const val = typeof resp[0] === \"string\" ? JSON.parse(resp[0]) : resp[0];\n    this.data[sessionId] = val.data;\n    return State.fromJSON(val.state);\n  }\n\n  clear(sessionId: string): State {\n    const _state = this.states[sessionId];\n    delete this.states[sessionId];\n    delete this.data[sessionId];\n\n    if (this.config.softDelete === false || this.config.softDelete == null) {\n      this.db\n        .query(`DELETE FROM ${this.tableName} WHERE session_id = ?`, [\n          sessionId,\n        ])\n        .catch((error: Error) => {\n          throw error;\n        });\n    } else {\n      this.db\n        .query(\n          `UPDATE ${this.tableName} SET deleted_at = ? WHERE session_id = ? ${this.softDeleteQuery}`,\n          [new Date().toISOString(), sessionId],\n        )\n        .catch((error: Error) => {\n          throw error;\n        });\n    }\n\n    return _state;\n  }\n\n  async set(sessionId: string, key: string, value: any): Promise<void> {\n    this.data[sessionId] ??= {};\n    this.data[sessionId][key] = value;\n\n    await this.db.query(\n      `UPDATE ${this.tableName} SET data = ? WHERE session_id = ? ${this.softDeleteQuery}`,\n      [JSON.stringify(this.data[sessionId]), sessionId],\n    );\n  }\n\n  async remove(sessionId: string, key: string): Promise<void> {\n    this.data[sessionId] ??= {};\n    delete this.data[sessionId][key];\n\n    await this.db.query(\n      `UPDATE ${this.tableName} SET data = ? WHERE session_id = ? ${this.softDeleteQuery}`,\n      [JSON.stringify(this.data[sessionId]), sessionId],\n    );\n  }\n\n  async get<T>(\n    sessionId: string,\n    key: string,\n    defaultValue?: T,\n  ): Promise<T | undefined> {\n    const [resp, _fields] = await this.db.query(\n      `SELECT data FROM ${this.tableName} WHERE session_id = ? ${this.softDeleteQuery} LIMIT 1`,\n      [sessionId],\n    );\n\n    if (resp == null) {\n      return defaultValue;\n    }\n\n    const val = typeof resp[0] === \"string\" ? JSON.parse(resp[0]) : resp[0];\n    return ((val?.data || \"{}\")[key] || defaultValue) as T;\n  }\n\n  async getAll<T>(sessionId: string): Promise<T | undefined> {\n    const [[val]] = await this.db.query(\n      `SELECT data FROM ${this.tableName} WHERE session_id = ? ${this.softDeleteQuery}`,\n      [sessionId],\n    );\n\n    if (val == null) {\n      return undefined;\n    }\n\n    return JSON.parse(val.data || \"{}\") as T;\n  }\n}\n","export type SessionOptions = RedisSessionOptions | SQLSessionOptions;\n\nexport interface BaseSessionOptions {\n\thost?: string | undefined;\n\tport?: number | undefined;\n\turl?: string;\n\tusername?: string | undefined;\n\tpassword?: string | undefined;\n\tdatabase?: string | number | undefined;\n}\n\nexport interface SQLSessionOptions extends BaseSessionOptions {\n\ttype: \"postgres\" | \"mysql\" | \"mssql\";\n\n\t/**\n\t * The name of the table to use for the session, default is `ussd_sessions`\n\t */\n\ttableName?: string;\n\n\t/**\n\t * The schema to use for the session table, default is `public`\n\t */\n\tschema?: string;\n\n\t/**\n\t * The name of the database to use\n\t */\n\tdatabase: string;\n\n\t/**\n\t * Whether to use soft delete or not, default is `false`.\n\t *\n\t * If set to `true`, the session will not be deleted from the database,\n\t * but will be marked as deleted by setting the `deleted_at` column to the current date and time.\n\t */\n\tsoftDelete?: boolean;\n}\n\nexport interface RedisSessionOptions extends BaseSessionOptions {\n\ttype: \"redis\";\n\tkeyPrefix?: string;\n}\n\nexport class PaginationOption {\n\t/**\n\t * Pagination is enabled by default if the content of the message to display is\n\t * more than the maximum 182 characters allowed.\n\t */\n\tenabled: boolean = true;\n\n\tnextPage: {\n\t\tdisplay: string;\n\t\tchoice: string;\n\t} = { display: \"*. More\", choice: \"*\" };\n\n\tpreviousPage: {\n\t\tdisplay: string;\n\t\tchoice: string;\n\t} = { display: \"#. Back\", choice: \"#\" };\n}\n","import { Request, Response } from \"@src/types/request\";\nimport { Gateway } from \"./base.gateway\";\nimport { State, StateMode } from \"@src/models\";\n\ninterface IEmergentRequest {\n\tType: \"initiation\" | \"response\" | \"release\" | \"timeout\";\n\tMobile: string;\n\tSessionId: string;\n\tServiceCode: string;\n\tMessage: string;\n\tOperator: string;\n}\n\nexport class EmergentTechnologyGateway extends Gateway {\n\tget sessionId(): string {\n\t\treturn (this.request.body as IEmergentRequest)?.SessionId!;\n\t}\n\n\tasync handleRequest(): Promise<State | undefined> {\n\t\tlet _state = await this.state;\n\n\t\t_state ??= new State();\n\n\t\tconst body = this.request.body as IEmergentRequest;\n\n\t\t_state.mode = this.getMode(body.Type.toLowerCase());\n\t\t_state.msisdn = body.Mobile;\n\t\t_state.sessionId = body.SessionId;\n\t\t_state.userData = body.Message;\n\n\t\tthis.request.state = _state;\n\t\tthis.request.msisdn = _state.msisdn;\n\t\tthis.request.serviceCode = body.ServiceCode;\n\n\t\t// The content of Message for session initiation is always the service short code value\n\t\t// We don't really need it, given that it is start of a session\n\t\tif (_state.mode === StateMode.start) {\n\t\t\tthis.request.input = \"\";\n\t\t} else {\n\t\t\tthis.request.input = body.Message;\n\t\t}\n\n\t\treturn _state;\n\t}\n\n\tasync handleResponse(req: Request, res: Response): Promise<void> {\n\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\tres.end(\n\t\t\tJSON.stringify({\n\t\t\t\tMessage: this.response.data,\n\t\t\t\tType: req.state.mode == StateMode.more ? \"Response\" : \"Release\",\n\t\t\t}),\n\t\t);\n\t}\n\n\tprivate getMode(type: string): StateMode {\n\t\tswitch (type.toLowerCase()) {\n\t\t\tcase \"initiation\":\n\t\t\t\treturn StateMode.start;\n\t\t\tcase \"response\":\n\t\t\t\treturn StateMode.more;\n\t\t\tcase \"release\":\n\t\t\tcase \"timeout\":\n\t\t\t\treturn StateMode.end;\n\t\t\tdefault:\n\t\t\t\treturn StateMode.start;\n\t\t}\n\t}\n}\n","import { SupportedGateway } from \"./helpers/constants\";\nimport { Gateway } from \"./gateways/base.gateway\";\nimport { WigalGateway } from \"./gateways/wigal.gateway\";\nimport { BaseSession, PostgresSession } from \"./sessions\";\nimport { MemcacheSession } from \"./sessions/memcache.session\";\nimport { MySQLSession } from \"./sessions/mysql.session\";\nimport { RedisSession } from \"./sessions/redis.session\";\nimport { PaginationOption, SessionOptions, Type } from \"./types\";\nimport { EmergentTechnologyGateway } from \"./gateways/emergent_technology.gateway\";\n\nexport class Config {\n\tprivate static instance: Config;\n\n\t#gateway: Type<Gateway>;\n\t#options: ConfigOptions;\n\n\tprivate _session: BaseSession | undefined = undefined;\n\n\tprivate constructor() {}\n\n\tpublic static getInstance(): Config {\n\t\tif (!Config.instance) {\n\t\t\tConfig.instance = new Config();\n\t\t}\n\n\t\treturn Config.instance;\n\t}\n\n\tinit(options: ConfigOptions) {\n\t\tthis.#options = options;\n\n\t\tif (typeof options.gateway == \"string\") {\n\t\t\tswitch (options.gateway) {\n\t\t\t\tcase SupportedGateway.wigal:\n\t\t\t\t\tthis.#gateway = WigalGateway;\n\t\t\t\t\tbreak;\n\t\t\t\tcase SupportedGateway.emergent_technology:\n\t\t\t\t\tthis.#gateway = EmergentTechnologyGateway;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\t// TODO: implement for custom gateway class\n\t\t}\n\n\t\t// Resolve session\n\t\tconst _session = options.session || \"memory\";\n\n\t\t// If session is already an instance of Session, then we are good to go\n\t\tif (this._session instanceof BaseSession) {\n\t\t\treturn this;\n\t\t}\n\n\t\tif (_session === \"memory\") {\n\t\t\tthis._session = MemcacheSession.getInstance();\n\t\t\treturn this;\n\t\t}\n\n\t\tif (typeof _session === \"object\") {\n\t\t\t// Configure is provided, so we need to create a new instance of the session\n\t\t\tif (_session?.type != null) {\n\t\t\t\tswitch (_session.type) {\n\t\t\t\t\tcase \"redis\":\n\t\t\t\t\t\tthis._session = RedisSession.getInstance();\n\t\t\t\t\t\tthis._session.configure(_session);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"postgres\":\n\t\t\t\t\t\tthis._session = PostgresSession.getInstance();\n\t\t\t\t\t\tthis._session.configure(_session);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"mssql\":\n\t\t\t\t\tcase \"mysql\":\n\t\t\t\t\t\tthis._session = MySQLSession.getInstance();\n\t\t\t\t\t\tthis._session.configure(_session);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t// case \"mongo\":\n\t\t\t\t\t//   throw new Error(\"Mongo session not implemented yet\");\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new Error(\"Invalid session type\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t// A session class is provided, so we need to create a new instance of the session\n\t\t\t// this._session = _session as unknown as Session;\n\t\t}\n\n\t\treturn this;\n\t}\n\n\tget gateway(): Type<Gateway> {\n\t\treturn this.#gateway;\n\t}\n\n\tget gatewayName(): SupportedGateway {\n\t\treturn this.#options.gateway as SupportedGateway;\n\t}\n\n\tget session(): BaseSession | undefined {\n\t\treturn this._session;\n\t}\n\n\tget options() {\n\t\treturn this.#options;\n\t}\n}\n\ntype CustomSession = Type<BaseSession>;\n\nexport class ConfigOptions {\n\tmiddlewares?: Type<Gateway>[];\n\tsession?: \"memory\" | SessionOptions | CustomSession;\n\tgateway: keyof typeof SupportedGateway;\n\tpagination?: PaginationOption = new PaginationOption();\n}\n","import { BaseSession } from \"@src/sessions\";\nimport { NextMenu, Session } from \"@src/types\";\nimport { Request, Response } from \"@src/types/request\";\n\nexport class MenuAction {\n\t// name: string; // FIXME: relevant? should be removed?\n\n\t/**\n\t * The choice that the user should enter to select this option\n\t * '*' is used to match any input. Useful for a catch-all option, must be the last option\n\t */\n\tchoice:\n\t\t| string\n\t\t| RegExp\n\t\t| ((\n\t\t\t\tinput: string | undefined,\n\t\t\t\treq: Request,\n\t\t\t\tres: Response,\n\t\t  ) => Promise<string>); // TODO: or function\n\t//FIXME: remove this\n\t// route: string; // Route ID\n\t// TODO: change return type to response\n\t// TODO: or link to action class\n\t// action?: Type<BaseAction>;\n\tdisplay?:\n\t\t| string\n\t\t| ((req: Request, res: Response) => Promise<string> | string); // text to display. or function? text?\n\t// validation?: string | RegExp | ((req: Request) => boolean); //FIXME: move to action class\n\t// error_message?: string;\n\tnext_menu?: NextMenu; // TODO: links to next menu\n\n\t// TODO: validate that either route or action is provided\n\t// suggest a name for a method that will be called when this option is selected by the user\n\thandler?: (req: Request) => Promise<void>;\n\t// handler: (get: () => void, set: (val: any) => Promise<void>) => void = (\n\t//   get,\n\t//   set\n\t// ) => {\n\t//   return;\n\t// };\n\n\t// get session(): Session {\n\t//   return {\n\t//     get: async <T>(key: string, defaultValue?: any) => {\n\t//       return await Config.getInstance().session?.get<T>(\n\t//         this.sessionId!,\n\t//         key,\n\t//         defaultValue\n\t//       );\n\t//     },\n\t//     getAll: <T>() => {\n\t//       return Config.getInstance().session?.getAll<T>(this.sessionId!);\n\t//     },\n\t//     set: (key: string, val: any) =>\n\t//       Config.getInstance().session?.set(this.sessionId!, key, val),\n\t//   };\n\t// }\n}\n","import { Config } from \"@src/config\";\nimport { FormInput, Session, ValidationResponse } from \"@src/types\";\nimport { MenuAction } from \"./action.menu\";\nimport { Request, Response } from \"@src/types/request\";\n\nexport abstract class BaseMenu {\n\tconstructor(\n\t\tprotected readonly request: Request,\n\t\tprotected readonly response: Response,\n\t) {}\n\n\tasync validate(_data?: string): Promise<ValidationResponse> {\n\t\treturn true;\n\t}\n\n\tpaginate(): Promise<boolean> | boolean {\n\t\treturn false;\n\t}\n\n\tabstract message(): Promise<string> | string | undefined;\n\n\tabstract nextMenu(): Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Terminate the current session\n\t *\n\t */\n\tend(): Promise<boolean> | boolean {\n\t\treturn false;\n\t}\n\n\tget sessionId(): string {\n\t\t// FIXME: this is not reliable, add to request object\n\t\treturn this.request.query?.sessionid!;\n\t}\n\n\tisStart(): Promise<boolean> | boolean {\n\t\treturn false;\n\t}\n\n\tget session(): Session {\n\t\treturn {\n\t\t\tget: async <T>(key: string, defaultValue?: any) => {\n\t\t\t\treturn await Config.getInstance().session?.get<T>(\n\t\t\t\t\tthis.sessionId!,\n\t\t\t\t\tkey,\n\t\t\t\t\tdefaultValue,\n\t\t\t\t);\n\t\t\t},\n\t\t\tgetAll: <T>() => {\n\t\t\t\treturn Config.getInstance().session?.getAll<T>(this.sessionId!);\n\t\t\t},\n\t\t\tset: (key: string, val: any) =>\n\t\t\t\tConfig.getInstance().session?.set(this.sessionId!, key, val),\n      remove: (key: string) =>\n        Config.getInstance().session?.remove(this.sessionId!, key),\n\t\t};\n\t}\n\n\t/**\n\t * Returns the current msisdn/phone number of the session.\n\t */\n\tget msisdn(): string {\n\t\treturn this.request.state.msisdn;\n\t}\n\n\tasync back(): Promise<string | undefined> {\n\t\treturn undefined;\n\t}\n\n\tabstract actions(): Promise<MenuAction[]> | MenuAction[];\n\n\tasync inputs(): Promise<FormInput[]> {\n\t\treturn [];\n\t}\n\n\tisForm(): boolean {\n\t\treturn false;\n\t}\n}\n","import type {\n  FormInput,\n  Null,\n  Request,\n  Response,\n  Type,\n  Validation,\n  ValidationResponse,\n} from \"@src/types\";\nimport type { MenuAction } from \"./action.menu\";\nimport type { BaseMenu } from \"./base.menu\";\n\nexport class DynamicMenu {\n  // TODO: Look for better class name\n\n  #id: string;\n  #formInputs: FormInput[] = [];\n  #isForm = false;\n  #end = false;\n  #paginate = false;\n  #message?:\n    | string\n    | Null\n    | ((req: Request, res: Response) => Promise<string> | string) = undefined;\n  #nextMenu?:\n    | string\n    | ((req: Request, res: Response) => Promise<string> | string) = undefined;\n\n  private _validation?: Validation;\n  private _actions: MenuAction[];\n  private _isStart = false;\n  private _currentOption?: MenuAction | undefined = undefined; // make private??\n  private _action?: Type<BaseMenu> | undefined = undefined;\n\n  constructor(id: string, action?: Type<BaseMenu>) {\n    this.#id = id;\n    this._action = action;\n  }\n\n  isForm(): DynamicMenu {\n    this.#isForm = true;\n    return this;\n  }\n\n  defaultNextMenu(\n    menu: string | ((req: Request, res: Response) => Promise<string> | string),\n  ): DynamicMenu {\n    this.#nextMenu = menu;\n    return this;\n  }\n\n  actions(items: MenuAction[]): DynamicMenu {\n    if (this._action !== undefined) {\n      throw new Error(\n        \"Cannot set options for a menu with an action. Menu #${this._id} has an action defined\",\n      );\n    }\n\n    this._actions = items;\n\n    return this;\n  }\n\n  inputs(items: FormInput[]): DynamicMenu {\n    this.#formInputs ??= [];\n    this.#formInputs = [...this.#formInputs, ...items];\n\n    if (this.#formInputs.length === 0) {\n      throw new Error(`Form menu #${this.id} must have at least one input!`);\n    }\n    return this;\n  }\n\n  start(): DynamicMenu {\n    // TODO: verify that only one start menu is defined. Move to Route class?\n    this._isStart = true;\n\n    return this;\n  }\n\n  validation(val: Validation) {\n    this._validation = val;\n    return this;\n  }\n\n  message(msg: string | Null | ((req: Request, res: Response) => Promise<string> | string)) {\n    this.#message = msg;\n    return this;\n  }\n\n  paginate() {\n    this.#paginate = true;\n    return this;\n  }\n\n  /**\n   * Terminate the current session\n   */\n  end(): void {\n    this.#end = true;\n  }\n\n  // TODO: rename to getactiona\n  getActions(): MenuAction[] {\n    return this._actions || [];\n  }\n\n  getInputs(): FormInput[] {\n    return this.#formInputs || [];\n  }\n\n  async getMessage(req: Request, res: Response): Promise<string | Null> {\n    if (typeof this.#message === \"function\") {\n      return this.#message(req, res);\n    }\n    return this.#message;\n  }\n\n  async getDefaultNextMenu(\n    req: Request,\n    res: Response,\n  ): Promise<string | undefined> {\n    if (typeof this.#nextMenu === \"function\") {\n      return this.#nextMenu(req, res);\n    }\n    return this.#nextMenu;\n  }\n\n  async validateInput(\n    req: Request,\n    res: Response,\n  ): Promise<ValidationResponse> {\n    if (this._validation == null) {\n      return true;\n    }\n\n    if (typeof this._validation === \"function\") {\n      return this._validation(req, res);\n    }\n\n    try {\n      return this._validation.test(req.state.userData);\n    } catch { }\n\n    return false;\n  }\n\n  /**\n   * Whether the current menu is a form menu or not.\n   * Not to be confused with `isForm` which is used to set a menu as a form menu.\n   *\n   * **NOTE**: This is for internal use only!\n   */\n  get isFormMenu(): boolean {\n    return this.#isForm;\n  }\n\n  get action() {\n    return this._action;\n  }\n\n  get id(): string {\n    return this.#id;\n  }\n\n  get isEnd(): boolean {\n    return this.#end;\n  }\n\n  get isStart(): boolean {\n    return this._isStart || false;\n  }\n\n  set currentOption(value: MenuAction | undefined) {\n    this._currentOption = value;\n  }\n\n  get currentOption(): MenuAction | undefined {\n    return this._currentOption;\n  }\n\n  get isPaginated(): boolean {\n    return this.#paginate;\n  }\n}\n","export { MenuAction } from \"./action.menu\";\nexport { BaseMenu } from \"./base.menu\";\nexport { DynamicMenu } from \"./dynamic_menu.menu\";\nexport { ValidationResponse } from \"@src/types\";\n\nimport { Request, Response } from \"@src/types/request\";\nimport { Type } from \"@src/types\";\nimport { BaseMenu } from \"@src/menus/base.menu\";\nimport { DynamicMenu } from \"@src/menus\";\nimport { MENU_CACHE } from \"@src/models\";\nimport { menuType } from \"..\";\n\nexport class Menus {\n  private static instance: Menus;\n\n  // private items: { [menuId: string]: Type<BaseMenu> | DynamicMenu } = {};\n\n  private constructor() { }\n\n  public static getInstance(): Menus {\n    if (!Menus.instance) {\n      Menus.instance = new Menus();\n    }\n\n    return Menus.instance;\n  }\n\n  add(cls: Type<BaseMenu>, name: string): void {\n    MENU_CACHE[name] = { menu: cls, paginated: false };\n\t}\n\n  menu(id: string): DynamicMenu {\n    const _menu = new DynamicMenu(id);\n    MENU_CACHE[id] = { menu: _menu, paginated: false };\n\n    return _menu;\n  }\n\n  get menus() {\n    return MENU_CACHE;\n  }\n\n  async getStartMenu(\n    req: Request,\n    res: Response,\n  ): Promise<{ id: string; obj: DynamicMenu | Type<BaseMenu> }> {\n    let startId: string | null = null;\n\n    for (const id in MENU_CACHE) {\n      let isStart = false;\n      const menu = MENU_CACHE[id]?.menu;\n\n      if (menuType(menu) === \"class\") {\n        if (menu instanceof BaseMenu) {\n          isStart = await menu.isStart();\n        } else {\n          // @ts-ignore\n          isStart = await new menu(req, res).isStart();\n        }\n      } else {\n        isStart = (menu as DynamicMenu).isStart;\n      }\n\n      if (isStart) {\n        startId = id;\n        break;\n      }\n    }\n\n    // console.log(start, MENU_CACHE)\n    if (startId == null) {\n      throw new Error(\"No start menu defined. Please define a start menu\");\n    }\n\n    return { id: startId, obj: MENU_CACHE[startId]?.menu };\n  }\n\n  getMenu(id: string): DynamicMenu | Type<BaseMenu> {\n    const menu = MENU_CACHE[id]?.menu;\n\n    if (menu == undefined) {\n      throw new Error(`Menu #${id} not found`);\n    }\n\n    return menu;\n  }\n}\n\nexport type Menu = Type<BaseMenu> | DynamicMenu;\n\nexport const MenuRouter = Menus.getInstance();\n","import type { BaseMenu, DynamicMenu, Menu, MenuAction } from \"../menus\";\n\nexport function menuType(val: Menu): \"class\" | \"dynamic\" {\n\t// TODO: document why this special case is needed\n\tif (/^DynamicMenu$/i.test(val.constructor.name)) {\n\t\treturn \"dynamic\";\n\t}\n\treturn \"class\";\n}\n\nexport async function getMenuActions(menu: Menu): Promise<MenuAction[]> {\n\tif (menuType(menu) === \"class\") {\n\t\treturn (await (menu as unknown as BaseMenu).actions()) || [];\n\t}\n\treturn await (menu as DynamicMenu).getActions();\n}\n","import type {\n\tBaseMenu,\n\tDynamicMenu,\n\tMenu,\n\tMenuAction,\n\tValidationResponse,\n} from \"@src/menus\";\nimport type { State } from \"@src/models\";\nimport type { FormInput, Null } from \"@src/types\";\nimport type { Request, Response } from \"@src/types/request\";\nimport { SupportedGateway } from \"./constants\";\nimport { menuType } from \"./menu.helper\";\n\nexport async function validateInput(opts: {\n\tstate: State;\n\tmenu?: Menu;\n\tformInput?: FormInput;\n\trequest: Request;\n\tresponse: Response;\n}): Promise<{ error: string | undefined; valid: boolean }> {\n\tconst { state, menu, formInput: input, request, response } = opts;\n\n\tif (menu == null && input == null) {\n\t\tthrow new Error(\"Either menu or input must be defined\");\n\t}\n\n\tlet resp: { error: string | undefined; valid: boolean } = {\n\t\t\tvalid: true,\n\t\t\terror: undefined,\n\t\t};\n\tlet status: ValidationResponse = true;\n\n\tif (menu != null) {\n\t\tif (menuType(menu) == \"class\") {\n\t\t\tstatus = await (menu as unknown as BaseMenu).validate(state?.userData);\n\t\t} else {\n\t\t\tstatus = await (menu as DynamicMenu).validateInput(request, response);\n\t\t}\n\t}\n\n\tif (input != null) {\n\t\tif (input.validate == null) {\n\t\t\tstatus = true;\n\t\t} else if (typeof input.validate == \"function\") {\n\t\t\tstatus = await input.validate(request, response);\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tstatus = (input.validate as RegExp).test(state?.userData);\n\t\t\t} catch (error) {}\n\t\t}\n\t}\n\n\tif (typeof status == \"string\") {\n\t\tresp = { valid: false, error: status };\n\t} else if (typeof status == \"boolean\" && status == false) {\n\t\tresp = { valid: false, error: undefined };\n\t}\n\n\treturn resp;\n}\n\nexport async function buildUserResponse(opts: {\n\tmenu: Menu | undefined;\n\tstate: State;\n\terrorMessage: string | undefined;\n\trequest: Request;\n\tresponse: Response;\n\tactions?: MenuAction[];\n}) {\n\tconst { menu, state, errorMessage, response, request } = opts;\n\n\tif (errorMessage != null) {\n\t\treturn errorMessage;\n\t}\n\n\t// No message to display, end session\n\tif (menu == null && state.isEnd) {\n\t\treturn \"\";\n\t}\n\n\t// TODO: build paginated response\n\n\tlet message: string | Null = undefined;\n\tif (menuType(menu!) === \"class\") {\n\t\tmessage = await (menu as unknown as BaseMenu).message();\n\t} else {\n\t\tmessage = await (menu as DynamicMenu).getMessage(request, response);\n\t}\n\n\t// If message is null, set it to empty string\n\tif (message == null) {\n\t\tmessage = \"\";\n\t}\n\n\t// Add actions to the message\n\tlet actions: MenuAction[] | undefined = opts.actions;\n\n\tif (actions == null) {\n\t\tif (menuType(menu!) === \"class\") {\n\t\t\tactions = (await (menu as unknown as BaseMenu).actions()) || [];\n\t\t} else {\n\t\t\tactions = await (menu as DynamicMenu).getActions();\n\t\t}\n\t}\n\n\tfor await (const action of actions) {\n\t\tif (action.display == null) continue;\n\n\t\tif (typeof action.display === \"function\") {\n\t\t\tmessage += `\\n${await action.display(request, response)}`;\n\t\t} else {\n\t\t\tmessage += `\\n${action.display}`;\n\t\t}\n\t}\n\n\treturn message;\n}\n\nexport { SupportedGateway };\nexport { getMenuActions, menuType } from \"./menu.helper\";\n","import { FormInput, NextMenu, Type, ValidationResponse } from \"@src/types\";\nimport { Request, Response } from \"@src/types/request\";\nimport { State } from \"@src/models\";\nimport { MenuRouter, DynamicMenu, Menu, Menus } from \"@src/menus\";\nimport { Config, ConfigOptions } from \"@src/config\";\nimport { BaseMenu, MenuAction } from \"@src/menus\";\nimport { menuType, validateInput } from \"@src/helpers\";\n\n// TODO: change to project name\nexport class FormMenuHandler {\n\tconstructor(\n\t\tprivate readonly request: Request,\n\t\tprivate readonly response: Response,\n\t\tprivate readonly menu: Menu,\n\t) {}\n\n\t#currentInput: FormInput | undefined = undefined;\n\t#nextInput: FormInput | undefined = undefined;\n\t#formInputs: FormInput[] = [];\n\t#errorMessage: string | undefined = undefined;\n\n\tget submittedInputs(): string[] {\n\t\treturn Object.keys(this.state.form?.submitted ?? {}) || [];\n\t}\n\n\tget state(): State {\n\t\treturn this.request.state;\n\t}\n\n\tprivate get session() {\n\t\treturn Config.getInstance().session!;\n\t}\n\n\tprivate async endSession() {\n\t\tthis.state.end();\n\t\tawait this.session.clear(this.state.sessionId);\n\t}\n\n\t// TODO: extract menu type to a separate type. is `any` for testing now\n\tasync handle(): Promise<NextMenu | undefined> {\n\t\t// Initialize state form object\n\t\tthis.request.state.form ??= {\n\t\t\tid: this.state.menu?.nextMenu!,\n\t\t\tnextInput: undefined,\n\t\t\tsubmitted: {},\n\t\t};\n\n\t\tif (menuType(this.menu) == \"class\") {\n\t\t\tthis.#formInputs = await (this.menu as unknown as BaseMenu).inputs();\n\t\t} else {\n\t\t\tthis.#formInputs = (this.menu as DynamicMenu).getInputs();\n\t\t}\n\n\t\t// First time the form menu is called, we pick the first input and display it\n\t\tif (\n\t\t\tthis.submittedInputs.length == 0 &&\n\t\t\tthis.state.form?.nextInput == null\n\t\t) {\n\t\t\tthis.#currentInput = this.#formInputs[0];\n\t\t\t// this.state.form!.currentInput = this.#currentInput.name;\n\t\t\tthis.state.form!.nextInput = this.#currentInput.name;\n\t\t\tthis.response.data = await this.buildResponse(this.#currentInput);\n\t\t\treturn this.#errorMessage != null\n\t\t\t\t? undefined\n\t\t\t\t: this.#currentInput?.next_menu;\n\t\t}\n\n\t\t// If the user has already entered an input, pick the next input\n\t\t// or else fallback to the current input\n\t\tif (this.state.form?.nextInput != null) {\n\t\t\tthis.#currentInput = this.#formInputs.find(\n\t\t\t\t(item) => item.name == this.state.form?.nextInput,\n\t\t\t);\n\t\t}\n\n\t\tawait this.handleInput();\n\n\t\t// TODO: check if next input is defined, if not, we terminate the session or navigate to the next menu\n\t\tthis.response.data = await this.buildResponse(this.#nextInput!);\n\n\t\treturn this.#errorMessage != null\n\t\t\t? undefined\n\t\t\t: this.#currentInput?.next_menu;\n\t}\n\n\tprivate async handleInput() {\n\t\t// TODO: check if state has input id, if not pick the first input\n\n\t\tif (this.#currentInput == null) {\n\t\t\tthrow new Error(\n\t\t\t\t`Input #${this.state.form?.id} is not defined in form/menu #${this.state.menu}`,\n\t\t\t);\n\t\t}\n\n\t\t// Validate input\n\t\tif (this.#currentInput.validate != null) {\n\t\t\tlet result = await validateInput({\n\t\t\t\tstate: this.state,\n\t\t\t\tformInput: this.#currentInput,\n\t\t\t\trequest: this.request,\n\t\t\t\tresponse: this.response,\n\t\t\t});\n\n\t\t\t// If input is invalid, stop processing and return the error message\n\t\t\tif (!result.valid) {\n\t\t\t\tthis.#errorMessage =\n\t\t\t\t\tresult.error || (await this.getDisplayText(this.#currentInput));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Validation passed, Save the input to the session\n\t\t\tconst temp =\n\t\t\t\t(await this.session.get<Record<string, string>>(\n\t\t\t\t\tthis.state.sessionId,\n\t\t\t\t\tthis.state.form!.id!,\n\t\t\t\t)) ?? {};\n\n\t\t\ttemp[this.#currentInput.name] = this.state.userData;\n\t\t\tawait this.session.set(this.state.sessionId, this.state!.form?.id!, temp);\n\t\t}\n\n\t\t// Call handler to allow the user to process the input\n\t\tif (this.#currentInput.handler != null) {\n\t\t\tawait this.#currentInput.handler(this.request);\n\t\t}\n\n\t\t// Track input as submitted\n\t\tthis.state.form!.submitted[this.#currentInput.name] = true;\n\n\t\t// Terminate the session if necessary\n\t\tif (this.#currentInput.end != null) {\n\t\t\tlet end =\n\t\t\t\ttypeof this.#currentInput.end == \"function\"\n\t\t\t\t\t? await this.#currentInput.end(this.request)\n\t\t\t\t\t: this.#currentInput.end;\n\n\t\t\tif (end && this.#currentInput.next_menu == null) {\n\t\t\t\treturn await this.endSession();\n\t\t\t}\n\t\t}\n\n\t\tawait this.resolveNextInput();\n\t}\n\n\tprivate async resolveNextInput() {\n\t\t// if (\n\t\t// \tthis.#currentInput?.next_input != null &&\n\t\t// \tthis.#currentInput?.next_menu != null\n\t\t// ) {\n\t\t// \tthrow new Error(\n\t\t// \t\t`Input #${this.#currentInput} has both next_input and next_menu defined. Please define only one`,\n\t\t// \t);\n\t\t// }\n\n\t\t// No next input & next menu, terminate the session\n\t\tif (\n\t\t\tthis.#currentInput?.next_input == null &&\n\t\t\tthis.#currentInput?.next_menu == null\n\t\t) {\n\t\t\treturn await this.endSession();\n\t\t}\n\n\t\t// No next input, but next menu is defined, navigate to the next menu (logic in handler())\n\t\tif (\n\t\t\tthis.#currentInput?.next_input == null &&\n\t\t\tthis.#currentInput?.next_menu != null\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof this.#currentInput.next_input == \"string\") {\n\t\t\tthis.state.form!.nextInput = this.#currentInput.next_input;\n\t\t} else if (typeof this.#currentInput.next_input == \"function\") {\n\t\t\tthis.state.form!.nextInput = await this.#currentInput.next_input(\n\t\t\t\tthis.request,\n\t\t\t);\n\t\t}\n\n\t\t// Lookup the next input\n\t\tthis.#nextInput = await this.#formInputs.find(\n\t\t\t(item) => item.name == this.state.form?.nextInput,\n\t\t);\n\t\tif (this.#nextInput == null) {\n\t\t\tawait this.endSession();\n\n\t\t\tthrow new Error(\n\t\t\t\t`Input #${this.state.form?.nextInput} is not defined in form/menu #${this.state.menu}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async buildResponse(input: FormInput) {\n\t\tlet message = \"\";\n\n\t\t// If error message is not empty, we simply show that\n\t\tif (this.#errorMessage != null) {\n\t\t\treturn this.#errorMessage;\n\t\t}\n\n\t\treturn await this.getDisplayText(input);\n\t}\n\n\tprivate async getDisplayText(input: FormInput) {\n\t\tif (typeof input?.display == \"function\") {\n\t\t\treturn await input.display(this.request);\n\t\t}\n\t\treturn input?.display;\n\t}\n}\n","import {\n\tFormInput,\n\tNextMenu,\n\tPaginationOption,\n\tType,\n\tValidationResponse,\n} from \"@src/types\";\nimport { Request, Response } from \"@src/types/request\";\nimport { State } from \"@src/models\";\nimport { MenuRouter, DynamicMenu, Menu, Menus } from \"@src/menus\";\nimport { Config, ConfigOptions } from \"@src/config\";\nimport { BaseMenu, MenuAction } from \"@src/menus\";\nimport {\n\tbuildUserResponse,\n\tgetMenuActions,\n\tmenuType,\n\tvalidateInput,\n} from \"@src/helpers\";\nimport { PaginationItem } from \"@src/types/pagination.type\";\nimport { MAXIMUM_CHARACTERS } from \"@src/helpers/constants\";\n\n// TODO: change to project name\nexport class PaginationHandler {\n\tconstructor(\n\t\tprivate readonly request: Request,\n\t\tprivate readonly response: Response,\n\t\tprivate readonly menu: Menu,\n\t\tprivate readonly menuId: string,\n\t) {}\n\n\tget state(): State {\n\t\treturn this.request.state;\n\t}\n\n\tasync handle() {\n\t\tconst paginationState =\n\t\t\tthis.state.pagination == null ? null : this.state.pagination[this.menuId];\n\n\t\t// Pages has already been generated and cached, nothing to do here\n\t\tif (paginationState != null) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet actions: MenuAction[] = await getMenuActions(this.menu);\n\n\t\tconst pages = await this.generatePaginationItems({\n\t\t\tactions: actions,\n\t\t\t// item: undefined,\n\t\t\tmenu: this.menu,\n\t\t\tunpaginatedActions: [],\n\t\t\tpage: 1,\n\t\t\tpages: [],\n\t\t});\n\n\t\tif (pages != null) {\n\t\t\tthis.state.pagination ??= {};\n\t\t\tthis.state.pagination[this.menuId] = {\n\t\t\t\tcurrentPage: undefined,\n\t\t\t\tpages: pages,\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate async generatePaginationItems(opts: {\n\t\tmenu: Menu;\n\t\t// item: PaginationItem | undefined,\n\t\tactions: MenuAction[];\n\t\tunpaginatedActions: MenuAction[];\n\t\tpage: number;\n\t\tpages: PaginationItem[];\n\t}): Promise<PaginationItem[] | undefined> {\n\t\t// Pagination item is null when starting\n\t\tconst isStart = opts.page == 1;\n\n\t\tconst { actions } = opts;\n\n\t\tlet message = await buildUserResponse({\n\t\t\tstate: this.state,\n\t\t\terrorMessage: undefined,\n\t\t\trequest: this.request,\n\t\t\tresponse: this.response,\n\t\t\tmenu: opts.menu,\n\t\t\tactions: actions,\n\t\t});\n\n\t\t// No more actions, return pagination tree\n\t\tif (actions.length === 0) {\n\t\t\treturn opts.pages;\n\t\t}\n\n\t\t// If pagination is enabled but its not necessary, return response as usual\n\t\t// if (message.length <= MAXIMUM_CHARACTERS && isStart) {\n\t\t//   return opts.item;\n\t\t// }\n\n\t\t// Add navigation options to message and compute characters length\n\t\tmessage += this.buildNavigationAction(isStart);\n\t\tconst charactersCount = message.length;\n\n\t\t// If the max characters is exceeded, remove last action item and\n\t\t// re-generate the message\n\t\tif (charactersCount > MAXIMUM_CHARACTERS) {\n\t\t\tconst temp = [...opts.actions];\n\t\t\topts.unpaginatedActions.push(temp.pop()!);\n\n\t\t\treturn this.generatePaginationItems({\n\t\t\t\tmenu: opts.menu,\n\t\t\t\tactions: temp,\n\t\t\t\tunpaginatedActions: opts.unpaginatedActions,\n\t\t\t\t// item: opts.item,\n\t\t\t\tpage: opts.page,\n\t\t\t\tpages: opts.pages,\n\t\t\t});\n\t\t}\n\n\t\t// Add nav actions to pagination items, and link object to prev/next page\n\t\tconst conf = PaginationHandler.paginationConfig;\n\t\tif (!isStart) {\n\t\t\tactions.push({\n\t\t\t\tchoice: conf.previousPage.choice,\n\t\t\t\tdisplay: conf.previousPage.display,\n\t\t\t\tnext_menu: this.menuId,\n\t\t\t});\n\t\t}\n\t\tif (opts.unpaginatedActions.length > 0) {\n\t\t\tactions.push({\n\t\t\t\tchoice: conf.nextPage.choice,\n\t\t\t\tdisplay: conf.nextPage.display,\n\t\t\t\tnext_menu: this.menuId,\n\t\t\t});\n\t\t}\n\n\t\tlet paginationItem: PaginationItem = {\n\t\t\tpage: opts.page,\n\t\t\tnextPage: undefined,\n\t\t\tpreviousPage: !isStart ? opts.page - 1 : undefined,\n\t\t\tdata: [...actions],\n\t\t};\n\n\t\tif (opts.unpaginatedActions.length > 0) {\n\t\t\topts.page += 1;\n\t\t\tpaginationItem.nextPage = opts.page;\n\t\t}\n\t\topts.pages.push(paginationItem);\n\n\t\t// Reset actions to unpaginated items\n\t\tconst temp = [...opts.unpaginatedActions];\n\t\topts.unpaginatedActions = [];\n\n\t\treturn this.generatePaginationItems({\n\t\t\tmenu: opts.menu,\n\t\t\tactions: temp,\n\t\t\tunpaginatedActions: opts.unpaginatedActions,\n\t\t\tpage: opts.page,\n\t\t\tpages: opts.pages,\n\t\t});\n\t}\n\n\tprivate buildNavigationAction(isStart: boolean = false) {\n\t\tconst conf = PaginationHandler.paginationConfig;\n\n\t\tif (isStart) {\n\t\t\treturn \"\\n\" + conf.nextPage.display;\n\t\t}\n\n\t\treturn `\\n${conf.previousPage.display}\\n${conf.nextPage.display}`;\n\t}\n\n\tstatic get paginationConfig() {\n\t\treturn Config.getInstance().options.pagination ?? new PaginationOption();\n\t}\n\n\tstatic isNavActionSelected(input?: string): boolean {\n\t\tif (input == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst options = [\n\t\t\tthis.paginationConfig.previousPage?.choice?.trim(),\n\t\t\tthis.paginationConfig.nextPage?.choice?.trim(),\n\t\t];\n\n\t\treturn options.includes(input?.trim());\n\t}\n\n\tstatic isPageActionSelected(state: State) {\n\t\treturn !this.isNavActionSelected(state.userData?.trim());\n\t}\n\n\tprivate static shouldGoToPreviousPage(input?: string): boolean {\n\t\tif (input == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn input.trim() == this.paginationConfig.previousPage?.choice?.trim();\n\t}\n\n\tstatic navigateToPage(state: State) {\n\t\tconst input = state.userData?.trim();\n\t\tlet { pages, currentPage } = state.pagination[state.menu?.nextMenu!];\n\n\t\tif (currentPage == null) {\n\t\t\t// We're now rendering first page\n\t\t\tcurrentPage = pages.find((i) => i.page == 1);\n\t\t} else if (PaginationHandler.shouldGoToPreviousPage(input)) {\n\t\t\tcurrentPage = pages.find((i) => i.page == currentPage?.previousPage);\n\n\t\t\t// No more previous pages, might be on the first page but user input prev page;\n\t\t\t// navigate to page 1\n\t\t\tif (currentPage == null) {\n\t\t\t\tcurrentPage = pages.find((i) => i.page == 1);\n\t\t\t}\n\t\t} else {\n\t\t\tcurrentPage = pages.find((i) => i.page == currentPage?.nextPage);\n\t\t}\n\n\t\tstate.pagination[state.menu?.nextMenu!].currentPage = currentPage;\n\t\treturn currentPage?.data || [];\n\t}\n}\n","import { NextMenu, Session, Type, ValidationResponse } from \"@src/types\";\nimport { Request, Response } from \"@src/types/request\";\nimport { MENU_CACHE, State, StateMode } from \"@src/models\";\nimport { MenuRouter, DynamicMenu, Menu, Menus } from \"@src/menus\";\nimport { Config, ConfigOptions } from \"@src/config\";\nimport { BaseMenu, MenuAction } from \"@src/menus\";\nimport {\n  buildUserResponse,\n  getMenuActions,\n  menuType,\n  validateInput,\n} from \"@src/helpers\";\nimport { FormMenuHandler } from \"./form_handler\";\nimport { PaginationHandler } from \"./pagination_handler\";\n\nexport class RequestHandler {\n  constructor(\n    private request: Request,\n    private response: Response,\n    private router: Menus,\n  ) { }\n\n  private async setCurrentMenu(id: string, state: State) {\n    state.menu ??= {\n      nextMenu: undefined,\n      visited: {},\n    };\n    state.menu.nextMenu = id;\n    await this.session.setState(state.sessionId, state);\n  }\n\n  private get config(): Config {\n    return Config.getInstance();\n  }\n\n  private get session() {\n    return this.config.session!;\n  }\n\n  private async isFormMenu(menu: Menu) {\n    if (menuType(menu) == \"class\") {\n      return (await (menu as unknown as BaseMenu).isForm()) || false;\n    } else {\n      return (await (menu as DynamicMenu).isFormMenu) || false;\n    }\n  }\n\n  private async isPaginatedMenu(menu: Menu) {\n    if (menuType(menu) == \"class\") {\n      return (await (menu as unknown as BaseMenu).paginate()) || false;\n    } else {\n      return (await (menu as DynamicMenu).isPaginated) || false;\n    }\n  }\n\n  private async formHandler(currentMenu: Menu, state: State) {\n    const formHandler = new FormMenuHandler(\n      this.request,\n      this.response,\n      currentMenu,\n    );\n    const nextMenu = await formHandler.handle();\n\n    if (!state.isEnd) {\n      await this.session.setState(state.sessionId, state);\n    }\n\n    if (nextMenu != null) {\n      currentMenu = (await this.navigateToNextMenu(state, nextMenu)).menu!;\n      this.response.data = await this.buildResponse(currentMenu, state);\n\n      await this.resolveGateway(\"response\");\n      return this.response;\n    }\n\n    await this.resolveGateway(\"response\");\n    return this.response;\n  }\n\n  async processRequest() {\n    this.router = MenuRouter;\n\n    let currentMenu: Menu | undefined = undefined;\n\n    // Resolve middlewares\n    const state = (await this.resolveGateway(\"request\"))!;\n\n    // Initialize state menu object\n    state.menu ??= {\n      nextMenu: undefined,\n      visited: {},\n    };\n    this.request.state = state;\n    this.request.session = this.getSession();\n\n    // Each menu is visited at least twice,\n    // (1) to get the menu options and display message to the, and\n    // (2) then get the user input and validate it\n    // (3) repeat (1) and (2) until the user input is valid or session is terminated\n    //\n\n    // If next menu is null, it means the menu is yet to be visited. Get options and display message\n    // else, validate user input and add the menu to the visited list\n    if (state.menu?.nextMenu == null) {\n      currentMenu = await this.lookupMenu(state);\n      this.response.data = await this.buildResponse(currentMenu, state);\n\n      await this.resolveGateway(\"response\");\n      return this.response;\n    } else {\n      currentMenu = await this.lookupMenu(state);\n    }\n\n    // If current menu is a form, use form handler\n    if (await this.isFormMenu(currentMenu)) {\n      return this.formHandler(currentMenu, state);\n    }\n\n    // Resolve menu options\n    if (await this.isPaginatedMenu(currentMenu)) {\n      if (PaginationHandler.isPageActionSelected(state)) {\n        await this.lookupMenuOptions(state, currentMenu);\n      } else {\n        await new PaginationHandler(\n          this.request,\n          this.response,\n          currentMenu,\n          state.menu?.nextMenu!,\n        ).handle();\n      }\n    } else {\n      await this.lookupMenuOptions(state, currentMenu);\n    }\n\n    const error = await this.validateUserData(state, currentMenu);\n    if (error != null) {\n      this.response.data = error;\n      await this.resolveGateway(\"response\");\n\n      return this.response;\n    }\n\n    let nextMenu: Menu | undefined = await this.resolveNextMenu(\n      state,\n      currentMenu,\n    );\n\n    if (nextMenu != null) {\n      // Next menu is paginated, build pagination items and render them\n      if (await this.isPaginatedMenu(nextMenu)) {\n        await new PaginationHandler(\n          this.request,\n          this.response,\n          nextMenu,\n          state.menu?.nextMenu!,\n        ).handle();\n      } else {\n        // Next menu is a form, use form handler to build response\n        if (await this.isFormMenu(nextMenu)) {\n          return this.formHandler(nextMenu, state);\n        }\n\n        // If menu terminates the session, end the session\n        let isEnd = false;\n        if (menuType(nextMenu) == \"class\") {\n          isEnd = await (nextMenu as unknown as BaseMenu).end();\n        } else {\n          isEnd = (nextMenu as DynamicMenu).isEnd;\n        }\n\n        if (isEnd) {\n          state.end();\n        }\n      }\n    }\n\n    this.response.data = await this.buildResponse(nextMenu, state);\n\n    await this.resolveGateway(\"response\");\n    return this.response;\n  }\n\n  private async validateUserData(\n    state: State,\n    menu: Menu,\n    error?: string,\n  ): Promise<string | undefined> {\n    // If its a paginated menu, and the user data matches nav choices, skip input validation\n    if (\n      (await this.isPaginatedMenu(menu)) &&\n      PaginationHandler.isNavActionSelected(state.userData?.trim())\n    ) {\n      return undefined;\n    }\n\n    let result = await validateInput({\n      state: state,\n      menu: menu,\n      request: this.request,\n      response: this.response,\n    });\n\n    // If input is invalid, stop processing and return the error message\n    if (!result.valid) {\n      error = result.error || (await this.buildResponse(menu, state));\n      return error;\n    }\n\n    return error;\n  }\n\n  private async buildResponse(\n    menu: Menu | undefined,\n    state: State,\n    errorMessage?: string,\n  ) {\n    // If its a paginated menu, and the user data matches nav choices, skip input validation\n    if (menu != null && (await this.isPaginatedMenu(menu))) {\n      return buildUserResponse({\n        menu,\n        actions: PaginationHandler.navigateToPage(state),\n        state,\n        errorMessage,\n        request: this.request,\n        response: this.response,\n      });\n    }\n\n    return buildUserResponse({\n      menu,\n      state,\n      errorMessage,\n      request: this.request,\n      response: this.response,\n    });\n  }\n\n  private async resolveNextMenu(state: State, currentMenu: Menu) {\n    // If paginated menu, and the user data matches nav choices, we stay on the same page\n    if (await this.isPaginatedMenu(currentMenu)) {\n      if (PaginationHandler.isNavActionSelected(state.userData?.trim())) {\n        return currentMenu;\n      }\n    }\n\n    const action = state.action;\n\n    // Execute the action handler\n    if (action?.handler != null) {\n      await action.handler(this.request);\n    }\n\n    // Resolve next menu and make it the current menu\n    const resp = await this.navigateToNextMenu(state, action?.next_menu);\n    if (resp.status) {\n      return resp.menu!;\n    }\n\n    // Action doesn't have next menu defined, fallback to default next menu\n    if (menuType(currentMenu) == \"class\") {\n      const _next = await (currentMenu as unknown as BaseMenu).nextMenu();\n\n      // No next menu, end session\n      if (_next == null) {\n        await this.endSession(state);\n        return undefined;\n      }\n\n      return (await this.navigateToNextMenu(state, _next)).menu;\n    }\n\n    const _next = await (currentMenu as DynamicMenu).getDefaultNextMenu(\n      this.request,\n      this.response,\n    );\n\n    if (_next != null) {\n      return (await this.navigateToNextMenu(state, _next)).menu;\n    }\n\n    await this.endSession(state);\n    return undefined;\n  }\n\n  /**\n   * Resolve next menu and make it the current menu.\n   */\n  private async navigateToNextMenu(state: State, next_menu?: NextMenu) {\n    let status = false,\n      menu: Menu | undefined = undefined,\n      id: string | undefined = undefined;\n\n    if (next_menu == null) return { status, menu };\n\n    if (typeof next_menu == \"string\") {\n      id = next_menu;\n      status = true;\n    } else if (typeof next_menu == \"function\") {\n      id = await next_menu(this.request, this.response);\n      status = true;\n    }\n\n    if (status) {\n      menu = this.instantiateMenu(this.router.getMenu(id!));\n      state.menu!.visited[state.menu!.nextMenu!] = true;\n      delete state.menu!.visited[id!];\n\n      this.setCurrentMenu(id!, state);\n    }\n\n    return { status, menu };\n  }\n\n  private instantiateMenu(menu: Menu): Menu {\n    if (menuType(menu) == \"class\") {\n      if (menu instanceof BaseMenu) {\n        return menu;\n      }\n\n      // @ts-ignore\n      return new menu(this.request, this.response);\n    }\n\n    return menu;\n  }\n\n  private async lookupMenuOptions(state: State, menu: Menu) {\n    const actions: MenuAction[] = await getMenuActions(menu);\n\n    // TODO: cache actions for pagination\n\n    // Loop through the actions, and find the one that matches the user input\n    const input = state.userData;\n    for (const action of actions) {\n      if (typeof action.choice == \"function\") {\n        const result = await action.choice(input, this.request, this.response);\n        if (result == input) {\n          state.action = action;\n          break;\n        }\n      }\n\n      if (\n        typeof action.choice == \"string\" ||\n        typeof action.choice == \"number\"\n      ) {\n        if (action.choice == input || action.choice == \"*\") {\n          state.action = action;\n          break;\n        }\n      }\n\n      try {\n        if ((action.choice as RegExp).test(input)) {\n          state.action = action;\n          break;\n        }\n      } catch (e) { }\n    }\n\n    this.session.setState(state.sessionId, state);\n  }\n\n  private async lookupMenu(state: State) {\n    let menu: Menu | undefined = undefined,\n      id: string | undefined = state.menu?.nextMenu;\n\n    // The user already has unterminated session, return the last visited menu\n    if (id != null) {\n      menu = this.router.getMenu(id);\n    } else if (state.isStart) {\n      // If it is a start request, lookup for the start menu\n      const value = await this.router.getStartMenu(this.request, this.response);\n      id = value.id;\n      menu = value.obj;\n    }\n\n    if (menu == null) {\n      throw new Error(`Menu for #${state.sessionId} is not defined`);\n    }\n\n    const instance = this.instantiateMenu(menu);\n    this.setCurrentMenu(id!, state);\n\n    // Check if it is a paginated menu\n    if (menuType(instance) == \"class\") {\n      MENU_CACHE[id!].paginated = await (\n        instance as unknown as BaseMenu\n      ).paginate();\n    } else {\n      MENU_CACHE[id!].paginated = (instance as DynamicMenu).isPaginated;\n    }\n\n    // Update session mode\n    state.mode = StateMode.more;\n\n    return instance;\n  }\n\n  private async resolveGateway(stage: \"request\" | \"response\") {\n    if (stage === \"request\") {\n      const item = new this.config.gateway(this.request, this.response);\n      const _state = (await item.handleRequest(this.request, this.response))!;\n      await this.session.setState(_state.sessionId, _state)!;\n\n      return _state;\n    }\n\n    if (stage === \"response\") {\n      const item = new this.config.gateway(this.request, this.response);\n      await item.handleResponse(this.request, this.response);\n    }\n\n    return;\n  }\n\n  private async endSession(state: State) {\n    state.end();\n    await this.session.clear(state.sessionId);\n  }\n\n  /**\n   * Exposes a simplified session API interface to be used in menus\n   *\n   */\n  getSession(): Session {\n    return {\n      get: async <T>(key: string, defaultValue?: any) => {\n        return await Config.getInstance().session?.get<T>(\n          this.request.state.sessionId!,\n          key,\n          defaultValue,\n        );\n      },\n      getAll: <T>() => {\n        return Config.getInstance().session?.getAll<T>(\n          this.request.state.sessionId,\n        );\n      },\n      set: (key: string, val: any) =>\n        Config.getInstance().session?.set(\n          this.request.state.sessionId,\n          key,\n          val,\n        ),\n      remove: (key) => {\n        return Config.getInstance().session?.remove(this.request.state.sessionId, key);\n      },\n    };\n  }\n}\n","import type { Ananse } from \"../index\";\nimport { randomUUID } from \"node:crypto\";\nimport { SupportedGateway } from \"@src/helpers/constants\";\n// @ts-ignore\n// import { TestContext } from \"@japa/runner/core\";\n\nexport class TestRunner {\n  #inputs: string[] = [];\n  #phone: string | undefined = undefined;\n  #debug: boolean = false;\n  #sessionId: string | undefined = undefined;\n  #rawResponse: Record<string, any> = {};\n  #server: any = undefined;\n  #config: Config = {} as Config;\n\n  constructor(_config: Config) {\n    this.#config = _config;\n  }\n\n  app(val: Ananse) {\n    this.#config.app = val;\n    return this;\n  }\n\n  gateway(val: SupportedGateway) {\n    this.#config.gateway = val;\n\n    return this;\n  }\n\n  steps(...steps: (string[] | number[])) {\n    this.#inputs = this.#inputs.concat(steps.map((step) => step.toString()));\n    return this;\n  }\n\n  input(value: string | number) {\n    this.#inputs ??= [];\n    this.#inputs.push(value.toString());\n\n    return this;\n  }\n\n  phone(val: string) {\n    this.#phone = val;\n    return this;\n  }\n\n  debug(val: boolean) {\n    this.#debug = val;\n    return this\n  }\n\n  sessionId(val: string) {\n    this.#sessionId = val;\n    return this;\n  }\n\n  async startServer() {\n    if (this.#config.app == null && this.#url == null) {\n      throw new Error(\n        \"Please provide an app to start the server, or a url to make request to\",\n      );\n    }\n\n    // If url is provided, run tests against the url instead of starting a server\n    if (this.#config.app == null && this.#url != null) {\n      return\n    }\n\n    if (this.#server != null) {\n      return this;\n    }\n\n    const url = new URL(this.#url)\n    // const test = getActiveTest();\n    // test?.cleanup(() => this.stopServer());\n\n    this.#server = this.#config.app?.listen(+url.port, url.hostname, () => {\n      if (this.#debug) {\n        console.log(`Server started at: ${this.#url}`);\n      }\n    });\n    return this.#server;\n    // } catch (error) {}\n    // server.listen(somePort)\n  }\n\n  async stopServer() {\n    // console.log(this.#server);\n    console.log(\"ending server\");\n    if (this.#server != null) {\n      await this.#server.close(() => {\n        console.log(\"Server stopped\");\n      });\n      // this.#server.close();\n      this.#server = undefined;\n\n      return;\n    }\n\n    return;\n  }\n\n  async send(url?: string) {\n    if (this.#config.app == null && this.#config.url == null) {\n      throw new Error(\n        \"Please provide an app to start the server, or a url to make request to\",\n      );\n    }\n\n    if (this.#inputs.length === 0) {\n      throw new Error(\n        \"No input provided. Please provide input using the input() or steps() method\",\n      );\n    }\n\n    const temp = [...this.#inputs];\n    for (const step of temp) {\n      try {\n        const resp = await fetch(this.reply(step, url), { headers: this.#config.headers ?? {} });\n\n        const data = await resp.text();\n\n        await this.parseResponse(data);\n      } catch (e) {\n        if (this.#debug) {\n          throw e;\n        }\n        console.log(e.message);\n      } finally {\n        this.#inputs.shift();\n      }\n    }\n\n    return {\n      text: () => {\n        if (this.#debug) {\n          console.log(this.#rawResponse);\n        }\n\n        if (this.#provider === SupportedGateway.wigal) {\n          return this.#rawResponse.userdata.replace(/\\^/g, \"\\n\")\n        }\n        throw new Error(`Text parsing is not implemented for ${this.#provider}`);\n      },\n      raw: () => {\n        return this.#rawResponse;\n      },\n    };\n  }\n\n\n  get #provider(): SupportedGateway {\n    return this.#config.gateway as SupportedGateway;\n  }\n\n  get #url(): string {\n    let val: string = this.#config.url || \"http://localhost:3000\";\n\n    if (val.startsWith(\"localhost\")) {\n      val = `http://${val}`;\n    }\n\n    return val;\n  }\n\n  private parseResponse(data: string) {\n    this.log(data);\n\n    if (this.#provider === SupportedGateway.wigal) {\n      const resp = data.split(\"|\");\n      this.#rawResponse = {\n        network: resp[0],\n        mode: resp[1] as any,\n        msisdn: resp[2],\n        sessionid: resp[3],\n        userdata: resp[4],\n        username: resp[5],\n        trafficid: resp[6],\n        other: resp[7],\n        isEndSession: resp[1] === \"end\",\n      };\n    } else {\n      throw new Error(\n        `Response parsing is not implemented for ${this.#provider}`,\n      );\n    }\n  }\n\n  private reply(input?: string, severUrl?: string) {\n    let data = { ...this.#rawResponse };\n\n    let url = \"\";\n    if (this.#provider === SupportedGateway.wigal) {\n      data ??= {};\n      data.mode ??= \"start\"\n\n      const sessionId = this.#sessionId || data.sessionid || randomUUID();\n      const mode = data.mode === \"end\" ? \"start\" : data.mode;\n\n      url = `${severUrl || this.#url}?network=${data.network || \"wigal_mtn_gh\"\n        }&sessionid=${sessionId}&mode=${mode}&msisdn=${this.#phone\n        }&userdata=${input}&username=${data.username || \"test_user\"\n        }&trafficid=${randomUUID()}&other=${data.other || \"\"}`;\n    } else {\n      throw new Error(`Reply is not implemented for ${this.#provider}`);\n    }\n\n    this.log(url);\n\n    return url;\n  }\n\n  private log(data: any) {\n    if (this.#debug === true) {\n      console.log(\"\");\n      console.log(data);\n      console.log(\"\");\n    }\n  }\n}\n\n/**\n * Japa plugin for testing ussd applications\n *\n */\nexport function anansePlugin(config: Config) {\n  const obj = new TestRunner(config);\n\n  // @ts-ignore\n  // biome-ignore lint/complexity/useArrowFunction: <explanation>\n  return function ({ emitter, runner, cliArgs, config }) {\n    // return function (emitter, config, runner, { Test, TestContext, Group }) {\n    emitter.on(\"test:cleanup\", async function () {\n      // await obj.startServer();\n      // TODO: stop server\n      console.log(\"setup initiated\");\n    });\n    emitter.on(\"group:cleanup\", async function () {\n      // await obj.startServer();\n      // TODO: stop server\n      console.log(\"setup sdd\");\n    });\n    // emitter.on(\"test:start\", async function () {\n    //   await obj.startServer();\n    //   // TODO: stop server\n    //   console.log(\"test started\");\n    // });\n\n    emitter.on(\"group:end\", function () {\n      obj.stopServer();\n      // TODO: stop server\n      console.log(\"test ended\");\n    });\n\n    // biome-ignore lint/complexity/useArrowFunction: <explanation>\n    // TestContext.getter(\"ussd\", function () {\n    //   return obj;\n    // });\n  };\n}\n\ninterface Config {\n  /**\n   * Instance of the Ananse application to use. This is required if the `url` is not provided.\n   */\n  app?: Ananse;\n\n  /**\n   * USSD server URL to make request to. Required if the `app` is not provided\n   * Defaults to http://localhost:3000\n   */\n  url?: string;\n\n  /**\n   * Additional headers to send with the HTTP request\n   */\n  headers?: Record<string, string>;\n\n  gateway: SupportedGateway;\n  phone?: string;\n  session?: string; //TODO: same props used in core\n}\n// @ts-ignore\n\n// declare module '@japa/runner/core' {\n//   interface TestContext {\n//     ussd: TestRunner\n//   }\n// }\n\n// declare module \"@japa/runner\" {\n//   interface TestContext {\n//     sleep(milliseconds: number): Promise<void>;\n//   }\n// }\n"],"mappings":"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","names":["_url","SupportedGateway","_formInputs","_a","temp","_next","_config","config"]}