ts-server-lib 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +8 -0
  3. package/db/TSJournal.d.ts +108 -0
  4. package/db/TSJournal.js +229 -0
  5. package/db/TSMongo.d.ts +103 -0
  6. package/db/TSMongo.js +516 -0
  7. package/db/TSRQW.d.ts +625 -0
  8. package/db/TSRQW.js +1204 -0
  9. package/db/TSRedis.d.ts +530 -0
  10. package/db/TSRedis.js +1368 -0
  11. package/db/TSRedisTB.d.ts +80 -0
  12. package/db/TSRedisTB.js +178 -0
  13. package/package.json +85 -0
  14. package/ussd/TSUssdMenu.d.ts +139 -0
  15. package/ussd/TSUssdMenu.js +368 -0
  16. package/ussd/TSUssdScreen.d.ts +58 -0
  17. package/ussd/TSUssdScreen.js +218 -0
  18. package/ussd/index.d.ts +3 -0
  19. package/ussd/index.js +19 -0
  20. package/ussd/providers/AfricasTalking.d.ts +3 -0
  21. package/ussd/providers/AfricasTalking.js +17 -0
  22. package/ussd/providers/AirtelDRC.d.ts +9 -0
  23. package/ussd/providers/AirtelDRC.js +31 -0
  24. package/ussd/providers/OrangeDRC.d.ts +5 -0
  25. package/ussd/providers/OrangeDRC.js +213 -0
  26. package/ussd/providers/VodacomDRC.d.ts +9 -0
  27. package/ussd/providers/VodacomDRC.js +48 -0
  28. package/ussd/providers/_.d.ts +55 -0
  29. package/ussd/providers/_.js +83 -0
  30. package/ussd/providers/index.d.ts +13 -0
  31. package/ussd/providers/index.js +56 -0
  32. package/utils/TSFifo.d.ts +109 -0
  33. package/utils/TSFifo.js +145 -0
  34. package/utils/TSFile.d.ts +36 -0
  35. package/utils/TSFile.js +244 -0
  36. package/utils/TSHash.d.ts +19 -0
  37. package/utils/TSHash.js +71 -0
  38. package/utils/TSRequest.d.ts +248 -0
  39. package/utils/TSRequest.js +689 -0
  40. package/utils/TSStub.d.ts +159 -0
  41. package/utils/TSStub.js +296 -0
  42. package/utils/abort.d.ts +18 -0
  43. package/utils/abort.js +97 -0
  44. package/utils/mime.json +11358 -0
  45. package/utils/object-keys.d.ts +39 -0
  46. package/utils/object-keys.js +52 -0
@@ -0,0 +1,368 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TSUssdMenu = exports.TSUssdState = void 0;
4
+ /**
5
+ * USSD Menu with state
6
+ *
7
+ * - General rules
8
+ * - Routing
9
+ * - ussd text in form 1*2*7
10
+ * - * bring back to start menu
11
+ *
12
+ * - Development Section
13
+ * - To publish local ussd receiver to remote through ssh
14
+ * # ssh -R 9093:localhost:9090 root@185.41.154.247 -N
15
+ *
16
+ */
17
+ const events_1 = require("events");
18
+ const object_keys_1 = require("../utils/object-keys");
19
+ class TSUssdState {
20
+ menu;
21
+ name;
22
+ run;
23
+ defaultNext;
24
+ val;
25
+ next = null;
26
+ constructor(menu) {
27
+ this.menu = menu;
28
+ this.name = null;
29
+ this.run = null;
30
+ this.defaultNext = null;
31
+ this.val = null;
32
+ }
33
+ }
34
+ exports.TSUssdState = TSUssdState;
35
+ class TSUssdMenu extends events_1.EventEmitter {
36
+ map;
37
+ static START_STATE = '__start__';
38
+ static config = {
39
+ debug: true,
40
+ general: { separator: ')' },
41
+ navigation: { back: '00', main: '01' },
42
+ pager: {
43
+ default: 3, maxLength: 90, mapping: {},
44
+ strategies: [{ term: ' - ', enabled: true, type: 'truncate' }]
45
+ }
46
+ };
47
+ session;
48
+ args;
49
+ states;
50
+ result;
51
+ onResult;
52
+ current;
53
+ val;
54
+ static forOwn(items, cb) {
55
+ const obj = items;
56
+ for (const i in obj) {
57
+ if (Object.hasOwn(obj, i)) {
58
+ cb(obj[i], i);
59
+ }
60
+ }
61
+ }
62
+ static mapArgs(args = {}, map = {}) {
63
+ const results = { provider: map.provider };
64
+ for (const p in args) {
65
+ if (Object.hasOwn(args, p) && Object.hasOwn(map, p)) {
66
+ results[String(map[p])] = args[p];
67
+ }
68
+ else if (Object.hasOwn(args, p)) {
69
+ results[p] = args[p];
70
+ }
71
+ }
72
+ return results;
73
+ }
74
+ static cleanRoute(route) {
75
+ if (route && route.includes('*000')) {
76
+ const reset = route.substring(route.lastIndexOf('*000') + 4);
77
+ route = '*' + (reset === '0' ? '' : reset);
78
+ }
79
+ return route;
80
+ }
81
+ constructor(args = {}, map) {
82
+ super();
83
+ this.map = map;
84
+ this.session = null;
85
+ this.args = TSUssdMenu.mapArgs(args, map);
86
+ this.states = {};
87
+ this.result = '';
88
+ this.onResult = null;
89
+ this.val = '';
90
+ }
91
+ /**
92
+ * A callback after result is set
93
+ */
94
+ callOnResult() {
95
+ if (this.onResult) {
96
+ this.onResult(this.result);
97
+ }
98
+ }
99
+ /**
100
+ * This method send message to the client and take the session opened
101
+ *
102
+ * @param text string Message to sent
103
+ */
104
+ con(text) {
105
+ this.result = 'CON ' + this.print(text);
106
+ this.callOnResult();
107
+ return this.result;
108
+ }
109
+ /**
110
+ * This method send message to the client and close the session
111
+ *
112
+ * @param text string Message to sent
113
+ */
114
+ end(text) {
115
+ this.result = 'END ' + this.print(text);
116
+ this.callOnResult();
117
+ if (this.session) {
118
+ this.session.end();
119
+ }
120
+ return this.result;
121
+ }
122
+ /**
123
+ * This method format the message and if debug calcualte the length
124
+ *
125
+ * @param text string Message to sent
126
+ */
127
+ print(text) {
128
+ return TSUssdMenu.config.debug ? '[' + (this.current ? this.current.name : 'N/A') + ':' + text.length + '] ' + text : text;
129
+ }
130
+ testLinkRule(rule, val) {
131
+ // if rule starts with *, treat as regex
132
+ if (typeof rule === 'string' && rule.startsWith('*')) {
133
+ return new RegExp(rule.substring(1)).test(val);
134
+ }
135
+ return rule === val;
136
+ }
137
+ /**
138
+ * Find state based on route — async/await, no external dependencies.
139
+ *
140
+ * next values may be: a string, a callback-based function (cb) => void,
141
+ * a function returning a string synchronously, or a function returning a Promise.
142
+ */
143
+ async resolveRoute(route) {
144
+ route = TSUssdMenu.cleanRoute(route);
145
+ const parts = !route ? [] : route.split('*');
146
+ let state = this.states[TSUssdMenu.START_STATE];
147
+ // CLY-3: emptiness, not a count. Cold path compared with the feed, so this is consistency rather than
148
+ // measured cost — but the pattern is the one being removed everywhere, and leaving exceptions is how a
149
+ // rule stops being applied.
150
+ if (!state.next || !(0, object_keys_1.hasOwnKey)(state.next)) {
151
+ return this.states[state.defaultNext];
152
+ }
153
+ if ('' in state.next) {
154
+ parts.unshift('');
155
+ }
156
+ while (parts.length > 0) {
157
+ const part = parts.shift();
158
+ let nextFound = false;
159
+ this.val = part ?? '';
160
+ for (const [link, next] of Object.entries(state.next)) {
161
+ if (!this.testLinkRule(link, part ?? ''))
162
+ continue;
163
+ let nextPath;
164
+ if (typeof next === 'string') {
165
+ nextPath = next;
166
+ }
167
+ else if (typeof next === 'function') {
168
+ // supports: callback-based fn(cb), sync-returning fn, and promise-returning fn
169
+ nextPath = await new Promise((resolve, reject) => {
170
+ const result = next(resolve);
171
+ if (typeof result === 'string')
172
+ resolve(result);
173
+ else if (result && typeof result.then === 'function')
174
+ result.then(resolve).catch(reject);
175
+ });
176
+ }
177
+ if (!nextPath)
178
+ continue;
179
+ state = this.states[nextPath];
180
+ this.current = state;
181
+ if (!state)
182
+ throw new Error(`[TSUssdMenu]: declared state does not exist: ${nextPath}`);
183
+ state.val = part ?? '';
184
+ nextFound = true;
185
+ break;
186
+ }
187
+ if (!nextFound && state.defaultNext) {
188
+ state = this.states[state.defaultNext];
189
+ this.current = state;
190
+ state.val = part;
191
+ }
192
+ }
193
+ return state;
194
+ }
195
+ runState(state) {
196
+ if (!state.run) {
197
+ return this.emit('error', new Error(`run function not defined for state: ${state.name}`));
198
+ }
199
+ state.run(state);
200
+ }
201
+ go(stateName) {
202
+ const state = this.states[stateName];
203
+ state.val = this.val;
204
+ this.current = state;
205
+ this.runState(state);
206
+ }
207
+ goStart() {
208
+ this.go(TSUssdMenu.START_STATE);
209
+ }
210
+ /**
211
+ * Configure custom session handler cross-compatible between callbacks and promises
212
+ *
213
+ * @example Memory configuration example with callback
214
+ *
215
+ * let sessions = {};
216
+ * let menu = new UssdMenu();
217
+ * menu.sessionConfig({
218
+ * start: (sessionId, callback) {
219
+ * // initialize current session if it doesn't exist this is called by menu.run()
220
+ * if(!(sessionId in sessions)) sessions[sessionId] = {};
221
+ * callback();
222
+ * },
223
+ * end: (sessionId, callback) {
224
+ * // clear current session this is called by menu.end()
225
+ * delete sessions[sessionId];
226
+ * callback();
227
+ * },
228
+ * set: (sessionId, key, value, callback) {
229
+ * // store key-value pair in current session
230
+ * sessions[sessionId][key] = value;
231
+ * callback();
232
+ * },
233
+ * get: (sessionId, key, callback) {
234
+ * // retrieve value by key in current session
235
+ * callback(null, sessions[sessionId][key]);
236
+ * },
237
+ * del: (sessionId, key, callback) {
238
+ * delete sessions[sessionId][key];
239
+ * callback();
240
+ * }
241
+ * });
242
+ *
243
+ * @param {Object} c object with implementation for get, set, start and end methods
244
+ * @param {String} a attribute of args
245
+ */
246
+ sessionConfig(c, a = 'sessionId') {
247
+ // creates a callback that calls the promise resolve and reject functions as well as the provided callback
248
+ const makeCb = (resolve, reject, cb) => (err, res) => {
249
+ if (err) {
250
+ if (cb) {
251
+ cb(err);
252
+ }
253
+ reject(err);
254
+ this.emit('error', err);
255
+ }
256
+ else {
257
+ if (cb) {
258
+ cb(null, res);
259
+ }
260
+ resolve(res);
261
+ }
262
+ };
263
+ // if p is a promise, handle its resolve and reject chains and invoke the provided callback
264
+ const resolveIfPromise = (p, resolve, reject, cb) => {
265
+ if (p && typeof p.then === 'function') {
266
+ p.then((res) => {
267
+ if (cb) {
268
+ cb(null, res);
269
+ }
270
+ resolve(res);
271
+ }).catch((err) => {
272
+ if (cb) {
273
+ cb(err);
274
+ }
275
+ reject(err);
276
+ this.emit('error', err);
277
+ });
278
+ }
279
+ };
280
+ // implement session methods based on user-defined handlers
281
+ this.session = {
282
+ start: async (cb) => new Promise((resolve, reject) => {
283
+ const res = c.start(this.args[a], makeCb(resolve, reject, cb));
284
+ resolveIfPromise(res, resolve, reject, cb);
285
+ }),
286
+ get: async (key, cb) => new Promise((resolve, reject) => {
287
+ const res = c.get(this.args[a], key, makeCb(resolve, reject, cb));
288
+ resolveIfPromise(res, resolve, reject, cb);
289
+ }),
290
+ del: async (key, cb) => new Promise((resolve, reject) => {
291
+ const res = c.del(this.args[a], key, makeCb(resolve, reject, cb));
292
+ resolveIfPromise(res, resolve, reject, cb);
293
+ }),
294
+ set: async (key, val, cb) => new Promise((resolve, reject) => {
295
+ const res = c.set(this.args[a], key, val, makeCb(resolve, reject, cb));
296
+ resolveIfPromise(res, resolve, reject, cb);
297
+ }),
298
+ end: async (cb) => new Promise((resolve, reject) => {
299
+ const res = c.end(this.args[a], makeCb(resolve, reject, cb));
300
+ resolveIfPromise(res, resolve, reject, cb);
301
+ })
302
+ };
303
+ }
304
+ /**
305
+ * Create a state on the ussd chain
306
+ *
307
+ * @param string name name of the state
308
+ * @param object options
309
+ * @param object options.next object mapping of route val to state names
310
+ * @param string options.defaultNext name of state to run when the given route from this state can't be resolved
311
+ * @param function options.run the method to run when this state is resolved
312
+ * @return TSUssdMenu the same instance of Ussd
313
+ */
314
+ state(name, options) {
315
+ const state = new TSUssdState(this);
316
+ this.states[name] = state;
317
+ state.name = name;
318
+ state.next = options.next ?? null;
319
+ state.run = options.run ?? null;
320
+ // default defaultNext to same state
321
+ state.defaultNext = options.defaultNext || name;
322
+ return this;
323
+ }
324
+ /**
325
+ * Create the start state of the ussd chain
326
+ */
327
+ startState(options) {
328
+ return this.state(TSUssdMenu.START_STATE, options);
329
+ }
330
+ /**
331
+ * Run the ussd menu
332
+ *
333
+ * @param function onResult a callback to handle on result
334
+ * @param TSUssdArgs options request args from the gateway api
335
+ */
336
+ run(onResult, map) {
337
+ if (map.provider !== this.args.provider) {
338
+ this.args = TSUssdMenu.mapArgs(this.args, map);
339
+ }
340
+ const mapAny = map;
341
+ const mapUnderscore = mapAny._;
342
+ if (mapUnderscore?.[String(this.args.phoneNumber)] && /\d/.test(String(mapUnderscore[String(this.args.phoneNumber)]))) {
343
+ this.args.phoneNumber = mapUnderscore[String(this.args.phoneNumber)];
344
+ }
345
+ else if (mapUnderscore) {
346
+ this.args._ = mapUnderscore;
347
+ }
348
+ this.onResult = onResult;
349
+ const run = async () => {
350
+ try {
351
+ const state = await this.resolveRoute(this.args.message);
352
+ this.current = state;
353
+ if (state)
354
+ this.runState(state);
355
+ }
356
+ catch (err) {
357
+ this.emit('error', err);
358
+ }
359
+ };
360
+ if (this.session) {
361
+ void this.session.start().then(run);
362
+ }
363
+ else {
364
+ void run();
365
+ }
366
+ }
367
+ }
368
+ exports.TSUssdMenu = TSUssdMenu;
@@ -0,0 +1,58 @@
1
+ import { TSUssdMenu } from './TSUssdMenu';
2
+ type PagerStrategy = {
3
+ term?: string;
4
+ type?: string;
5
+ };
6
+ export declare class TSUssdScreen {
7
+ menu: TSUssdMenu;
8
+ storage?: {
9
+ hget: (k: string, f: string) => Promise<unknown>;
10
+ hset: (k: string, f: string, d: unknown) => Promise<unknown>;
11
+ } | undefined;
12
+ strategies: {
13
+ truncate: (data: {
14
+ name?: string;
15
+ }[], c: PagerStrategy & {
16
+ term: string;
17
+ }, factor: number) => {
18
+ name?: string;
19
+ }[];
20
+ };
21
+ static nav(command: number | string): unknown;
22
+ static pagerChoice(max?: number, min?: number): string;
23
+ constructor(menu: TSUssdMenu, storage?: {
24
+ hget: (k: string, f: string) => Promise<unknown>;
25
+ hset: (k: string, f: string, d: unknown) => Promise<unknown>;
26
+ } | undefined);
27
+ render(items: unknown[]): void;
28
+ bind(m: number | string, s: unknown): void;
29
+ screen(title: number | string, items: Record<string, unknown>): any;
30
+ list(items: unknown[], title?: string, key?: string, meta?: Record<string, boolean>): string;
31
+ getAndSet(key: string, field: string): Promise<Record<string, unknown> | null>;
32
+ pager(items: {
33
+ name?: string;
34
+ length?: number;
35
+ }[] | undefined, key: string, pagerSize: number): Promise<{
36
+ meta: {
37
+ command: number;
38
+ total: number;
39
+ length: number;
40
+ offset: number;
41
+ limit: number;
42
+ };
43
+ data: unknown[];
44
+ }>;
45
+ _pager(items: unknown[] | undefined, key: string, pagerSize: number): Promise<{
46
+ meta: {
47
+ command: number;
48
+ total: number;
49
+ length: number;
50
+ offset: number;
51
+ limit: number;
52
+ };
53
+ data: unknown[];
54
+ }>;
55
+ pagerPickAndSet(dataSet: unknown[] | undefined, field: string, size?: number): Promise<any>;
56
+ checkPick(entity: unknown, state?: string): boolean;
57
+ }
58
+ export {};
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TSUssdScreen = void 0;
7
+ const i18n_1 = __importDefault(require("i18n"));
8
+ const TSUssdMenu_1 = require("./TSUssdMenu");
9
+ class TSUssdScreen {
10
+ menu;
11
+ storage;
12
+ strategies = {
13
+ truncate: (data, c, factor) => {
14
+ let parts = [];
15
+ let factors = {};
16
+ let f = 0;
17
+ TSUssdMenu_1.TSUssdMenu.forOwn(data, (i) => {
18
+ const item = i;
19
+ factors = {};
20
+ if (item.name && item.name.length > factor) {
21
+ parts = item.name.split(c.term);
22
+ if (parts.length > 1) {
23
+ f = factor / parts.length;
24
+ if (parts[0].length < f) {
25
+ factors[1] = factor - parts[0].length;
26
+ factors[0] = parts[0].length;
27
+ }
28
+ else if (parts[1].length < f) {
29
+ factors[0] = factor - parts[1].length;
30
+ factors[1] = parts[1].length;
31
+ }
32
+ parts.forEach((v, k) => parts[k] = v.substring(0, factors[k] ?? f));
33
+ item.name = parts.join(c.term);
34
+ }
35
+ }
36
+ });
37
+ return data;
38
+ }
39
+ };
40
+ static nav(command) {
41
+ return TSUssdMenu_1.TSUssdMenu.config.navigation[String(command)];
42
+ }
43
+ static pagerChoice(max = 3, min = 1) {
44
+ return '*\\^\\[' + min + '-' + max + '\\]\\{1\\}\\$';
45
+ }
46
+ constructor(menu, storage) {
47
+ this.menu = menu;
48
+ this.storage = storage;
49
+ }
50
+ render(items) {
51
+ TSUssdMenu_1.TSUssdMenu.forOwn(items, (m) => { const mr = m; if (mr)
52
+ TSUssdMenu_1.TSUssdMenu.forOwn(mr, (v, k) => { if (v !== undefined && k !== undefined)
53
+ this.bind(k, v); }); });
54
+ }
55
+ bind(m, s) {
56
+ const sObj = s;
57
+ if (m === 'start') {
58
+ this.menu.startState({
59
+ run: () => this.menu.con(sObj.next ? i18n_1.default.__(String(m) + '.' + sObj.key, TSUssdMenu_1.TSUssdMenu.config.general || {}) : this.screen(m, sObj)), next: sObj.next ? sObj.next : sObj
60
+ });
61
+ }
62
+ else {
63
+ let options = {};
64
+ if (typeof s === 'function') {
65
+ options.run = s;
66
+ }
67
+ else if (typeof s === 'object' && s !== null && Object.hasOwn(s, 'next')) {
68
+ const navMain = TSUssdScreen.nav('main');
69
+ sObj.next = { [navMain]: TSUssdMenu_1.TSUssdMenu.START_STATE, ...sObj.next };
70
+ options = sObj;
71
+ }
72
+ else {
73
+ options.run = () => this.menu.con(this.screen(m, sObj));
74
+ const navMain = TSUssdScreen.nav('main');
75
+ options.next = { [navMain]: TSUssdMenu_1.TSUssdMenu.START_STATE, ...sObj };
76
+ }
77
+ this.menu.state(String(m), options);
78
+ }
79
+ }
80
+ screen(title, items) {
81
+ let context = i18n_1.default.__(String(title), TSUssdMenu_1.TSUssdMenu.config.general || {});
82
+ const data = TSUssdMenu_1.TSUssdMenu.config.general || {};
83
+ TSUssdMenu_1.TSUssdMenu.forOwn(items, (v, k) => {
84
+ if (v != null && !isNaN(Number(k))) {
85
+ context += isNaN(Number(k)) ? '\n' : '\n' + k + data.separator + ' ';
86
+ context += i18n_1.default.__(String(title) + '.' + v, data || {});
87
+ }
88
+ });
89
+ if (this.menu.current.name !== TSUssdMenu_1.TSUssdMenu.START_STATE) {
90
+ context += '\n' + TSUssdScreen.nav('main');
91
+ context += data.separator + ' ' + i18n_1.default.__('main', data);
92
+ }
93
+ return context;
94
+ }
95
+ list(items, title = '', key = 'name', meta = { back: true, main: true }) {
96
+ let context = title;
97
+ TSUssdMenu_1.TSUssdMenu.forOwn(items, (v, k) => {
98
+ if (typeof v === 'object' && v !== null && key in v) {
99
+ context += isNaN(Number(k)) ? '\n' : '\n' + (parseInt(String(k), 10) + 1) + TSUssdMenu_1.TSUssdMenu.config.general.separator + ' ';
100
+ context += v[key];
101
+ }
102
+ else if (['number', 'string', 'boolean'].includes(typeof v)) {
103
+ context += String(v).indexOf('\n') === -1 ? '\n' + v : String(v);
104
+ }
105
+ else {
106
+ console.error(`TSUssdScreen:list[${key} -> ${k}] not mapped with ->`, JSON.stringify(v, null, 2));
107
+ }
108
+ });
109
+ TSUssdMenu_1.TSUssdMenu.forOwn(meta, (v, k) => { if (k !== undefined)
110
+ context += '\n' + TSUssdScreen.nav(String(k)) + TSUssdMenu_1.TSUssdMenu.config.general.separator + ' ' + i18n_1.default.__(String(k)); });
111
+ return context;
112
+ }
113
+ async getAndSet(key, field) {
114
+ if (!this.storage)
115
+ throw new Error('TSUssdScreen: storage not configured');
116
+ let data = await this.storage.hget(String(this.menu.args.phoneNumber), field);
117
+ if (this.menu.val && this.menu.val.toString().length > 0) {
118
+ if (!data) {
119
+ data = { [key]: this.menu.val };
120
+ }
121
+ else {
122
+ data[key] = this.menu.val;
123
+ }
124
+ await this.storage.hset(String(this.menu.args.phoneNumber), field, data);
125
+ }
126
+ return data;
127
+ }
128
+ async pager(items = [], key, pagerSize) {
129
+ let length = 0;
130
+ const result = await this._pager(items, key, pagerSize);
131
+ TSUssdMenu_1.TSUssdMenu.forOwn(result.data, (i) => { const it = i; length += (it.name?.length ?? 0) || (it.length ?? 0); });
132
+ if (TSUssdMenu_1.TSUssdMenu.config.pager.maxLength < length) {
133
+ TSUssdMenu_1.TSUssdMenu.forOwn(TSUssdMenu_1.TSUssdMenu.config.pager.strategies, (s) => {
134
+ const strat = s;
135
+ if (strat.enabled && strat.type && this.strategies[strat.type]) {
136
+ this.strategies[strat.type](result.data, strat, parseInt((TSUssdMenu_1.TSUssdMenu.config.pager.maxLength / pagerSize).toString(), 10));
137
+ }
138
+ });
139
+ }
140
+ return result;
141
+ }
142
+ async _pager(items = [], key, pagerSize) {
143
+ if (!this.storage)
144
+ throw new Error('TSUssdScreen: storage not configured');
145
+ const pagerOptions = (await this.storage.hget(String(this.menu.args.phoneNumber), key + 'Pager') || {}), command = parseInt(this.menu.val, 10), calculatePaging = () => {
146
+ if (pagerSize >= items.length) {
147
+ offset = 0;
148
+ limit = pagerSize;
149
+ }
150
+ else if (command === 7) {
151
+ limit = offset;
152
+ offset -= pagerSize;
153
+ if (offset < 0) {
154
+ offset = 0;
155
+ limit = pagerSize;
156
+ }
157
+ }
158
+ else if (command === 9) {
159
+ offset = limit;
160
+ limit = offset + pagerSize;
161
+ if (limit > items.length) {
162
+ limit = items.length;
163
+ offset = limit - pagerSize;
164
+ }
165
+ }
166
+ };
167
+ let offset = pagerOptions.offset || 0, limit = pagerOptions.limit || (offset + pagerSize);
168
+ calculatePaging();
169
+ let data = items.slice(offset, limit), { length } = data, dataLength = 0;
170
+ TSUssdMenu_1.TSUssdMenu.forOwn(data, (i) => { const it = i; dataLength += it.name ? it.name.length : (it.length ?? 0); });
171
+ if (TSUssdMenu_1.TSUssdMenu.config.pager.maxLength < dataLength) {
172
+ pagerSize = pagerSize - 1;
173
+ offset = pagerOptions.offset || 0;
174
+ limit = pagerOptions.limit || (offset + pagerSize);
175
+ calculatePaging();
176
+ data = items.slice(offset, limit);
177
+ length = data.length;
178
+ }
179
+ await this.storage.hset(String(this.menu.args.phoneNumber), key + 'Pager', { offset: offset, limit: limit });
180
+ if (offset < 1 && items.length > limit) {
181
+ data.push(i18n_1.default.__('\n9 [Next]', TSUssdMenu_1.TSUssdMenu.config.general));
182
+ }
183
+ else if (items.length > limit && offset > 0) {
184
+ data.push(i18n_1.default.__('\n7 [Prev] | 9 [Next]', TSUssdMenu_1.TSUssdMenu.config.general));
185
+ }
186
+ else if (offset > 0) {
187
+ data.push(i18n_1.default.__('\n7 [Prev]', TSUssdMenu_1.TSUssdMenu.config.general));
188
+ }
189
+ return { meta: { command: command, total: items.length, length: length, offset: offset, limit: limit }, data: data };
190
+ }
191
+ async pagerPickAndSet(dataSet = [], field, size = 0) {
192
+ if (!this.storage)
193
+ throw new Error('TSUssdScreen: storage not configured');
194
+ const pagerOptions = (await this.storage.hget(String(this.menu.args.phoneNumber), field + 'Pager') || {}), offset = pagerOptions.offset || 0, limit = pagerOptions.limit || (offset + size), cursor = parseInt(this.menu.val, 10) - 1;
195
+ let data;
196
+ dataSet = size ? dataSet.slice(offset, limit) : dataSet;
197
+ if (size && dataSet.length >= cursor) {
198
+ data = dataSet[cursor];
199
+ }
200
+ if (!size) {
201
+ data = dataSet[cursor + offset];
202
+ }
203
+ await this.storage.hset(String(this.menu.args.phoneNumber), field, data);
204
+ return data;
205
+ }
206
+ checkPick(entity, state) {
207
+ if (!entity && state) {
208
+ this.menu.go(state);
209
+ return false;
210
+ }
211
+ else if (!entity) {
212
+ this.menu.end(i18n_1.default.__('invalid.input', { input: this.menu.val }));
213
+ return false;
214
+ }
215
+ return true;
216
+ }
217
+ }
218
+ exports.TSUssdScreen = TSUssdScreen;
@@ -0,0 +1,3 @@
1
+ export * from './TSUssdMenu';
2
+ export * from './TSUssdScreen';
3
+ export * from './providers/index';
package/ussd/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./TSUssdMenu"), exports);
18
+ __exportStar(require("./TSUssdScreen"), exports);
19
+ __exportStar(require("./providers/index"), exports);
@@ -0,0 +1,3 @@
1
+ export declare function response(text: string, _rr?: unknown): string;
2
+ export declare function request(request: any, _response: unknown): any;
3
+ export declare function text(_prev: string | undefined, body: any): any;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.response = response;
4
+ exports.request = request;
5
+ exports.text = text;
6
+ const _1 = require("./_");
7
+ function response(text, _rr) {
8
+ return text;
9
+ }
10
+ function request(request, _response) {
11
+ const body = (0, _1.body)(request);
12
+ request.body = (0, _1.mapArgs)(body, _1.providersMap[_1.TSUssdProviders.africastalking]);
13
+ return request.body;
14
+ }
15
+ function text(_prev = '', body) {
16
+ return body.message;
17
+ }