b2b-platform-utils 1.1.25 → 1.1.28
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/errorsMap.js +380 -116
- package/fileSystem.js +114 -0
- package/package.json +5 -1
package/errorsMap.js
CHANGED
|
@@ -1,70 +1,333 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
// Purpose: Static error map with backward compatibility and dynamic shortcuts.
|
|
4
4
|
// - Old style calls like errorsMap.mailTemplateNotFound?.() continue to work.
|
|
5
|
-
// - Any property access matching an errorKey returns a function that yields the
|
|
5
|
+
// - Any property access matching an errorKey returns a function that yields the errorKey STRING.
|
|
6
6
|
// - New API: get(), getString(), getCode(), list(), and legacy getSingleError().
|
|
7
7
|
// - Cache-first: if localCache.errorsMap exists, it overrides static entries.
|
|
8
8
|
|
|
9
|
-
const { getValue } = require(
|
|
9
|
+
const { getValue } = require("./localCache");
|
|
10
10
|
|
|
11
11
|
// ---- Static fallback (source of truth when cache is empty) ----
|
|
12
12
|
const STATIC_ERRORS = [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
13
|
+
{
|
|
14
|
+
errorCode: 1000,
|
|
15
|
+
errorKey: "userNotFound",
|
|
16
|
+
httpCode: 404,
|
|
17
|
+
description: "User was not found",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
errorCode: 1001,
|
|
21
|
+
errorKey: "unauthorized",
|
|
22
|
+
httpCode: 401,
|
|
23
|
+
description: "The server requires user authentication",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
errorCode: 1002,
|
|
27
|
+
errorKey: "gameCategoryNotFound",
|
|
28
|
+
httpCode: 404,
|
|
29
|
+
description: "Game`s category was not found",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
errorCode: 1003,
|
|
33
|
+
errorKey: "mediaResourceNotFound",
|
|
34
|
+
httpCode: 404,
|
|
35
|
+
description: "Media resource was not found",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
errorCode: 1004,
|
|
39
|
+
errorKey: "roleNotFound",
|
|
40
|
+
httpCode: 404,
|
|
41
|
+
description: "Role or permission were not found",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
errorCode: 1005,
|
|
45
|
+
errorKey: "duplicatedUniqueField",
|
|
46
|
+
httpCode: 422,
|
|
47
|
+
description: "Some of unique fields were duplicated.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
errorCode: 1006,
|
|
51
|
+
errorKey: "gameCollectionNotFound",
|
|
52
|
+
httpCode: 404,
|
|
53
|
+
description: "Game`s collection was not found",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
errorCode: 1007,
|
|
57
|
+
errorKey: "gameTagNotFound",
|
|
58
|
+
httpCode: 404,
|
|
59
|
+
description: "Game`s tag was not found",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
errorCode: 1008,
|
|
63
|
+
errorKey: "invalidConstraintField",
|
|
64
|
+
httpCode: 422,
|
|
65
|
+
description: "Some of foreign keys fields were invalid.",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
errorCode: 1009,
|
|
69
|
+
errorKey: "gameProviderNotFound",
|
|
70
|
+
httpCode: 404,
|
|
71
|
+
description: "Game`s provider was not found",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
errorCode: 1010,
|
|
75
|
+
errorKey: "gameNotFound",
|
|
76
|
+
httpCode: 404,
|
|
77
|
+
description: "Game was not found",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
errorCode: 1011,
|
|
81
|
+
errorKey: "undefinedRequest",
|
|
82
|
+
httpCode: 422,
|
|
83
|
+
description: "Request data or format is undefined for handler.",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
errorCode: 1012,
|
|
87
|
+
errorKey: "widgetNotFound",
|
|
88
|
+
httpCode: 404,
|
|
89
|
+
description: "Widget[CMS] was not found",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
errorCode: 1013,
|
|
93
|
+
errorKey: "segmentNotFound",
|
|
94
|
+
httpCode: 404,
|
|
95
|
+
description: "User`s Segment was not found",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
errorCode: 1014,
|
|
99
|
+
errorKey: "bonusNotFound",
|
|
100
|
+
httpCode: 404,
|
|
101
|
+
description: "Bonus was not found",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
errorCode: 1015,
|
|
105
|
+
errorKey: "userBonusNotFound",
|
|
106
|
+
httpCode: 404,
|
|
107
|
+
description: "User`s bonus was not found",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
errorCode: 1016,
|
|
111
|
+
errorKey: "freeSpinsNotFound",
|
|
112
|
+
httpCode: 404,
|
|
113
|
+
description: "Free spins was not found",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
errorCode: 1017,
|
|
117
|
+
errorKey: "gameWagerListNotFound",
|
|
118
|
+
httpCode: 404,
|
|
119
|
+
description: "Game`s wager list was not found",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
errorCode: 1018,
|
|
123
|
+
errorKey: "bonusAlreadyInUse",
|
|
124
|
+
httpCode: 422,
|
|
125
|
+
description: "Bonus is already use(d)",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
errorCode: 1019,
|
|
129
|
+
errorKey: "bonusRestrictForClaim",
|
|
130
|
+
httpCode: 422,
|
|
131
|
+
description: "Bonus does not fit for user`s claiming",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
errorCode: 1020,
|
|
135
|
+
errorKey: "bonusRewardAttachError",
|
|
136
|
+
httpCode: 422,
|
|
137
|
+
description: "Bonus reward can not be attached.",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
errorCode: 1021,
|
|
141
|
+
errorKey: "loyaltyLevelNotFound",
|
|
142
|
+
httpCode: 404,
|
|
143
|
+
description: "Loyalty level was not found",
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
errorCode: 1022,
|
|
147
|
+
errorKey: "loyaltyPointNotFound",
|
|
148
|
+
httpCode: 404,
|
|
149
|
+
description: "Loyalty point was not found",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
errorCode: 1023,
|
|
153
|
+
errorKey: "notificationNotFound",
|
|
154
|
+
httpCode: 404,
|
|
155
|
+
description: "Notification was not found",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
errorCode: 1024,
|
|
159
|
+
errorKey: "bannerNotFound",
|
|
160
|
+
httpCode: 404,
|
|
161
|
+
description: "Banner was not found",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
errorCode: 1025,
|
|
165
|
+
errorKey: "paymentProviderNotFound",
|
|
166
|
+
httpCode: 404,
|
|
167
|
+
description: "Payment provider was not found",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
errorCode: 1026,
|
|
171
|
+
errorKey: "paymentMethodNotFound",
|
|
172
|
+
httpCode: 404,
|
|
173
|
+
description: "Payment method was not found",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
errorCode: 1027,
|
|
177
|
+
errorKey: "depositNotFound",
|
|
178
|
+
httpCode: 404,
|
|
179
|
+
description: "Deposit was not found",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
errorCode: 1028,
|
|
183
|
+
errorKey: "withdrawalNotFound",
|
|
184
|
+
httpCode: 404,
|
|
185
|
+
description: "Withdrawal was not found",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
errorCode: 1029,
|
|
189
|
+
errorKey: "noteNotFound",
|
|
190
|
+
httpCode: 404,
|
|
191
|
+
description: "User`s Note was not found",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
errorCode: 1030,
|
|
195
|
+
errorKey: "promoCodeNotFound",
|
|
196
|
+
httpCode: 404,
|
|
197
|
+
description: "Promo Code was not found",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
errorCode: 1031,
|
|
201
|
+
errorKey: "cashBackNotFound",
|
|
202
|
+
httpCode: 404,
|
|
203
|
+
description: "Cash Back was not found",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
errorCode: 1032,
|
|
207
|
+
errorKey: "tournamentNotFound",
|
|
208
|
+
httpCode: 404,
|
|
209
|
+
description: "Tournament was not found",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
errorCode: 1033,
|
|
213
|
+
errorKey: "pageNotFound",
|
|
214
|
+
httpCode: 404,
|
|
215
|
+
description: "Page was not found",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
errorCode: 1034,
|
|
219
|
+
errorKey: "gameCashBackListNotFound",
|
|
220
|
+
httpCode: 404,
|
|
221
|
+
description: "Game`s cash back list was not found",
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
errorCode: 1035,
|
|
225
|
+
errorKey: "bonusDepositRequired",
|
|
226
|
+
httpCode: 404,
|
|
227
|
+
description: "For bonus activation desposit requited.",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
errorCode: 1036,
|
|
231
|
+
errorKey: "taskNotFound",
|
|
232
|
+
httpCode: 404,
|
|
233
|
+
description: "Task for schedule was not found.",
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
errorCode: 1037,
|
|
237
|
+
errorKey: "bonusConfirmationRequired",
|
|
238
|
+
httpCode: 404,
|
|
239
|
+
description:
|
|
240
|
+
"For bonus activation confirmation of personal user data requited.",
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
errorCode: 1038,
|
|
244
|
+
errorKey: "promoNotFound",
|
|
245
|
+
httpCode: 404,
|
|
246
|
+
description: "Promo was not found.",
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
errorCode: 1039,
|
|
250
|
+
errorKey: "badSignature",
|
|
251
|
+
httpCode: 401,
|
|
252
|
+
description: "Untrusted request, bad signature.",
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
errorCode: 1040,
|
|
256
|
+
errorKey: "mailTemplateNotFound",
|
|
257
|
+
httpCode: 404,
|
|
258
|
+
description: "Email template was not found.",
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
errorCode: 1041,
|
|
262
|
+
errorKey: "domainNotFound",
|
|
263
|
+
httpCode: 404,
|
|
264
|
+
description: "Domain was not found.",
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
errorCode: 1042,
|
|
268
|
+
errorKey: "domainAlreadyExists",
|
|
269
|
+
httpCode: 422,
|
|
270
|
+
description: "Domain already in use.",
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
errorCode: 1043,
|
|
274
|
+
errorKey: "domainIsNotActivated",
|
|
275
|
+
httpCode: 422,
|
|
276
|
+
description: "Current Domain is not activated yet.",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
errorCode: 1044,
|
|
280
|
+
errorKey: "domainCountriesNotFound",
|
|
281
|
+
httpCode: 404,
|
|
282
|
+
description: "Domain`s Countries were not found for primary zone.",
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
errorCode: 1045,
|
|
286
|
+
errorKey: "footerPartnersNotFound",
|
|
287
|
+
httpCode: 404,
|
|
288
|
+
description: "Footer Partners block were not found.",
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
errorCode: 1046,
|
|
292
|
+
errorKey: "footerBrandCoreNotFound",
|
|
293
|
+
httpCode: 404,
|
|
294
|
+
description: "Footer Brand Core block were not found.",
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
errorCode: 1047,
|
|
298
|
+
errorKey: "footerPaymentsNotFound",
|
|
299
|
+
httpCode: 404,
|
|
300
|
+
description: "Footer Payments block were not found.",
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
errorCode: 1048,
|
|
304
|
+
errorKey: "footerMainTextNotFound",
|
|
305
|
+
httpCode: 404,
|
|
306
|
+
description: "Footer Main Text block were not found.",
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
errorCode: 1049,
|
|
310
|
+
errorKey: "footerApplicationsNotFound",
|
|
311
|
+
httpCode: 404,
|
|
312
|
+
description: "Footer Applications block were not found.",
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
errorCode: 1050,
|
|
316
|
+
errorKey: "footerBlockAlreadyExists",
|
|
317
|
+
httpCode: 422,
|
|
318
|
+
description: "Footer block item already exists.",
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
errorCode: 1051,
|
|
322
|
+
errorKey: "invalidTwoFaCode",
|
|
323
|
+
httpCode: 422,
|
|
324
|
+
description: "Two Factor Auth code is invalid or expired.",
|
|
325
|
+
},
|
|
65
326
|
];
|
|
66
327
|
|
|
67
|
-
const STATIC_BY_KEY = Object.fromEntries(
|
|
328
|
+
const STATIC_BY_KEY = Object.fromEntries(
|
|
329
|
+
STATIC_ERRORS.map((e) => [e.errorKey, e])
|
|
330
|
+
);
|
|
68
331
|
|
|
69
332
|
/**
|
|
70
333
|
* Resolve error by key from cache (if any) or static table.
|
|
@@ -72,77 +335,78 @@ const STATIC_BY_KEY = Object.fromEntries(STATIC_ERRORS.map((e) => [e.errorKey, e
|
|
|
72
335
|
* @returns {object|undefined}
|
|
73
336
|
*/
|
|
74
337
|
function resolveErrorByKey(errorKey) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
338
|
+
const cached = getValue("errorsMap");
|
|
339
|
+
if (Array.isArray(cached) && cached.length) {
|
|
340
|
+
const hit = cached.find((item) => item?.errorKey === errorKey);
|
|
341
|
+
if (hit) return hit;
|
|
342
|
+
}
|
|
343
|
+
return STATIC_BY_KEY[errorKey];
|
|
81
344
|
}
|
|
82
345
|
|
|
83
346
|
// --- Base API object (do not export directly; we wrap it in a Proxy) ---
|
|
84
347
|
const api = {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Legacy accessor kept for compatibility (returns JSON string).
|
|
350
|
+
* @param {string} errorKey
|
|
351
|
+
* @returns {string|undefined}
|
|
352
|
+
*/
|
|
353
|
+
getSingleError: (errorKey) => {
|
|
354
|
+
const obj = resolveErrorByKey(errorKey);
|
|
355
|
+
return obj ? JSON.stringify(obj) : undefined;
|
|
356
|
+
},
|
|
94
357
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
358
|
+
/**
|
|
359
|
+
* New API: get full error object.
|
|
360
|
+
* @param {string} errorKey
|
|
361
|
+
* @returns {object|undefined}
|
|
362
|
+
*/
|
|
363
|
+
get: (errorKey) => resolveErrorByKey(errorKey),
|
|
101
364
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
365
|
+
/**
|
|
366
|
+
* New API: get JSON string version (same shape as legacy).
|
|
367
|
+
* @param {string} errorKey
|
|
368
|
+
* @returns {string|undefined}
|
|
369
|
+
*/
|
|
370
|
+
getString: (errorKey) => {
|
|
371
|
+
const obj = resolveErrorByKey(errorKey);
|
|
372
|
+
return obj ? JSON.stringify(obj) : undefined;
|
|
373
|
+
},
|
|
111
374
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
375
|
+
/**
|
|
376
|
+
* New API: get numeric error code.
|
|
377
|
+
* @param {string} errorKey
|
|
378
|
+
* @returns {number|undefined}
|
|
379
|
+
*/
|
|
380
|
+
getCode: (errorKey) => resolveErrorByKey(errorKey)?.errorCode,
|
|
118
381
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
382
|
+
/**
|
|
383
|
+
* New API: list all static errors (useful for diagnostics).
|
|
384
|
+
* @returns {object[]}
|
|
385
|
+
*/
|
|
386
|
+
list: () => [...STATIC_ERRORS],
|
|
124
387
|
};
|
|
125
388
|
|
|
126
|
-
// --- Dynamic shortcuts: errorsMap.<errorKey>() returns
|
|
389
|
+
// --- Dynamic shortcuts: errorsMap.<errorKey>() returns errorKey STRING ---
|
|
127
390
|
module.exports = new Proxy(api, {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
391
|
+
get(target, prop, receiver) {
|
|
392
|
+
// If method exists (get, getString, etc.) → return it
|
|
393
|
+
if (prop in target) {
|
|
394
|
+
return Reflect.get(target, prop, receiver);
|
|
395
|
+
}
|
|
133
396
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
397
|
+
// If property is a known errorKey (static or cached) → return a function () => errorKey (STRING)
|
|
398
|
+
if (typeof prop === "string" && resolveErrorByKey(prop)) {
|
|
399
|
+
return function () {
|
|
400
|
+
return prop;
|
|
401
|
+
};
|
|
402
|
+
}
|
|
140
403
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
},
|
|
404
|
+
return undefined;
|
|
405
|
+
},
|
|
144
406
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
407
|
+
has(target, prop) {
|
|
408
|
+
return (
|
|
409
|
+
prop in target || (typeof prop === "string" && !!resolveErrorByKey(prop))
|
|
410
|
+
);
|
|
411
|
+
},
|
|
412
|
+
});
|
package/fileSystem.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
const http = require("http");
|
|
4
|
+
const https = require("https");
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* @module fileSystem
|
|
5
8
|
* @description
|
|
@@ -299,6 +302,116 @@ function getAbsoluteMediaResourceUrl(service, instance, fileName) {
|
|
|
299
302
|
return `${baseUrl}/file/${service}/${instance}/${fileName}`;
|
|
300
303
|
}
|
|
301
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Download remote resource to Buffer or read local file to Buffer.
|
|
307
|
+
*
|
|
308
|
+
* @param {string} source - URL (http/https) or local file path.
|
|
309
|
+
* @param {Object} [options]
|
|
310
|
+
* @param {number} [options.timeoutMs=15000] - Request timeout.
|
|
311
|
+
* @param {number} [options.maxBytes=26214400] - Max allowed size in bytes (default 25MB).
|
|
312
|
+
* @param {number} [options.maxRedirects=3] - Max redirects to follow.
|
|
313
|
+
* @returns {Promise<Buffer>}
|
|
314
|
+
*/
|
|
315
|
+
function downloadToBuffer(source, options = {}) {
|
|
316
|
+
const timeoutMs = Number.isInteger(options.timeoutMs)
|
|
317
|
+
? options.timeoutMs
|
|
318
|
+
: 15000;
|
|
319
|
+
const maxBytes = Number.isInteger(options.maxBytes)
|
|
320
|
+
? options.maxBytes
|
|
321
|
+
: 25 * 1024 * 1024;
|
|
322
|
+
const maxRedirects = Number.isInteger(options.maxRedirects)
|
|
323
|
+
? options.maxRedirects
|
|
324
|
+
: 3;
|
|
325
|
+
|
|
326
|
+
if (!source || typeof source !== "string") {
|
|
327
|
+
return Promise.reject(
|
|
328
|
+
new TypeError("downloadToBuffer: source must be a non-empty string")
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const trimmed = source.trim();
|
|
333
|
+
|
|
334
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
335
|
+
return downloadHttpToBuffer(trimmed, { timeoutMs, maxBytes, maxRedirects });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const absPath = resolvePath(trimmed);
|
|
339
|
+
return fsPromises.readFile(absPath);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Download an HTTP/HTTPS resource to Buffer with basic redirect support.
|
|
344
|
+
*
|
|
345
|
+
* @param {string} url
|
|
346
|
+
* @param {Object} options
|
|
347
|
+
* @param {number} options.timeoutMs
|
|
348
|
+
* @param {number} options.maxBytes
|
|
349
|
+
* @param {number} options.maxRedirects
|
|
350
|
+
* @returns {Promise<Buffer>}
|
|
351
|
+
*/
|
|
352
|
+
function downloadHttpToBuffer(url, options) {
|
|
353
|
+
return new Promise((resolve, reject) => {
|
|
354
|
+
const client = url.startsWith("https://") ? https : http;
|
|
355
|
+
|
|
356
|
+
const req = client.get(url, (res) => {
|
|
357
|
+
const statusCode = res.statusCode || 0;
|
|
358
|
+
|
|
359
|
+
// Follow redirects
|
|
360
|
+
if (
|
|
361
|
+
statusCode >= 300 &&
|
|
362
|
+
statusCode < 400 &&
|
|
363
|
+
res.headers.location &&
|
|
364
|
+
options.maxRedirects > 0
|
|
365
|
+
) {
|
|
366
|
+
res.resume();
|
|
367
|
+
const nextUrl = new URL(res.headers.location, url).toString();
|
|
368
|
+
return resolve(
|
|
369
|
+
downloadHttpToBuffer(nextUrl, {
|
|
370
|
+
...options,
|
|
371
|
+
maxRedirects: options.maxRedirects - 1,
|
|
372
|
+
})
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
377
|
+
res.resume();
|
|
378
|
+
return reject(
|
|
379
|
+
new Error(
|
|
380
|
+
`downloadToBuffer: request failed with status ${statusCode}`
|
|
381
|
+
)
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const chunks = [];
|
|
386
|
+
let received = 0;
|
|
387
|
+
|
|
388
|
+
res.on("data", (chunk) => {
|
|
389
|
+
received += chunk.length;
|
|
390
|
+
if (received > options.maxBytes) {
|
|
391
|
+
req.destroy(
|
|
392
|
+
new Error(
|
|
393
|
+
`downloadToBuffer: maxBytes exceeded (${options.maxBytes})`
|
|
394
|
+
)
|
|
395
|
+
);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
chunks.push(chunk);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
402
|
+
res.on("error", reject);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
req.setTimeout(options.timeoutMs, () => {
|
|
406
|
+
req.destroy(
|
|
407
|
+
new Error(`downloadToBuffer: timeout after ${options.timeoutMs}ms`)
|
|
408
|
+
);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
req.on("error", reject);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
302
415
|
// -----------------------------------------------------------------------------
|
|
303
416
|
// Exports
|
|
304
417
|
// -----------------------------------------------------------------------------
|
|
@@ -311,6 +424,7 @@ module.exports = {
|
|
|
311
424
|
readJsonFile,
|
|
312
425
|
removeFile,
|
|
313
426
|
unlinkIfExists,
|
|
427
|
+
downloadToBuffer,
|
|
314
428
|
|
|
315
429
|
// MIME helpers
|
|
316
430
|
getImageExtension,
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "b2b-platform-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.28",
|
|
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",
|
|
7
7
|
"private": false,
|
|
8
|
+
"scripts": {
|
|
9
|
+
"publish-update": "npm version patch --no-git-tag-version && npm publish",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
8
12
|
"files": [
|
|
9
13
|
"*.js",
|
|
10
14
|
"README.md"
|