corebasic 1.0.230 → 1.0.232

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.
@@ -16,10 +16,13 @@ import axios from 'axios';
16
16
  // @ts-ignore
17
17
  import jwt from 'jsonwebtoken';
18
18
  let features = {};
19
- let apis = {};
20
- const getFeatureMethod = api => api.split(' ')[0].toLowerCase();
21
- const getFeatureUrl = api => api.split(' ')[1];
22
- const getFeature = name => features[name];
19
+ let apis = {
20
+ // <api>: {<feature>: FeatureEntry}
21
+ };
22
+ const getFeatureMethod = (api) => api.split(' ')[0].toLowerCase();
23
+ const getFeatureUrl = (api) => { const url = api.split(' ')[1]; if (!url)
24
+ throw new Error(`Invalid Feature url in api: ${api}`); return url; };
25
+ const getFeature = (name) => features[name];
23
26
  const DEPLOY_TOKEN_SECRET = process.env.DEPLOY_TOKEN_SECRET || "MY_SECRET_DEPLOY_TOKEN";
24
27
  const SERVICE_ACCESS_TOKEN = jwt.sign({ app: process.env.APP_DEPLOYMENT_NAME }, DEPLOY_TOKEN_SECRET, { expiresIn: '365d' });
25
28
  const NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN = await (async () => { if (process.env.NDCURVE_DEVELOPER_SERVICE)
@@ -50,7 +53,7 @@ async function loadLocalFeatures() {
50
53
  let SLYP_FEATURES_LIST = await loadLocalFeatures();
51
54
  let appId = Utils.uid();
52
55
  let SERVICE_ADDRESS = process.env.APP_ENDPOINT || 'http://127.0.0.1:3000';
53
- export const get = feature => {
56
+ export const get = (feature) => {
54
57
  const throwError = () => { throw new Error(`Feature ${feature} not found in internal or external list during Features.subscribe call`); };
55
58
  const baseFeature = getFeature(feature);
56
59
  let { api, service = SERVICE_ADDRESS, topic } = baseFeature ? baseFeature : (SLYP_FEATURES_LIST[feature] ?? throwError());
@@ -68,16 +71,27 @@ export const send = async (meta, feature, data, params) => {
68
71
  if (url.includes(':'))
69
72
  throw new Error(`Error: Internal feature call send on feature: ${feature} when api/params/substitution`);
70
73
  }
71
- let payload = { ...meta, data, feature, txn: Utils.uid() };
74
+ const payload = { ...meta, data, feature, txn: Utils.uid() };
72
75
  service = service.replace('http://slyp.app', 'https://slyp.app');
73
76
  // return (await axios[method](`${service}${url}`, { data: payload, headers: {jwt: headers.jwt}, timeout: 1000 })).data
74
77
  // Used to work. But request to 127.0.0.1 fails to send the specified headers along with the request.
75
78
  // return (await axios[method](`${service}${url}`, payload, {headers: {jwt: SERVICE_ACCESS_TOKEN, service: true}, timeout: 1000 })).data // Worked Earlier, but issue spotted
76
- if (getFeature(feature)) { // Local call
79
+ if (baseFeature) { // Local call
77
80
  let response;
78
- let req = { meta, body: payload, params, method };
81
+ let req = { body: payload, params: params ?? {}, method, path: url, url: '' };
79
82
  req = JSON.parse(JSON.stringify(req));
80
- let res = { json: payload => response = payload, end: () => true };
83
+ const callback = (payload) => { response = payload; };
84
+ const res = {
85
+ json: callback,
86
+ send: callback,
87
+ end: () => { },
88
+ status: (_status) => { return { send: callback, json: callback }; },
89
+ sendStatus: (_status) => { },
90
+ setHeader: (_header, _value) => { },
91
+ flushHeaders: () => { },
92
+ headersSent: false,
93
+ writableEnded: false,
94
+ };
81
95
  await apiHandler(req, res);
82
96
  return response;
83
97
  }
@@ -109,7 +123,8 @@ async function announce() {
109
123
  function getFeaturelessFeature(req) {
110
124
  if (Session.ALLOWED_URLS.includes(req.path)) {
111
125
  for (let key in features) {
112
- if (features[key].featureless && features[key].api === req.method.toUpperCase() + ' ' + req.path) {
126
+ const feature = features[key];
127
+ if (feature.featureless && feature.api === req.method.toUpperCase() + ' ' + req.path) {
113
128
  return getFeature(key);
114
129
  }
115
130
  }
@@ -129,44 +144,77 @@ const apiHandler = async (req, res) => {
129
144
  console.warn(`Feature: ${req.body.feature} not available`);
130
145
  throw { status: 404, message: `Resource not found. Feature ${req.body.feature} not available.` };
131
146
  }
147
+ feature = feature; // typescript validation with !
132
148
  let topic = feature.topic;
133
- let params = getFeatureUrl(feature.api).split("/").filter(item => item.startsWith(":")).map(item => item.replace(":", ""));
149
+ let params = getFeatureUrl(feature.api).split("/").filter((item) => item.startsWith(":")).map((item) => item.replace(":", ""));
134
150
  for (let param of params)
135
151
  if (!req.params[param])
136
152
  throw { status: 404, message: "Resource not found. One or more url parameter not specified." };
137
- let meta = { ...req.body, data: undefined };
138
- req.meta = meta;
153
+ let meta = { ...req.body, data: undefined }; // TODO: Must also include invoiceTxn
139
154
  if (process.env.USE_DEFAULT_COMPANY) {
140
- req.meta.company = 'DEFAULT_COMPANY';
141
- req.meta.outlet = 'DEFAULT_OUTLET';
155
+ meta.company = 'DEFAULT_COMPANY';
156
+ meta.outlet = 'DEFAULT_OUTLET';
142
157
  }
158
+ meta.txn = meta.txn ?? Utils.uid();
159
+ const message = {
160
+ meta,
161
+ data: req.body.data,
162
+ params: req.params,
163
+ // To be removed 1
164
+ date: new Date().getTime(),
165
+ // To be removed 2
166
+ topic,
167
+ feature: req.body.feature,
168
+ user: req.body.user,
169
+ txn: meta.txn,
170
+ };
171
+ const appReq = {
172
+ meta: message.meta,
173
+ body: message,
174
+ params: message.params,
175
+ method: method,
176
+ path: req.path,
177
+ url: req.url,
178
+ headers: req.headers,
179
+ query: req.query,
180
+ on: req.on,
181
+ };
182
+ const appRes = res;
143
183
  if (method === "get") {
144
184
  try {
145
- req.body = { ...req.body, topic };
146
- await feature.handler(req, res);
185
+ // await feature.handler({...req, headers: req.headers, body: {...req.body, topic} }, res)
186
+ // req.body = {...req.body, topic}
187
+ await feature.handler(appReq, appRes);
147
188
  }
148
189
  catch (err) {
149
190
  if (process.env.DEBUG_MODE)
150
- console.log('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
191
+ console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
151
192
  throw { status: 500, message: "Failed to GET feature", ...err };
152
193
  }
153
194
  }
154
195
  else if (method !== "get" && feature.bypass) {
155
196
  try {
156
- await feature.handler(topic, prepareMessage(req), req, res);
197
+ await feature.handler(topic, message, appReq, appRes);
157
198
  }
158
199
  catch (err) {
159
200
  if (process.env.DEBUG_MODE)
160
- console.log('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
201
+ console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
161
202
  throw { status: 500, message: "Failed to POST feature", ...err };
162
203
  }
163
204
  }
164
- else
165
- await commandAction(req, res, topic);
205
+ else {
206
+ try {
207
+ await Kafka.send(`Features.${topic}`, message, message.user, { compression: Kafka.CompressionTypes.GZIP });
208
+ }
209
+ catch {
210
+ throw { status: 500, message: "Failed to queue the transaction" };
211
+ }
212
+ appRes.json({ data: { txn: message.txn, success: true, status: "Queued", featureQueued: true } });
213
+ }
166
214
  }
167
215
  catch (err) {
168
216
  if (process.env.DEBUG_MODE)
169
- console.log('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
217
+ console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
170
218
  try { // Sometimes error occurs when disconnecting stream from front end
171
219
  res.status(err.status ?? 500).json(err);
172
220
  }
@@ -203,6 +251,8 @@ function registerApi() {
203
251
  for (let api in apis) {
204
252
  let method = getFeatureMethod(api);
205
253
  let url = api.split(' ')[1];
254
+ if (!url)
255
+ throw new Error(`No url path found in api ${api} during Features.registerApi()`);
206
256
  if (api === 'GET /transactions/:id')
207
257
  continue;
208
258
  ExpressApp[method](url, apiHandler);
@@ -219,10 +269,10 @@ export const start = async (app, url, file) => {
219
269
  app.get('/features', async (req, res) => {
220
270
  let exp_features = {};
221
271
  for (let [key, { api }] of Object.entries(features))
222
- exp_features[key] = { api, service: `${SERVICE_ADDRESS}` };
272
+ exp_features[key] = { api, service: `${SERVICE_ADDRESS}`, subscribe: '', topic: '' };
223
273
  if (process.env.LOAD_LOCAL_FEATURES) {
224
274
  for (let key in SLYP_FEATURES_LIST)
225
- SLYP_FEATURES_LIST[key].headers = { JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN };
275
+ SLYP_FEATURES_LIST[key].headers = { JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN }; // TODO: CRITICAL: Security issue
226
276
  }
227
277
  res.json({ data: { ...SLYP_FEATURES_LIST, ...exp_features } });
228
278
  });
@@ -230,20 +280,21 @@ export const start = async (app, url, file) => {
230
280
  await registerFeatures(features);
231
281
  // Get unique kafka topics list from features
232
282
  let kafkaTopics = [];
233
- for (let name in features) {
283
+ for (const name in features) {
234
284
  let feature = features[name];
235
- if (feature.api.split(' ')[0].toLowerCase() === "get")
285
+ if (feature.api.split(' ')[0]?.toLowerCase() === "get")
236
286
  continue;
237
287
  kafkaTopics = [...new Set(kafkaTopics.concat([feature.topic]))];
238
288
  }
239
289
  // Subscribe to each topic
240
290
  for (let topic of kafkaTopics) {
241
- Kafka.receive(`Features.${topic}`, async (topic, message) => {
291
+ const groupId = '';
292
+ Kafka.receive(`Features.${topic}`, groupId, async (topic, message) => {
242
293
  if (Utils.isEmpty(message.meta?.company))
243
294
  message.meta = { ...message.meta, company: "GLOBAL", outlet: "GLOBAL" };
244
295
  if (!message?.feature)
245
296
  return;
246
- const timer = ms => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
297
+ const timer = (ms) => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
247
298
  topic = topic.replace(/^.*Features./, '');
248
299
  while (true) {
249
300
  try {
@@ -251,7 +302,12 @@ export const start = async (app, url, file) => {
251
302
  // // TODO: use $useChunks: [] once support is added in dip insert
252
303
  // await Dip.insert(globalMeta, `users.txns`, { _id: `${message.user}_${iso_date}_${message.txn}`, user: message.user, feature: message.feature, date: message.date, created: message.date, updated: message.date, status: "Queued" }, {idempotent: true}) // Can always update the status later
253
304
  // TODO: Avoid duplicate processing
254
- await features[message.feature].handler(topic, message);
305
+ const feature = features[message.feature];
306
+ if (!feature) {
307
+ console.warn(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`);
308
+ throw new Error(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`);
309
+ }
310
+ await feature.handler(topic, message);
255
311
  await Dip.insert(globalMeta, "Features.txns", { _id: message.txn, status: "Processed" }, { idempotent: true });
256
312
  break;
257
313
  }
@@ -296,31 +352,13 @@ export const start = async (app, url, file) => {
296
352
  console.log('Messaging failed to start. Maybe missing redis');
297
353
  }
298
354
  };
299
- function prepareMessage(req) {
300
- let txn = req.body.txn ?? Utils.uid();
301
- let _id = req.body.data?._id ?? txn;
302
- let { data, feature, app, user, outlet, company, client, version } = req.body;
303
- let meta = req.meta;
304
- let params = req.params;
305
- return { data, params, meta, feature, app, user, outlet, company, client, version, txn, id: _id, date: new Date().getTime() };
306
- }
307
- const commandAction = async (req, res, topic) => {
308
- let message = prepareMessage(req);
309
- try {
310
- await Kafka.send(`Features.${topic}`, message, message.user, { compression: Kafka.CompressionTypes.GZIP });
311
- }
312
- catch {
313
- throw { status: 500, message: "Failed to queue the transaction" };
314
- }
315
- res.json({ data: { txn: message.txn, success: true, status: "Queued", featureQueued: true } });
316
- };
317
355
  let subscriptions = {
318
356
  // "coins.query.hello": "invoices.command.add", // Consumer : Feature of Topic
319
357
  };
320
358
  let subscribed_consumers = {};
321
359
  async function subscribe() {
322
360
  let Features = { get, send };
323
- for (let [key, { subscribe }] of Object.entries(features))
361
+ for (let [key, { subscribe }] of Object.entries(features)) {
324
362
  if (!Utils.isEmpty(subscribe) && subscriptions[key] !== subscribe && subscribed_consumers[key]) {
325
363
  try {
326
364
  await subscribed_consumers[key].disconnect();
@@ -328,30 +366,37 @@ async function subscribe() {
328
366
  catch (_) { }
329
367
  delete subscribed_consumers[key];
330
368
  }
331
- for (let key in SLYP_FEATURES_LIST)
332
- if (!Utils.isEmpty(SLYP_FEATURES_LIST[key].subscribe) && subscriptions[key] !== SLYP_FEATURES_LIST[key].subscribe && subscribed_consumers[key]) {
369
+ }
370
+ for (let key in SLYP_FEATURES_LIST) {
371
+ const feature = SLYP_FEATURES_LIST[key];
372
+ if (!Utils.isEmpty(feature?.subscribe) && subscriptions[key] !== feature?.subscribe && subscribed_consumers[key]) {
333
373
  try {
334
374
  await subscribed_consumers[key].disconnect();
335
375
  }
336
376
  catch (_) { }
337
377
  delete subscribed_consumers[key];
338
378
  }
339
- for (let key in SLYP_FEATURES_LIST)
340
- if (!Utils.isEmpty(SLYP_FEATURES_LIST[key].subscribe)) {
341
- subscriptions[key] = SLYP_FEATURES_LIST[key].subscribe;
379
+ }
380
+ for (let key in SLYP_FEATURES_LIST) {
381
+ const feature = SLYP_FEATURES_LIST[key];
382
+ if (!Utils.isEmpty(feature?.subscribe)) {
383
+ subscriptions[key] = feature.subscribe;
342
384
  }
343
- for (let [key, { subscribe }] of Object.entries(features))
385
+ }
386
+ for (let [key, { subscribe }] of Object.entries(features)) {
344
387
  if (!Utils.isEmpty(subscribe)) {
345
388
  subscriptions[key] = subscribe;
346
389
  }
390
+ }
347
391
  for (let consumer in subscriptions) {
348
392
  let publisher = subscriptions[consumer];
349
- let { topic } = getFeature(publisher);
350
- if (subscribed_consumers[consumer] || !getFeature(publisher))
393
+ const publisherFeature = getFeature(publisher);
394
+ if (subscribed_consumers[consumer] || !publisherFeature)
351
395
  continue;
352
- let kafka_consumer = Kafka.receive(`Features.${topic}`, consumer, async (topic, message) => {
396
+ let { topic } = publisherFeature;
397
+ let kafka_consumer_promise = Kafka.receive(`Features.${topic}`, consumer, async (topic, message) => {
353
398
  topic = topic.replace(/^.*Features./, '');
354
- const timer = ms => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
399
+ const timer = (ms) => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
355
400
  while (true) {
356
401
  try {
357
402
  await Features.send(message.meta, consumer, message.data, message.params);
@@ -363,6 +408,11 @@ async function subscribe() {
363
408
  await timer(10000);
364
409
  }
365
410
  });
366
- subscribed_consumers[consumer] = kafka_consumer;
411
+ kafka_consumer_promise
412
+ .then(kafka_consumer => {
413
+ subscribed_consumers[consumer] = kafka_consumer;
414
+ }).catch(err => {
415
+ throw new Error("Error: Remote service to service kafka Feature subscription.");
416
+ });
367
417
  }
368
418
  }
@@ -2,7 +2,9 @@
2
2
  import { Kafka, logLevel as KafkaLogLevel, CompressionTypes as KafkaCompressionTypes } from 'kafkajs';
3
3
  export const logLevel = KafkaLogLevel;
4
4
  export const CompressionTypes = KafkaCompressionTypes;
5
- let kafka, producer, admin;
5
+ let kafka;
6
+ let producer;
7
+ let admin;
6
8
  let appName;
7
9
  let topicPrefix = '';
8
10
  export const start = (arg) => {
@@ -31,8 +33,7 @@ export const createTopic = async (topic, partition, replicas) => {
31
33
  // ===========
32
34
  let consumers = [];
33
35
  const start_consumer = async function (topic, groupId, callback) {
34
- callback = typeof groupId === "string" ? callback : groupId;
35
- groupId = typeof groupId === "string" ? `${appName}.${groupId}` : `${appName}.${topic}`;
36
+ groupId = groupId ? `${appName}.${groupId}` : `${appName}.${topic}`;
36
37
  const consumer = kafka.consumer({ groupId: groupId });
37
38
  consumers.push(consumer);
38
39
  await consumer.connect();
@@ -46,7 +47,7 @@ const start_consumer = async function (topic, groupId, callback) {
46
47
  success = await callback(topic, JSON.parse(message.value), partition);
47
48
  }
48
49
  catch (ex) {
49
- success = await callback(topic, message.value, partition);
50
+ success = await callback(topic, message.value, partition); // NOTE: Beware: Ensure Kafka.receive<T> can handle raw message type that is not json parseable
50
51
  }
51
52
  await consumer.commitOffsets([{ topic, partition, offset: (Number(message.offset) + 1).toString() }]);
52
53
  },
@@ -79,7 +80,7 @@ const start_producer = async function (topic, message, key, options) {
79
80
  // start_producer('quickstart-events', 'Hello KafkaJS user! Little')
80
81
  export const receive = async function (topic, groupId, callback) {
81
82
  topic = topicPrefix + topic;
82
- return (await start_consumer(topic, groupId, callback));
83
+ return await start_consumer(topic, groupId, callback);
83
84
  };
84
85
  export const send = async function (topic, message, key, options) {
85
86
  topic = topicPrefix + topic;
@@ -4,14 +4,17 @@ import axios from 'axios';
4
4
  // @ts-ignore
5
5
  import { createClient } from 'redis';
6
6
  const url = process.env.REDIS_URL || "redis://localhost:6380";
7
- // url: 'redis://alice:foobared@localhost:6380'
8
- let consumer, publisher;
7
+ let consumer;
8
+ let publisher;
9
9
  let users_pool = {};
10
10
  async function disconnect(user, uid) {
11
11
  if (!users_pool[user])
12
12
  return;
13
- let [{ res }] = users_pool[user].reqres.filter(item => item.uid === uid).concat([{}]);
14
- res.end();
13
+ let res_items = users_pool[user].reqres.filter(item => item.uid === uid);
14
+ if (res_items) {
15
+ const res = res_items[0].res;
16
+ res.end();
17
+ }
15
18
  if (users_pool[user].listeners[uid]) {
16
19
  if (users_pool[user].listeners.count == 1)
17
20
  await consumer.unsubscribe(user, users_pool[user].listeners[uid]);
@@ -8,7 +8,7 @@ const REFRESH_TOKEN_SECRET = process.env.JWT_REFRESH_TOKEN_SECRET || "MY_SECRET_
8
8
  const DEPLOY_TOKEN_SECRET = process.env.DEPLOY_TOKEN_SECRET || "MY_SECRET_DEPLOY_TOKEN";
9
9
  const NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN_SECRET = process.env.NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN_PUBLIC_KEY;
10
10
  let urlsAllowed = [];
11
- export var ALLOWED_URLS = [];
11
+ export let ALLOWED_URLS = [];
12
12
  export const start = (expressApp, allowedUrls) => {
13
13
  urlsAllowed = ["/refreshToken", "/login"].concat(allowedUrls ?? []);
14
14
  ALLOWED_URLS = urlsAllowed;
@@ -54,7 +54,7 @@ export const start = (expressApp, allowedUrls) => {
54
54
  throw null;
55
55
  }
56
56
  catch (error) {
57
- return res.status(401).json({ message: error?.message === "Access Denied" ? error.message : 'Unauthorized' });
57
+ res.status(401).json({ message: error?.message === "Access Denied" ? "Access Denied" : 'Unauthorized' });
58
58
  }
59
59
  });
60
60
  app.post("/refreshToken", (req, res) => {
package/libs/auth.ts CHANGED
@@ -7,12 +7,51 @@ import * as Dip from './dip.ts'
7
7
  import * as Utils from './utils.ts'
8
8
  import * as Session from './session.ts'
9
9
 
10
- let validateFn, validateErrMessage
11
- export const validate = (callback, errMessage) => {
10
+
11
+ type AuthRequest = {
12
+ body: {
13
+ mob?: string
14
+ phone?: string
15
+ code?: string
16
+ otp?: string
17
+ clientId?: string
18
+ app?: string
19
+ hash?: string
20
+ userId?: string
21
+ data?: Record<string, unknown>
22
+ }
23
+ }
24
+
25
+ type AuthResponse = {
26
+ status: (code: number) => AuthResponse
27
+ json: (body: unknown) => void
28
+ }
29
+
30
+ type AuthApp = {
31
+ post: (
32
+ path: string,
33
+ handler: (req: AuthRequest, res: AuthResponse) => Promise<void>
34
+ ) => void
35
+ }
36
+
37
+ type ValidateFunction = (req: AuthRequest) => boolean
38
+
39
+ type SuccessCallback = (
40
+ req: AuthRequest,
41
+ res: AuthResponse,
42
+ data: Record<string, unknown>
43
+ ) => void | Promise<void>
44
+
45
+
46
+
47
+ let validateFn: ValidateFunction | undefined
48
+ let validateErrMessage: string | undefined
49
+
50
+ export const validate = (callback: ValidateFunction, errMessage?: string) => {
12
51
  validateFn = callback
13
52
  }
14
53
 
15
- export const start = (app, successCallback) => {
54
+ export const start = (app: AuthApp, successCallback?: SuccessCallback) => {
16
55
  app.post("/login", async (req, res) => {
17
56
  if (validateFn && !validateFn(req)) {
18
57
  res.status(401).json({ mode: 'login', success: false, info: validateErrMessage ?? 'Validation Failed', message: 'Login Server Error' })
@@ -21,7 +60,7 @@ export const start = (app, successCallback) => {
21
60
  try {
22
61
  let login = await attemptLogin(req, res)
23
62
  if (login.mode === 'verify' && login.success) {
24
- let tokens = Session.generateAccessToken(login.userId, req.body.clientId)
63
+ let tokens = Session.generateAccessToken(login.userId, req.body.clientId ?? '')
25
64
  let response = {...login, tokens}
26
65
  if (successCallback)
27
66
  await successCallback(req, res, response)
@@ -35,12 +74,12 @@ export const start = (app, successCallback) => {
35
74
  })
36
75
  }
37
76
 
38
- async function attemptLogin(req, res) {
77
+ async function attemptLogin(req: AuthRequest, res: AuthResponse) {
39
78
  let meta = {company: "GLOBAL", outlet: "GLOBAL"}
40
79
 
41
80
  let expiry = 300000
42
- let userMob = req.body.mob ?? req.body.phone
43
- const code = req.body.code
81
+ let userMob = req.body.mob ?? req.body.phone ?? ''
82
+ const code = req.body.code ?? ''
44
83
  let mob = userMob.endsWith('123456789') ? '0123456789' : Utils.parseMob(userMob, code)
45
84
  let time = new Date().getTime()
46
85
  let collection = (req.body.app ? req.body.app + '.' : '') + "auth.login"
@@ -73,7 +112,7 @@ async function attemptLogin(req, res) {
73
112
  }
74
113
 
75
114
 
76
- async function sendOtp(mob, otp, app, hash) {
115
+ async function sendOtp(mob: string, otp: string, app?: string, hash?: string) {
77
116
  if (process.env.LOGIN_OTP_DISABLE) {
78
117
  console.log(otp);
79
118
  return true
@@ -81,7 +120,7 @@ async function sendOtp(mob, otp, app, hash) {
81
120
  try {
82
121
  if (app && hash)
83
122
  app = encodeURIComponent(`${app} (#${hash})`) // Of the form '<app> (#<hash>)' for better readability. Note: Axios works with SlypBusiness%20(%23<HASH>) but Curl requires SlypBusiness%20%28%23<HASH>%29
84
- let api = process.env.SMS_API.replace(':phone', mob).replace(':otp', otp).replace(':app', app ?? 'app')
123
+ let api = process.env.SMS_API!.replace(':phone', mob).replace(':otp', otp).replace(':app', app ?? 'app')
85
124
  await axios.get(api)
86
125
  return true
87
126
  } catch {
@@ -9,6 +9,11 @@ import compression from 'compression'
9
9
  // Regarding Client compressing data send to server, I think if server has `Content-Encoding: gzip` set, then client might compress before sending. But not sure. Need to check.
10
10
  // Http Stream or ServerSideEvents require res.flush() after res.write(). res.flush function is added by the express compression middleware
11
11
 
12
- export const start = app => {
12
+
13
+ type ExpressApp = {
14
+ use: (fn: unknown) => unknown
15
+ }
16
+
17
+ export const start = (app: ExpressApp) => {
13
18
  app.use(Utils.excludeMiddleware(compression({threshold: 1024}))) // Response above 1024 bytes should be compressed. Default is also 1KB
14
19
  }
package/libs/cpp.ts CHANGED
@@ -1,8 +1,10 @@
1
1
 
2
2
  import crypto from 'node:crypto';
3
3
 
4
+ type ValueRef = { value: bigint }
5
+ type BytesRef = { value: number }
4
6
 
5
- function readContent(buffer: Uint8Array, valueRef, bytesRef) { // Buffer is assignable to Uint8Array so no need for Buffer | Uint8Array
7
+ function readContent(buffer: Uint8Array, valueRef: ValueRef, bytesRef: BytesRef) { // Buffer is assignable to Uint8Array so no need for Buffer | Uint8Array
6
8
  const MASK = 0x7F;
7
9
  const MSB = 0x80;
8
10
  const MAX_VLQ_BYTES = 10;
@@ -21,7 +23,7 @@ function readContent(buffer: Uint8Array, valueRef, bytesRef) { // Buffer is assi
21
23
  return false;
22
24
  }
23
25
 
24
- const byte = view[lenBytes];
26
+ const byte = view[lenBytes]!;
25
27
  lenBytes++;
26
28
 
27
29
  length |= BigInt(byte & MASK) << shift;
@@ -41,7 +43,7 @@ function readContent(buffer: Uint8Array, valueRef, bytesRef) { // Buffer is assi
41
43
 
42
44
 
43
45
 
44
- export function vlqToUint64(arrayBuffer) {
46
+ export function vlqToUint64(arrayBuffer: Uint8Array) {
45
47
  // Objects act as containers to simulate C++ pointers/references
46
48
  const valueRef = { value: 0n };
47
49
  const bytesRef = { value: 0 };
@@ -63,7 +65,7 @@ export function vlqToUint64(arrayBuffer) {
63
65
 
64
66
 
65
67
 
66
- function writeVlqcontent(buffer, value) {
68
+ function writeVlqcontent(buffer: Buffer, value: number | bigint) {
67
69
  const MASK = 0x7Fn;
68
70
  const MSB = 0x80;
69
71
  let bufferPtr = 0;
@@ -81,7 +83,7 @@ function writeVlqcontent(buffer, value) {
81
83
  return bufferPtr;
82
84
  }
83
85
 
84
- export function uint64ToVlq(value) {
86
+ export function uint64ToVlq(value: number | bigint) {
85
87
  // 10 bytes is the maximum VLQ size for a uint64
86
88
  const out = Buffer.allocUnsafe(10);
87
89
  const size = writeVlqcontent(out, value);
@@ -93,7 +95,7 @@ export function uint64ToVlq(value) {
93
95
 
94
96
 
95
97
 
96
- export function sliceArrayBuffer(buffer, offset, length) {
98
+ export function sliceArrayBuffer(buffer: Buffer | Uint8Array, offset: number, length: number) {
97
99
  // Ensure we are working with a Node.js Buffer
98
100
  const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
99
101
  const totalSize = buf.length;
@@ -113,13 +115,13 @@ export function sliceArrayBuffer(buffer, offset, length) {
113
115
  return buf.subarray(offset, offset + length); // COW/Shallow copy I hope or else what is the use of this function
114
116
  }
115
117
 
116
- export function stringToUtf8ArrayBuffer(arg) {
118
+ export function stringToUtf8ArrayBuffer(arg: string) {
117
119
  // Converts the string into a Node.js Buffer encoded in UTF-8
118
120
  return Buffer.from(arg, 'utf8');
119
121
  }
120
122
 
121
123
 
122
- export function numberToSortableBytes(value) {
124
+ export function numberToSortableBytes(value: number) {
123
125
  // 1. Create an 8-byte buffer and a data view to read/write raw bits
124
126
  const buffer = Buffer.alloc(8);
125
127
  const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
@@ -148,7 +150,7 @@ export function numberToSortableBytes(value) {
148
150
 
149
151
 
150
152
 
151
- export function defaultHash(buffer) {
153
+ export function defaultHash(buffer: Buffer | Uint8Array) {
152
154
  const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
153
155
 
154
156
  // Uses the high-speed, non-cryptographic xxhash64 algorithm
@@ -160,7 +162,7 @@ export function defaultHash(buffer) {
160
162
  }
161
163
 
162
164
 
163
- export function memcmpEqual(buffer1, buffer2) {
165
+ export function memcmpEqual(buffer1: Buffer | Uint8Array, buffer2: Buffer | Uint8Array) {
164
166
  const b1 = Buffer.isBuffer(buffer1) ? buffer1 : Buffer.from(buffer1);
165
167
  const b2 = Buffer.isBuffer(buffer2) ? buffer2 : Buffer.from(buffer2);
166
168
 
@@ -168,7 +170,7 @@ export function memcmpEqual(buffer1, buffer2) {
168
170
  }
169
171
 
170
172
 
171
- export function arrayBufferToString(buffer) {
173
+ export function arrayBufferToString(buffer: Buffer | Uint8Array) {
172
174
  const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
173
175
 
174
176
  // Decodes the buffer bytes as a UTF-8 JavaScript string
@@ -180,7 +182,7 @@ export function arrayBufferToString(buffer) {
180
182
 
181
183
 
182
184
  // Matches QString Response::getSliceAsText
183
- export function getSliceAsText(rawTextBuffer, offset: number, length: number): string {
185
+ export function getSliceAsText(rawTextBuffer: Buffer, offset: number, length: number): string {
184
186
  // Reuses the identical slice boundary logic from above
185
187
  try {
186
188
  const slice = getSliceAsArrayBuffer(rawTextBuffer, offset, length);
@@ -191,7 +193,7 @@ export function getSliceAsText(rawTextBuffer, offset: number, length: number): s
191
193
  }
192
194
 
193
195
  // Matches QByteArray Response::getSliceAsArrayBuffer
194
- export function getSliceAsArrayBuffer(rawTextBuffer, offset: number, length: number): Buffer {
196
+ export function getSliceAsArrayBuffer(rawTextBuffer: Buffer, offset: number, length: number): Buffer {
195
197
  const totalSize = rawTextBuffer.length;
196
198
 
197
199
  if (offset < 0 || offset >= totalSize) {
@@ -1,4 +1,4 @@
1
- import {getNormalizedBounds, type Bounds} from './index.ts'
1
+ import {getNormalizedBounds, type AbsoluteBounds} from './index.ts'
2
2
  import {addUTCYears, addUTCMonths, addUTCDays} from '../../utils.ts'
3
3
 
4
4
 
@@ -6,7 +6,7 @@ const MONTH_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Se
6
6
  const MONTH_LONG = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
7
7
 
8
8
 
9
- export function formatDate(value, format) {
9
+ export function formatDate(value: string | number | Date, format: string) {
10
10
 
11
11
  const dateObj = value instanceof Date ? value : new Date(value)
12
12
 
@@ -59,7 +59,7 @@ export function formatDate(value, format) {
59
59
  D: () => dates.join(separator),
60
60
  };
61
61
 
62
- return format.replace(/YYYY|MMMM|MMM|YY|MM|DD|D/g, token => replacements[token]());
62
+ return format.replace(/YYYY|MMMM|MMM|YY|MM|DD|D/g, token => replacements[token as keyof typeof replacements]());
63
63
  }
64
64
 
65
65
 
@@ -83,7 +83,7 @@ export function formatDate(value, format) {
83
83
 
84
84
 
85
85
 
86
- export function fillDates(bounds_t, format) {
86
+ export function fillDates(bounds_t: AbsoluteBounds, format: string) {
87
87
  const result = [];
88
88
 
89
89
  let step;
@@ -94,11 +94,11 @@ export function fillDates(bounds_t, format) {
94
94
  else
95
95
  step = "year";
96
96
 
97
- let bounds: Bounds = { ...bounds_t, from: new Date(bounds_t.from), to: new Date(bounds_t.to) }
97
+ let bounds: AbsoluteBounds = { ...bounds_t, from: new Date(bounds_t.from), to: new Date(bounds_t.to) }
98
98
 
99
99
  bounds = getNormalizedBounds(bounds, "date")
100
100
 
101
- while (bounds.from <= bounds.to) {
101
+ while (bounds.from! <= bounds.to!) {
102
102
  result.push(formatDate(bounds.from, format));
103
103
 
104
104
  switch (step) {