b2b-platform-utils 1.1.0 → 1.1.2

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/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/localCache.js CHANGED
@@ -1,34 +1,115 @@
1
1
  'use strict';
2
2
 
3
- // Purpose: Lightweight in-memory key-value store scoped to the running process.
4
- // Note: Added setService(key) to allow passing microservice key without breaking existing API.
3
+ /**
4
+ * @module inMemoryConfig
5
+ * @description
6
+ * Process-scoped in-memory key-value store for configuration.
7
+ * - Backward compatible with existing API surface.
8
+ * - Adds currency helpers with explicit opt-in ENV fallback.
9
+ *
10
+ * Design goals:
11
+ * 1) No side effects on import (ENV is read lazily, only if enabled).
12
+ * 2) "Common" bag is the single source of truth for cross-cutting config.
13
+ * 3) Services that do NOT use base currency remain unaffected by ENV.
14
+ */
5
15
 
16
+ // ---- Internal state ---------------------------------------------------------
17
+
18
+ /** @type {Record<string, any>} */
6
19
  let cacheData = {};
20
+
21
+ /** @type {string} */
7
22
  let serviceKey = 'notification';
23
+
24
+ /** @type {number} */
8
25
  let bufferChunkSize = 512000;
26
+
27
+ /** @type {string} */
9
28
  const GEO_DEFAULT = 'WW';
10
29
 
30
+ /** @type {boolean} */
31
+ let envBaseCurrencyFallbackEnabled = false; // opt-in per process
32
+
33
+ // ---- Utilities --------------------------------------------------------------
34
+
35
+ /**
36
+ * Ensure the "common" bag exists and return it by reference.
37
+ * @returns {Record<string, any>}
38
+ */
39
+ function ensureCommonBag() {
40
+ if (!cacheData.common || typeof cacheData.common !== 'object') {
41
+ cacheData.common = {};
42
+ }
43
+ return cacheData.common;
44
+ }
45
+
46
+ /**
47
+ * Normalize to a 3-letter uppercase currency code.
48
+ * Returns null if invalid.
49
+ * @param {unknown} value
50
+ * @returns {string|null}
51
+ */
52
+ function normalizeCurrencyCode(value) {
53
+ if (typeof value !== 'string') return null;
54
+ const code = value.trim().toUpperCase();
55
+ return /^[A-Z]{3}$/.test(code) ? code : null;
56
+ }
57
+
58
+ // ---- Public API: KV ---------------------------------------------------------
59
+
60
+ /**
61
+ * Set a top-level key in the cache.
62
+ * @param {string} key
63
+ * @param {any} value
64
+ */
11
65
  function setValue(key, value) {
12
66
  cacheData[key] = value;
13
67
  }
14
68
 
69
+ /**
70
+ * Get a top-level value from the cache.
71
+ * @param {string} key
72
+ * @returns {any|null}
73
+ */
15
74
  function getValue(key) {
16
- return cacheData[key] || null;
75
+ return Object.prototype.hasOwnProperty.call(cacheData, key)
76
+ ? cacheData[key]
77
+ : null;
17
78
  }
18
79
 
19
- function getCommonValue() {
20
- return cacheData['common'] || {};
80
+ /**
81
+ * Shallow-merge a config object into the cache.
82
+ * @param {Record<string, any>} data
83
+ */
84
+ function applyConfig(data) {
85
+ for (const k in data) {
86
+ if (Object.prototype.hasOwnProperty.call(data, k)) {
87
+ setValue(k, data[k]);
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Return a direct reference to the whole cache (mutable).
94
+ * @returns {Record<string, any>}
95
+ */
96
+ function getData() {
97
+ return cacheData;
21
98
  }
22
99
 
100
+ // ---- Public API: service/meta ----------------------------------------------
101
+
23
102
  /**
24
103
  * Return current microservice key.
104
+ * @returns {string}
25
105
  */
26
106
  function getService() {
27
107
  return serviceKey;
28
108
  }
29
109
 
30
110
  /**
31
- * Allow overriding the microservice key at runtime (non-breaking addition).
111
+ * Override microservice key at runtime.
112
+ * @param {string} key
32
113
  */
33
114
  function setService(key) {
34
115
  if (typeof key === 'string' && key.length) {
@@ -36,12 +117,18 @@ function setService(key) {
36
117
  }
37
118
  }
38
119
 
120
+ /**
121
+ * Get configured buffer chunk size.
122
+ * @returns {number}
123
+ */
39
124
  function getBufferChunkSize() {
40
125
  return bufferChunkSize;
41
126
  }
42
127
 
43
128
  /**
44
- * Initialize environment/customer from process params (kept as-is).
129
+ * Initialize environment/customer from process params.
130
+ * Kept for compatibility with existing bootstrap flows.
131
+ * @param {string[]} params
45
132
  */
46
133
  function parseInitConfig(params) {
47
134
  const environment = params[2];
@@ -51,37 +138,93 @@ function parseInitConfig(params) {
51
138
  }
52
139
 
53
140
  /**
54
- * Shallow-apply a config object into the cache.
141
+ * Return the "common" bag snapshot ({} if missing).
142
+ * @returns {Record<string, any>}
55
143
  */
56
- function applyConfig(data) {
57
- for (const i in data) {
58
- setValue(i, data[i]);
59
- }
144
+ function getCommonValue() {
145
+ return cacheData.common && typeof cacheData.common === 'object'
146
+ ? cacheData.common
147
+ : {};
60
148
  }
61
149
 
150
+ // ---- Public API: domain helpers --------------------------------------------
151
+
62
152
  /**
63
- * Return the whole cache snapshot (by reference).
153
+ * Read GEODefault from "common" or return fallback.
154
+ * @returns {string}
64
155
  */
65
- function getData() {
66
- return cacheData;
156
+ function getDefaultGEO() {
157
+ return getValue('common')?.GEODefault || GEO_DEFAULT;
67
158
  }
68
159
 
69
160
  /**
70
- * Read GEODefault from "common" bag or return fallback.
161
+ * Enable or disable ENV fallback for BaseCurrency.
162
+ * When enabled and "common.BaseCurrency" is not set,
163
+ * getBaseCurrency() will use process.env.BASE_CURRENCY.
164
+ * @param {boolean} enabled
71
165
  */
72
- function getDefaultGEO() {
73
- return getValue('common')?.GEODefault || GEO_DEFAULT;
166
+ function enableEnvBaseCurrencyFallback(enabled = true) {
167
+ envBaseCurrencyFallbackEnabled = Boolean(enabled);
168
+ }
169
+
170
+ /**
171
+ * Get BaseCurrency from "common", otherwise (optionally) from ENV.
172
+ * Returns null if nothing is set or valid.
173
+ * @returns {string|null}
174
+ */
175
+ function getBaseCurrency() {
176
+ // 1) Primary source: "common.BaseCurrency"
177
+ const fromCommon = getValue('common')?.BaseCurrency;
178
+ const normalizedCommon = normalizeCurrencyCode(fromCommon);
179
+ if (normalizedCommon) return normalizedCommon;
180
+
181
+ // 2) Optional fallback: process.env.BASE_CURRENCY (lazy read)
182
+ if (envBaseCurrencyFallbackEnabled) {
183
+ const normalizedEnv = normalizeCurrencyCode(process.env.BASE_CURRENCY);
184
+ if (normalizedEnv) return normalizedEnv;
185
+ }
186
+
187
+ // 3) Nothing found
188
+ return null;
189
+ }
190
+
191
+ /**
192
+ * Explicitly set BaseCurrency into the "common" bag.
193
+ * @param {string} code - 3-letter code, e.g., "USD", "EUR", "GBP".
194
+ * @throws {Error} If code is invalid.
195
+ */
196
+ function setBaseCurrency(code) {
197
+ const normalized = normalizeCurrencyCode(code);
198
+ if (!normalized) {
199
+ throw new Error(
200
+ 'Invalid currency code. Expected a 3-letter uppercase code like "USD", "EUR", or "GBP".'
201
+ );
202
+ }
203
+ const common = ensureCommonBag();
204
+ common.BaseCurrency = normalized;
74
205
  }
75
206
 
207
+ // ---- Exports ---------------------------------------------------------------
208
+
76
209
  module.exports = {
210
+ // KV
77
211
  setValue,
78
212
  getValue,
79
- parseInitConfig,
80
213
  applyConfig,
81
214
  getData,
215
+
216
+ // Service/meta
82
217
  getService,
83
218
  setService,
84
219
  getBufferChunkSize,
220
+ parseInitConfig,
221
+ getCommonValue,
222
+
223
+ // Domain helpers
85
224
  getDefaultGEO,
86
- getCommonValue
225
+
226
+ // Currency helpers
227
+ getBaseCurrency,
228
+ setBaseCurrency,
229
+ enableEnvBaseCurrencyFallback
87
230
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "b2b-platform-utils",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
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
+ }