stratal 0.0.15 → 0.0.16
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/{application-Du0d8O_e.d.mts → application-zG8b-pol.d.mts} +4 -4
- package/dist/{application-Du0d8O_e.d.mts.map → application-zG8b-pol.d.mts.map} +1 -1
- package/dist/{base-email.provider-CNwsPbwm.mjs → base-email.provider-Cuw4OAB0.mjs} +1 -1
- package/dist/{base-email.provider-CNwsPbwm.mjs.map → base-email.provider-Cuw4OAB0.mjs.map} +1 -1
- package/dist/bin/quarry.mjs +47 -43
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +7 -6
- package/dist/cache/index.mjs.map +1 -1
- package/dist/colors-DJaRDXoS.mjs +16 -0
- package/dist/colors-DJaRDXoS.mjs.map +1 -0
- package/dist/{command-DcebcSrL.d.mts → command-B-QH-Vu3.d.mts} +2 -2
- package/dist/{command-DcebcSrL.d.mts.map → command-B-QH-Vu3.d.mts.map} +1 -1
- package/dist/{command-DG_u5ob2.mjs → command-BvCOD6df.mjs} +10 -9
- package/dist/{command-DG_u5ob2.mjs.map → command-BvCOD6df.mjs.map} +1 -1
- package/dist/config/index.d.mts +2 -2
- package/dist/config/index.mjs +7 -6
- package/dist/config/index.mjs.map +1 -1
- package/dist/cron/index.d.mts +1 -1
- package/dist/cron/index.mjs +1 -1
- package/dist/{cron-manager-BRh86QCS.mjs → cron-manager-DR7fiG6o.mjs} +1 -1
- package/dist/{cron-manager-BRh86QCS.mjs.map → cron-manager-DR7fiG6o.mjs.map} +1 -1
- package/dist/di/index.d.mts +1 -1
- package/dist/email/index.d.mts +3 -3
- package/dist/email/index.mjs +13 -11
- package/dist/email/index.mjs.map +1 -1
- package/dist/{en-uVIaxFXR.mjs → en-DaewN8hc.mjs} +1 -1
- package/dist/{en-uVIaxFXR.mjs.map → en-DaewN8hc.mjs.map} +1 -1
- package/dist/errors/index.d.mts +1 -1
- package/dist/errors-H3TZnVeX.mjs +67 -0
- package/dist/errors-H3TZnVeX.mjs.map +1 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/{gateway-context-90CQEQDR.mjs → gateway-context-BkZ4UKaX.mjs} +2 -2
- package/dist/{gateway-context-90CQEQDR.mjs.map → gateway-context-BkZ4UKaX.mjs.map} +1 -1
- package/dist/guards/index.d.mts +3 -3
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-DMbsAxSX.mjs → guards-DUk_Kzst.mjs} +1 -1
- package/dist/{guards-DMbsAxSX.mjs.map → guards-DUk_Kzst.mjs.map} +1 -1
- package/dist/i18n/index.d.mts +2 -2
- package/dist/i18n/index.mjs +11 -10
- package/dist/i18n/messages/en/index.d.mts +1 -1
- package/dist/i18n/messages/en/index.mjs +1 -1
- package/dist/i18n/validation/index.d.mts +1 -1
- package/dist/i18n/validation/index.mjs +1 -1
- package/dist/{i18n.module-qNrpIVts.mjs → i18n.module-W8OJxg3d.mjs} +8 -8
- package/dist/{i18n.module-qNrpIVts.mjs.map → i18n.module-W8OJxg3d.mjs.map} +1 -1
- package/dist/{index-D69rxo8H.d.mts → index-BJWm863C.d.mts} +5 -5
- package/dist/{index-D69rxo8H.d.mts.map → index-BJWm863C.d.mts.map} +1 -1
- package/dist/{index-H-Su81aK.d.mts → index-D9iYu2Yc.d.mts} +3 -3
- package/dist/{index-H-Su81aK.d.mts.map → index-D9iYu2Yc.d.mts.map} +1 -1
- package/dist/{index-CpAN9ENH.d.mts → index-DVhdhLvE.d.mts} +2 -2
- package/dist/{index-CpAN9ENH.d.mts.map → index-DVhdhLvE.d.mts.map} +1 -1
- package/dist/{index-CSuHOJc3.d.mts → index-D_w_Rmtd.d.mts} +1 -1
- package/dist/{index-CSuHOJc3.d.mts.map → index-D_w_Rmtd.d.mts.map} +1 -1
- package/dist/{index-Cfkie8JM.d.mts → index-Dp6A5ywM.d.mts} +1 -1
- package/dist/{index-Cfkie8JM.d.mts.map → index-Dp6A5ywM.d.mts.map} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +16 -15
- package/dist/{is-command-MZDCH-0T.mjs → is-command-BfCgWAcQ.mjs} +2 -2
- package/dist/{is-command-MZDCH-0T.mjs.map → is-command-BfCgWAcQ.mjs.map} +1 -1
- package/dist/{is-seeder-BN9Ej1r7.mjs → is-seeder-CebjZCDn.mjs} +1 -1
- package/dist/{is-seeder-BN9Ej1r7.mjs.map → is-seeder-CebjZCDn.mjs.map} +1 -1
- package/dist/logger/index.d.mts +1 -1
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +2 -2
- package/dist/{middleware-iRhNjsPH.mjs → middleware-C0Ebzswy.mjs} +2 -2
- package/dist/{middleware-iRhNjsPH.mjs.map → middleware-C0Ebzswy.mjs.map} +1 -1
- package/dist/module/index.d.mts +3 -3
- package/dist/module/index.mjs +7 -6
- package/dist/{module-BH7t7BGG.mjs → module-BgdxxzBe.mjs} +4 -4
- package/dist/{module-BH7t7BGG.mjs.map → module-BgdxxzBe.mjs.map} +1 -1
- package/dist/openapi/index.d.mts +3 -3
- package/dist/openapi/index.mjs +11 -10
- package/dist/quarry/index.d.mts +12 -4
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +5 -4
- package/dist/{quarry-registry-BPmKVjhG.mjs → quarry-registry-DCwqVcRp.mjs} +11 -3
- package/dist/{quarry-registry-BPmKVjhG.mjs.map → quarry-registry-DCwqVcRp.mjs.map} +1 -1
- package/dist/queue/index.d.mts +1 -1
- package/dist/queue/index.mjs +8 -7
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-BdXWUvIM.mjs → queue.module-BZvmeAMj.mjs} +2 -2
- package/dist/{queue.module-BdXWUvIM.mjs.map → queue.module-BZvmeAMj.mjs.map} +1 -1
- package/dist/{resend.provider-CQT5be5E.mjs → resend.provider-BCCACQAU.mjs} +2 -2
- package/dist/{resend.provider-CQT5be5E.mjs.map → resend.provider-BCCACQAU.mjs.map} +1 -1
- package/dist/router/index.d.mts +1 -1
- package/dist/router/index.mjs +11 -10
- package/dist/{router-context-BLn4PrRG.mjs → router-context-BEJe9HEB.mjs} +1 -1
- package/dist/{router-context-BLn4PrRG.mjs.map → router-context-BEJe9HEB.mjs.map} +1 -1
- package/dist/s3-storage.provider-BLlzQYiJ.mjs +336 -0
- package/dist/s3-storage.provider-BLlzQYiJ.mjs.map +1 -0
- package/dist/seeder/index.d.mts +4 -4
- package/dist/seeder/index.mjs +4 -3
- package/dist/{seeder-DatfjJvU.mjs → seeder-Cupi5jl-.mjs} +3 -3
- package/dist/{seeder-DatfjJvU.mjs.map → seeder-Cupi5jl-.mjs.map} +1 -1
- package/dist/{smtp.provider-Cj7BUFbJ.mjs → smtp.provider-B8XtOcHU.mjs} +2 -2
- package/dist/{smtp.provider-Cj7BUFbJ.mjs.map → smtp.provider-B8XtOcHU.mjs.map} +1 -1
- package/dist/storage/index.d.mts +6 -272
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +11 -9
- package/dist/{storage-BtcfgibD.mjs → storage-By_ow2o_.mjs} +34 -410
- package/dist/storage-By_ow2o_.mjs.map +1 -0
- package/dist/{stratal-Cm0Yy8v4.mjs → stratal-CE0iTz4f.mjs} +7 -7
- package/dist/{stratal-Cm0Yy8v4.mjs.map → stratal-CE0iTz4f.mjs.map} +1 -1
- package/dist/{types-N84Ak6YT.d.mts → types-CLhOhYsQ.d.mts} +1 -1
- package/dist/{types-N84Ak6YT.d.mts.map → types-CLhOhYsQ.d.mts.map} +1 -1
- package/dist/{types-Cu4jkeiH.d.mts → types-DahElfUw.d.mts} +1 -1
- package/dist/types-DahElfUw.d.mts.map +1 -0
- package/dist/usage-generator-C9hWziY4.mjs +158 -0
- package/dist/usage-generator-C9hWziY4.mjs.map +1 -0
- package/dist/{validation-Dbt-snjx.mjs → validation-Bh875Lyg.mjs} +1 -1
- package/dist/{validation-Dbt-snjx.mjs.map → validation-Bh875Lyg.mjs.map} +1 -1
- package/dist/websocket/index.d.mts +2 -2
- package/dist/websocket/index.mjs +2 -2
- package/dist/workers/index.d.mts +1 -1
- package/dist/workers/index.mjs +16 -15
- package/dist/workers/index.mjs.map +1 -1
- package/package.json +3 -7
- package/dist/storage-BtcfgibD.mjs.map +0 -1
- package/dist/types-Cu4jkeiH.d.mts.map +0 -1
- package/dist/usage-generator-BTZDk5zx.mjs +0 -75
- package/dist/usage-generator-BTZDk5zx.mjs.map +0 -1
|
@@ -1,375 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { s as Scope } from "./errors-CtCi1wn6.mjs";
|
|
2
2
|
import { i as Transient, n as __decorateParam, r as __decorateMetadata, t as __decorate } from "./decorate-D5j-d9_z.mjs";
|
|
3
|
-
import { r as Module } from "./module-
|
|
4
|
-
import { a as withI18n, i as z } from "./validation-
|
|
3
|
+
import { r as Module } from "./module-BgdxxzBe.mjs";
|
|
4
|
+
import { a as withI18n, i as z } from "./validation-Bh875Lyg.mjs";
|
|
5
|
+
import { a as InvalidDiskError, c as DiskNotConfiguredError, n as StorageProviderNotSupportedError, r as PresignedUrlInvalidExpiryError } from "./errors-H3TZnVeX.mjs";
|
|
5
6
|
import { inject } from "tsyringe";
|
|
6
|
-
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, HeadObjectCommand, ListMultipartUploadsCommand, ListPartsCommand, PutObjectCommand, S3Client, UploadPartCommand } from "@aws-sdk/client-s3";
|
|
7
|
-
import { Upload } from "@aws-sdk/lib-storage";
|
|
8
|
-
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
9
|
-
import { DOMParser } from "@xmldom/xmldom";
|
|
10
|
-
//#region src/storage/errors/disk-not-configured.error.ts
|
|
11
|
-
var DiskNotConfiguredError = class extends ApplicationError {
|
|
12
|
-
constructor(disk) {
|
|
13
|
-
super("errors.storage.diskNotConfigured", ERROR_CODES.SYSTEM.CONFIGURATION_ERROR, { disk });
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region src/storage/errors/file-not-found.error.ts
|
|
18
|
-
var FileNotFoundError = class extends ApplicationError {
|
|
19
|
-
constructor(path) {
|
|
20
|
-
super("errors.storage.fileNotFound", ERROR_CODES.RESOURCE.NOT_FOUND, { path });
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/storage/errors/file-too-large.error.ts
|
|
25
|
-
var FileTooLargeError = class extends ApplicationError {
|
|
26
|
-
constructor(size, maxSize) {
|
|
27
|
-
super("errors.storage.fileTooLarge", ERROR_CODES.VALIDATION.INVALID_FORMAT, {
|
|
28
|
-
size,
|
|
29
|
-
maxSize
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/storage/errors/invalid-disk.error.ts
|
|
35
|
-
var InvalidDiskError = class extends ApplicationError {
|
|
36
|
-
constructor(disk) {
|
|
37
|
-
super("errors.storage.invalidDisk", ERROR_CODES.SYSTEM.CONFIGURATION_ERROR, { disk });
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/storage/errors/invalid-file-type.error.ts
|
|
42
|
-
var InvalidFileTypeError = class extends ApplicationError {
|
|
43
|
-
constructor(mimeType) {
|
|
44
|
-
super("errors.storage.invalidFileType", ERROR_CODES.VALIDATION.INVALID_FORMAT, { mimeType });
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
//#endregion
|
|
48
|
-
//#region src/storage/errors/presigned-url-invalid-expiry.error.ts
|
|
49
|
-
var PresignedUrlInvalidExpiryError = class extends ApplicationError {
|
|
50
|
-
constructor(expiresIn, min, max) {
|
|
51
|
-
super("errors.storage.presignedUrlInvalidExpiry", ERROR_CODES.VALIDATION.INVALID_FORMAT, {
|
|
52
|
-
expiresIn,
|
|
53
|
-
min,
|
|
54
|
-
max
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
//#endregion
|
|
59
|
-
//#region src/storage/errors/storage-provider-not-supported.error.ts
|
|
60
|
-
var StorageProviderNotSupportedError = class extends ApplicationError {
|
|
61
|
-
constructor(provider) {
|
|
62
|
-
super("errors.storage.providerNotSupported", ERROR_CODES.SYSTEM.CONFIGURATION_ERROR, { provider });
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/storage/errors/storage-response-body-missing.error.ts
|
|
67
|
-
var StorageResponseBodyMissingError = class extends ApplicationError {
|
|
68
|
-
constructor(path) {
|
|
69
|
-
super("errors.storage.responseBodyMissing", ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR, { path });
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
//#endregion
|
|
73
|
-
//#region src/storage/providers/s3-storage.provider.ts
|
|
74
|
-
/**
|
|
75
|
-
* S3 Storage Provider
|
|
76
|
-
* Implements storage operations using AWS SDK for S3-compatible storage
|
|
77
|
-
* Works with AWS S3, Cloudflare R2, MinIO, and other S3-compatible services
|
|
78
|
-
*
|
|
79
|
-
* Implements IS3MultipartProvider for multipart upload support needed by TUS
|
|
80
|
-
*/
|
|
81
|
-
var S3StorageProvider = class {
|
|
82
|
-
client;
|
|
83
|
-
bucket;
|
|
84
|
-
disk;
|
|
85
|
-
constructor(config) {
|
|
86
|
-
this.client = new S3Client({
|
|
87
|
-
region: config.region || "auto",
|
|
88
|
-
endpoint: config.endpoint,
|
|
89
|
-
credentials: {
|
|
90
|
-
accessKeyId: config.accessKeyId,
|
|
91
|
-
secretAccessKey: config.secretAccessKey
|
|
92
|
-
},
|
|
93
|
-
forcePathStyle: true
|
|
94
|
-
});
|
|
95
|
-
this.bucket = config.bucket;
|
|
96
|
-
this.disk = config.disk;
|
|
97
|
-
}
|
|
98
|
-
async upload(body, path, options) {
|
|
99
|
-
const command = new PutObjectCommand({
|
|
100
|
-
Bucket: this.bucket,
|
|
101
|
-
Key: path,
|
|
102
|
-
Body: body,
|
|
103
|
-
ContentType: options.mimeType,
|
|
104
|
-
ContentLength: options.size,
|
|
105
|
-
Metadata: options.metadata,
|
|
106
|
-
Tagging: options.tagging
|
|
107
|
-
});
|
|
108
|
-
await this.client.send(command);
|
|
109
|
-
return {
|
|
110
|
-
path,
|
|
111
|
-
disk: this.disk,
|
|
112
|
-
fullPath: `${this.bucket}/${path}`,
|
|
113
|
-
size: options.size,
|
|
114
|
-
mimeType: options.mimeType ?? "application/octet-stream",
|
|
115
|
-
uploadedAt: /* @__PURE__ */ new Date()
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
async download(path) {
|
|
119
|
-
const command = new GetObjectCommand({
|
|
120
|
-
Bucket: this.bucket,
|
|
121
|
-
Key: path
|
|
122
|
-
});
|
|
123
|
-
const response = await this.client.send(command);
|
|
124
|
-
if (!response.Body) throw new StorageResponseBodyMissingError(path);
|
|
125
|
-
return {
|
|
126
|
-
toStream: () => response.Body?.transformToWebStream(),
|
|
127
|
-
contentType: response.ContentType ?? "application/octet-stream",
|
|
128
|
-
size: response.ContentLength ?? 0,
|
|
129
|
-
metadata: response.Metadata,
|
|
130
|
-
toString: () => response.Body?.transformToString(),
|
|
131
|
-
toArrayBuffer: () => response.Body?.transformToByteArray()
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
async delete(path) {
|
|
135
|
-
const command = new DeleteObjectCommand({
|
|
136
|
-
Bucket: this.bucket,
|
|
137
|
-
Key: path
|
|
138
|
-
});
|
|
139
|
-
await this.client.send(command);
|
|
140
|
-
}
|
|
141
|
-
async exists(path) {
|
|
142
|
-
try {
|
|
143
|
-
const command = new HeadObjectCommand({
|
|
144
|
-
Bucket: this.bucket,
|
|
145
|
-
Key: path
|
|
146
|
-
});
|
|
147
|
-
await this.client.send(command);
|
|
148
|
-
return true;
|
|
149
|
-
} catch {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
async getPresignedUrl(path, method, expiresIn) {
|
|
154
|
-
let command;
|
|
155
|
-
switch (method) {
|
|
156
|
-
case "GET":
|
|
157
|
-
command = new GetObjectCommand({
|
|
158
|
-
Bucket: this.bucket,
|
|
159
|
-
Key: path
|
|
160
|
-
});
|
|
161
|
-
break;
|
|
162
|
-
case "PUT":
|
|
163
|
-
command = new PutObjectCommand({
|
|
164
|
-
Bucket: this.bucket,
|
|
165
|
-
Key: path
|
|
166
|
-
});
|
|
167
|
-
break;
|
|
168
|
-
case "DELETE":
|
|
169
|
-
command = new DeleteObjectCommand({
|
|
170
|
-
Bucket: this.bucket,
|
|
171
|
-
Key: path
|
|
172
|
-
});
|
|
173
|
-
break;
|
|
174
|
-
case "HEAD":
|
|
175
|
-
command = new HeadObjectCommand({
|
|
176
|
-
Bucket: this.bucket,
|
|
177
|
-
Key: path
|
|
178
|
-
});
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
181
|
-
return {
|
|
182
|
-
url: await getSignedUrl(this.client, command, { expiresIn }),
|
|
183
|
-
expiresIn,
|
|
184
|
-
expiresAt: new Date(Date.now() + expiresIn * 1e3),
|
|
185
|
-
method
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Chunked upload for streaming data without known size
|
|
190
|
-
* Uses @aws-sdk/lib-storage for multipart upload handling
|
|
191
|
-
*
|
|
192
|
-
* Benefits:
|
|
193
|
-
* - Handles unknown Content-Length automatically
|
|
194
|
-
* - Automatic retry on transient failures
|
|
195
|
-
* - Cleanup of partial uploads on error
|
|
196
|
-
* - Works with S3-compatible storage (R2, MinIO, RustFS)
|
|
197
|
-
*
|
|
198
|
-
* @param body - Content to upload (stream or buffer)
|
|
199
|
-
* @param path - Full path including disk root
|
|
200
|
-
* @param options - Upload options (mimeType required, size optional)
|
|
201
|
-
* @returns Upload result with metadata
|
|
202
|
-
*/
|
|
203
|
-
async chunkedUpload(body, path, options) {
|
|
204
|
-
await new Upload({
|
|
205
|
-
client: this.client,
|
|
206
|
-
params: {
|
|
207
|
-
Bucket: this.bucket,
|
|
208
|
-
Key: path,
|
|
209
|
-
Body: body,
|
|
210
|
-
ContentType: options.mimeType
|
|
211
|
-
},
|
|
212
|
-
queueSize: 4,
|
|
213
|
-
partSize: 5 * 1024 * 1024,
|
|
214
|
-
leavePartsOnError: false
|
|
215
|
-
}).done();
|
|
216
|
-
const headResponse = await this.client.send(new HeadObjectCommand({
|
|
217
|
-
Bucket: this.bucket,
|
|
218
|
-
Key: path
|
|
219
|
-
}));
|
|
220
|
-
return {
|
|
221
|
-
path,
|
|
222
|
-
disk: this.disk,
|
|
223
|
-
fullPath: `${this.bucket}/${path}`,
|
|
224
|
-
size: headResponse.ContentLength ?? options.size ?? 0,
|
|
225
|
-
mimeType: options.mimeType ?? "application/octet-stream",
|
|
226
|
-
uploadedAt: /* @__PURE__ */ new Date()
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Get the bucket name
|
|
231
|
-
*/
|
|
232
|
-
getBucket() {
|
|
233
|
-
return this.bucket;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Get object metadata without downloading the body
|
|
237
|
-
*/
|
|
238
|
-
async headObject(key) {
|
|
239
|
-
const response = await this.client.send(new HeadObjectCommand({
|
|
240
|
-
Bucket: this.bucket,
|
|
241
|
-
Key: key
|
|
242
|
-
}));
|
|
243
|
-
return {
|
|
244
|
-
size: response.ContentLength ?? 0,
|
|
245
|
-
contentType: response.ContentType,
|
|
246
|
-
metadata: response.Metadata
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Delete multiple objects in a single request
|
|
251
|
-
*/
|
|
252
|
-
async deleteObjects(keys) {
|
|
253
|
-
if (keys.length === 0) return {
|
|
254
|
-
deleted: 0,
|
|
255
|
-
errors: []
|
|
256
|
-
};
|
|
257
|
-
const response = await this.client.send(new DeleteObjectsCommand({
|
|
258
|
-
Bucket: this.bucket,
|
|
259
|
-
Delete: { Objects: keys.map((key) => ({ Key: key })) }
|
|
260
|
-
}));
|
|
261
|
-
return {
|
|
262
|
-
deleted: response.Deleted?.length ?? 0,
|
|
263
|
-
errors: (response.Errors ?? []).map((e) => ({
|
|
264
|
-
key: e.Key ?? "",
|
|
265
|
-
code: e.Code ?? "Unknown",
|
|
266
|
-
message: e.Message ?? "Unknown error"
|
|
267
|
-
}))
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Create a multipart upload
|
|
272
|
-
*/
|
|
273
|
-
async createMultipartUpload(key, options) {
|
|
274
|
-
const response = await this.client.send(new CreateMultipartUploadCommand({
|
|
275
|
-
Bucket: this.bucket,
|
|
276
|
-
Key: key,
|
|
277
|
-
ContentType: options?.contentType,
|
|
278
|
-
CacheControl: options?.cacheControl,
|
|
279
|
-
Metadata: options?.metadata,
|
|
280
|
-
Tagging: options?.tagging
|
|
281
|
-
}));
|
|
282
|
-
return {
|
|
283
|
-
uploadId: response.UploadId ?? "",
|
|
284
|
-
key: response.Key ?? ""
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Upload a part to an existing multipart upload
|
|
289
|
-
*/
|
|
290
|
-
async uploadPart(key, uploadId, partNumber, body) {
|
|
291
|
-
return {
|
|
292
|
-
etag: (await this.client.send(new UploadPartCommand({
|
|
293
|
-
Bucket: this.bucket,
|
|
294
|
-
Key: key,
|
|
295
|
-
UploadId: uploadId,
|
|
296
|
-
PartNumber: partNumber,
|
|
297
|
-
Body: body,
|
|
298
|
-
ContentLength: body.length
|
|
299
|
-
}))).ETag ?? "",
|
|
300
|
-
partNumber
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Complete a multipart upload
|
|
305
|
-
*/
|
|
306
|
-
async completeMultipartUpload(key, uploadId, parts) {
|
|
307
|
-
const response = await this.client.send(new CompleteMultipartUploadCommand({
|
|
308
|
-
Bucket: this.bucket,
|
|
309
|
-
Key: key,
|
|
310
|
-
UploadId: uploadId,
|
|
311
|
-
MultipartUpload: { Parts: parts.map((p) => ({
|
|
312
|
-
ETag: p.etag,
|
|
313
|
-
PartNumber: p.partNumber
|
|
314
|
-
})) }
|
|
315
|
-
}));
|
|
316
|
-
return {
|
|
317
|
-
location: response.Location,
|
|
318
|
-
key: response.Key ?? ""
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Abort a multipart upload
|
|
323
|
-
*/
|
|
324
|
-
async abortMultipartUpload(key, uploadId) {
|
|
325
|
-
await this.client.send(new AbortMultipartUploadCommand({
|
|
326
|
-
Bucket: this.bucket,
|
|
327
|
-
Key: key,
|
|
328
|
-
UploadId: uploadId
|
|
329
|
-
}));
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* List parts of a multipart upload
|
|
333
|
-
*/
|
|
334
|
-
async listParts(key, uploadId, partNumberMarker) {
|
|
335
|
-
const response = await this.client.send(new ListPartsCommand({
|
|
336
|
-
Bucket: this.bucket,
|
|
337
|
-
Key: key,
|
|
338
|
-
UploadId: uploadId,
|
|
339
|
-
PartNumberMarker: partNumberMarker
|
|
340
|
-
}));
|
|
341
|
-
return {
|
|
342
|
-
parts: (response.Parts ?? []).map((p) => ({
|
|
343
|
-
partNumber: p.PartNumber ?? 0,
|
|
344
|
-
etag: p.ETag ?? "",
|
|
345
|
-
size: p.Size ?? 0
|
|
346
|
-
})),
|
|
347
|
-
isTruncated: response.IsTruncated ?? false,
|
|
348
|
-
nextPartNumberMarker: response.NextPartNumberMarker?.toString()
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* List all in-progress multipart uploads
|
|
353
|
-
*/
|
|
354
|
-
async listMultipartUploads(keyMarker, uploadIdMarker) {
|
|
355
|
-
const response = await this.client.send(new ListMultipartUploadsCommand({
|
|
356
|
-
Bucket: this.bucket,
|
|
357
|
-
KeyMarker: keyMarker,
|
|
358
|
-
UploadIdMarker: uploadIdMarker
|
|
359
|
-
}));
|
|
360
|
-
return {
|
|
361
|
-
uploads: (response.Uploads ?? []).map((u) => ({
|
|
362
|
-
key: u.Key ?? "",
|
|
363
|
-
uploadId: u.UploadId ?? "",
|
|
364
|
-
initiated: u.Initiated
|
|
365
|
-
})),
|
|
366
|
-
isTruncated: response.IsTruncated ?? false,
|
|
367
|
-
nextKeyMarker: response.NextKeyMarker,
|
|
368
|
-
nextUploadIdMarker: response.NextUploadIdMarker
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
//#endregion
|
|
373
7
|
//#region src/storage/storage.tokens.ts
|
|
374
8
|
/**
|
|
375
9
|
* Dependency injection tokens for the Storage module
|
|
@@ -384,6 +18,7 @@ const STORAGE_TOKENS = {
|
|
|
384
18
|
//#region src/storage/services/storage-manager.service.ts
|
|
385
19
|
let StorageManagerService = class StorageManagerService {
|
|
386
20
|
providers = /* @__PURE__ */ new Map();
|
|
21
|
+
creationPromises = /* @__PURE__ */ new Map();
|
|
387
22
|
diskConfigs = /* @__PURE__ */ new Map();
|
|
388
23
|
constructor(options) {
|
|
389
24
|
this.options = options;
|
|
@@ -401,23 +36,36 @@ let StorageManagerService = class StorageManagerService {
|
|
|
401
36
|
* @param diskName - Name of the disk
|
|
402
37
|
* @returns Storage provider instance
|
|
403
38
|
*/
|
|
404
|
-
getProvider(diskName) {
|
|
39
|
+
async getProvider(diskName) {
|
|
405
40
|
const cached = this.providers.get(diskName);
|
|
406
41
|
if (cached) return cached;
|
|
42
|
+
const inflight = this.creationPromises.get(diskName);
|
|
43
|
+
if (inflight) return inflight;
|
|
407
44
|
const diskConfig = this.diskConfigs.get(diskName);
|
|
408
45
|
if (!diskConfig) throw new DiskNotConfiguredError(diskName);
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
46
|
+
const promise = this.createProvider(diskConfig).then((provider) => {
|
|
47
|
+
this.providers.set(diskName, provider);
|
|
48
|
+
this.creationPromises.delete(diskName);
|
|
49
|
+
return provider;
|
|
50
|
+
}).catch((error) => {
|
|
51
|
+
this.creationPromises.delete(diskName);
|
|
52
|
+
throw error;
|
|
53
|
+
});
|
|
54
|
+
this.creationPromises.set(diskName, promise);
|
|
55
|
+
return promise;
|
|
412
56
|
}
|
|
413
57
|
/**
|
|
414
58
|
* Create a provider instance based on configuration
|
|
59
|
+
* Dynamically imports S3StorageProvider to avoid loading AWS SDK at module evaluation time
|
|
415
60
|
* @param config - Storage entry configuration
|
|
416
61
|
* @returns Storage provider instance
|
|
417
62
|
*/
|
|
418
|
-
createProvider(config) {
|
|
63
|
+
async createProvider(config) {
|
|
419
64
|
switch (config.provider) {
|
|
420
|
-
case "s3":
|
|
65
|
+
case "s3": {
|
|
66
|
+
const { S3StorageProvider } = await import("./s3-storage.provider-BLlzQYiJ.mjs");
|
|
67
|
+
return new S3StorageProvider(config);
|
|
68
|
+
}
|
|
421
69
|
case "gcs": throw new StorageProviderNotSupportedError(config.provider);
|
|
422
70
|
default: throw new StorageProviderNotSupportedError(config.provider);
|
|
423
71
|
}
|
|
@@ -454,31 +102,6 @@ StorageManagerService = __decorate([
|
|
|
454
102
|
__decorateMetadata("design:paramtypes", [Object])
|
|
455
103
|
], StorageManagerService);
|
|
456
104
|
//#endregion
|
|
457
|
-
//#region src/storage/dom.polyfill.ts
|
|
458
|
-
/**
|
|
459
|
-
* DOM polyfills for Cloudflare Workers
|
|
460
|
-
*
|
|
461
|
-
* AWS SDK v3 uses DOMParser and Node for XML parsing, which are not available in Workers.
|
|
462
|
-
* This module must be imported BEFORE any AWS SDK imports (including transitive).
|
|
463
|
-
*
|
|
464
|
-
* @see https://github.com/aws/aws-sdk-js-v3/issues/7375
|
|
465
|
-
*/
|
|
466
|
-
globalThis.DOMParser = DOMParser;
|
|
467
|
-
if (typeof globalThis.Node === "undefined") globalThis.Node = {
|
|
468
|
-
ELEMENT_NODE: 1,
|
|
469
|
-
ATTRIBUTE_NODE: 2,
|
|
470
|
-
TEXT_NODE: 3,
|
|
471
|
-
CDATA_SECTION_NODE: 4,
|
|
472
|
-
ENTITY_REFERENCE_NODE: 5,
|
|
473
|
-
ENTITY_NODE: 6,
|
|
474
|
-
PROCESSING_INSTRUCTION_NODE: 7,
|
|
475
|
-
COMMENT_NODE: 8,
|
|
476
|
-
DOCUMENT_NODE: 9,
|
|
477
|
-
DOCUMENT_TYPE_NODE: 10,
|
|
478
|
-
DOCUMENT_FRAGMENT_NODE: 11,
|
|
479
|
-
NOTATION_NODE: 12
|
|
480
|
-
};
|
|
481
|
-
//#endregion
|
|
482
105
|
//#region src/storage/services/storage.service.ts
|
|
483
106
|
var _ref;
|
|
484
107
|
let StorageService = class StorageService {
|
|
@@ -496,7 +119,7 @@ let StorageService = class StorageService {
|
|
|
496
119
|
*/
|
|
497
120
|
async upload(body, relativePath, options, disk) {
|
|
498
121
|
const diskName = this.resolveDisk(disk);
|
|
499
|
-
const provider = this.storageManager.getProvider(diskName);
|
|
122
|
+
const provider = await this.storageManager.getProvider(diskName);
|
|
500
123
|
const fullPath = this.buildFullPath(relativePath, diskName);
|
|
501
124
|
return provider.upload(body, fullPath, options);
|
|
502
125
|
}
|
|
@@ -508,7 +131,7 @@ let StorageService = class StorageService {
|
|
|
508
131
|
*/
|
|
509
132
|
async download(relativePath, disk) {
|
|
510
133
|
const diskName = this.resolveDisk(disk);
|
|
511
|
-
const provider = this.storageManager.getProvider(diskName);
|
|
134
|
+
const provider = await this.storageManager.getProvider(diskName);
|
|
512
135
|
const fullPath = this.buildFullPath(relativePath, diskName);
|
|
513
136
|
return provider.download(fullPath);
|
|
514
137
|
}
|
|
@@ -519,7 +142,7 @@ let StorageService = class StorageService {
|
|
|
519
142
|
*/
|
|
520
143
|
async delete(relativePath, disk) {
|
|
521
144
|
const diskName = this.resolveDisk(disk);
|
|
522
|
-
const provider = this.storageManager.getProvider(diskName);
|
|
145
|
+
const provider = await this.storageManager.getProvider(diskName);
|
|
523
146
|
const fullPath = this.buildFullPath(relativePath, diskName);
|
|
524
147
|
await provider.delete(fullPath);
|
|
525
148
|
}
|
|
@@ -531,7 +154,7 @@ let StorageService = class StorageService {
|
|
|
531
154
|
*/
|
|
532
155
|
async exists(relativePath, disk) {
|
|
533
156
|
const diskName = this.resolveDisk(disk);
|
|
534
|
-
const provider = this.storageManager.getProvider(diskName);
|
|
157
|
+
const provider = await this.storageManager.getProvider(diskName);
|
|
535
158
|
const fullPath = this.buildFullPath(relativePath, diskName);
|
|
536
159
|
return provider.exists(fullPath);
|
|
537
160
|
}
|
|
@@ -575,7 +198,7 @@ let StorageService = class StorageService {
|
|
|
575
198
|
*/
|
|
576
199
|
async getPresignedUrl(relativePath, method, expiresIn, disk) {
|
|
577
200
|
const diskName = this.resolveDisk(disk);
|
|
578
|
-
const provider = this.storageManager.getProvider(diskName);
|
|
201
|
+
const provider = await this.storageManager.getProvider(diskName);
|
|
579
202
|
const fullPath = this.buildFullPath(relativePath, diskName);
|
|
580
203
|
const validatedExpiresIn = this.validateExpiresIn(expiresIn);
|
|
581
204
|
return provider.getPresignedUrl(fullPath, method, validatedExpiresIn);
|
|
@@ -653,7 +276,7 @@ let StorageService = class StorageService {
|
|
|
653
276
|
*/
|
|
654
277
|
async chunkedUpload(body, relativePath, options, disk) {
|
|
655
278
|
const diskName = this.resolveDisk(disk);
|
|
656
|
-
const provider = this.storageManager.getProvider(diskName);
|
|
279
|
+
const provider = await this.storageManager.getProvider(diskName);
|
|
657
280
|
const fullPath = this.buildFullPath(relativePath, diskName);
|
|
658
281
|
return provider.chunkedUpload(body, fullPath, options);
|
|
659
282
|
}
|
|
@@ -730,7 +353,8 @@ let StorageModule = _StorageModule = class StorageModule {
|
|
|
730
353
|
};
|
|
731
354
|
StorageModule = _StorageModule = __decorate([Module({ providers: [{
|
|
732
355
|
provide: STORAGE_TOKENS.StorageManager,
|
|
733
|
-
useClass: StorageManagerService
|
|
356
|
+
useClass: StorageManagerService,
|
|
357
|
+
scope: Scope.Singleton
|
|
734
358
|
}, {
|
|
735
359
|
provide: STORAGE_TOKENS.StorageService,
|
|
736
360
|
useClass: StorageService
|
|
@@ -782,6 +406,6 @@ const uploadResultSchema = z.object({
|
|
|
782
406
|
uploadedAt: z.date()
|
|
783
407
|
});
|
|
784
408
|
//#endregion
|
|
785
|
-
export {
|
|
409
|
+
export { deleteFileInputSchema as a, StorageManagerService as c, fileExistsInputSchema as i, STORAGE_TOKENS as l, getPresignedUrlInputSchema as n, StorageModule as o, presignedUrlResultSchema as r, StorageService as s, uploadResultSchema as t };
|
|
786
410
|
|
|
787
|
-
//# sourceMappingURL=storage-
|
|
411
|
+
//# sourceMappingURL=storage-By_ow2o_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-By_ow2o_.mjs","names":[],"sources":["../src/storage/storage.tokens.ts","../src/storage/services/storage-manager.service.ts","../src/storage/services/storage.service.ts","../src/storage/storage.module.ts","../src/storage/contracts/delete-file.input.ts","../src/storage/contracts/file-exists.input.ts","../src/storage/contracts/get-presigned-url.input.ts","../src/storage/contracts/upload-file.input.ts"],"sourcesContent":["/**\n * Dependency injection tokens for the Storage module\n * Using Symbol-based tokens to avoid magic strings\n */\nexport const STORAGE_TOKENS = {\n Options: Symbol.for('stratal:storage:options'),\n StorageService: Symbol.for('stratal:storage:service'),\n StorageManager: Symbol.for('stratal:storage:manager'),\n} as const\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { DiskNotConfiguredError, StorageProviderNotSupportedError } from '../errors'\nimport type { IStorageProvider } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig, StorageEntry } from '../types'\n\n/**\n * Storage Manager Service\n * Manages multiple storage providers (one per disk)\n * Handles lazy initialization and caching of S3Clients\n */\n@Transient(STORAGE_TOKENS.StorageManager)\nexport class StorageManagerService {\n private readonly providers = new Map<string, IStorageProvider>()\n private readonly creationPromises = new Map<string, Promise<IStorageProvider>>()\n private readonly diskConfigs = new Map<string, StorageEntry>()\n\n constructor(\n @inject(STORAGE_TOKENS.Options)\n private readonly options: StorageConfig\n ) {\n this.initializeDiskConfigs()\n }\n\n /**\n * Initialize disk configurations from options\n */\n private initializeDiskConfigs(): void {\n for (const entry of this.options.storage) {\n this.diskConfigs.set(entry.disk, entry)\n }\n }\n\n /**\n * Get provider for a specific disk\n * Lazily initializes provider on first access\n * @param diskName - Name of the disk\n * @returns Storage provider instance\n */\n async getProvider(diskName: string): Promise<IStorageProvider> {\n // Return cached provider if exists\n const cached = this.providers.get(diskName)\n if (cached) {\n return cached\n }\n\n // Return in-flight creation promise to deduplicate concurrent calls\n const inflight = this.creationPromises.get(diskName)\n if (inflight) {\n return inflight\n }\n\n // Get disk configuration\n const diskConfig = this.diskConfigs.get(diskName)\n if (!diskConfig) {\n throw new DiskNotConfiguredError(diskName)\n }\n\n // Create provider and deduplicate concurrent calls\n const promise = this.createProvider(diskConfig).then((provider) => {\n this.providers.set(diskName, provider)\n this.creationPromises.delete(diskName)\n return provider\n }).catch((error: unknown) => {\n this.creationPromises.delete(diskName)\n throw error\n })\n\n this.creationPromises.set(diskName, promise)\n\n return promise\n }\n\n /**\n * Create a provider instance based on configuration\n * Dynamically imports S3StorageProvider to avoid loading AWS SDK at module evaluation time\n * @param config - Storage entry configuration\n * @returns Storage provider instance\n */\n private async createProvider(config: StorageEntry): Promise<IStorageProvider> {\n switch (config.provider) {\n case 's3': {\n const { S3StorageProvider } = await import('../providers/s3-storage.provider')\n return new S3StorageProvider(config)\n }\n case 'gcs':\n throw new StorageProviderNotSupportedError(config.provider)\n default:\n throw new StorageProviderNotSupportedError(config.provider)\n }\n }\n\n /**\n * Get disk configuration\n * @param diskName - Name of the disk\n * @returns Storage entry configuration\n */\n getDiskConfig(diskName: string): StorageEntry {\n const config = this.diskConfigs.get(diskName)\n if (!config) {\n throw new DiskNotConfiguredError(diskName)\n }\n return config\n }\n\n /**\n * Check if a disk exists\n * @param diskName - Name of the disk\n * @returns True if disk exists, false otherwise\n */\n hasDisk(diskName: string): boolean {\n return this.diskConfigs.has(diskName)\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return Array.from(this.diskConfigs.keys())\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport {\n InvalidDiskError,\n PresignedUrlInvalidExpiryError,\n} from '../errors'\nimport type { StreamingBlobPayloadInputTypes } from '../providers/storage-provider.interface'\nimport { STORAGE_TOKENS } from '../storage.tokens'\nimport type { StorageConfig } from '../types'\nimport { StorageManagerService } from './storage-manager.service'\n\n/**\n * Storage Service\n *\n * Main facade for storage operations.\n * Request-scoped for proper isolation.\n *\n * @example\n * ```typescript\n * @inject(STORAGE_TOKENS.StorageService)\n * private readonly storage: StorageService\n *\n * await this.storage.upload(file, 'documents/report.pdf')\n * ```\n */\n@Transient(STORAGE_TOKENS.StorageService)\nexport class StorageService {\n constructor(\n @inject(STORAGE_TOKENS.StorageManager)\n protected readonly storageManager: StorageManagerService,\n @inject(STORAGE_TOKENS.Options)\n protected readonly options: StorageConfig\n ) { }\n\n /**\n * Upload content to storage\n * @param body - Content to upload (stream, buffer, or string)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options including size and mime type\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async upload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: UploadOptions,\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.upload(body, fullPath, options)\n }\n\n /**\n * Download a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Download result with stream and metadata\n */\n async download(relativePath: string, disk?: string): Promise<DownloadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.download(fullPath)\n }\n\n /**\n * Delete a file from storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n */\n async delete(relativePath: string, disk?: string): Promise<void> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n await provider.delete(fullPath)\n }\n\n /**\n * Check if a file exists in storage\n * @param relativePath - Relative path within the disk\n * @param disk - Optional disk name (uses default if not provided)\n * @returns True if file exists, false otherwise\n */\n async exists(relativePath: string, disk?: string): Promise<boolean> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.exists(fullPath)\n }\n\n /**\n * Generate a presigned download URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDownloadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'GET', expiresIn, disk)\n }\n\n /**\n * Generate a presigned upload URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedUploadUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'PUT', expiresIn, disk)\n }\n\n /**\n * Generate a presigned delete URL\n * @param relativePath - Relative path within the disk\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n async getPresignedDeleteUrl(\n relativePath: string,\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n return this.getPresignedUrl(relativePath, 'DELETE', expiresIn, disk)\n }\n\n /**\n * Generate a presigned URL for any method\n * @param relativePath - Relative path within the disk\n * @param method - HTTP method (GET, PUT, DELETE, HEAD)\n * @param expiresIn - Optional expiry time in seconds (uses default if not provided)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Presigned URL result\n */\n protected async getPresignedUrl(\n relativePath: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn?: number,\n disk?: string\n ): Promise<PresignedUrlResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n const validatedExpiresIn = this.validateExpiresIn(expiresIn)\n\n return provider.getPresignedUrl(fullPath, method, validatedExpiresIn)\n }\n\n /**\n * Resolve disk name (use default if not provided)\n * @param disk - Optional disk name\n * @returns Resolved disk name\n */\n protected resolveDisk(disk?: string): string {\n const diskName = disk ?? this.options.defaultStorageDisk\n\n if (!this.storageManager.hasDisk(diskName)) {\n throw new InvalidDiskError(diskName)\n }\n\n return diskName\n }\n\n /**\n * Build full path with disk root and path template substitution\n * @param relativePath - Relative path within the disk\n * @param diskName - Name of the disk\n * @returns Full path including disk root\n */\n protected buildFullPath(relativePath: string, diskName: string): string {\n const diskConfig = this.storageManager.getDiskConfig(diskName)\n let root = diskConfig.root || ''\n\n // Substitute template variables\n root = this.substituteTemplateVariables(root)\n\n // Combine root and relative path\n const fullPath = `${root}/${relativePath}`.replace(/\\/+/g, '/').replace(/^\\//, '')\n\n return fullPath\n }\n\n /**\n * Substitute template variables in path\n * Override this method in subclasses to add custom substitutions\n *\n * @param path - Path with template variables\n * @returns Path with substituted variables\n */\n protected substituteTemplateVariables(path: string): string {\n let result = path\n\n // Substitute {date}, {year}, {month}\n const now = new Date()\n result = result.replace(/{date}/g, now.toISOString().split('T')[0])\n result = result.replace(/{year}/g, now.getFullYear().toString())\n result = result.replace(/{month}/g, (now.getMonth() + 1).toString().padStart(2, '0'))\n\n return result\n }\n\n /**\n * Validate expiry time for presigned URLs\n * @param expiresIn - Optional expiry time in seconds\n * @returns Validated expiry time\n */\n protected validateExpiresIn(expiresIn?: number): number {\n const presignedUrlConfig = this.options.presignedUrl\n const validatedExpiresIn = expiresIn ?? presignedUrlConfig.defaultExpiry\n\n // S3 presigned URL limits: 1 second to 7 days (604800 seconds)\n const minExpiry = 1\n const maxExpiry = presignedUrlConfig.maxExpiry\n\n if (validatedExpiresIn < minExpiry || validatedExpiresIn > maxExpiry) {\n throw new PresignedUrlInvalidExpiryError(validatedExpiresIn, minExpiry, maxExpiry)\n }\n\n return validatedExpiresIn\n }\n\n /**\n * Get all available disk names\n * @returns Array of disk names\n */\n getAvailableDisks(): string[] {\n return this.storageManager.getAvailableDisks()\n }\n\n /**\n * Chunked upload for streaming data without known size\n * Uses multipart upload under the hood - handles retries and large files\n *\n * Use this method when:\n * - Content-Length is unknown or unreliable\n * - Uploading from streams that can't be rewound\n * - Need automatic retry handling for transient failures\n *\n * @param body - Content to upload (stream or buffer)\n * @param relativePath - Relative path within the disk\n * @param options - Upload options (mimeType required, size optional)\n * @param disk - Optional disk name (uses default if not provided)\n * @returns Upload result with metadata\n */\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n relativePath: string,\n options: Omit<UploadOptions, 'size'> & { size?: number },\n disk?: string\n ): Promise<UploadResult> {\n const diskName = this.resolveDisk(disk)\n const provider = await this.storageManager.getProvider(diskName)\n const fullPath = this.buildFullPath(relativePath, diskName)\n\n return provider.chunkedUpload(body, fullPath, options)\n }\n}\n","/**\n * Storage Module\n * Provides file storage capabilities using AWS S3\n * Supports multiple disk configurations with dynamic path templates\n */\n\nimport { Scope } from '../di/types'\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport { StorageManagerService } from './services/storage-manager.service'\nimport { StorageService } from './services/storage.service'\nimport { STORAGE_TOKENS } from './storage.tokens'\nimport type { StorageConfig } from './types'\n\n/**\n * Storage module options\n * Same as StorageConfig from types.ts\n */\nexport type StorageModuleOptions = StorageConfig\n\n@Module({\n providers: [{ provide: STORAGE_TOKENS.StorageManager, useClass: StorageManagerService, scope: Scope.Singleton },\n { provide: STORAGE_TOKENS.StorageService, useClass: StorageService },],\n})\nexport class StorageModule {\n /**\n * Configure StorageModule with static options\n *\n * @param options - Storage configuration options\n * @returns Dynamic module with storage infrastructure\n *\n * @example\n * ```typescript\n * StorageModule.forRoot({\n * storage: [{ disk: 'uploads', provider: 's3', ... }],\n * defaultStorageDisk: 'uploads',\n * presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 }\n * })\n * ```\n */\n static forRoot(options: StorageModuleOptions): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n { provide: STORAGE_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n /**\n * Configure StorageModule with async factory\n *\n * Use when configuration depends on other services.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with storage infrastructure\n *\n * @example\n * ```typescript\n * StorageModule.forRootAsync({\n * inject: [storageConfig.KEY],\n * useFactory: (storage) => ({\n * storage: storage.storage,\n * defaultStorageDisk: storage.defaultStorageDisk,\n * presignedUrl: storage.presignedUrl\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<StorageModuleOptions>): DynamicModule {\n return {\n module: StorageModule,\n providers: [\n {\n provide: STORAGE_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n}\n","import { z, withI18n } from '../../i18n/validation'\n\nexport const deleteFileInputSchema = z.object({\n path: z.string().min(1, withI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type DeleteFileInput = z.infer<typeof deleteFileInputSchema>\n","import { z, withI18n } from '../../i18n/validation'\n\nexport const fileExistsInputSchema = z.object({\n path: z.string().min(1, withI18n('zodI18n.errors.custom.filePathRequired')),\n disk: z.string().optional(),\n})\n\nexport type FileExistsInput = z.infer<typeof fileExistsInputSchema>\n","import { z, withI18n } from '../../i18n/validation'\n\nexport const getPresignedUrlInputSchema = z.object({\n path: z.string().min(1, withI18n('zodI18n.errors.custom.filePathRequired')),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']).default('GET'),\n expiresIn: z.number().int().min(1).max(604800).optional(),\n disk: z.string().optional(),\n})\n\nexport type GetPresignedUrlInput = z.infer<typeof getPresignedUrlInputSchema>\n\nexport const presignedUrlResultSchema = z.object({\n url: z.string().url(),\n expiresIn: z.number(),\n expiresAt: z.date(),\n method: z.enum(['GET', 'PUT', 'DELETE', 'HEAD']),\n})\n\nexport type PresignedUrlResult = z.infer<typeof presignedUrlResultSchema>\n","import { z } from '../../i18n/validation'\n\n/**\n * Upload options for streaming uploads\n */\nexport interface UploadOptions {\n /**\n * Size of the content in bytes\n */\n size: number\n /**\n * MIME type of the content\n */\n mimeType?: string\n /**\n * Custom metadata to store with the object (S3-specific)\n * Stored as S3 object metadata headers\n */\n metadata?: Record<string, string>\n /**\n * Object tagging for lifecycle policies (S3-specific)\n * Format: key=value (e.g., \"Tus-Completed=true\")\n */\n tagging?: string\n}\n\nexport const uploadResultSchema = z.object({\n path: z.string(),\n disk: z.string(),\n fullPath: z.string(),\n size: z.number(),\n mimeType: z.string(),\n uploadedAt: z.date(),\n})\n\nexport type UploadResult = z.infer<typeof uploadResultSchema>\n"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { S as ApplicationError, c as Container, r as GlobalErrorHandler, s as Scope, t as StratalNotInitializedError } from "./errors-CtCi1wn6.mjs";
|
|
2
2
|
import { l as DI_TOKENS } from "./decorate-D5j-d9_z.mjs";
|
|
3
3
|
import { i as LoggerService, n as PrettyFormatter, o as LogLevel, r as JsonFormatter, s as LOGGER_TOKENS, t as ConsoleTransport } from "./logger-BR1-s1Um.mjs";
|
|
4
|
-
import { t as ModuleRegistry } from "./module-
|
|
4
|
+
import { t as ModuleRegistry } from "./module-BgdxxzBe.mjs";
|
|
5
5
|
import { r as getListenerHandlers, t as EventRegistry } from "./events-CXl-o1Ad.mjs";
|
|
6
6
|
import { CacheModule } from "./cache/index.mjs";
|
|
7
|
-
import { t as CronManager } from "./cron-manager-
|
|
8
|
-
import { g as OpenAPIModule, n as HonoApp, t as I18nModule } from "./i18n.module-
|
|
9
|
-
import { t as QuarryRegistry } from "./quarry-registry-
|
|
10
|
-
import { t as QueueModule } from "./queue.module-
|
|
11
|
-
import { i as SeederRegistry, n as DbSeedListCommand, r as SEEDER_TOKENS, t as DbSeedCommand } from "./seeder-
|
|
7
|
+
import { t as CronManager } from "./cron-manager-DR7fiG6o.mjs";
|
|
8
|
+
import { g as OpenAPIModule, n as HonoApp, t as I18nModule } from "./i18n.module-W8OJxg3d.mjs";
|
|
9
|
+
import { t as QuarryRegistry } from "./quarry-registry-DCwqVcRp.mjs";
|
|
10
|
+
import { t as QueueModule } from "./queue.module-BZvmeAMj.mjs";
|
|
11
|
+
import { i as SeederRegistry, n as DbSeedListCommand, r as SEEDER_TOKENS, t as DbSeedCommand } from "./seeder-Cupi5jl-.mjs";
|
|
12
12
|
import { container, injectable } from "tsyringe";
|
|
13
13
|
import "reflect-metadata";
|
|
14
14
|
//#region src/application.ts
|
|
@@ -302,4 +302,4 @@ var Stratal = class Stratal {
|
|
|
302
302
|
//#endregion
|
|
303
303
|
export { Application as n, Stratal as t };
|
|
304
304
|
|
|
305
|
-
//# sourceMappingURL=stratal-
|
|
305
|
+
//# sourceMappingURL=stratal-CE0iTz4f.mjs.map
|