@hdriel/aws-utils 1.2.2 → 1.2.4

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/dist/index.cjs CHANGED
@@ -421,6 +421,15 @@ var getUnitBytes = (bytes2, unit) => {
421
421
  return bytes2;
422
422
  }
423
423
  };
424
+ function hasNonAscii(str) {
425
+ return /[^\x00-\x7F]/.test(str);
426
+ }
427
+ function encodeS3Metadata(value) {
428
+ if (hasNonAscii(value)) {
429
+ return Buffer.from(value, "utf8").toString("base64");
430
+ }
431
+ return value;
432
+ }
424
433
 
425
434
  // src/aws/s3/s3-file.ts
426
435
  var import_buffer = require("buffer");
@@ -1412,7 +1421,7 @@ var S3Stream = class _S3Stream extends S3File {
1412
1421
  this.maxUploadFileSizeRestriction = maxUploadFileSizeRestriction;
1413
1422
  this.s3Limiter = concurrencyVideoLimit ? (0, import_p_limit.default)(concurrencyVideoLimit) : null;
1414
1423
  }
1415
- streamObjectFile(_0) {
1424
+ getObjectFileStream(_0) {
1416
1425
  return __async(this, arguments, function* (fileKey, {
1417
1426
  Range,
1418
1427
  checkFileExists = true,
@@ -1632,7 +1641,7 @@ var S3Stream = class _S3Stream extends S3File {
1632
1641
  next(Error(`File not found: "${normalizedKey}"`));
1633
1642
  return;
1634
1643
  }
1635
- stream = yield this.streamObjectFile(normalizedKey, {
1644
+ stream = yield this.getObjectFileStream(normalizedKey, {
1636
1645
  abortSignal: abort.signal,
1637
1646
  checkFileExists: false
1638
1647
  });
@@ -1642,9 +1651,9 @@ var S3Stream = class _S3Stream extends S3File {
1642
1651
  return;
1643
1652
  }
1644
1653
  const fileInfo = yield this.fileInfo(normalizedKey);
1645
- const fileName = filename || normalizedKey.split("/").pop() || "download";
1646
1654
  const contentType = fileInfo.ContentType || "application/octet-stream";
1647
1655
  const ext = (0, import_pathe2.extname)(fileKey).slice(1).toLowerCase();
1656
+ const fileName = filename || normalizedKey.split("/").pop() || `${Date.now()}.${ext}`;
1648
1657
  const inlineTypes = ["text/", "image/", "application/pdf", "video/", "audio/"];
1649
1658
  const canDisplayInline = SUPPORTED_IFRAME_EXTENSIONS.includes(ext) || inlineTypes.some((type) => contentType.startsWith(type));
1650
1659
  res.setHeader("Content-Type", contentType);
@@ -1660,6 +1669,7 @@ var S3Stream = class _S3Stream extends S3File {
1660
1669
  if (cachingAge) {
1661
1670
  res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
1662
1671
  }
1672
+ res.setHeader("Access-Control-Expose-Headers", "Content-Type, Content-Disposition, Content-Length");
1663
1673
  stream.on("error", (err) => {
1664
1674
  var _a3, _b2;
1665
1675
  (_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "Stream error", { fileKey: normalizedKey, error: err });
@@ -1734,7 +1744,7 @@ var S3Stream = class _S3Stream extends S3File {
1734
1744
  var _a3, _b2, _c2;
1735
1745
  try {
1736
1746
  if (abort.signal.aborted) return null;
1737
- const stream = yield this.streamObjectFile(fileKey2, { abortSignal: abort.signal });
1747
+ const stream = yield this.getObjectFileStream(fileKey2, { abortSignal: abort.signal });
1738
1748
  if (!stream) {
1739
1749
  (_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { fileKey: fileKey2 });
1740
1750
  return null;
@@ -1883,10 +1893,22 @@ var S3Stream = class _S3Stream extends S3File {
1883
1893
  bucket: this.bucket,
1884
1894
  contentType: import_multer_s3.default.AUTO_CONTENT_TYPE,
1885
1895
  metadata: (req, file, cb) => __async(this, null, function* () {
1886
- const baseMetadata = __spreadProps(__spreadValues({}, file), { directory: normalizedPath });
1896
+ const originalName = decodeURIComponent(file.originalname);
1897
+ const baseMetadata = __spreadProps(__spreadValues({}, file), {
1898
+ directory: normalizedPath,
1899
+ // Encode non-ASCII characters for S3 metadata
1900
+ originalname: encodeS3Metadata(originalName),
1901
+ // Optional: Add a flag to know it's encoded
1902
+ // @ts-ignore
1903
+ "originalname-encoded": hasNonAscii(originalName) ? "base64" : "plain"
1904
+ });
1887
1905
  if (customMetadata) {
1888
1906
  const additionalMetadata = typeof customMetadata === "function" ? yield customMetadata(req, file) : customMetadata;
1889
- Object.assign(baseMetadata, additionalMetadata);
1907
+ const sanitizedMetadata = {};
1908
+ for (const [key, value] of Object.entries(additionalMetadata)) {
1909
+ sanitizedMetadata[key] = typeof value === "string" ? encodeS3Metadata(value) : String(value);
1910
+ }
1911
+ Object.assign(baseMetadata, sanitizedMetadata);
1890
1912
  }
1891
1913
  cb(null, baseMetadata);
1892
1914
  }),
package/dist/index.d.cts CHANGED
@@ -283,7 +283,7 @@ declare class S3Stream extends S3File {
283
283
  private readonly maxUploadFileSizeRestriction;
284
284
  private readonly s3Limiter;
285
285
  constructor({ maxUploadFileSizeRestriction, concurrencyVideoLimit, ...props }: S3StreamProps);
286
- protected streamObjectFile(fileKey: string, { Range, checkFileExists, abortSignal, }?: {
286
+ getObjectFileStream(fileKey: string, { Range, checkFileExists, abortSignal, }?: {
287
287
  Range?: string;
288
288
  checkFileExists?: boolean;
289
289
  abortSignal?: AbortSignal;
package/dist/index.d.ts CHANGED
@@ -283,7 +283,7 @@ declare class S3Stream extends S3File {
283
283
  private readonly maxUploadFileSizeRestriction;
284
284
  private readonly s3Limiter;
285
285
  constructor({ maxUploadFileSizeRestriction, concurrencyVideoLimit, ...props }: S3StreamProps);
286
- protected streamObjectFile(fileKey: string, { Range, checkFileExists, abortSignal, }?: {
286
+ getObjectFileStream(fileKey: string, { Range, checkFileExists, abortSignal, }?: {
287
287
  Range?: string;
288
288
  checkFileExists?: boolean;
289
289
  abortSignal?: AbortSignal;
package/dist/index.js CHANGED
@@ -381,6 +381,15 @@ var getUnitBytes = (bytes2, unit) => {
381
381
  return bytes2;
382
382
  }
383
383
  };
384
+ function hasNonAscii(str) {
385
+ return /[^\x00-\x7F]/.test(str);
386
+ }
387
+ function encodeS3Metadata(value) {
388
+ if (hasNonAscii(value)) {
389
+ return Buffer.from(value, "utf8").toString("base64");
390
+ }
391
+ return value;
392
+ }
384
393
 
385
394
  // src/aws/s3/s3-file.ts
386
395
  import { Buffer as Buffer2 } from "buffer";
@@ -1400,7 +1409,7 @@ var S3Stream = class _S3Stream extends S3File {
1400
1409
  this.maxUploadFileSizeRestriction = maxUploadFileSizeRestriction;
1401
1410
  this.s3Limiter = concurrencyVideoLimit ? pLimit(concurrencyVideoLimit) : null;
1402
1411
  }
1403
- streamObjectFile(_0) {
1412
+ getObjectFileStream(_0) {
1404
1413
  return __async(this, arguments, function* (fileKey, {
1405
1414
  Range,
1406
1415
  checkFileExists = true,
@@ -1620,7 +1629,7 @@ var S3Stream = class _S3Stream extends S3File {
1620
1629
  next(Error(`File not found: "${normalizedKey}"`));
1621
1630
  return;
1622
1631
  }
1623
- stream = yield this.streamObjectFile(normalizedKey, {
1632
+ stream = yield this.getObjectFileStream(normalizedKey, {
1624
1633
  abortSignal: abort.signal,
1625
1634
  checkFileExists: false
1626
1635
  });
@@ -1630,9 +1639,9 @@ var S3Stream = class _S3Stream extends S3File {
1630
1639
  return;
1631
1640
  }
1632
1641
  const fileInfo = yield this.fileInfo(normalizedKey);
1633
- const fileName = filename || normalizedKey.split("/").pop() || "download";
1634
1642
  const contentType = fileInfo.ContentType || "application/octet-stream";
1635
1643
  const ext = extname(fileKey).slice(1).toLowerCase();
1644
+ const fileName = filename || normalizedKey.split("/").pop() || `${Date.now()}.${ext}`;
1636
1645
  const inlineTypes = ["text/", "image/", "application/pdf", "video/", "audio/"];
1637
1646
  const canDisplayInline = SUPPORTED_IFRAME_EXTENSIONS.includes(ext) || inlineTypes.some((type) => contentType.startsWith(type));
1638
1647
  res.setHeader("Content-Type", contentType);
@@ -1648,6 +1657,7 @@ var S3Stream = class _S3Stream extends S3File {
1648
1657
  if (cachingAge) {
1649
1658
  res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
1650
1659
  }
1660
+ res.setHeader("Access-Control-Expose-Headers", "Content-Type, Content-Disposition, Content-Length");
1651
1661
  stream.on("error", (err) => {
1652
1662
  var _a3, _b2;
1653
1663
  (_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "Stream error", { fileKey: normalizedKey, error: err });
@@ -1722,7 +1732,7 @@ var S3Stream = class _S3Stream extends S3File {
1722
1732
  var _a3, _b2, _c2;
1723
1733
  try {
1724
1734
  if (abort.signal.aborted) return null;
1725
- const stream = yield this.streamObjectFile(fileKey2, { abortSignal: abort.signal });
1735
+ const stream = yield this.getObjectFileStream(fileKey2, { abortSignal: abort.signal });
1726
1736
  if (!stream) {
1727
1737
  (_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { fileKey: fileKey2 });
1728
1738
  return null;
@@ -1871,10 +1881,22 @@ var S3Stream = class _S3Stream extends S3File {
1871
1881
  bucket: this.bucket,
1872
1882
  contentType: multerS3.AUTO_CONTENT_TYPE,
1873
1883
  metadata: (req, file, cb) => __async(this, null, function* () {
1874
- const baseMetadata = __spreadProps(__spreadValues({}, file), { directory: normalizedPath });
1884
+ const originalName = decodeURIComponent(file.originalname);
1885
+ const baseMetadata = __spreadProps(__spreadValues({}, file), {
1886
+ directory: normalizedPath,
1887
+ // Encode non-ASCII characters for S3 metadata
1888
+ originalname: encodeS3Metadata(originalName),
1889
+ // Optional: Add a flag to know it's encoded
1890
+ // @ts-ignore
1891
+ "originalname-encoded": hasNonAscii(originalName) ? "base64" : "plain"
1892
+ });
1875
1893
  if (customMetadata) {
1876
1894
  const additionalMetadata = typeof customMetadata === "function" ? yield customMetadata(req, file) : customMetadata;
1877
- Object.assign(baseMetadata, additionalMetadata);
1895
+ const sanitizedMetadata = {};
1896
+ for (const [key, value] of Object.entries(additionalMetadata)) {
1897
+ sanitizedMetadata[key] = typeof value === "string" ? encodeS3Metadata(value) : String(value);
1898
+ }
1899
+ Object.assign(baseMetadata, sanitizedMetadata);
1878
1900
  }
1879
1901
  cb(null, baseMetadata);
1880
1902
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hdriel/aws-utils",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Simplified AWS SDK (v3) utilities for S3 (upload, download, streaming) with TypeScript support",
5
5
  "author": "Hadriel Benjo (https://github.com/hdriel)",
6
6
  "type": "module",