@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 +28 -6
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +28 -6
- package/package.json +1 -1
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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.
|
|
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",
|