b2b-platform-utils 1.1.26 → 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.
Files changed (2) hide show
  1. package/fileSystem.js +114 -0
  2. package/package.json +5 -1
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.26",
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"