b2b-platform-utils 0.1.0 → 1.1.1

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.
package/cryptoWrapper.js CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  // Purpose: PBKDF2-based password hashing/validation with a salt fetched from cache or config file.
4
- // Note: Public API preserved exactly.
5
4
 
6
5
  const crypto = require('crypto');
7
6
  const { getValue } = require('./localCache');
@@ -12,31 +11,30 @@ let keyLen = 64;
12
11
 
13
12
  module.exports = {
14
13
  /**
15
- * Get salt value from in-memory cache or fallback to config file.
14
+ * Get salt value from in-memory cache or fallback to config file ./config/config.json (resolved from process.cwd()).
16
15
  */
17
16
  getSalt: () => {
18
17
  let salt = getValue('salt');
19
- // Migrations/seeders flow when keys are not existing in runtime
20
18
  if (!salt) {
21
- let config = readFile('./config/config.json');
22
- config = JSON.parse(config);
23
- salt = config?.salt;
19
+ try {
20
+ let config = readFile('./config/config.json'); // resolved relative to service root
21
+ config = JSON.parse(config);
22
+ salt = config?.salt;
23
+ } catch {
24
+ // Intentionally swallow ENOENT/parse errors; caller will fail deterministically if salt is missing.
25
+ }
24
26
  }
25
27
  return salt;
26
28
  },
27
29
 
28
- /**
29
- * Derive a password hash using PBKDF2-SHA512.
30
- */
30
+ /** Derive a password hash using PBKDF2-SHA512. */
31
31
  generatePassword: (password) => {
32
32
  return crypto
33
33
  .pbkdf2Sync(password, module.exports.getSalt(), iterationsCount, keyLen, 'sha512')
34
34
  .toString('hex');
35
35
  },
36
36
 
37
- /**
38
- * Constant-time style comparison for provided password vs stored hash.
39
- */
37
+ /** Constant-time style comparison for provided password vs stored hash. */
40
38
  validatePassword: (password, hash) => {
41
39
  const derived = crypto
42
40
  .pbkdf2Sync(password, module.exports.getSalt(), iterationsCount, keyLen, 'sha512')
package/errorsMap.js CHANGED
@@ -1,25 +1,112 @@
1
1
  'use strict';
2
2
 
3
- // Purpose: Provide a consistent way to look up predefined error objects from in-memory cache.
4
- // Note: Keeps the original public API and behavior (returns JSON.stringify(currentError)).
3
+ // Purpose: Static error map with backward compatibility and dynamic shortcuts.
4
+ // - Old style calls like errorsMap.mailTemplateNotFound?.() continue to work.
5
+ // - Any property access matching an errorKey returns a function that yields JSON.stringify(error).
6
+ // - New API: get(), getString(), getCode(), list(), and legacy getSingleError().
7
+ // - Cache-first: if localCache.errorsMap exists, it overrides static entries.
5
8
 
6
9
  const { getValue } = require('./localCache');
7
10
 
8
- module.exports = {
9
- duplicatedUniqueField: () => module.exports.getSingleError('duplicatedUniqueField'),
10
- invalidConstraintField: () => module.exports.getSingleError('invalidConstraintField'),
11
- mediaResourceNotFound: () => module.exports.getSingleError('mediaResourceNotFound'),
12
- notificationNotFound: () => module.exports.getSingleError('notificationNotFound'),
13
- mailTemplateNotFound: () => module.exports.getSingleError('mailTemplateNotFound'),
14
- undefinedRequest: () => module.exports.getSingleError('undefinedRequest'),
15
-
16
- /**
17
- * Returns a stringified error object by key from the cached "errorsMap" array.
18
- * Keeps original behavior: JSON.stringify(undefined) => undefined.
19
- */
20
- getSingleError: (errorKey) => {
21
- const errors = getValue('errorsMap');
22
- const currentError = errors?.find((item) => item.errorKey === errorKey);
23
- return JSON.stringify(currentError);
11
+ // ---- Static fallback (source of truth when cache is empty) ----
12
+ const STATIC_ERRORS = [
13
+ { errorCode: 1000, errorKey: 'userNotFound', httpCode: 404, description: 'User was not found' },
14
+ { errorCode: 1001, errorKey: 'unauthorized', httpCode: 401, description: 'The server requires user authentication' },
15
+ { errorCode: 1002, errorKey: 'gameCategoryNotFound', httpCode: 404, description: 'Game`s category was not found' },
16
+ { errorCode: 1003, errorKey: 'mediaResourceNotFound', httpCode: 404, description: 'Media resource was not found' },
17
+ { errorCode: 1004, errorKey: 'roleNotFound', httpCode: 404, description: 'Role or permission were not found' },
18
+ { errorCode: 1005, errorKey: 'duplicatedUniqueField', httpCode: 422, description: 'Some of unique fields were duplicated.' },
19
+ { errorCode: 1006, errorKey: 'gameCollectionNotFound', httpCode: 404, description: 'Game`s collection was not found' },
20
+ { errorCode: 1007, errorKey: 'gameTagNotFound', httpCode: 404, description: 'Game`s tag was not found' },
21
+ { errorCode: 1008, errorKey: 'invalidConstraintField', httpCode: 422, description: 'Some of foreign keys fields were invalid.' },
22
+ { errorCode: 1009, errorKey: 'gameProviderNotFound', httpCode: 404, description: 'Game`s provider was not found' },
23
+ { errorCode: 1010, errorKey: 'gameNotFound', httpCode: 404, description: 'Game was not found' },
24
+ { errorCode: 1011, errorKey: 'undefinedRequest', httpCode: 422, description: 'Request data or format is undefined for handler.' },
25
+ { errorCode: 1012, errorKey: 'widgetNotFound', httpCode: 404, description: 'Widget[CMS] was not found' },
26
+ { errorCode: 1013, errorKey: 'segmentNotFound', httpCode: 404, description: 'User`s Segment was not found' },
27
+ { errorCode: 1014, errorKey: 'bonusNotFound', httpCode: 404, description: 'Bonus was not found' },
28
+ { errorCode: 1015, errorKey: 'userBonusNotFound', httpCode: 404, description: 'User`s bonus was not found' },
29
+ { errorCode: 1016, errorKey: 'freeSpinsNotFound', httpCode: 404, description: 'Free spins was not found' },
30
+ { errorCode: 1017, errorKey: 'gameWagerListNotFound', httpCode: 404, description: 'Game`s wager list was not found' },
31
+ { errorCode: 1018, errorKey: 'bonusAlreadyInUse', httpCode: 422, description: 'Bonus is already use(d)' },
32
+ { errorCode: 1019, errorKey: 'bonusRestrictForClaim', httpCode: 422, description: 'Bonus does not fit for user`s claiming' },
33
+ { errorCode: 1020, errorKey: 'bonusRewardAttachError', httpCode: 422, description: 'Bonus reward can not be attached.' },
34
+ { errorCode: 1021, errorKey: 'loyaltyLevelNotFound', httpCode: 404, description: 'Loyalty level was not found' },
35
+ { errorCode: 1022, errorKey: 'loyaltyPointNotFound', httpCode: 404, description: 'Loyalty point was not found' },
36
+ { errorCode: 1023, errorKey: 'notificationNotFound', httpCode: 404, description: 'Notification was not found' },
37
+ { errorCode: 1024, errorKey: 'bannerNotFound', httpCode: 404, description: 'Banner was not found' },
38
+ { errorCode: 1025, errorKey: 'paymentProviderNotFound', httpCode: 404, description: 'Payment provider was not found' },
39
+ { errorCode: 1026, errorKey: 'paymentMethodNotFound', httpCode: 404, description: 'Payment method was not found' },
40
+ { errorCode: 1027, errorKey: 'depositNotFound', httpCode: 404, description: 'Deposit was not found' },
41
+ { errorCode: 1028, errorKey: 'withdrawalNotFound', httpCode: 404, description: 'Withdrawal was not found' },
42
+ { errorCode: 1029, errorKey: 'noteNotFound', httpCode: 404, description: 'User`s Note was not found' },
43
+ { errorCode: 1030, errorKey: 'promoCodeNotFound', httpCode: 404, description: 'Promo Code was not found' },
44
+ { errorCode: 1031, errorKey: 'cashBackNotFound', httpCode: 404, description: 'Cash Back was not found' },
45
+ { errorCode: 1032, errorKey: 'tournamentNotFound', httpCode: 404, description: 'Tournament was not found' },
46
+ { errorCode: 1033, errorKey: 'pageNotFound', httpCode: 404, description: 'Page was not found' },
47
+ { errorCode: 1034, errorKey: 'gameCashBackListNotFound', httpCode: 404, description: 'Game`s cash back list was not found' },
48
+ { errorCode: 1035, errorKey: 'bonusDepositRequired', httpCode: 404, description: 'For bonus activation desposit requited.' },
49
+ { errorCode: 1036, errorKey: 'taskNotFound', httpCode: 404, description: 'Task for schedule was not found.' },
50
+ { errorCode: 1037, errorKey: 'bonusConfirmationRequired', httpCode: 404, description: 'For bonus activation confirmation of personal user data requited.' },
51
+ { errorCode: 1038, errorKey: 'promoNotFound', httpCode: 404, description: 'Promo was not found.' },
52
+ { errorCode: 1039, errorKey: 'badSignature', httpCode: 401, description: 'Untrusted request, bad signature.' },
53
+ { errorCode: 1040, errorKey: 'mailTemplateNotFound', httpCode: 404, description: 'Email template was not found.' }
54
+ ];
55
+
56
+ const STATIC_BY_KEY = Object.fromEntries(STATIC_ERRORS.map(e => [e.errorKey, e]));
57
+
58
+ /** Resolve error by key from cache (if any) or static table. */
59
+ function resolveErrorByKey(errorKey) {
60
+ const cached = getValue('errorsMap');
61
+ if (Array.isArray(cached) && cached.length) {
62
+ const hit = cached.find((item) => item?.errorKey === errorKey);
63
+ if (hit) return hit;
24
64
  }
65
+ return STATIC_BY_KEY[errorKey];
66
+ }
67
+
68
+ // --- Base API object (do not export directly; we wrap it in a Proxy) ---
69
+ const api = {
70
+ /** Legacy accessor kept for compatibility. */
71
+ getSingleError: (errorKey) => {
72
+ const obj = resolveErrorByKey(errorKey);
73
+ return obj ? JSON.stringify(obj) : undefined;
74
+ },
75
+
76
+ /** New API: get full error object. */
77
+ get: (errorKey) => resolveErrorByKey(errorKey),
78
+
79
+ /** New API: get JSON version (same as legacy). */
80
+ getString: (errorKey) => {
81
+ const obj = resolveErrorByKey(errorKey);
82
+ return obj ? JSON.stringify(obj) : undefined;
83
+ },
84
+
85
+ /** New API: get numeric error code. */
86
+ getCode: (errorKey) => resolveErrorByKey(errorKey)?.errorCode,
87
+
88
+ /** New API: list all static errors (useful for diagnostics). */
89
+ list: () => [...STATIC_ERRORS]
25
90
  };
91
+
92
+ // --- Dynamic shortcuts: errorsMap.<errorKey>() returns JSON string ---
93
+ // This keeps old style calls working across all services without hand-written wrappers.
94
+ module.exports = new Proxy(api, {
95
+ get(target, prop, receiver) {
96
+ // If method exists (get, getString, etc.) → return it
97
+ if (prop in target) return Reflect.get(target, prop, receiver);
98
+
99
+ // If property is a known errorKey → return a function () => getString(errorKey)
100
+ if (typeof prop === 'string' && STATIC_BY_KEY[prop]) {
101
+ return () => target.getString(prop);
102
+ }
103
+
104
+ // Otherwise, undefined (so optional chaining ?.() is safe)
105
+ return undefined;
106
+ },
107
+
108
+ // Optional: make `prop in errorsMap` truthy for known keys
109
+ has(target, prop) {
110
+ return prop in target || (typeof prop === 'string' && !!STATIC_BY_KEY[prop]);
111
+ }
112
+ });
package/fileSystem.js CHANGED
@@ -1,28 +1,40 @@
1
1
  'use strict';
2
2
 
3
- // Purpose: Simple sync file helpers for small config/state blobs.
3
+ // Purpose: Simple sync file helpers that resolve relative paths from the service root (process.cwd()).
4
4
 
5
5
  const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ /** Resolve absolute path; if relative, resolve from process.cwd(). */
9
+ function resolvePath(fileName) {
10
+ return path.isAbsolute(fileName) ? fileName : path.resolve(process.cwd(), fileName);
11
+ }
6
12
 
7
13
  module.exports = {
8
14
  /**
9
15
  * Save data as JSON into a file (overwrites if exists).
16
+ * Ensures parent directory exists.
10
17
  */
11
18
  saveFile: (fileName, data) => {
12
- fs.writeFileSync(fileName, JSON.stringify(data));
19
+ const p = resolvePath(fileName);
20
+ fs.mkdirSync(path.dirname(p), { recursive: true });
21
+ fs.writeFileSync(p, JSON.stringify(data));
13
22
  },
14
23
 
15
24
  /**
16
25
  * Read text content from a file using UTF-8.
26
+ * Relative paths are resolved from process.cwd().
17
27
  */
18
28
  readFile: (fileName) => {
19
- return fs.readFileSync(fileName, 'utf-8');
29
+ const p = resolvePath(fileName);
30
+ return fs.readFileSync(p, 'utf-8');
20
31
  },
21
32
 
22
33
  /**
23
34
  * Remove a file from the filesystem.
24
35
  */
25
36
  removeFile: (fileName) => {
26
- fs.unlinkSync(fileName);
37
+ const p = resolvePath(fileName);
38
+ fs.unlinkSync(p);
27
39
  }
28
40
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "b2b-platform-utils",
3
- "version": "0.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Shared utilities for Node.js microservices: errors map, local cache, logger, numbers, dates, filesystem, media optimization, paginator, slugger, crypto wrapper, sanitize HTML, sorting.",
5
5
  "type": "commonjs",
6
6
  "license": "KingSizer",
@@ -22,4 +22,4 @@
22
22
  "moment": "^2.30.1",
23
23
  "sharp": "^0.33.0"
24
24
  }
25
- }
25
+ }