@zucker-framework/storage 1.0.0 → 1.0.5
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/README.md +19 -0
- package/dist/index.d.mts +135 -14
- package/dist/index.d.ts +135 -14
- package/dist/index.js +323 -25
- package/dist/index.mjs +316 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,18 +31,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
var index_exports = {};
|
|
33
33
|
__export(index_exports, {
|
|
34
|
+
BufferedFileManagerStore: () => BufferedFileManagerStore,
|
|
34
35
|
DANGEROUS_EXTENSIONS: () => DANGEROUS_EXTENSIONS,
|
|
35
36
|
FILE_MANAGER: () => FILE_MANAGER,
|
|
36
37
|
FileLifecycleManager: () => FileLifecycleManager,
|
|
37
38
|
LocalFileStorage: () => LocalFileStorage,
|
|
38
39
|
S3Storage: () => S3Storage,
|
|
39
40
|
StorageProvider: () => StorageProvider,
|
|
41
|
+
StorageTransferError: () => StorageTransferError,
|
|
42
|
+
UnavailableFileManager: () => UnavailableFileManager,
|
|
40
43
|
WebDavStorage: () => WebDavStorage,
|
|
41
44
|
ZuckerStorageModule: () => ZuckerStorageModule,
|
|
42
45
|
generateDateBasedKey: () => generateDateBasedKey,
|
|
46
|
+
hydrateStorageUrls: () => hydrateStorageUrls,
|
|
43
47
|
isFileDenied: () => isFileDenied,
|
|
44
48
|
isSafeOriginalFileName: () => isSafeOriginalFileName,
|
|
45
|
-
sanitizeFileName: () => sanitizeFileName
|
|
49
|
+
sanitizeFileName: () => sanitizeFileName,
|
|
50
|
+
transferVerifiedObject: () => transferVerifiedObject,
|
|
51
|
+
verifyStoredObject: () => verifyStoredObject
|
|
46
52
|
});
|
|
47
53
|
module.exports = __toCommonJS(index_exports);
|
|
48
54
|
|
|
@@ -431,6 +437,7 @@ LocalFileStorage = _ts_decorate([
|
|
|
431
437
|
var import_common2 = require("@nestjs/common");
|
|
432
438
|
var import_client_s3 = require("@aws-sdk/client-s3");
|
|
433
439
|
var import_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
440
|
+
var import_node_stream = require("stream");
|
|
434
441
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
435
442
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
436
443
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -523,6 +530,9 @@ var S3Storage = class _S3Storage {
|
|
|
523
530
|
if (!response.Body) {
|
|
524
531
|
throw new Error("Stored object response had no body");
|
|
525
532
|
}
|
|
533
|
+
if (!(response.Body instanceof import_node_stream.Readable)) {
|
|
534
|
+
throw new Error("Stored object response must be a Node readable stream");
|
|
535
|
+
}
|
|
526
536
|
return {
|
|
527
537
|
stream: response.Body,
|
|
528
538
|
size: response.ContentLength ?? 0
|
|
@@ -629,7 +639,8 @@ __name(encodeKeyPath, "encodeKeyPath");
|
|
|
629
639
|
// src/webdav-storage.ts
|
|
630
640
|
var import_common3 = require("@nestjs/common");
|
|
631
641
|
var path2 = __toESM(require("path"));
|
|
632
|
-
var
|
|
642
|
+
var import_node_stream2 = require("stream");
|
|
643
|
+
var import_promises = require("stream/promises");
|
|
633
644
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
634
645
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
635
646
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -662,7 +673,7 @@ var WebDavStorage = class _WebDavStorage {
|
|
|
662
673
|
providerType = "webdav";
|
|
663
674
|
options;
|
|
664
675
|
constructor(options) {
|
|
665
|
-
if (!options.url || !options.username || !options.password) {
|
|
676
|
+
if (!options.url || !options.allowAnonymous && (!options.username || !options.password)) {
|
|
666
677
|
throw new Error("WebDAV url, username and password are required");
|
|
667
678
|
}
|
|
668
679
|
this.options = options;
|
|
@@ -689,7 +700,7 @@ var WebDavStorage = class _WebDavStorage {
|
|
|
689
700
|
const remotePath = this.resolvePath(key);
|
|
690
701
|
const parentDir = remotePath.substring(0, remotePath.lastIndexOf("/"));
|
|
691
702
|
if (parentDir && parentDir !== "/") {
|
|
692
|
-
await this.
|
|
703
|
+
await this.ensureRemoteDirectory(parentDir);
|
|
693
704
|
}
|
|
694
705
|
await client.putFileContents(remotePath, buffer, {
|
|
695
706
|
contentLength: buffer.length,
|
|
@@ -705,21 +716,87 @@ var WebDavStorage = class _WebDavStorage {
|
|
|
705
716
|
};
|
|
706
717
|
}
|
|
707
718
|
async uploadStream(stream, key, options) {
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
719
|
+
const controller = new AbortController();
|
|
720
|
+
const abort = /* @__PURE__ */ __name(() => controller.abort(options?.signal?.reason), "abort");
|
|
721
|
+
options?.signal?.addEventListener("abort", abort, {
|
|
722
|
+
once: true
|
|
723
|
+
});
|
|
724
|
+
if (options?.signal?.aborted) abort();
|
|
725
|
+
let size = 0;
|
|
726
|
+
const counter = new import_node_stream2.Transform({
|
|
727
|
+
transform(chunk, _encoding, callback) {
|
|
728
|
+
size += chunk.length;
|
|
729
|
+
callback(null, chunk);
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
let transfer;
|
|
733
|
+
let pumping;
|
|
734
|
+
const sourceError = /* @__PURE__ */ __name((error) => controller.abort(error), "sourceError");
|
|
735
|
+
stream.on("error", sourceError);
|
|
736
|
+
(0, import_node_stream2.addAbortSignal)(controller.signal, stream);
|
|
737
|
+
try {
|
|
738
|
+
controller.signal.throwIfAborted();
|
|
739
|
+
const client = await this.getClient();
|
|
740
|
+
const remotePath = this.resolvePath(key);
|
|
741
|
+
await this.ensureRemoteDirectory(path2.posix.dirname(remotePath), controller.signal);
|
|
742
|
+
controller.signal.throwIfAborted();
|
|
743
|
+
transfer = client.putFileContents(remotePath, counter, {
|
|
744
|
+
overwrite: true,
|
|
745
|
+
contentLength: false,
|
|
746
|
+
signal: controller.signal,
|
|
747
|
+
headers: options?.contentType ? {
|
|
748
|
+
"Content-Type": options.contentType
|
|
749
|
+
} : void 0
|
|
750
|
+
});
|
|
751
|
+
pumping = (0, import_promises.pipeline)(stream, counter, {
|
|
752
|
+
signal: controller.signal
|
|
753
|
+
});
|
|
754
|
+
await Promise.all([
|
|
755
|
+
transfer,
|
|
756
|
+
pumping
|
|
757
|
+
]);
|
|
758
|
+
return {
|
|
759
|
+
key,
|
|
760
|
+
url: this.buildUrl(key),
|
|
761
|
+
size
|
|
762
|
+
};
|
|
763
|
+
} catch (error) {
|
|
764
|
+
controller.abort(error);
|
|
765
|
+
stream.destroy(error instanceof Error ? error : new Error("Storage upload failed"));
|
|
766
|
+
counter.destroy();
|
|
767
|
+
await Promise.allSettled([
|
|
768
|
+
transfer,
|
|
769
|
+
pumping,
|
|
770
|
+
(0, import_promises.finished)(stream, {
|
|
771
|
+
cleanup: true
|
|
772
|
+
})
|
|
773
|
+
]);
|
|
774
|
+
throw error;
|
|
775
|
+
} finally {
|
|
776
|
+
options?.signal?.removeEventListener("abort", abort);
|
|
777
|
+
stream.off("error", sourceError);
|
|
711
778
|
}
|
|
712
|
-
return this.upload(Buffer.concat(chunks), key, options);
|
|
713
779
|
}
|
|
714
|
-
async download(key) {
|
|
780
|
+
async download(key, options) {
|
|
781
|
+
options?.signal?.throwIfAborted();
|
|
715
782
|
const client = await this.getClient();
|
|
716
783
|
const remotePath = this.resolvePath(key);
|
|
717
|
-
const stat = await client.stat(remotePath
|
|
718
|
-
|
|
719
|
-
|
|
784
|
+
const stat = await client.stat(remotePath, ...options?.signal ? [
|
|
785
|
+
{
|
|
786
|
+
signal: options.signal
|
|
787
|
+
}
|
|
788
|
+
] : []);
|
|
789
|
+
if (stat.type !== "file") throw new Error("Remote path is not a file");
|
|
790
|
+
options?.signal?.throwIfAborted();
|
|
791
|
+
const stream = client.createReadStream(remotePath, ...options?.signal ? [
|
|
792
|
+
{
|
|
793
|
+
signal: options.signal
|
|
794
|
+
}
|
|
795
|
+
] : []);
|
|
796
|
+
if (options?.signal) (0, import_node_stream2.addAbortSignal)(options.signal, stream);
|
|
720
797
|
return {
|
|
721
|
-
stream
|
|
722
|
-
size: stat.size
|
|
798
|
+
stream,
|
|
799
|
+
size: stat.size
|
|
723
800
|
};
|
|
724
801
|
}
|
|
725
802
|
async delete(key) {
|
|
@@ -744,22 +821,26 @@ var WebDavStorage = class _WebDavStorage {
|
|
|
744
821
|
async getDownloadUrl(key) {
|
|
745
822
|
return this.getPublicUrl(key);
|
|
746
823
|
}
|
|
747
|
-
async list(prefix) {
|
|
824
|
+
async list(prefix, options = {}) {
|
|
748
825
|
const client = await this.getClient();
|
|
749
826
|
const remotePath = this.resolvePath(prefix);
|
|
750
827
|
try {
|
|
751
828
|
const items = await client.getDirectoryContents(remotePath, {
|
|
752
|
-
deep: true
|
|
829
|
+
deep: options.deep ?? true
|
|
753
830
|
});
|
|
754
|
-
return items.filter((item) => item.type === "file").map((item) => ({
|
|
831
|
+
return items.filter((item) => item.type === "file" || options.includeDirectories).map((item) => ({
|
|
755
832
|
key: this.stripBasePath(item.filename),
|
|
833
|
+
type: item.type,
|
|
834
|
+
filename: item.filename,
|
|
835
|
+
basename: item.basename,
|
|
836
|
+
lastmod: item.lastmod,
|
|
756
837
|
size: item.size ?? 0,
|
|
757
838
|
lastModified: new Date(item.lastmod ?? Date.now()),
|
|
758
839
|
contentType: item.mime
|
|
759
840
|
}));
|
|
760
841
|
} catch (error) {
|
|
761
842
|
const err = error;
|
|
762
|
-
if (err.status === 404) return [];
|
|
843
|
+
if (err.status === 404 && options.missing !== "throw") return [];
|
|
763
844
|
throw error;
|
|
764
845
|
}
|
|
765
846
|
}
|
|
@@ -804,7 +885,12 @@ var WebDavStorage = class _WebDavStorage {
|
|
|
804
885
|
/**
|
|
805
886
|
* 递归创建目录
|
|
806
887
|
*/
|
|
807
|
-
|
|
888
|
+
/** Accepts a storage key, with the same traversal protection as upload. */
|
|
889
|
+
async ensureDirectory(key) {
|
|
890
|
+
return this.ensureRemoteDirectory(this.resolvePath(key));
|
|
891
|
+
}
|
|
892
|
+
async ensureRemoteDirectory(dirPath, signal) {
|
|
893
|
+
if (signal) return this.createDirectoryIfMissing(dirPath, signal);
|
|
808
894
|
const inflight = this.directoryPromises.get(dirPath);
|
|
809
895
|
if (inflight) return inflight;
|
|
810
896
|
const creation = this.createDirectoryIfMissing(dirPath);
|
|
@@ -817,21 +903,35 @@ var WebDavStorage = class _WebDavStorage {
|
|
|
817
903
|
}
|
|
818
904
|
}
|
|
819
905
|
}
|
|
820
|
-
async createDirectoryIfMissing(dirPath) {
|
|
906
|
+
async createDirectoryIfMissing(dirPath, signal) {
|
|
907
|
+
signal?.throwIfAborted();
|
|
821
908
|
const client = await this.getClient();
|
|
822
|
-
if (await client.exists(dirPath
|
|
909
|
+
if (await client.exists(dirPath, ...signal ? [
|
|
910
|
+
{
|
|
911
|
+
signal
|
|
912
|
+
}
|
|
913
|
+
] : [])) return;
|
|
823
914
|
const parent = dirPath.substring(0, dirPath.lastIndexOf("/"));
|
|
824
915
|
if (parent && parent !== "/") {
|
|
825
|
-
await this.
|
|
916
|
+
await this.ensureRemoteDirectory(parent, signal);
|
|
826
917
|
}
|
|
827
918
|
try {
|
|
828
|
-
|
|
919
|
+
signal?.throwIfAborted();
|
|
920
|
+
await client.createDirectory(dirPath, ...signal ? [
|
|
921
|
+
{
|
|
922
|
+
signal
|
|
923
|
+
}
|
|
924
|
+
] : []);
|
|
829
925
|
} catch (error) {
|
|
830
926
|
const status = error.status;
|
|
831
927
|
if ([
|
|
832
928
|
405,
|
|
833
929
|
409
|
|
834
|
-
].includes(status ?? 0) && await client.exists(dirPath
|
|
930
|
+
].includes(status ?? 0) && await client.exists(dirPath, ...signal ? [
|
|
931
|
+
{
|
|
932
|
+
signal
|
|
933
|
+
}
|
|
934
|
+
] : [])) return;
|
|
835
935
|
throw error;
|
|
836
936
|
}
|
|
837
937
|
}
|
|
@@ -1057,6 +1157,198 @@ ZuckerStorageModule = _ts_decorate5([
|
|
|
1057
1157
|
(0, import_common5.Module)({})
|
|
1058
1158
|
], ZuckerStorageModule);
|
|
1059
1159
|
|
|
1160
|
+
// src/storage-url-hydrator.ts
|
|
1161
|
+
function isPlainRecord(value) {
|
|
1162
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
1163
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1164
|
+
return prototype === Object.prototype || prototype === null;
|
|
1165
|
+
}
|
|
1166
|
+
__name(isPlainRecord, "isPlainRecord");
|
|
1167
|
+
async function hydrateStorageUrls(value, options) {
|
|
1168
|
+
const signedUrls = /* @__PURE__ */ new Map();
|
|
1169
|
+
const hydrate = /* @__PURE__ */ __name(async (current) => {
|
|
1170
|
+
if (Array.isArray(current)) return Promise.all(current.map(hydrate));
|
|
1171
|
+
if (!isPlainRecord(current)) return current;
|
|
1172
|
+
const entries = await Promise.all(Object.entries(current).map(async ([key2, nested]) => [
|
|
1173
|
+
key2,
|
|
1174
|
+
await hydrate(nested)
|
|
1175
|
+
]));
|
|
1176
|
+
const result = Object.fromEntries(entries);
|
|
1177
|
+
const key = result[options.storageKeyField];
|
|
1178
|
+
if (typeof key === "string" && key.length > 0 && options.allowedStorageKeys.has(key)) {
|
|
1179
|
+
let signed = signedUrls.get(key);
|
|
1180
|
+
if (!signed) {
|
|
1181
|
+
signed = options.getDownloadUrl(key);
|
|
1182
|
+
signedUrls.set(key, signed);
|
|
1183
|
+
}
|
|
1184
|
+
const url = await signed;
|
|
1185
|
+
for (const field of options.urlFields) {
|
|
1186
|
+
Object.defineProperty(result, field, {
|
|
1187
|
+
value: url,
|
|
1188
|
+
writable: true,
|
|
1189
|
+
enumerable: true,
|
|
1190
|
+
configurable: true
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return result;
|
|
1195
|
+
}, "hydrate");
|
|
1196
|
+
return hydrate(value);
|
|
1197
|
+
}
|
|
1198
|
+
__name(hydrateStorageUrls, "hydrateStorageUrls");
|
|
1199
|
+
|
|
1200
|
+
// src/unavailable-file-manager.ts
|
|
1201
|
+
var UnavailableFileManager = class {
|
|
1202
|
+
static {
|
|
1203
|
+
__name(this, "UnavailableFileManager");
|
|
1204
|
+
}
|
|
1205
|
+
providerType;
|
|
1206
|
+
message;
|
|
1207
|
+
constructor(providerType, message = `Storage provider ${providerType} is not configured`) {
|
|
1208
|
+
this.providerType = providerType;
|
|
1209
|
+
this.message = message;
|
|
1210
|
+
}
|
|
1211
|
+
isConfigured() {
|
|
1212
|
+
return false;
|
|
1213
|
+
}
|
|
1214
|
+
unavailable() {
|
|
1215
|
+
return new Error(this.message);
|
|
1216
|
+
}
|
|
1217
|
+
upload(_buffer, _key, _options) {
|
|
1218
|
+
return Promise.reject(this.unavailable());
|
|
1219
|
+
}
|
|
1220
|
+
uploadStream(_stream, _key, _options) {
|
|
1221
|
+
return Promise.reject(this.unavailable());
|
|
1222
|
+
}
|
|
1223
|
+
download(_key) {
|
|
1224
|
+
return Promise.reject(this.unavailable());
|
|
1225
|
+
}
|
|
1226
|
+
delete(_key) {
|
|
1227
|
+
return Promise.reject(this.unavailable());
|
|
1228
|
+
}
|
|
1229
|
+
exists(_key) {
|
|
1230
|
+
return Promise.resolve(false);
|
|
1231
|
+
}
|
|
1232
|
+
getPublicUrl(_key) {
|
|
1233
|
+
return Promise.reject(this.unavailable());
|
|
1234
|
+
}
|
|
1235
|
+
getDownloadUrl(_key) {
|
|
1236
|
+
return Promise.reject(this.unavailable());
|
|
1237
|
+
}
|
|
1238
|
+
list(_prefix) {
|
|
1239
|
+
return Promise.resolve([]);
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
// src/verified-object-transfer.ts
|
|
1244
|
+
var import_node_crypto2 = require("crypto");
|
|
1245
|
+
var StorageTransferError = class extends Error {
|
|
1246
|
+
static {
|
|
1247
|
+
__name(this, "StorageTransferError");
|
|
1248
|
+
}
|
|
1249
|
+
code;
|
|
1250
|
+
constructor(code) {
|
|
1251
|
+
super(code), this.code = code;
|
|
1252
|
+
this.name = "StorageTransferError";
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
function verifyStoredObject(object, expected, detectMimeType, errorFactory = (code) => new StorageTransferError(code)) {
|
|
1256
|
+
if (object.reportedSize !== object.body.length || object.body.length !== expected.size) throw errorFactory("TARGET_SIZE_CONFLICT");
|
|
1257
|
+
if (detectMimeType(object.body) !== expected.mimeType) throw errorFactory("TARGET_MIME_CONFLICT");
|
|
1258
|
+
if ((0, import_node_crypto2.createHash)("sha256").update(object.body).digest("hex") !== expected.sha256) throw errorFactory("TARGET_HASH_CONFLICT");
|
|
1259
|
+
}
|
|
1260
|
+
__name(verifyStoredObject, "verifyStoredObject");
|
|
1261
|
+
async function transferVerifiedObject(options) {
|
|
1262
|
+
const fail = options.errorFactory ?? ((code) => new StorageTransferError(code));
|
|
1263
|
+
const body = options.source.body;
|
|
1264
|
+
if (options.source.reportedSize !== body.length || body.length === 0) throw fail("SOURCE_SIZE_MISMATCH");
|
|
1265
|
+
const metadata = {
|
|
1266
|
+
sha256: (0, import_node_crypto2.createHash)("sha256").update(body).digest("hex"),
|
|
1267
|
+
size: body.length,
|
|
1268
|
+
mimeType: options.detectMimeType(body)
|
|
1269
|
+
};
|
|
1270
|
+
if (await options.target.exists(options.destinationKey)) {
|
|
1271
|
+
verifyStoredObject(await options.target.read(options.destinationKey), metadata, options.detectMimeType, fail);
|
|
1272
|
+
return {
|
|
1273
|
+
...metadata,
|
|
1274
|
+
targetDisposition: options.mode === "dry-run" ? "planned" : "reused"
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
if (options.mode === "dry-run" || !options.allowUpload) return {
|
|
1278
|
+
...metadata,
|
|
1279
|
+
targetDisposition: "planned"
|
|
1280
|
+
};
|
|
1281
|
+
const uploaded = await options.target.write(options.destinationKey, body, {
|
|
1282
|
+
contentType: metadata.mimeType,
|
|
1283
|
+
metadata: {
|
|
1284
|
+
sha256: metadata.sha256,
|
|
1285
|
+
size: String(metadata.size),
|
|
1286
|
+
mimeType: metadata.mimeType
|
|
1287
|
+
},
|
|
1288
|
+
private: true
|
|
1289
|
+
});
|
|
1290
|
+
if (uploaded.size !== metadata.size) throw fail("UPLOAD_SIZE_MISMATCH");
|
|
1291
|
+
verifyStoredObject(await options.target.read(options.destinationKey), metadata, options.detectMimeType, fail);
|
|
1292
|
+
return {
|
|
1293
|
+
...metadata,
|
|
1294
|
+
targetDisposition: "uploaded"
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
__name(transferVerifiedObject, "transferVerifiedObject");
|
|
1298
|
+
|
|
1299
|
+
// src/buffered-file-manager-store.ts
|
|
1300
|
+
var BufferedFileManagerStore = class {
|
|
1301
|
+
static {
|
|
1302
|
+
__name(this, "BufferedFileManagerStore");
|
|
1303
|
+
}
|
|
1304
|
+
manager;
|
|
1305
|
+
maxObjectBytes;
|
|
1306
|
+
errorFactory;
|
|
1307
|
+
constructor(manager, maxObjectBytes, errorFactory = (code) => new StorageTransferError(code)) {
|
|
1308
|
+
this.manager = manager;
|
|
1309
|
+
this.maxObjectBytes = maxObjectBytes;
|
|
1310
|
+
this.errorFactory = errorFactory;
|
|
1311
|
+
if (!Number.isSafeInteger(maxObjectBytes) || maxObjectBytes < 1) throw new RangeError("maxObjectBytes must be a positive integer");
|
|
1312
|
+
if (!manager.isConfigured()) throw errorFactory("STORAGE_NOT_CONFIGURED");
|
|
1313
|
+
}
|
|
1314
|
+
async read(key) {
|
|
1315
|
+
const downloaded = await this.manager.download(key);
|
|
1316
|
+
if (downloaded.size < 1 || downloaded.size > this.maxObjectBytes) {
|
|
1317
|
+
downloaded.stream.destroy();
|
|
1318
|
+
throw this.errorFactory("OBJECT_SIZE_OUT_OF_BOUNDS");
|
|
1319
|
+
}
|
|
1320
|
+
const chunks = [];
|
|
1321
|
+
let size = 0;
|
|
1322
|
+
for await (const chunk of downloaded.stream) {
|
|
1323
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1324
|
+
size += buffer.length;
|
|
1325
|
+
if (size > this.maxObjectBytes) {
|
|
1326
|
+
downloaded.stream.destroy();
|
|
1327
|
+
throw this.errorFactory("OBJECT_SIZE_OUT_OF_BOUNDS");
|
|
1328
|
+
}
|
|
1329
|
+
chunks.push(buffer);
|
|
1330
|
+
}
|
|
1331
|
+
return {
|
|
1332
|
+
body: Buffer.concat(chunks),
|
|
1333
|
+
reportedSize: downloaded.size
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
exists(key) {
|
|
1337
|
+
return this.manager.exists(key);
|
|
1338
|
+
}
|
|
1339
|
+
async write(key, body, options) {
|
|
1340
|
+
if (body.length > this.maxObjectBytes) throw this.errorFactory("OBJECT_SIZE_OUT_OF_BOUNDS");
|
|
1341
|
+
const result = await this.manager.upload(body, key, {
|
|
1342
|
+
contentType: options.contentType,
|
|
1343
|
+
metadata: options.metadata,
|
|
1344
|
+
acl: "private"
|
|
1345
|
+
});
|
|
1346
|
+
return {
|
|
1347
|
+
size: result.size
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1060
1352
|
// src/index.ts
|
|
1061
1353
|
var StorageProvider = /* @__PURE__ */ (function(StorageProvider2) {
|
|
1062
1354
|
StorageProvider2["LOCAL"] = "LOCAL";
|
|
@@ -1066,16 +1358,22 @@ var StorageProvider = /* @__PURE__ */ (function(StorageProvider2) {
|
|
|
1066
1358
|
})({});
|
|
1067
1359
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1068
1360
|
0 && (module.exports = {
|
|
1361
|
+
BufferedFileManagerStore,
|
|
1069
1362
|
DANGEROUS_EXTENSIONS,
|
|
1070
1363
|
FILE_MANAGER,
|
|
1071
1364
|
FileLifecycleManager,
|
|
1072
1365
|
LocalFileStorage,
|
|
1073
1366
|
S3Storage,
|
|
1074
1367
|
StorageProvider,
|
|
1368
|
+
StorageTransferError,
|
|
1369
|
+
UnavailableFileManager,
|
|
1075
1370
|
WebDavStorage,
|
|
1076
1371
|
ZuckerStorageModule,
|
|
1077
1372
|
generateDateBasedKey,
|
|
1373
|
+
hydrateStorageUrls,
|
|
1078
1374
|
isFileDenied,
|
|
1079
1375
|
isSafeOriginalFileName,
|
|
1080
|
-
sanitizeFileName
|
|
1376
|
+
sanitizeFileName,
|
|
1377
|
+
transferVerifiedObject,
|
|
1378
|
+
verifyStoredObject
|
|
1081
1379
|
});
|