corebasic 1.0.231 → 1.0.233

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: '', headers: {}, query: {}, on: (_event, _callback) => { } };
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
  }
@@ -95,7 +109,7 @@ async function announce() {
95
109
  let exp_features = {};
96
110
  for (let [key, { api, subscribe }] of Object.entries(features))
97
111
  exp_features[key] = { api, service: SERVICE_ADDRESS, subscribe, topic: "" };
98
- await Messaging.subscribe(`${process.env.REDIS_CHANNEL_PREFIX}_SLYP_FEATURES_LIST`, async (message, channel) => {
112
+ await Messaging.subscribe(`${process.env.REDIS_CHANNEL_PREFIX}_SLYP_FEATURES_LIST`, async (message, _channel) => {
99
113
  let { uid, ...msg } = JSON.parse(message);
100
114
  if (uid !== appId && !appids[uid]) {
101
115
  appids[uid] = true;
@@ -109,11 +123,13 @@ 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
  }
116
131
  }
132
+ return undefined;
117
133
  }
118
134
  const apiHandler = async (req, res) => {
119
135
  let method = req.method.toLowerCase();
@@ -129,44 +145,77 @@ const apiHandler = async (req, res) => {
129
145
  console.warn(`Feature: ${req.body.feature} not available`);
130
146
  throw { status: 404, message: `Resource not found. Feature ${req.body.feature} not available.` };
131
147
  }
148
+ feature = feature; // typescript validation with !
132
149
  let topic = feature.topic;
133
- let params = getFeatureUrl(feature.api).split("/").filter(item => item.startsWith(":")).map(item => item.replace(":", ""));
150
+ let params = getFeatureUrl(feature.api).split("/").filter((item) => item.startsWith(":")).map((item) => item.replace(":", ""));
134
151
  for (let param of params)
135
152
  if (!req.params[param])
136
153
  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;
154
+ let meta = { ...req.body, data: undefined }; // TODO: Must also include invoiceTxn
139
155
  if (process.env.USE_DEFAULT_COMPANY) {
140
- req.meta.company = 'DEFAULT_COMPANY';
141
- req.meta.outlet = 'DEFAULT_OUTLET';
156
+ meta.company = 'DEFAULT_COMPANY';
157
+ meta.outlet = 'DEFAULT_OUTLET';
142
158
  }
159
+ meta.txn = meta.txn ?? Utils.uid();
160
+ const message = {
161
+ meta,
162
+ data: req.body.data,
163
+ params: req.params,
164
+ // To be removed 1
165
+ date: new Date().getTime(),
166
+ // To be removed 2
167
+ topic,
168
+ feature: req.body.feature,
169
+ user: req.body.user,
170
+ txn: meta.txn,
171
+ };
172
+ const appReq = {
173
+ meta: message.meta,
174
+ body: message,
175
+ params: message.params,
176
+ method: method,
177
+ path: req.path,
178
+ url: req.url,
179
+ headers: req.headers,
180
+ query: req.query,
181
+ on: req.on,
182
+ };
183
+ const appRes = res;
143
184
  if (method === "get") {
144
185
  try {
145
- req.body = { ...req.body, topic };
146
- await feature.handler(req, res);
186
+ // await feature.handler({...req, headers: req.headers, body: {...req.body, topic} }, res)
187
+ // req.body = {...req.body, topic}
188
+ await feature.handler(appReq, appRes);
147
189
  }
148
190
  catch (err) {
149
191
  if (process.env.DEBUG_MODE)
150
- console.log('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
192
+ console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
151
193
  throw { status: 500, message: "Failed to GET feature", ...err };
152
194
  }
153
195
  }
154
196
  else if (method !== "get" && feature.bypass) {
155
197
  try {
156
- await feature.handler(topic, prepareMessage(req), req, res);
198
+ await feature.handler(topic, message, appReq, appRes);
157
199
  }
158
200
  catch (err) {
159
201
  if (process.env.DEBUG_MODE)
160
- console.log('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
202
+ console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
161
203
  throw { status: 500, message: "Failed to POST feature", ...err };
162
204
  }
163
205
  }
164
- else
165
- await commandAction(req, res, topic);
206
+ else {
207
+ try {
208
+ await Kafka.send(`Features.${topic}`, message, message.user, { compression: Kafka.CompressionTypes.GZIP });
209
+ }
210
+ catch {
211
+ throw { status: 500, message: "Failed to queue the transaction" };
212
+ }
213
+ appRes.json({ data: { txn: message.txn, success: true, status: "Queued", featureQueued: true } });
214
+ }
166
215
  }
167
216
  catch (err) {
168
217
  if (process.env.DEBUG_MODE)
169
- console.log('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
218
+ console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
170
219
  try { // Sometimes error occurs when disconnecting stream from front end
171
220
  res.status(err.status ?? 500).json(err);
172
221
  }
@@ -203,6 +252,8 @@ function registerApi() {
203
252
  for (let api in apis) {
204
253
  let method = getFeatureMethod(api);
205
254
  let url = api.split(' ')[1];
255
+ if (!url)
256
+ throw new Error(`No url path found in api ${api} during Features.registerApi()`);
206
257
  if (api === 'GET /transactions/:id')
207
258
  continue;
208
259
  ExpressApp[method](url, apiHandler);
@@ -216,13 +267,13 @@ export const start = async (app, url, file) => {
216
267
  PROJECT_ROOT_URL = url;
217
268
  features = await Utils.fileToJson(url, file);
218
269
  await announce();
219
- app.get('/features', async (req, res) => {
270
+ app.get('/features', async (_req, res) => {
220
271
  let exp_features = {};
221
272
  for (let [key, { api }] of Object.entries(features))
222
- exp_features[key] = { api, service: `${SERVICE_ADDRESS}` };
273
+ exp_features[key] = { api, service: `${SERVICE_ADDRESS}`, subscribe: '', topic: '' };
223
274
  if (process.env.LOAD_LOCAL_FEATURES) {
224
275
  for (let key in SLYP_FEATURES_LIST)
225
- SLYP_FEATURES_LIST[key].headers = { JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN };
276
+ SLYP_FEATURES_LIST[key].headers = { JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN }; // TODO: CRITICAL: Security issue
226
277
  }
227
278
  res.json({ data: { ...SLYP_FEATURES_LIST, ...exp_features } });
228
279
  });
@@ -230,20 +281,21 @@ export const start = async (app, url, file) => {
230
281
  await registerFeatures(features);
231
282
  // Get unique kafka topics list from features
232
283
  let kafkaTopics = [];
233
- for (let name in features) {
284
+ for (const name in features) {
234
285
  let feature = features[name];
235
- if (feature.api.split(' ')[0].toLowerCase() === "get")
286
+ if (feature.api.split(' ')[0]?.toLowerCase() === "get")
236
287
  continue;
237
288
  kafkaTopics = [...new Set(kafkaTopics.concat([feature.topic]))];
238
289
  }
239
290
  // Subscribe to each topic
240
291
  for (let topic of kafkaTopics) {
241
- Kafka.receive(`Features.${topic}`, async (topic, message) => {
292
+ const groupId = '';
293
+ Kafka.receive(`Features.${topic}`, groupId, async (topic, message) => {
242
294
  if (Utils.isEmpty(message.meta?.company))
243
295
  message.meta = { ...message.meta, company: "GLOBAL", outlet: "GLOBAL" };
244
296
  if (!message?.feature)
245
297
  return;
246
- const timer = ms => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
298
+ const timer = (ms) => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
247
299
  topic = topic.replace(/^.*Features./, '');
248
300
  while (true) {
249
301
  try {
@@ -251,7 +303,12 @@ export const start = async (app, url, file) => {
251
303
  // // TODO: use $useChunks: [] once support is added in dip insert
252
304
  // 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
305
  // TODO: Avoid duplicate processing
254
- await features[message.feature].handler(topic, message);
306
+ const feature = features[message.feature];
307
+ if (!feature) {
308
+ console.warn(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`);
309
+ throw new Error(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`);
310
+ }
311
+ await feature.handler(topic, message);
255
312
  await Dip.insert(globalMeta, "Features.txns", { _id: message.txn, status: "Processed" }, { idempotent: true });
256
313
  break;
257
314
  }
@@ -296,31 +353,13 @@ export const start = async (app, url, file) => {
296
353
  console.log('Messaging failed to start. Maybe missing redis');
297
354
  }
298
355
  };
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
356
  let subscriptions = {
318
357
  // "coins.query.hello": "invoices.command.add", // Consumer : Feature of Topic
319
358
  };
320
359
  let subscribed_consumers = {};
321
360
  async function subscribe() {
322
361
  let Features = { get, send };
323
- for (let [key, { subscribe }] of Object.entries(features))
362
+ for (let [key, { subscribe }] of Object.entries(features)) {
324
363
  if (!Utils.isEmpty(subscribe) && subscriptions[key] !== subscribe && subscribed_consumers[key]) {
325
364
  try {
326
365
  await subscribed_consumers[key].disconnect();
@@ -328,30 +367,37 @@ async function subscribe() {
328
367
  catch (_) { }
329
368
  delete subscribed_consumers[key];
330
369
  }
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]) {
370
+ }
371
+ for (let key in SLYP_FEATURES_LIST) {
372
+ const feature = SLYP_FEATURES_LIST[key];
373
+ if (!Utils.isEmpty(feature?.subscribe) && subscriptions[key] !== feature?.subscribe && subscribed_consumers[key]) {
333
374
  try {
334
375
  await subscribed_consumers[key].disconnect();
335
376
  }
336
377
  catch (_) { }
337
378
  delete subscribed_consumers[key];
338
379
  }
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;
380
+ }
381
+ for (let key in SLYP_FEATURES_LIST) {
382
+ const feature = SLYP_FEATURES_LIST[key];
383
+ if (!Utils.isEmpty(feature?.subscribe)) {
384
+ subscriptions[key] = feature.subscribe;
342
385
  }
343
- for (let [key, { subscribe }] of Object.entries(features))
386
+ }
387
+ for (let [key, { subscribe }] of Object.entries(features)) {
344
388
  if (!Utils.isEmpty(subscribe)) {
345
389
  subscriptions[key] = subscribe;
346
390
  }
391
+ }
347
392
  for (let consumer in subscriptions) {
348
393
  let publisher = subscriptions[consumer];
349
- let { topic } = getFeature(publisher);
350
- if (subscribed_consumers[consumer] || !getFeature(publisher))
394
+ const publisherFeature = getFeature(publisher);
395
+ if (subscribed_consumers[consumer] || !publisherFeature)
351
396
  continue;
352
- let kafka_consumer = Kafka.receive(`Features.${topic}`, consumer, async (topic, message) => {
397
+ let { topic } = publisherFeature;
398
+ let kafka_consumer_promise = Kafka.receive(`Features.${topic}`, consumer, async (topic, message) => {
353
399
  topic = topic.replace(/^.*Features./, '');
354
- const timer = ms => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
400
+ const timer = (ms) => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
355
401
  while (true) {
356
402
  try {
357
403
  await Features.send(message.meta, consumer, message.data, message.params);
@@ -363,6 +409,11 @@ async function subscribe() {
363
409
  await timer(10000);
364
410
  }
365
411
  });
366
- subscribed_consumers[consumer] = kafka_consumer;
412
+ kafka_consumer_promise
413
+ .then(kafka_consumer => {
414
+ subscribed_consumers[consumer] = kafka_consumer;
415
+ }).catch(() => {
416
+ throw new Error("Error: Remote service to service kafka Feature subscription.");
417
+ });
367
418
  }
368
419
  }
@@ -33,23 +33,20 @@ export const createTopic = async (topic, partition, replicas) => {
33
33
  // ===========
34
34
  let consumers = [];
35
35
  const start_consumer = async function (topic, groupId, callback) {
36
- callback = typeof groupId === "string" ? callback : groupId;
37
- groupId = typeof groupId === "string" ? `${appName}.${groupId}` : `${appName}.${topic}`;
36
+ groupId = groupId ? `${appName}.${groupId}` : `${appName}.${topic}`;
38
37
  const consumer = kafka.consumer({ groupId: groupId });
39
38
  consumers.push(consumer);
40
39
  await consumer.connect();
41
40
  await consumer.subscribe({ topic: topic, fromBeginning: true });
42
- const fn = callback;
43
41
  await consumer.run({
44
42
  autoCommit: false,
45
43
  eachMessage: async ({ topic, partition, message }) => {
46
- // fn(topic, message.value.toString(), partition) // toString() returns array so won't parse if json.
47
- let success;
44
+ // callback(topic, message.value.toString(), partition) // toString() returns array so won't parse if json.
48
45
  try {
49
- success = await fn(topic, JSON.parse(message.value), partition);
46
+ await callback(topic, JSON.parse(message.value), partition);
50
47
  }
51
48
  catch (ex) {
52
- success = await fn(topic, message.value, partition);
49
+ await callback(topic, message.value, partition); // NOTE: Beware: Ensure Kafka.receive<T> can handle raw message type that is not json parseable
53
50
  }
54
51
  await consumer.commitOffsets([{ topic, partition, offset: (Number(message.offset) + 1).toString() }]);
55
52
  },
@@ -82,7 +79,7 @@ const start_producer = async function (topic, message, key, options) {
82
79
  // start_producer('quickstart-events', 'Hello KafkaJS user! Little')
83
80
  export const receive = async function (topic, groupId, callback) {
84
81
  topic = topicPrefix + topic;
85
- return (await start_consumer(topic, groupId, callback));
82
+ return await start_consumer(topic, groupId, callback);
86
83
  };
87
84
  export const send = async function (topic, message, key, options) {
88
85
  topic = topicPrefix + topic;
@@ -34,7 +34,7 @@ async function connect({ user, req, res, uid }) {
34
34
  message = JSON.parse(message);
35
35
  }
36
36
  catch { }
37
- for (let { req, res } of users_pool[user].reqres) {
37
+ for (let { res } of users_pool[user].reqres) {
38
38
  res.write(JSON.stringify({ message, channel }) + "\n");
39
39
  if (res.flush) // If compression enabled
40
40
  res.flush();
@@ -26,7 +26,7 @@ export function isEmpty(str) {
26
26
  }
27
27
  export let GLOBAL_META = { company: "GLOBAL", outlet: "GLOBAL" };
28
28
  export function isEmptyJson(json) {
29
- for (let i in json)
29
+ for (let _i in json)
30
30
  return false;
31
31
  return true;
32
32
  }
package/libs/auth.ts CHANGED
@@ -47,7 +47,7 @@ type SuccessCallback = (
47
47
  let validateFn: ValidateFunction | undefined
48
48
  let validateErrMessage: string | undefined
49
49
 
50
- export const validate = (callback: ValidateFunction, errMessage?: string) => {
50
+ export const validate = (callback: ValidateFunction, _errMessage?: string) => {
51
51
  validateFn = callback
52
52
  }
53
53
 
@@ -58,7 +58,7 @@ export const start = (app: AuthApp, successCallback?: SuccessCallback) => {
58
58
  return
59
59
  }
60
60
  try {
61
- let login = await attemptLogin(req, res)
61
+ let login = await attemptLogin(req)
62
62
  if (login.mode === 'verify' && login.success) {
63
63
  let tokens = Session.generateAccessToken(login.userId, req.body.clientId ?? '')
64
64
  let response = {...login, tokens}
@@ -74,7 +74,7 @@ export const start = (app: AuthApp, successCallback?: SuccessCallback) => {
74
74
  })
75
75
  }
76
76
 
77
- async function attemptLogin(req: AuthRequest, res: AuthResponse) {
77
+ async function attemptLogin(req: AuthRequest) {
78
78
  let meta = {company: "GLOBAL", outlet: "GLOBAL"}
79
79
 
80
80
  let expiry = 300000
@@ -89,7 +89,7 @@ function flatten_value(target_key: string, value: unknown, flattened: unknown[],
89
89
  }
90
90
 
91
91
 
92
- export function entries(target_key: string, query: unknown, binary_slice?: Buffer): unknown[] {
92
+ export function entries(target_key: string, query: unknown, _binary_slice?: Buffer): unknown[] {
93
93
  let result = flatten_id_values(target_key, query); // extract all valid _id
94
94
 
95
95
  // let cows = result.map(v => extract_direct_id(v, binary_slice))
@@ -140,7 +140,7 @@ type SuffixItem = { key?: string; value?: string; type: string; ls_threshold?: n
140
140
  type CollectionsJsonType = Record<string, {policy?: {when: object, suffix: SuffixItem[]}[]}>
141
141
  type Arg = { db: string; collection: string[]; }
142
142
 
143
- export async function applySuffixPolicy(COLLECTIONS_JSON: CollectionsJsonType, cols: string | string[], query: Record<string, any>, insertMode?: boolean, arg?: Arg) {
143
+ export async function applySuffixPolicy(COLLECTIONS_JSON: CollectionsJsonType, cols: string | string[], query: Record<string, any>, insertMode: boolean, arg: Arg) {
144
144
  const collections = Array.isArray(cols) ? cols : [cols]
145
145
  let suffixes: string[] = []
146
146
 
@@ -6,7 +6,7 @@ type SuffixItem = { key?: string; value?: string; type: string; ls_threshold?: n
6
6
  type Arg = { db: string; collection: string[]; }
7
7
 
8
8
  const EXPLICIT_SUFFIX_POLICY_TYPES = new Set([ "string", "date", "number" ]);
9
- export async function suffix(doc: Record<string, any>, policies: SuffixItem[], insertMode?: boolean, arg?: Arg) {
9
+ export async function suffix(doc: Record<string, any>, policies: SuffixItem[], insertMode: boolean, arg: Arg) {
10
10
  let suffixes = [""]
11
11
  for (const policy of policies) {
12
12
 
@@ -92,9 +92,8 @@ export async function suffix(doc: Record<string, any>, policies: SuffixItem[], i
92
92
  if (!keys.length && !insertMode) {
93
93
  ls = true
94
94
  for (const suffix of suffixes) {
95
- const collections = arg?.collection ?? []
96
- for (const collection of collections) {
97
- const dirs = (await Dip.operation("ls", { db: arg?.db, collection: collection, suffix })).filter((dir: string) => !dir.startsWith('chunk-'))
95
+ for (const collection of arg.collection) {
96
+ const dirs = (await Dip.operation("ls", { db: arg.db, collection: collection, suffix })).filter((dir: string) => !dir.startsWith('chunk-'))
98
97
  suffixAssociatedKeys[suffix] = suffixAssociatedKeys[suffix] ?? []
99
98
  suffixAssociatedKeys[suffix].push(...dirs)
100
99
  }