stratal 0.0.18 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +8 -8
  2. package/dist/{base-email.provider-Cuw4OAB0.mjs → base-email.provider-mjynzewK.mjs} +1 -1
  3. package/dist/{base-email.provider-Cuw4OAB0.mjs.map → base-email.provider-mjynzewK.mjs.map} +1 -1
  4. package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
  5. package/dist/bin/quarry.mjs +21 -36
  6. package/dist/bin/quarry.mjs.map +1 -1
  7. package/dist/cache/index.d.mts +3 -2
  8. package/dist/cache/index.d.mts.map +1 -1
  9. package/dist/cache/index.mjs +3 -3
  10. package/dist/{colors-BTAnQRGU.mjs → colors-DJaRDXoS.mjs} +1 -1
  11. package/dist/{colors-BTAnQRGU.mjs.map → colors-DJaRDXoS.mjs.map} +1 -1
  12. package/dist/{command-DjGqCYHv.mjs → command-BgSlsS4M.mjs} +2 -2
  13. package/dist/{command-DjGqCYHv.mjs.map → command-BgSlsS4M.mjs.map} +1 -1
  14. package/dist/{command-B1YuV-UZ.d.mts → command-DsQq56Lp.d.mts} +2 -2
  15. package/dist/{command-B1YuV-UZ.d.mts.map → command-DsQq56Lp.d.mts.map} +1 -1
  16. package/dist/config/index.d.mts +81 -37
  17. package/dist/config/index.d.mts.map +1 -1
  18. package/dist/config/index.mjs +126 -45
  19. package/dist/config/index.mjs.map +1 -1
  20. package/dist/{consumer-registry-BkuHXR_u.d.mts → consumer-registry-Doom7BEh.d.mts} +1 -1
  21. package/dist/{consumer-registry-BkuHXR_u.d.mts.map → consumer-registry-Doom7BEh.d.mts.map} +1 -1
  22. package/dist/controller.decorator-LZY9aHYG.mjs +66 -0
  23. package/dist/controller.decorator-LZY9aHYG.mjs.map +1 -0
  24. package/dist/cron/index.d.mts +4 -3
  25. package/dist/cron/index.d.mts.map +1 -1
  26. package/dist/cron/index.mjs +1 -1
  27. package/dist/{cron-manager-1KnZvojs.mjs → cron-manager-C30t9UZM.mjs} +29 -19
  28. package/dist/cron-manager-C30t9UZM.mjs.map +1 -0
  29. package/dist/{cron-manager-BnEZquBL.d.mts → cron-manager-RuPtFVLy.d.mts} +27 -13
  30. package/dist/cron-manager-RuPtFVLy.d.mts.map +1 -0
  31. package/dist/di/index.d.mts +1 -1
  32. package/dist/di/index.mjs +2 -2
  33. package/dist/email/index.d.mts +3 -3
  34. package/dist/email/index.mjs +87 -10
  35. package/dist/email/index.mjs.map +1 -1
  36. package/dist/{en-3QnZwP-u.mjs → en-rHmW6vD9.mjs} +5 -31
  37. package/dist/en-rHmW6vD9.mjs.map +1 -0
  38. package/dist/env-CamWD-U1.d.mts +25 -0
  39. package/dist/env-CamWD-U1.d.mts.map +1 -0
  40. package/dist/errors/index.d.mts +1 -1
  41. package/dist/errors/index.mjs +1 -1
  42. package/dist/{errors--RBIvDXr.mjs → errors-B4pYgYON.mjs} +161 -7
  43. package/dist/errors-B4pYgYON.mjs.map +1 -0
  44. package/dist/{errors-B7hCnXgB.mjs → errors-BUyUfr2Z.mjs} +14 -7
  45. package/dist/errors-BUyUfr2Z.mjs.map +1 -0
  46. package/dist/events/index.d.mts +2 -2
  47. package/dist/events/index.mjs +1 -1
  48. package/dist/{events-UTJliZhl.mjs → events-COKixqnG.mjs} +2 -2
  49. package/dist/{events-UTJliZhl.mjs.map → events-COKixqnG.mjs.map} +1 -1
  50. package/dist/{gateway-context-BdBFoQd8.mjs → gateway-context-cqZ8wMoi.mjs} +4 -65
  51. package/dist/gateway-context-cqZ8wMoi.mjs.map +1 -0
  52. package/dist/guards/index.d.mts +14 -5
  53. package/dist/guards/index.d.mts.map +1 -1
  54. package/dist/guards/index.mjs +1 -1
  55. package/dist/{guards-MtDgcHnF.mjs → guards-DMbsAxSX.mjs} +1 -1
  56. package/dist/guards-DMbsAxSX.mjs.map +1 -0
  57. package/dist/http-method.decorator-BT3ufnz8.mjs +96 -0
  58. package/dist/http-method.decorator-BT3ufnz8.mjs.map +1 -0
  59. package/dist/i18n/index.d.mts +3 -3
  60. package/dist/i18n/index.mjs +2 -2
  61. package/dist/i18n/messages/en/index.d.mts +1 -1
  62. package/dist/i18n/messages/en/index.mjs +1 -1
  63. package/dist/i18n/utils/index.mjs +1 -1
  64. package/dist/i18n/validation/index.d.mts +1 -1
  65. package/dist/i18n/validation/index.mjs +1 -1
  66. package/dist/{i18n.module-BpLLLCTg.mjs → i18n.module-CI_prYFD.mjs} +74 -196
  67. package/dist/i18n.module-CI_prYFD.mjs.map +1 -0
  68. package/dist/{index-Dfpd_ypO.d.mts → index-B437eK7p.d.mts} +26 -12
  69. package/dist/index-B437eK7p.d.mts.map +1 -0
  70. package/dist/{index-BR23zDMy.d.mts → index-CWRS7Ri3.d.mts} +1 -1
  71. package/dist/{index-BR23zDMy.d.mts.map → index-CWRS7Ri3.d.mts.map} +1 -1
  72. package/dist/{index-BDh9J2KD.d.mts → index-DFhEeFfC.d.mts} +4 -30
  73. package/dist/{index-BDh9J2KD.d.mts.map → index-DFhEeFfC.d.mts.map} +1 -1
  74. package/dist/{index-BrmS34sa.d.mts → index-DPFqRs8L.d.mts} +70 -39
  75. package/dist/index-DPFqRs8L.d.mts.map +1 -0
  76. package/dist/{index-DPxmo6AY.d.mts → index-Dnqm9ZB6.d.mts} +5 -4
  77. package/dist/index-Dnqm9ZB6.d.mts.map +1 -0
  78. package/dist/index-SHx31sBJ.d.mts +101 -0
  79. package/dist/index-SHx31sBJ.d.mts.map +1 -0
  80. package/dist/index.d.mts +3 -2
  81. package/dist/index.d.mts.map +1 -1
  82. package/dist/index.mjs +1 -1
  83. package/dist/{is-command-PvULqiTa.mjs → is-command-C6a7WTPw.mjs} +2 -2
  84. package/dist/{is-command-PvULqiTa.mjs.map → is-command-C6a7WTPw.mjs.map} +1 -1
  85. package/dist/{is-seeder-BN9Ej1r7.mjs → is-seeder-CebjZCDn.mjs} +1 -1
  86. package/dist/{is-seeder-BN9Ej1r7.mjs.map → is-seeder-CebjZCDn.mjs.map} +1 -1
  87. package/dist/logger/index.d.mts +1 -1
  88. package/dist/logger/index.mjs +1 -1
  89. package/dist/{logger-c0ftIK4G.mjs → logger-V6Ms3QnQ.mjs} +38 -20
  90. package/dist/{logger-c0ftIK4G.mjs.map → logger-V6Ms3QnQ.mjs.map} +1 -1
  91. package/dist/macroable/index.d.mts +2 -0
  92. package/dist/macroable/index.mjs +2 -0
  93. package/dist/macroable-BmufBshB.mjs +122 -0
  94. package/dist/macroable-BmufBshB.mjs.map +1 -0
  95. package/dist/module/index.d.mts +2 -2
  96. package/dist/module/index.mjs +1 -1
  97. package/dist/{module-C3YZ-kZN.mjs → module-qGE_1duv.mjs} +31 -18
  98. package/dist/module-qGE_1duv.mjs.map +1 -0
  99. package/dist/openapi/index.d.mts +3 -3
  100. package/dist/openapi/index.mjs +2 -2
  101. package/dist/{openapi-tools.service-B77QXD56.mjs → openapi-tools.service-CYWGuhue.mjs} +4 -1
  102. package/dist/{openapi-tools.service-B77QXD56.mjs.map → openapi-tools.service-CYWGuhue.mjs.map} +1 -1
  103. package/dist/{openapi.service-6yj0BUY4.d.mts → openapi.service-Bv_NioM9.d.mts} +3 -3
  104. package/dist/{openapi.service-6yj0BUY4.d.mts.map → openapi.service-Bv_NioM9.d.mts.map} +1 -1
  105. package/dist/quarry/index.d.mts +7 -7
  106. package/dist/quarry/index.d.mts.map +1 -1
  107. package/dist/quarry/index.mjs +4 -4
  108. package/dist/{quarry-registry-CQCIlYTO.mjs → quarry-registry-DFfRRkA7.mjs} +17 -15
  109. package/dist/quarry-registry-DFfRRkA7.mjs.map +1 -0
  110. package/dist/queue/index.d.mts +2 -2
  111. package/dist/queue/index.mjs +2 -2
  112. package/dist/{queue.module-DIjD6nr-.mjs → queue.module-P-G-nCYz.mjs} +4 -4
  113. package/dist/{queue.module-DIjD6nr-.mjs.map → queue.module-P-G-nCYz.mjs.map} +1 -1
  114. package/dist/r2-storage.provider-LdzK9tfG.mjs +244 -0
  115. package/dist/r2-storage.provider-LdzK9tfG.mjs.map +1 -0
  116. package/dist/{resend.provider-Bvw36rQy.mjs → resend.provider-bwILp0WI.mjs} +2 -2
  117. package/dist/{resend.provider-Bvw36rQy.mjs.map → resend.provider-bwILp0WI.mjs.map} +1 -1
  118. package/dist/router/index.d.mts +2 -2
  119. package/dist/router/index.mjs +7 -5
  120. package/dist/seeder/index.d.mts +3 -3
  121. package/dist/seeder/index.mjs +2 -2
  122. package/dist/{seeder-D7VXULXB.mjs → seeder-BcqIFa2X.mjs} +5 -5
  123. package/dist/{seeder-D7VXULXB.mjs.map → seeder-BcqIFa2X.mjs.map} +1 -1
  124. package/dist/{setup-BRIN-iYT.mjs → setup-CtekcwuO.mjs} +1 -1
  125. package/dist/{setup-BRIN-iYT.mjs.map → setup-CtekcwuO.mjs.map} +1 -1
  126. package/dist/signed-url-COX7cCWR.mjs +74 -0
  127. package/dist/signed-url-COX7cCWR.mjs.map +1 -0
  128. package/dist/{smtp.provider-CAwpvzvD.mjs → smtp.provider-B07yuARi.mjs} +2 -2
  129. package/dist/{smtp.provider-CAwpvzvD.mjs.map → smtp.provider-B07yuARi.mjs.map} +1 -1
  130. package/dist/storage/index.d.mts +39 -17
  131. package/dist/storage/index.d.mts.map +1 -1
  132. package/dist/storage/index.mjs +3 -3
  133. package/dist/storage/providers/index.d.mts +30 -70
  134. package/dist/storage/providers/index.d.mts.map +1 -1
  135. package/dist/storage/providers/index.mjs +2 -2
  136. package/dist/{storage-CJ-QOwNv.mjs → storage-P6X4h9So.mjs} +101 -27
  137. package/dist/storage-P6X4h9So.mjs.map +1 -0
  138. package/dist/{storage-provider.interface-YRtyYBxV.d.mts → storage-provider.interface-CC1nniHk.d.mts} +20 -21
  139. package/dist/storage-provider.interface-CC1nniHk.d.mts.map +1 -0
  140. package/dist/{stratal-B7G4i9-N.mjs → stratal-BCiwCFN9.mjs} +57 -26
  141. package/dist/stratal-BCiwCFN9.mjs.map +1 -0
  142. package/dist/{types-CN0zONAZ.d.mts → types-DIWemRad.d.mts} +1 -1
  143. package/dist/types-DIWemRad.d.mts.map +1 -0
  144. package/dist/{usage-generator-Cl1HPlUp.mjs → usage-generator-MBcRo0Q2.mjs} +2 -2
  145. package/dist/{usage-generator-Cl1HPlUp.mjs.map → usage-generator-MBcRo0Q2.mjs.map} +1 -1
  146. package/dist/{validation-B4bePOa_.mjs → validation-Dbg3ehdP.mjs} +1 -1
  147. package/dist/{validation-B4bePOa_.mjs.map → validation-Dbg3ehdP.mjs.map} +1 -1
  148. package/dist/websocket/index.d.mts +3 -3
  149. package/dist/websocket/index.mjs +1 -1
  150. package/dist/workers/index.d.mts +2 -1
  151. package/dist/workers/index.d.mts.map +1 -1
  152. package/dist/workers/index.mjs +2 -2
  153. package/package.json +27 -39
  154. package/dist/cron-manager-1KnZvojs.mjs.map +0 -1
  155. package/dist/cron-manager-BnEZquBL.d.mts.map +0 -1
  156. package/dist/en-3QnZwP-u.mjs.map +0 -1
  157. package/dist/errors--RBIvDXr.mjs.map +0 -1
  158. package/dist/errors-B7hCnXgB.mjs.map +0 -1
  159. package/dist/gateway-context-BdBFoQd8.mjs.map +0 -1
  160. package/dist/guards-MtDgcHnF.mjs.map +0 -1
  161. package/dist/i18n.module-BpLLLCTg.mjs.map +0 -1
  162. package/dist/index-BrmS34sa.d.mts.map +0 -1
  163. package/dist/index-DPxmo6AY.d.mts.map +0 -1
  164. package/dist/index-Dfpd_ypO.d.mts.map +0 -1
  165. package/dist/module-C3YZ-kZN.mjs.map +0 -1
  166. package/dist/quarry-registry-CQCIlYTO.mjs.map +0 -1
  167. package/dist/s3-storage.provider-BAhHDMI3.mjs +0 -343
  168. package/dist/s3-storage.provider-BAhHDMI3.mjs.map +0 -1
  169. package/dist/storage-CJ-QOwNv.mjs.map +0 -1
  170. package/dist/storage-provider.interface-YRtyYBxV.d.mts.map +0 -1
  171. package/dist/stratal-B7G4i9-N.mjs.map +0 -1
  172. package/dist/types-CN0zONAZ.d.mts.map +0 -1
@@ -0,0 +1,244 @@
1
+ import { t as __exportAll } from "./chunk-D1SwGrFN.mjs";
2
+ import { t as signUrl } from "./signed-url-COX7cCWR.mjs";
3
+ import { n as R2PresignedUrlSecretMissingError, t as StorageResponseBodyMissingError } from "./errors-BUyUfr2Z.mjs";
4
+ //#region src/storage/providers/r2-storage.provider.ts
5
+ var r2_storage_provider_exports = /* @__PURE__ */ __exportAll({ R2StorageProvider: () => R2StorageProvider });
6
+ const MIN_PART_SIZE = 5 * 1024 * 1024;
7
+ const MULTIPART_PREFIX = "__multipart/";
8
+ const PARTS_PREFIX = "__parts/";
9
+ /**
10
+ * R2 Storage Provider
11
+ * Implements storage operations using native Cloudflare R2 bindings
12
+ *
13
+ * Implements IMultipartProvider for multipart upload support needed by TUS.
14
+ * Tracks multipart upload metadata using companion R2 objects so that
15
+ * listParts/listMultipartUploads work in all environments (local, test, prod).
16
+ */
17
+ var R2StorageProvider = class {
18
+ routeBasePath;
19
+ constructor(config, bucket, env, routeConfig) {
20
+ this.config = config;
21
+ this.bucket = bucket;
22
+ this.env = env;
23
+ this.routeBasePath = routeConfig?.basePath ?? "/storage";
24
+ }
25
+ async upload(body, path, options) {
26
+ const customMetadata = { ...options.metadata };
27
+ if (options.tagging) customMetadata["x-tags"] = options.tagging;
28
+ await this.bucket.put(path, body, {
29
+ httpMetadata: { contentType: options.mimeType },
30
+ customMetadata
31
+ });
32
+ return {
33
+ path,
34
+ disk: this.config.disk,
35
+ fullPath: path,
36
+ size: options.size,
37
+ mimeType: options.mimeType ?? "application/octet-stream",
38
+ uploadedAt: /* @__PURE__ */ new Date()
39
+ };
40
+ }
41
+ async download(path) {
42
+ const obj = await this.bucket.get(path);
43
+ if (!obj) throw new StorageResponseBodyMissingError(path);
44
+ const objBody = obj;
45
+ return {
46
+ toStream: () => objBody.body,
47
+ contentType: obj.httpMetadata?.contentType ?? "application/octet-stream",
48
+ size: obj.size,
49
+ metadata: obj.customMetadata,
50
+ toString: () => objBody.text(),
51
+ toArrayBuffer: () => objBody.bytes()
52
+ };
53
+ }
54
+ async delete(path) {
55
+ await this.bucket.delete(path);
56
+ }
57
+ async exists(path) {
58
+ return await this.bucket.head(path) !== null;
59
+ }
60
+ async getPresignedUrl(path, method, expiresIn) {
61
+ const secret = this.env.APP_SECRET;
62
+ if (!secret) throw new R2PresignedUrlSecretMissingError();
63
+ return {
64
+ url: await signUrl(`${`${this.routeBasePath}/${this.config.disk}/${path}`}?method=${method}`, secret, { expiresIn }),
65
+ expiresIn,
66
+ expiresAt: new Date(Date.now() + expiresIn * 1e3),
67
+ method
68
+ };
69
+ }
70
+ async chunkedUpload(body, path, options) {
71
+ const multipartUpload = await this.bucket.createMultipartUpload(path, {
72
+ httpMetadata: { contentType: options.mimeType },
73
+ customMetadata: options.metadata
74
+ });
75
+ const parts = [];
76
+ try {
77
+ if (body instanceof ReadableStream) {
78
+ const reader = body.getReader();
79
+ let buffer = new Uint8Array(0);
80
+ let partNumber = 1;
81
+ while (true) {
82
+ const { done, value } = await reader.read();
83
+ if (value) {
84
+ const newBuffer = new Uint8Array(buffer.length + value.length);
85
+ newBuffer.set(buffer, 0);
86
+ newBuffer.set(value, buffer.length);
87
+ buffer = newBuffer;
88
+ }
89
+ while (buffer.length >= MIN_PART_SIZE) {
90
+ const partData = buffer.slice(0, MIN_PART_SIZE);
91
+ buffer = buffer.slice(MIN_PART_SIZE);
92
+ const part = await multipartUpload.uploadPart(partNumber, partData);
93
+ parts.push(part);
94
+ partNumber++;
95
+ }
96
+ if (done) {
97
+ if (buffer.length > 0) {
98
+ const part = await multipartUpload.uploadPart(partNumber, buffer);
99
+ parts.push(part);
100
+ }
101
+ break;
102
+ }
103
+ }
104
+ } else if (body !== null && body !== void 0) {
105
+ const part = await multipartUpload.uploadPart(1, body);
106
+ parts.push(part);
107
+ }
108
+ await multipartUpload.complete(parts);
109
+ } catch (error) {
110
+ await multipartUpload.abort();
111
+ throw error;
112
+ }
113
+ const headResult = await this.bucket.head(path);
114
+ return {
115
+ path,
116
+ disk: this.config.disk,
117
+ fullPath: path,
118
+ size: headResult?.size ?? options.size ?? 0,
119
+ mimeType: options.mimeType ?? "application/octet-stream",
120
+ uploadedAt: /* @__PURE__ */ new Date()
121
+ };
122
+ }
123
+ getBucket() {
124
+ return this.config.binding;
125
+ }
126
+ async headObject(key) {
127
+ const obj = await this.bucket.head(key);
128
+ if (!obj) return null;
129
+ return {
130
+ size: obj.size,
131
+ contentType: obj.httpMetadata?.contentType,
132
+ metadata: obj.customMetadata
133
+ };
134
+ }
135
+ async deleteObjects(keys) {
136
+ if (keys.length === 0) return {
137
+ deleted: 0,
138
+ errors: []
139
+ };
140
+ await this.bucket.delete(keys);
141
+ return {
142
+ deleted: keys.length,
143
+ errors: []
144
+ };
145
+ }
146
+ async createMultipartUpload(key, options) {
147
+ const upload = await this.bucket.createMultipartUpload(key, {
148
+ httpMetadata: {
149
+ contentType: options?.contentType,
150
+ cacheControl: options?.cacheControl
151
+ },
152
+ customMetadata: options?.metadata
153
+ });
154
+ await this.bucket.put(`${MULTIPART_PREFIX}${upload.uploadId}.json`, JSON.stringify({
155
+ key: upload.key,
156
+ uploadId: upload.uploadId,
157
+ initiated: (/* @__PURE__ */ new Date()).toISOString()
158
+ }), { httpMetadata: { contentType: "application/json" } });
159
+ return {
160
+ uploadId: upload.uploadId,
161
+ key: upload.key
162
+ };
163
+ }
164
+ async uploadPart(key, uploadId, partNumber, body) {
165
+ const part = await this.bucket.resumeMultipartUpload(key, uploadId).uploadPart(partNumber, body);
166
+ await this.bucket.put(`${PARTS_PREFIX}${uploadId}/${partNumber}`, JSON.stringify({
167
+ partNumber: part.partNumber,
168
+ etag: part.etag,
169
+ size: body.length
170
+ }), { httpMetadata: { contentType: "application/json" } });
171
+ return {
172
+ etag: part.etag,
173
+ partNumber: part.partNumber
174
+ };
175
+ }
176
+ async completeMultipartUpload(key, uploadId, parts) {
177
+ const result = await this.bucket.resumeMultipartUpload(key, uploadId).complete(parts.map((p) => ({
178
+ partNumber: p.partNumber,
179
+ etag: p.etag
180
+ })));
181
+ await this.cleanupTrackingObjects(uploadId);
182
+ return { key: result.key };
183
+ }
184
+ async abortMultipartUpload(key, uploadId) {
185
+ await this.bucket.resumeMultipartUpload(key, uploadId).abort();
186
+ await this.cleanupTrackingObjects(uploadId);
187
+ }
188
+ async listParts(_key, uploadId, partNumberMarker) {
189
+ const prefix = `${PARTS_PREFIX}${uploadId}/`;
190
+ const listed = await this.bucket.list({
191
+ prefix,
192
+ cursor: partNumberMarker
193
+ });
194
+ return {
195
+ parts: (await Promise.all(listed.objects.map(async (obj) => {
196
+ const data = await this.bucket.get(obj.key);
197
+ if (!data) return null;
198
+ return JSON.parse(await data.text());
199
+ }))).filter((p) => p !== null).sort((a, b) => a.partNumber - b.partNumber),
200
+ isTruncated: listed.truncated,
201
+ nextPartNumberMarker: listed.truncated ? listed.cursor : void 0
202
+ };
203
+ }
204
+ async listMultipartUploads(keyMarker, _uploadIdMarker) {
205
+ const listed = await this.bucket.list({
206
+ prefix: MULTIPART_PREFIX,
207
+ cursor: keyMarker
208
+ });
209
+ return {
210
+ uploads: (await Promise.all(listed.objects.map(async (obj) => {
211
+ const data = await this.bucket.get(obj.key);
212
+ if (!data) return null;
213
+ const parsed = JSON.parse(await data.text());
214
+ return {
215
+ key: parsed.key,
216
+ uploadId: parsed.uploadId,
217
+ initiated: new Date(parsed.initiated)
218
+ };
219
+ }))).filter((u) => u !== null),
220
+ isTruncated: listed.truncated,
221
+ nextKeyMarker: listed.truncated ? listed.cursor : void 0,
222
+ nextUploadIdMarker: void 0
223
+ };
224
+ }
225
+ async cleanupTrackingObjects(uploadId) {
226
+ const partsPrefix = `${PARTS_PREFIX}${uploadId}/`;
227
+ let cursor;
228
+ const keysToDelete = [];
229
+ do {
230
+ const listed = await this.bucket.list({
231
+ prefix: partsPrefix,
232
+ cursor
233
+ });
234
+ for (const obj of listed.objects) keysToDelete.push(obj.key);
235
+ cursor = listed.truncated ? listed.cursor : void 0;
236
+ } while (cursor);
237
+ keysToDelete.push(`${MULTIPART_PREFIX}${uploadId}.json`);
238
+ if (keysToDelete.length > 0) await this.bucket.delete(keysToDelete);
239
+ }
240
+ };
241
+ //#endregion
242
+ export { r2_storage_provider_exports as n, R2StorageProvider as t };
243
+
244
+ //# sourceMappingURL=r2-storage.provider-LdzK9tfG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-storage.provider-LdzK9tfG.mjs","names":[],"sources":["../src/storage/providers/r2-storage.provider.ts"],"sourcesContent":["import { type StratalEnv } from '../../env'\nimport { signUrl } from '../../router/signed-url'\nimport type { DownloadResult, PresignedUrlResult, UploadOptions, UploadResult } from '../contracts'\nimport { R2PresignedUrlSecretMissingError, StorageResponseBodyMissingError } from '../errors'\nimport type { StorageEntry, StorageRouteConfig } from '../types'\nimport type {\n CompletedPart,\n CompleteMultipartResult,\n CreateMultipartOptions,\n CreateMultipartResult,\n DeleteObjectsResult,\n HeadObjectResult,\n IMultipartProvider,\n ListMultipartUploadsResult,\n ListPartsResult,\n UploadPartResult,\n} from './multipart-provider.interface'\nimport type { StreamingBlobPayloadInputTypes } from './storage-provider.interface'\n\nconst MIN_PART_SIZE = 5 * 1024 * 1024 // 5MB\nconst MULTIPART_PREFIX = '__multipart/'\nconst PARTS_PREFIX = '__parts/'\n\n/**\n * R2 Storage Provider\n * Implements storage operations using native Cloudflare R2 bindings\n *\n * Implements IMultipartProvider for multipart upload support needed by TUS.\n * Tracks multipart upload metadata using companion R2 objects so that\n * listParts/listMultipartUploads work in all environments (local, test, prod).\n */\nexport class R2StorageProvider implements IMultipartProvider {\n private readonly routeBasePath: string\n\n constructor(\n private readonly config: StorageEntry,\n private readonly bucket: R2Bucket,\n private readonly env: StratalEnv,\n routeConfig?: StorageRouteConfig\n ) {\n this.routeBasePath = routeConfig?.basePath ?? '/storage'\n }\n\n async upload(\n body: StreamingBlobPayloadInputTypes,\n path: string,\n options: UploadOptions\n ): Promise<UploadResult> {\n const customMetadata: Record<string, string> = { ...options.metadata }\n if (options.tagging) {\n customMetadata['x-tags'] = options.tagging\n }\n\n await this.bucket.put(path, body as ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob | null, {\n httpMetadata: {\n contentType: options.mimeType,\n },\n customMetadata,\n })\n\n return {\n path,\n disk: this.config.disk,\n fullPath: path,\n size: options.size,\n mimeType: options.mimeType ?? 'application/octet-stream',\n uploadedAt: new Date(),\n }\n }\n\n async download(path: string): Promise<DownloadResult> {\n const obj = await this.bucket.get(path)\n\n if (!obj) {\n throw new StorageResponseBodyMissingError(path)\n }\n\n // R2ObjectBody has body, text(), arrayBuffer(), etc.\n const objBody = obj\n\n return {\n toStream: () => objBody.body as ReadableStream<Uint8Array>,\n contentType: obj.httpMetadata?.contentType ?? 'application/octet-stream',\n size: obj.size,\n metadata: obj.customMetadata,\n toString: () => objBody.text(),\n toArrayBuffer: () => objBody.bytes(),\n }\n }\n\n async delete(path: string): Promise<void> {\n await this.bucket.delete(path)\n }\n\n async exists(path: string): Promise<boolean> {\n const head = await this.bucket.head(path)\n return head !== null\n }\n\n async getPresignedUrl(\n path: string,\n method: 'GET' | 'PUT' | 'DELETE' | 'HEAD',\n expiresIn: number\n ): Promise<PresignedUrlResult> {\n const secret = this.env.APP_SECRET;\n if (!secret) {\n throw new R2PresignedUrlSecretMissingError()\n }\n\n // Build a relative URL pointing to the StorageController route\n const routePath = `${this.routeBasePath}/${this.config.disk}/${path}`\n const urlWithMethod = `${routePath}?method=${method}`\n const url = await signUrl(urlWithMethod, secret, { expiresIn })\n\n return {\n url,\n expiresIn,\n expiresAt: new Date(Date.now() + expiresIn * 1000),\n method,\n }\n }\n\n async chunkedUpload(\n body: StreamingBlobPayloadInputTypes,\n path: string,\n options: Omit<UploadOptions, 'size'> & { size?: number }\n ): Promise<UploadResult> {\n const multipartUpload = await this.bucket.createMultipartUpload(path, {\n httpMetadata: {\n contentType: options.mimeType,\n },\n customMetadata: options.metadata,\n })\n\n const parts: R2UploadedPart[] = []\n\n try {\n if (body instanceof ReadableStream) {\n const reader = (body as ReadableStream<Uint8Array>).getReader()\n let buffer = new Uint8Array(0)\n let partNumber = 1\n\n while (true) {\n const { done, value } = await reader.read()\n\n if (value) {\n const newBuffer = new Uint8Array(buffer.length + value.length)\n newBuffer.set(buffer, 0)\n newBuffer.set(value, buffer.length)\n buffer = newBuffer\n }\n\n while (buffer.length >= MIN_PART_SIZE) {\n const partData = buffer.slice(0, MIN_PART_SIZE)\n buffer = buffer.slice(MIN_PART_SIZE)\n const part = await multipartUpload.uploadPart(partNumber, partData)\n parts.push(part)\n partNumber++\n }\n\n if (done) {\n // Upload remaining buffer as final part\n if (buffer.length > 0) {\n const part = await multipartUpload.uploadPart(partNumber, buffer)\n parts.push(part)\n }\n break\n }\n }\n } else if (body !== null && body !== undefined) {\n // Non-stream body: upload as single part\n const part = await multipartUpload.uploadPart(1, body as ArrayBuffer | ArrayBufferView | string | Blob)\n parts.push(part)\n }\n\n await multipartUpload.complete(parts)\n } catch (error) {\n await multipartUpload.abort()\n throw error\n }\n\n // Get actual size via head\n const headResult = await this.bucket.head(path)\n\n return {\n path,\n disk: this.config.disk,\n fullPath: path,\n size: headResult?.size ?? options.size ?? 0,\n mimeType: options.mimeType ?? 'application/octet-stream',\n uploadedAt: new Date(),\n }\n }\n\n // ============================================\n // IMultipartProvider - Multipart upload methods\n // ============================================\n\n getBucket(): string {\n return this.config.binding\n }\n\n async headObject(key: string): Promise<HeadObjectResult | null> {\n const obj = await this.bucket.head(key)\n if (!obj) {\n return null\n }\n return {\n size: obj.size,\n contentType: obj.httpMetadata?.contentType,\n metadata: obj.customMetadata,\n }\n }\n\n async deleteObjects(keys: string[]): Promise<DeleteObjectsResult> {\n if (keys.length === 0) {\n return { deleted: 0, errors: [] }\n }\n\n await this.bucket.delete(keys)\n\n return {\n deleted: keys.length,\n errors: [],\n }\n }\n\n async createMultipartUpload(\n key: string,\n options?: CreateMultipartOptions\n ): Promise<CreateMultipartResult> {\n const upload = await this.bucket.createMultipartUpload(key, {\n httpMetadata: {\n contentType: options?.contentType,\n cacheControl: options?.cacheControl,\n },\n customMetadata: options?.metadata,\n })\n\n // Track upload for listMultipartUploads\n await this.bucket.put(\n `${MULTIPART_PREFIX}${upload.uploadId}.json`,\n JSON.stringify({ key: upload.key, uploadId: upload.uploadId, initiated: new Date().toISOString() }),\n { httpMetadata: { contentType: 'application/json' } }\n )\n\n return {\n uploadId: upload.uploadId,\n key: upload.key,\n }\n }\n\n async uploadPart(\n key: string,\n uploadId: string,\n partNumber: number,\n body: Uint8Array\n ): Promise<UploadPartResult> {\n const upload = this.bucket.resumeMultipartUpload(key, uploadId)\n const part = await upload.uploadPart(partNumber, body)\n\n // Track part for listParts\n await this.bucket.put(\n `${PARTS_PREFIX}${uploadId}/${partNumber}`,\n JSON.stringify({ partNumber: part.partNumber, etag: part.etag, size: body.length }),\n { httpMetadata: { contentType: 'application/json' } }\n )\n\n return {\n etag: part.etag,\n partNumber: part.partNumber,\n }\n }\n\n async completeMultipartUpload(\n key: string,\n uploadId: string,\n parts: CompletedPart[]\n ): Promise<CompleteMultipartResult> {\n const upload = this.bucket.resumeMultipartUpload(key, uploadId)\n const result = await upload.complete(\n parts.map((p) => ({\n partNumber: p.partNumber,\n etag: p.etag,\n }))\n )\n\n await this.cleanupTrackingObjects(uploadId)\n\n return {\n key: result.key,\n }\n }\n\n async abortMultipartUpload(key: string, uploadId: string): Promise<void> {\n const upload = this.bucket.resumeMultipartUpload(key, uploadId)\n await upload.abort()\n\n await this.cleanupTrackingObjects(uploadId)\n }\n\n async listParts(\n _key: string,\n uploadId: string,\n partNumberMarker?: string\n ): Promise<ListPartsResult> {\n const prefix = `${PARTS_PREFIX}${uploadId}/`\n const listed = await this.bucket.list({\n prefix,\n cursor: partNumberMarker,\n })\n\n const parts = await Promise.all(\n listed.objects.map(async (obj) => {\n const data = await this.bucket.get(obj.key)\n if (!data) {\n return null\n }\n return JSON.parse(await data.text()) as { partNumber: number; etag: string; size: number }\n })\n )\n\n const validParts = parts\n .filter((p): p is NonNullable<typeof p> => p !== null)\n .sort((a, b) => a.partNumber - b.partNumber)\n\n return {\n parts: validParts,\n isTruncated: listed.truncated,\n nextPartNumberMarker: listed.truncated ? listed.cursor : undefined,\n }\n }\n\n async listMultipartUploads(\n keyMarker?: string,\n _uploadIdMarker?: string\n ): Promise<ListMultipartUploadsResult> {\n const listed = await this.bucket.list({\n prefix: MULTIPART_PREFIX,\n cursor: keyMarker,\n })\n\n const uploads = await Promise.all(\n listed.objects.map(async (obj) => {\n const data = await this.bucket.get(obj.key)\n if (!data) {\n return null\n }\n const parsed = JSON.parse(await data.text()) as { key: string; uploadId: string; initiated: string }\n return {\n key: parsed.key,\n uploadId: parsed.uploadId,\n initiated: new Date(parsed.initiated),\n }\n })\n )\n\n return {\n uploads: uploads.filter((u): u is NonNullable<typeof u> => u !== null),\n isTruncated: listed.truncated,\n nextKeyMarker: listed.truncated ? listed.cursor : undefined,\n nextUploadIdMarker: undefined,\n }\n }\n\n // ============================================\n // Private helpers\n // ============================================\n\n private async cleanupTrackingObjects(uploadId: string): Promise<void> {\n // Delete part tracking objects\n const partsPrefix = `${PARTS_PREFIX}${uploadId}/`\n let cursor: string | undefined\n const keysToDelete: string[] = []\n\n do {\n const listed = await this.bucket.list({ prefix: partsPrefix, cursor })\n for (const obj of listed.objects) {\n keysToDelete.push(obj.key)\n }\n cursor = listed.truncated ? listed.cursor : undefined\n } while (cursor)\n\n // Delete multipart tracking object\n keysToDelete.push(`${MULTIPART_PREFIX}${uploadId}.json`)\n\n if (keysToDelete.length > 0) {\n await this.bucket.delete(keysToDelete)\n }\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { EmailResendApiFailedError, ResendApiKeyMissingError } from "./email/index.mjs";
2
- import { t as BaseEmailProvider } from "./base-email.provider-Cuw4OAB0.mjs";
2
+ import { t as BaseEmailProvider } from "./base-email.provider-mjynzewK.mjs";
3
3
  import { Resend } from "resend";
4
4
  //#region src/email/providers/resend.provider.ts
5
5
  /**
@@ -64,4 +64,4 @@ var ResendProvider = class extends BaseEmailProvider {
64
64
  //#endregion
65
65
  export { ResendProvider };
66
66
 
67
- //# sourceMappingURL=resend.provider-Bvw36rQy.mjs.map
67
+ //# sourceMappingURL=resend.provider-bwILp0WI.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"resend.provider-Bvw36rQy.mjs","names":[],"sources":["../src/email/providers/resend.provider.ts"],"sourcesContent":["import { Resend } from 'resend'\nimport type { ResolvedEmailAttachment, ResolvedEmailMessage } from '../contracts'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EmailResendApiFailedError, ResendApiKeyMissingError } from '../errors'\nimport { BaseEmailProvider } from './base-email.provider'\nimport type { EmailSendResult } from './email-provider.interface'\n\n/**\n * Resend Email Provider\n *\n * Implementation of IEmailProvider using Resend API\n * Docs: https://resend.com/docs\n */\nexport class ResendProvider extends BaseEmailProvider {\n private readonly client: Resend\n private readonly defaultFrom: { name: string; email: string }\n\n constructor(\n private readonly options: EmailModuleOptions\n ) {\n super()\n\n // Validate Resend API key\n if (!this.options.apiKey) {\n throw new ResendApiKeyMissingError()\n }\n\n this.client = new Resend(this.options.apiKey)\n this.defaultFrom = this.options.from\n }\n\n async send(message: ResolvedEmailMessage): Promise<EmailSendResult> {\n try {\n const from = message.from\n ? `${message.from.name} <${message.from.email}>`\n : `${this.defaultFrom.name} <${this.defaultFrom.email}>`\n\n const to = Array.isArray(message.to) ? message.to : [message.to]\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- Resend SDK types\n const response = await this.client.emails.send({\n from,\n to,\n subject: message.subject,\n html: message.html,\n text: message.text,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment -- Resend template extension\n ...((message as any).template && { template: (message as any).template }),\n ...(message.replyTo && { replyTo: message.replyTo }),\n ...(message.cc && { cc: message.cc }),\n ...(message.bcc && { bcc: message.bcc }),\n ...(message.attachments && {\n attachments: await Promise.all(\n message.attachments.map(async attachment => ({\n filename: attachment.filename,\n content: await this.toBuffer(attachment.content),\n }))\n ),\n }),\n })\n\n if (response.error) {\n throw new EmailResendApiFailedError()\n }\n\n return {\n messageId: response.data.id,\n accepted: true,\n metadata: {\n provider: 'resend',\n },\n }\n } catch (error) {\n if (error instanceof EmailResendApiFailedError || error instanceof ResendApiKeyMissingError) {\n throw error\n }\n\n throw new EmailResendApiFailedError()\n }\n }\n\n /**\n * Convert attachment content to Buffer\n *\n * Resend SDK expects Buffer for attachment content.\n * If content is already a Buffer, return as-is.\n * If content is a ReadableStream, convert to Buffer.\n */\n private async toBuffer(content: ResolvedEmailAttachment['content']): Promise<Buffer> {\n if (Buffer.isBuffer(content)) {\n return content\n }\n // Convert ReadableStream to Buffer\n const response = new Response(content)\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n }\n}\n"],"mappings":";;;;;;;;;;AAaA,IAAa,iBAAb,cAAoC,kBAAkB;CACpD;CACA;CAEA,YACE,SACA;AACA,SAAO;AAFU,OAAA,UAAA;AAKjB,MAAI,CAAC,KAAK,QAAQ,OAChB,OAAM,IAAI,0BAA0B;AAGtC,OAAK,SAAS,IAAI,OAAO,KAAK,QAAQ,OAAO;AAC7C,OAAK,cAAc,KAAK,QAAQ;;CAGlC,MAAM,KAAK,SAAyD;AAClE,MAAI;GACF,MAAM,OAAO,QAAQ,OACjB,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,KAC5C,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,YAAY,MAAM;GAExD,MAAM,KAAK,MAAM,QAAQ,QAAQ,GAAG,GAAG,QAAQ,KAAK,CAAC,QAAQ,GAAG;GAGhE,MAAM,WAAW,MAAM,KAAK,OAAO,OAAO,KAAK;IAC7C;IACA;IACA,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,MAAM,QAAQ;IAEd,GAAK,QAAgB,YAAY,EAAE,UAAW,QAAgB,UAAU;IACxE,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;IACnD,GAAI,QAAQ,MAAM,EAAE,IAAI,QAAQ,IAAI;IACpC,GAAI,QAAQ,OAAO,EAAE,KAAK,QAAQ,KAAK;IACvC,GAAI,QAAQ,eAAe,EACzB,aAAa,MAAM,QAAQ,IACzB,QAAQ,YAAY,IAAI,OAAM,gBAAe;KAC3C,UAAU,WAAW;KACrB,SAAS,MAAM,KAAK,SAAS,WAAW,QAAQ;KACjD,EAAE,CACJ,EACF;IACF,CAAC;AAEF,OAAI,SAAS,MACX,OAAM,IAAI,2BAA2B;AAGvC,UAAO;IACL,WAAW,SAAS,KAAK;IACzB,UAAU;IACV,UAAU,EACR,UAAU,UACX;IACF;WACM,OAAO;AACd,OAAI,iBAAiB,6BAA6B,iBAAiB,yBACjE,OAAM;AAGR,SAAM,IAAI,2BAA2B;;;;;;;;;;CAWzC,MAAc,SAAS,SAA8D;AACnF,MAAI,OAAO,SAAS,QAAQ,CAC1B,QAAO;EAIT,MAAM,cAAc,MADH,IAAI,SAAS,QAAQ,CACH,aAAa;AAChD,SAAO,OAAO,KAAK,YAAY"}
1
+ {"version":3,"file":"resend.provider-bwILp0WI.mjs","names":[],"sources":["../src/email/providers/resend.provider.ts"],"sourcesContent":["import { Resend } from 'resend'\nimport type { ResolvedEmailAttachment, ResolvedEmailMessage } from '../contracts'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EmailResendApiFailedError, ResendApiKeyMissingError } from '../errors'\nimport { BaseEmailProvider } from './base-email.provider'\nimport type { EmailSendResult } from './email-provider.interface'\n\n/**\n * Resend Email Provider\n *\n * Implementation of IEmailProvider using Resend API\n * Docs: https://resend.com/docs\n */\nexport class ResendProvider extends BaseEmailProvider {\n private readonly client: Resend\n private readonly defaultFrom: { name: string; email: string }\n\n constructor(\n private readonly options: EmailModuleOptions\n ) {\n super()\n\n // Validate Resend API key\n if (!this.options.apiKey) {\n throw new ResendApiKeyMissingError()\n }\n\n this.client = new Resend(this.options.apiKey)\n this.defaultFrom = this.options.from\n }\n\n async send(message: ResolvedEmailMessage): Promise<EmailSendResult> {\n try {\n const from = message.from\n ? `${message.from.name} <${message.from.email}>`\n : `${this.defaultFrom.name} <${this.defaultFrom.email}>`\n\n const to = Array.isArray(message.to) ? message.to : [message.to]\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- Resend SDK types\n const response = await this.client.emails.send({\n from,\n to,\n subject: message.subject,\n html: message.html,\n text: message.text,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment -- Resend template extension\n ...((message as any).template && { template: (message as any).template }),\n ...(message.replyTo && { replyTo: message.replyTo }),\n ...(message.cc && { cc: message.cc }),\n ...(message.bcc && { bcc: message.bcc }),\n ...(message.attachments && {\n attachments: await Promise.all(\n message.attachments.map(async attachment => ({\n filename: attachment.filename,\n content: await this.toBuffer(attachment.content),\n }))\n ),\n }),\n })\n\n if (response.error) {\n throw new EmailResendApiFailedError()\n }\n\n return {\n messageId: response.data.id,\n accepted: true,\n metadata: {\n provider: 'resend',\n },\n }\n } catch (error) {\n if (error instanceof EmailResendApiFailedError || error instanceof ResendApiKeyMissingError) {\n throw error\n }\n\n throw new EmailResendApiFailedError()\n }\n }\n\n /**\n * Convert attachment content to Buffer\n *\n * Resend SDK expects Buffer for attachment content.\n * If content is already a Buffer, return as-is.\n * If content is a ReadableStream, convert to Buffer.\n */\n private async toBuffer(content: ResolvedEmailAttachment['content']): Promise<Buffer> {\n if (Buffer.isBuffer(content)) {\n return content\n }\n // Convert ReadableStream to Buffer\n const response = new Response(content)\n const arrayBuffer = await response.arrayBuffer()\n return Buffer.from(arrayBuffer)\n }\n}\n"],"mappings":";;;;;;;;;;AAaA,IAAa,iBAAb,cAAoC,kBAAkB;CACpD;CACA;CAEA,YACE,SACA;AACA,SAAO;AAFU,OAAA,UAAA;AAKjB,MAAI,CAAC,KAAK,QAAQ,OAChB,OAAM,IAAI,0BAA0B;AAGtC,OAAK,SAAS,IAAI,OAAO,KAAK,QAAQ,OAAO;AAC7C,OAAK,cAAc,KAAK,QAAQ;;CAGlC,MAAM,KAAK,SAAyD;AAClE,MAAI;GACF,MAAM,OAAO,QAAQ,OACjB,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,KAC5C,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,YAAY,MAAM;GAExD,MAAM,KAAK,MAAM,QAAQ,QAAQ,GAAG,GAAG,QAAQ,KAAK,CAAC,QAAQ,GAAG;GAGhE,MAAM,WAAW,MAAM,KAAK,OAAO,OAAO,KAAK;IAC7C;IACA;IACA,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,MAAM,QAAQ;IAEd,GAAK,QAAgB,YAAY,EAAE,UAAW,QAAgB,UAAU;IACxE,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,SAAS;IACnD,GAAI,QAAQ,MAAM,EAAE,IAAI,QAAQ,IAAI;IACpC,GAAI,QAAQ,OAAO,EAAE,KAAK,QAAQ,KAAK;IACvC,GAAI,QAAQ,eAAe,EACzB,aAAa,MAAM,QAAQ,IACzB,QAAQ,YAAY,IAAI,OAAM,gBAAe;KAC3C,UAAU,WAAW;KACrB,SAAS,MAAM,KAAK,SAAS,WAAW,QAAQ;KACjD,EAAE,CACJ,EACF;IACF,CAAC;AAEF,OAAI,SAAS,MACX,OAAM,IAAI,2BAA2B;AAGvC,UAAO;IACL,WAAW,SAAS,KAAK;IACzB,UAAU;IACV,UAAU,EACR,UAAU,UACX;IACF;WACM,OAAO;AACd,OAAI,iBAAiB,6BAA6B,iBAAiB,yBACjE,OAAM;AAGR,SAAM,IAAI,2BAA2B;;;;;;;;;;CAWzC,MAAc,SAAS,SAA8D;AACnF,MAAI,OAAO,SAAS,QAAQ,CAC1B,QAAO;EAIT,MAAM,cAAc,MAAM,IADL,SAAS,QACI,CAAC,aAAa;AAChD,SAAO,OAAO,KAAK,YAAY"}
@@ -1,2 +1,2 @@
1
- import { $ as SSEMessage, $n as HTTP_METHODS, $t as RouteConfigurable, At as getRouteDecoratedMethods, Bn as ConventionRouteMetadata, Bt as getControllerRoute, Ct as errorResponseSchema, Dt as uuidParamSchema, Et as successMessageSchema, Ft as Patch, G as buildRouteUrl, Gn as RouteConfig, Gt as generateConventionRouteName, H as SignedUriOptions, Hn as LocalePathConfig, Ht as createMiddlewareChain, In as LocalePathService, It as Post, J as RouteRegistry, Jn as RouteResponseObject, Jt as toOpenAPIPath, K as RegisteredRoute, Kn as RouteMetadata, Kt as getPathSpecificityScore, Ln as ResolvedPath, Lt as Put, Mt as All, Nt as Delete, Ot as validationErrorResponseSchema, Pt as Get, Qn as VersioningOptions, Rn as HonoApp, Rt as Controller, Sr as StratalRouteMap, St as commonErrorSchemas, Tt as paginationQuerySchema, U as Uri, Un as RouteBody, Ut as extractDomainParamNames, V as RouterContext, Vn as ExplicitRouteMetadata, Vt as getControllerVersion, W as UriOptions, Wn as RouteBodyObject, Wt as extractParamNames, Xn as RouterVariables, Xt as ROUTER_TOKENS, Y as VersioningService, Yn as RouterEnv, Yt as route, Zn as SecurityScheme, Zt as RouteRegistrationService, _t as signUrl, at as MissingEnvironmentVariableError, br as SerializedRoute, bt as createDomainMiddleware, ct as RouteNameNotFoundError, dt as RouteNotFoundError, en as Router, er as ROUTER_CONTEXT_KEYS, et as SSEStreamingApi, ft as OpenAPIValidationError, gt as SignedUrlOptions, ht as ControllerRegistrationError, in as IController, it as InvalidSignatureError, jt as getRouteMetadata, kt as Route, lt as RouterUseScopeError, mt as HonoAppAlreadyConfiguredError, nn as Middleware, nr as SECURITY_SCHEMES, nt as DomainMismatchError, ot as MissingRouteParamError, pt as OpenAPIRouteRegistrationError, q as RouteRegistrationInput, qn as RouteResponse, qt as sortRoutesBySpecificity, rn as Next, rr as VERSION_NEUTRAL, rt as DuplicateRouteNameError, st as ResponseValidationError, tn as RouterGroupConfig, tr as ROUTE_METADATA_KEYS, tt as StreamingApi, ut as SchemaValidationError, vr as RouteName, vt as verifySignedUrl, wt as paginatedResponseSchema, xr as SerializedRoutes, xt as parseDomainPattern, yr as RouteParams, yt as VerifySignatureMiddleware, zn as ControllerOptions, zt as getControllerOptions } from "../index-BrmS34sa.mjs";
2
- export { All, Controller, ControllerOptions, ControllerRegistrationError, ConventionRouteMetadata, Delete, DomainMismatchError, DuplicateRouteNameError, ExplicitRouteMetadata, Get, HTTP_METHODS, HonoApp, HonoAppAlreadyConfiguredError, IController, InvalidSignatureError, LocalePathConfig, LocalePathService, Middleware, MissingEnvironmentVariableError, MissingRouteParamError, Next, OpenAPIRouteRegistrationError, OpenAPIValidationError, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, RegisteredRoute, ResolvedPath, ResponseValidationError, Route, RouteBody, RouteBodyObject, RouteConfig, RouteConfigurable, RouteMetadata, RouteName, RouteNameNotFoundError, RouteNotFoundError, RouteParams, RouteRegistrationInput, RouteRegistrationService, RouteRegistry, RouteResponse, RouteResponseObject, Router, RouterContext, RouterEnv, RouterGroupConfig, RouterUseScopeError, RouterVariables, SECURITY_SCHEMES, SSEMessage, SSEStreamingApi, SchemaValidationError, SecurityScheme, SerializedRoute, SerializedRoutes, SignedUriOptions, SignedUrlOptions, StratalRouteMap, StreamingApi, Uri, UriOptions, VERSION_NEUTRAL, VerifySignatureMiddleware, VersioningOptions, VersioningService, buildRouteUrl, commonErrorSchemas, createDomainMiddleware, createMiddlewareChain, errorResponseSchema, extractDomainParamNames, extractParamNames, generateConventionRouteName, getControllerOptions, getControllerRoute, getControllerVersion, getPathSpecificityScore, getRouteDecoratedMethods, getRouteMetadata, paginatedResponseSchema, paginationQuerySchema, parseDomainPattern, route, signUrl, sortRoutesBySpecificity, successMessageSchema, toOpenAPIPath, uuidParamSchema, validationErrorResponseSchema, verifySignedUrl };
1
+ import { $ as SSEMessage, $n as VersioningOptions, At as Route, Bn as ControllerOptions, Bt as getControllerOptions, Ct as commonErrorSchemas, Dt as successMessageSchema, Et as paginationQuerySchema, Ft as Get, G as buildRouteUrl, Gn as RouteBodyObject, Gt as extractParamNames, H as SignedUriOptions, Hn as ExplicitRouteMetadata, Ht as getControllerVersion, It as Patch, J as RouteRegistry, Jn as RouteResponse, Jt as sortRoutesBySpecificity, K as RegisteredRoute, Kn as RouteConfig, Kt as generateConventionRouteName, Ln as LocalePathService, Lt as Post, Mt as getRouteMetadata, Nt as All, Ot as uuidParamSchema, Pt as Delete, Qn as SecurityScheme, Qt as RouteRegistrationService, Rn as ResolvedPath, Rt as Put, Sr as StratalRouteMap, St as parseDomainPattern, Tt as paginatedResponseSchema, U as Uri, Un as LocalePathConfig, Ut as createMiddlewareChain, V as RouterContext, Vn as ConventionRouteMetadata, Vt as getControllerRoute, W as UriOptions, Wn as RouteBody, Wt as extractDomainParamNames, Xn as RouterEnv, Xt as route, Y as VersioningService, Yn as RouteResponseObject, Yt as toOpenAPIPath, Zn as RouterVariables, Zt as ROUTER_TOKENS, _t as SignedUrlOptions, an as IController, at as MiddlewareNextCalledMultipleTimesError, br as SerializedRoute, bt as VerifySignatureMiddleware, ct as ResponseValidationError, dt as SchemaValidationError, en as RouteConfigurable, er as HTTP_METHODS, et as SSEStreamingApi, ft as RouteNotFoundError, gt as ControllerRegistrationError, ht as HonoAppAlreadyConfiguredError, in as Next, ir as VERSION_NEUTRAL, it as InvalidSignatureError, jt as getRouteDecoratedMethods, kt as validationErrorResponseSchema, lt as RouteNameNotFoundError, mt as OpenAPIRouteRegistrationError, nn as RouterGroupConfig, nr as ROUTE_METADATA_KEYS, nt as DomainMismatchError, ot as MissingEnvironmentVariableError, pt as OpenAPIValidationError, q as RouteRegistrationInput, qn as RouteMetadata, qt as getPathSpecificityScore, rn as Middleware, rr as SECURITY_SCHEMES, rt as DuplicateRouteNameError, st as MissingRouteParamError, tn as Router, tr as ROUTER_CONTEXT_KEYS, tt as StreamingApi, ut as RouterUseScopeError, vr as RouteName, vt as signUrl, wt as errorResponseSchema, xr as SerializedRoutes, xt as createDomainMiddleware, yr as RouteParams, yt as verifySignedUrl, zn as HonoApp, zt as Controller } from "../index-DPFqRs8L.mjs";
2
+ export { All, Controller, ControllerOptions, ControllerRegistrationError, ConventionRouteMetadata, Delete, DomainMismatchError, DuplicateRouteNameError, ExplicitRouteMetadata, Get, HTTP_METHODS, HonoApp, HonoAppAlreadyConfiguredError, IController, InvalidSignatureError, LocalePathConfig, LocalePathService, Middleware, MiddlewareNextCalledMultipleTimesError, MissingEnvironmentVariableError, MissingRouteParamError, Next, OpenAPIRouteRegistrationError, OpenAPIValidationError, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, RegisteredRoute, ResolvedPath, ResponseValidationError, Route, RouteBody, RouteBodyObject, RouteConfig, RouteConfigurable, RouteMetadata, RouteName, RouteNameNotFoundError, RouteNotFoundError, RouteParams, RouteRegistrationInput, RouteRegistrationService, RouteRegistry, RouteResponse, RouteResponseObject, Router, RouterContext, RouterEnv, RouterGroupConfig, RouterUseScopeError, RouterVariables, SECURITY_SCHEMES, SSEMessage, SSEStreamingApi, SchemaValidationError, SecurityScheme, SerializedRoute, SerializedRoutes, SignedUriOptions, SignedUrlOptions, StratalRouteMap, StreamingApi, Uri, UriOptions, VERSION_NEUTRAL, VerifySignatureMiddleware, VersioningOptions, VersioningService, buildRouteUrl, commonErrorSchemas, createDomainMiddleware, createMiddlewareChain, errorResponseSchema, extractDomainParamNames, extractParamNames, generateConventionRouteName, getControllerOptions, getControllerRoute, getControllerVersion, getPathSpecificityScore, getRouteDecoratedMethods, getRouteMetadata, paginatedResponseSchema, paginationQuerySchema, parseDomainPattern, route, signUrl, sortRoutesBySpecificity, successMessageSchema, toOpenAPIPath, uuidParamSchema, validationErrorResponseSchema, verifySignedUrl };
@@ -1,5 +1,7 @@
1
- import { V as ROUTER_TOKENS, d as ROUTER_CONTEXT_KEYS, f as ROUTE_METADATA_KEYS, l as HTTP_METHODS, m as VERSION_NEUTRAL, p as SECURITY_SCHEMES, s as RouterContext } from "../errors--RBIvDXr.mjs";
2
- import { _ as OpenAPIRouteRegistrationError, c as InvalidSignatureError, d as ResponseValidationError, f as RouteNameNotFoundError, g as OpenAPIValidationError, h as RouteNotFoundError, l as MissingEnvironmentVariableError, m as SchemaValidationError, n as Router, o as DomainMismatchError, p as RouterUseScopeError, s as DuplicateRouteNameError, u as MissingRouteParamError, v as HonoAppAlreadyConfiguredError, y as ControllerRegistrationError } from "../module-C3YZ-kZN.mjs";
3
- import { A as Delete, C as successMessageSchema, D as getRouteDecoratedMethods, E as Route, F as createMiddlewareChain, I as createDomainMiddleware, L as parseDomainPattern, M as Patch, N as Post, O as getRouteMetadata, P as Put, S as paginationQuerySchema, T as validationErrorResponseSchema, _ as sortRoutesBySpecificity, a as buildRouteUrl, b as errorResponseSchema, c as RouteRegistry, d as HonoApp, f as RouteRegistrationService, g as getPathSpecificityScore, h as generateConventionRouteName, i as Uri, j as Get, k as All, l as VersioningService, m as extractParamNames, n as VerifySignatureMiddleware, o as signUrl, p as extractDomainParamNames, r as route, s as verifySignedUrl, u as LocalePathService, v as toOpenAPIPath, w as uuidParamSchema, x as paginatedResponseSchema, y as commonErrorSchemas } from "../i18n.module-BpLLLCTg.mjs";
4
- import { f as Controller, h as getControllerVersion, m as getControllerRoute, p as getControllerOptions } from "../gateway-context-BdBFoQd8.mjs";
5
- export { All, Controller, ControllerRegistrationError, Delete, DomainMismatchError, DuplicateRouteNameError, Get, HTTP_METHODS, HonoApp, HonoAppAlreadyConfiguredError, InvalidSignatureError, LocalePathService, MissingEnvironmentVariableError, MissingRouteParamError, OpenAPIRouteRegistrationError, OpenAPIValidationError, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, ResponseValidationError, Route, RouteNameNotFoundError, RouteNotFoundError, RouteRegistrationService, RouteRegistry, Router, RouterContext, RouterUseScopeError, SECURITY_SCHEMES, SchemaValidationError, Uri, VERSION_NEUTRAL, VerifySignatureMiddleware, VersioningService, buildRouteUrl, commonErrorSchemas, createDomainMiddleware, createMiddlewareChain, errorResponseSchema, extractDomainParamNames, extractParamNames, generateConventionRouteName, getControllerOptions, getControllerRoute, getControllerVersion, getPathSpecificityScore, getRouteDecoratedMethods, getRouteMetadata, paginatedResponseSchema, paginationQuerySchema, parseDomainPattern, route, signUrl, sortRoutesBySpecificity, successMessageSchema, toOpenAPIPath, uuidParamSchema, validationErrorResponseSchema, verifySignedUrl };
1
+ import { V as ROUTER_TOKENS, d as ROUTER_CONTEXT_KEYS, f as ROUTE_METADATA_KEYS, l as HTTP_METHODS, m as VERSION_NEUTRAL, p as SECURITY_SCHEMES, s as RouterContext } from "../errors-B4pYgYON.mjs";
2
+ import { _ as OpenAPIValidationError, b as ControllerRegistrationError, c as InvalidSignatureError, d as MissingRouteParamError, f as ResponseValidationError, g as RouteNotFoundError, h as SchemaValidationError, l as MiddlewareNextCalledMultipleTimesError, m as RouterUseScopeError, n as Router, o as DomainMismatchError, p as RouteNameNotFoundError, s as DuplicateRouteNameError, u as MissingEnvironmentVariableError, v as OpenAPIRouteRegistrationError, y as HonoAppAlreadyConfiguredError } from "../module-qGE_1duv.mjs";
3
+ import { C as validationErrorResponseSchema, D as createMiddlewareChain, E as getRouteMetadata, O as createDomainMiddleware, S as uuidParamSchema, T as getRouteDecoratedMethods, _ as commonErrorSchemas, a as buildRouteUrl, b as paginationQuerySchema, c as LocalePathService, d as extractDomainParamNames, f as extractParamNames, g as toOpenAPIPath, h as sortRoutesBySpecificity, i as Uri, k as parseDomainPattern, l as HonoApp, m as getPathSpecificityScore, n as VerifySignatureMiddleware, o as RouteRegistry, p as generateConventionRouteName, r as route, s as VersioningService, u as RouteRegistrationService, v as errorResponseSchema, w as Route, x as successMessageSchema, y as paginatedResponseSchema } from "../i18n.module-CI_prYFD.mjs";
4
+ import { i as getControllerVersion, n as getControllerOptions, r as getControllerRoute, t as Controller } from "../controller.decorator-LZY9aHYG.mjs";
5
+ import { a as Post, i as Patch, n as Delete, o as Put, r as Get, t as All } from "../http-method.decorator-BT3ufnz8.mjs";
6
+ import { n as verifySignedUrl, t as signUrl } from "../signed-url-COX7cCWR.mjs";
7
+ export { All, Controller, ControllerRegistrationError, Delete, DomainMismatchError, DuplicateRouteNameError, Get, HTTP_METHODS, HonoApp, HonoAppAlreadyConfiguredError, InvalidSignatureError, LocalePathService, MiddlewareNextCalledMultipleTimesError, MissingEnvironmentVariableError, MissingRouteParamError, OpenAPIRouteRegistrationError, OpenAPIValidationError, Patch, Post, Put, ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, ROUTE_METADATA_KEYS, ResponseValidationError, Route, RouteNameNotFoundError, RouteNotFoundError, RouteRegistrationService, RouteRegistry, Router, RouterContext, RouterUseScopeError, SECURITY_SCHEMES, SchemaValidationError, Uri, VERSION_NEUTRAL, VerifySignatureMiddleware, VersioningService, buildRouteUrl, commonErrorSchemas, createDomainMiddleware, createMiddlewareChain, errorResponseSchema, extractDomainParamNames, extractParamNames, generateConventionRouteName, getControllerOptions, getControllerRoute, getControllerVersion, getPathSpecificityScore, getRouteDecoratedMethods, getRouteMetadata, paginatedResponseSchema, paginationQuerySchema, parseDomainPattern, route, signUrl, sortRoutesBySpecificity, successMessageSchema, toOpenAPIPath, uuidParamSchema, validationErrorResponseSchema, verifySignedUrl };
@@ -1,6 +1,6 @@
1
- import { Cr as Container, X as Application, d as ApplicationError } from "../index-BrmS34sa.mjs";
2
- import { t as Constructor } from "../types-CN0zONAZ.mjs";
3
- import { t as Command } from "../command-B1YuV-UZ.mjs";
1
+ import { Cr as Container, X as Application, d as ApplicationError } from "../index-DPFqRs8L.mjs";
2
+ import { t as Constructor } from "../types-DIWemRad.mjs";
3
+ import { t as Command } from "../command-DsQq56Lp.mjs";
4
4
 
5
5
  //#region src/seeder/seeder.d.ts
6
6
  declare const SEEDER_INTERNALS: unique symbol;
@@ -1,3 +1,3 @@
1
- import { n as SEEDER_INTERNALS, r as Seeder, t as isSeeder } from "../is-seeder-BN9Ej1r7.mjs";
2
- import { a as SeederNameCollisionError, i as SeederRegistry, n as DbSeedListCommand, o as SeederNotRegisteredError, r as SEEDER_TOKENS, t as DbSeedCommand } from "../seeder-D7VXULXB.mjs";
1
+ import { n as SEEDER_INTERNALS, r as Seeder, t as isSeeder } from "../is-seeder-CebjZCDn.mjs";
2
+ import { a as SeederNameCollisionError, i as SeederRegistry, n as DbSeedListCommand, o as SeederNotRegisteredError, r as SEEDER_TOKENS, t as DbSeedCommand } from "../seeder-BcqIFa2X.mjs";
3
3
  export { DbSeedCommand, DbSeedListCommand, SEEDER_INTERNALS, SEEDER_TOKENS, Seeder, SeederNameCollisionError, SeederNotRegisteredError, SeederRegistry, isSeeder };
@@ -1,7 +1,7 @@
1
- import { H as ApplicationError, k as ERROR_CODES } from "./errors--RBIvDXr.mjs";
2
- import { a as __decorate, o as __decorateParam, s as __decorateMetadata } from "./logger-c0ftIK4G.mjs";
3
- import { t as Command } from "./command-DjGqCYHv.mjs";
4
- import { n as SEEDER_INTERNALS } from "./is-seeder-BN9Ej1r7.mjs";
1
+ import { H as ApplicationError, k as ERROR_CODES } from "./errors-B4pYgYON.mjs";
2
+ import { a as __decorate, o as __decorateParam, s as __decorateMetadata } from "./logger-V6Ms3QnQ.mjs";
3
+ import { t as Command } from "./command-BgSlsS4M.mjs";
4
+ import { n as SEEDER_INTERNALS } from "./is-seeder-CebjZCDn.mjs";
5
5
  import { inject } from "tsyringe";
6
6
  //#region src/seeder/errors.ts
7
7
  var SeederNotRegisteredError = class extends ApplicationError {
@@ -132,4 +132,4 @@ DbSeedCommand = __decorate([__decorateParam(0, inject(SEEDER_TOKENS.SeederRegist
132
132
  //#endregion
133
133
  export { SeederNameCollisionError as a, SeederRegistry as i, DbSeedListCommand as n, SeederNotRegisteredError as o, SEEDER_TOKENS as r, DbSeedCommand as t };
134
134
 
135
- //# sourceMappingURL=seeder-D7VXULXB.mjs.map
135
+ //# sourceMappingURL=seeder-BcqIFa2X.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"seeder-D7VXULXB.mjs","names":[],"sources":["../src/seeder/errors.ts","../src/seeder/seeder-registry.ts","../src/seeder/commands/db-seed-list.command.ts","../src/seeder/commands/db-seed.command.ts"],"sourcesContent":["import { ApplicationError, ERROR_CODES } from '../errors'\n\nexport class SeederNotRegisteredError extends ApplicationError {\n constructor(name: string) {\n super(\n 'errors.seederNotRegistered',\n ERROR_CODES.SYSTEM.SEEDER_NOT_REGISTERED,\n { name },\n )\n }\n}\n\nexport class SeederNameCollisionError extends ApplicationError {\n constructor(name: string) {\n super(\n 'errors.seederNameCollision',\n ERROR_CODES.SYSTEM.SEEDER_NAME_COLLISION,\n { name },\n )\n }\n}\n","import type { Application } from '../application'\nimport type { Container } from '../di/container'\nimport type { Constructor } from '../types'\nimport { SeederNameCollisionError, SeederNotRegisteredError } from './errors'\nimport { type Seeder, SEEDER_INTERNALS } from './seeder'\n\nexport const SEEDER_TOKENS = {\n SeederRegistry: Symbol.for('stratal:seeders:registry'),\n} as const\n\nexport class SeederRegistry {\n private seeders = new Set<Constructor<Seeder>>()\n private nameIndex = new Map<string, Constructor<Seeder>>()\n\n constructor(private app: Application) { }\n\n register(SeederClass: Constructor<Seeder>): void {\n const existing = this.nameIndex.get(SeederClass.name)\n if (existing && existing !== SeederClass) {\n throw new SeederNameCollisionError(SeederClass.name)\n }\n this.seeders.add(SeederClass)\n this.nameIndex.set(SeederClass.name, SeederClass)\n }\n\n async run(SeederClass: Constructor<Seeder>, options?: { container?: Container }): Promise<void> {\n if (!this.seeders.has(SeederClass)) {\n throw new SeederNotRegisteredError(SeederClass.name)\n }\n\n const execute = async (container: Container) => {\n const seeder = container.resolve<Seeder>(SeederClass)\n seeder[SEEDER_INTERNALS] = {\n run: (cls) => this.run(cls, { container }),\n container,\n }\n await seeder.run()\n }\n\n if (options?.container) {\n await execute(options.container)\n } else {\n const mockContext = this.app.createMockRouterContext('en')\n await this.app.container.runInRequestScope(mockContext, execute)\n }\n }\n\n async runAll(options?: { container?: Container }): Promise<void> {\n for (const SeederClass of this.seeders) {\n await this.run(SeederClass, options)\n }\n }\n\n find(name: string): Constructor<Seeder> | undefined {\n return this.nameIndex.get(name)\n }\n\n has(SeederClass: Constructor<Seeder>): boolean {\n return this.seeders.has(SeederClass)\n }\n\n list(): { className: string }[] {\n return [...this.seeders].map(cls => ({ className: cls.name }))\n }\n}\n","import { inject } from 'tsyringe'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedListCommand extends Command {\n static command = 'db:seed:list'\n static description = 'List available database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n handle(): undefined | number {\n const list = this.seeders.list()\n if (list.length === 0) {\n this.info('No seeders found')\n return 0\n }\n this.table(['Class'], list.map(s => [s.className]))\n\n return undefined\n }\n}\n","import { inject } from 'tsyringe'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedCommand extends Command {\n static command = 'db:seed {names* : Seeder class names} {--a|all : Run all seeders} {--dry-run : Preview without executing}'\n static description = 'Run database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n async handle(): Promise<number | undefined> {\n const names = this.array('names')\n const all = this.boolean('all')\n const dryRun = this.boolean('dry-run')\n\n if (names.length > 0 && all) {\n this.warn(`Ignoring \"${names.join(', ')}\" because --all takes precedence`)\n }\n\n if (names.length === 0 && !all) {\n this.fail('Specify one or more seeder class names or use --all')\n return 1\n }\n\n if (dryRun) {\n if (all) {\n const list = this.seeders.list()\n this.info('Dry run — would execute:')\n for (const s of list) {\n this.info(` ${s.className}`)\n }\n } else {\n this.info('Dry run — would execute:')\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n this.info(` ${SeederClass.name}`)\n }\n }\n return 0\n }\n\n if (all) {\n await this.seeders.runAll()\n this.success('All seeders completed')\n } else {\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n await this.seeders.run(SeederClass)\n this.success(`Seeder \"${name}\" completed`)\n }\n }\n\n return 0\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"seeder-BcqIFa2X.mjs","names":[],"sources":["../src/seeder/errors.ts","../src/seeder/seeder-registry.ts","../src/seeder/commands/db-seed-list.command.ts","../src/seeder/commands/db-seed.command.ts"],"sourcesContent":["import { ApplicationError, ERROR_CODES } from '../errors'\n\nexport class SeederNotRegisteredError extends ApplicationError {\n constructor(name: string) {\n super(\n 'errors.seederNotRegistered',\n ERROR_CODES.SYSTEM.SEEDER_NOT_REGISTERED,\n { name },\n )\n }\n}\n\nexport class SeederNameCollisionError extends ApplicationError {\n constructor(name: string) {\n super(\n 'errors.seederNameCollision',\n ERROR_CODES.SYSTEM.SEEDER_NAME_COLLISION,\n { name },\n )\n }\n}\n","import type { Application } from '../application'\nimport type { Container } from '../di/container'\nimport type { Constructor } from '../types'\nimport { SeederNameCollisionError, SeederNotRegisteredError } from './errors'\nimport { type Seeder, SEEDER_INTERNALS } from './seeder'\n\nexport const SEEDER_TOKENS = {\n SeederRegistry: Symbol.for('stratal:seeders:registry'),\n} as const\n\nexport class SeederRegistry {\n private seeders = new Set<Constructor<Seeder>>()\n private nameIndex = new Map<string, Constructor<Seeder>>()\n\n constructor(private app: Application) { }\n\n register(SeederClass: Constructor<Seeder>): void {\n const existing = this.nameIndex.get(SeederClass.name)\n if (existing && existing !== SeederClass) {\n throw new SeederNameCollisionError(SeederClass.name)\n }\n this.seeders.add(SeederClass)\n this.nameIndex.set(SeederClass.name, SeederClass)\n }\n\n async run(SeederClass: Constructor<Seeder>, options?: { container?: Container }): Promise<void> {\n if (!this.seeders.has(SeederClass)) {\n throw new SeederNotRegisteredError(SeederClass.name)\n }\n\n const execute = async (container: Container) => {\n const seeder = container.resolve<Seeder>(SeederClass)\n seeder[SEEDER_INTERNALS] = {\n run: (cls) => this.run(cls, { container }),\n container,\n }\n await seeder.run()\n }\n\n if (options?.container) {\n await execute(options.container)\n } else {\n const mockContext = this.app.createMockRouterContext('en')\n await this.app.container.runInRequestScope(mockContext, execute)\n }\n }\n\n async runAll(options?: { container?: Container }): Promise<void> {\n for (const SeederClass of this.seeders) {\n await this.run(SeederClass, options)\n }\n }\n\n find(name: string): Constructor<Seeder> | undefined {\n return this.nameIndex.get(name)\n }\n\n has(SeederClass: Constructor<Seeder>): boolean {\n return this.seeders.has(SeederClass)\n }\n\n list(): { className: string }[] {\n return [...this.seeders].map(cls => ({ className: cls.name }))\n }\n}\n","import { inject } from 'tsyringe'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedListCommand extends Command {\n static command = 'db:seed:list'\n static description = 'List available database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n handle(): undefined | number {\n const list = this.seeders.list()\n if (list.length === 0) {\n this.info('No seeders found')\n return 0\n }\n this.table(['Class'], list.map(s => [s.className]))\n\n return undefined\n }\n}\n","import { inject } from 'tsyringe'\nimport { Command } from '../../quarry/command'\nimport { type SeederRegistry, SEEDER_TOKENS } from '../seeder-registry'\n\nexport class DbSeedCommand extends Command {\n static command = 'db:seed {names* : Seeder class names} {--a|all : Run all seeders} {--dry-run : Preview without executing}'\n static description = 'Run database seeders'\n\n constructor(@inject(SEEDER_TOKENS.SeederRegistry) private seeders: SeederRegistry) {\n super()\n }\n\n async handle(): Promise<number | undefined> {\n const names = this.array('names')\n const all = this.boolean('all')\n const dryRun = this.boolean('dry-run')\n\n if (names.length > 0 && all) {\n this.warn(`Ignoring \"${names.join(', ')}\" because --all takes precedence`)\n }\n\n if (names.length === 0 && !all) {\n this.fail('Specify one or more seeder class names or use --all')\n return 1\n }\n\n if (dryRun) {\n if (all) {\n const list = this.seeders.list()\n this.info('Dry run — would execute:')\n for (const s of list) {\n this.info(` ${s.className}`)\n }\n } else {\n this.info('Dry run — would execute:')\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n this.info(` ${SeederClass.name}`)\n }\n }\n return 0\n }\n\n if (all) {\n await this.seeders.runAll()\n this.success('All seeders completed')\n } else {\n for (const name of names) {\n const SeederClass = this.seeders.find(name)\n if (!SeederClass) {\n this.fail(`Seeder \"${name}\" not found`)\n return 1\n }\n await this.seeders.run(SeederClass)\n this.success(`Seeder \"${name}\" completed`)\n }\n }\n\n return 0\n }\n}\n"],"mappings":"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"}
@@ -34,4 +34,4 @@ function setupI18nCompiler() {
34
34
  //#endregion
35
35
  export { setupI18nCompiler as t };
36
36
 
37
- //# sourceMappingURL=setup-BRIN-iYT.mjs.map
37
+ //# sourceMappingURL=setup-CtekcwuO.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup-BRIN-iYT.mjs","names":[],"sources":["../src/i18n/utils/setup.ts"],"sourcesContent":["/**\n * I18n Setup - Message Compiler Registration\n *\n * Registers a JIT (Just-In-Time) message compiler for @intlify/core-base\n * that works in Cloudflare Workers edge runtime.\n *\n * This must be called ONCE at application startup, before any I18nService instances are created.\n */\n\nimport { compile, registerMessageCompiler } from '@intlify/core-base'\n\nlet isRegistered = false\n\n/**\n * Setup JIT message compiler for i18n\n *\n * Registers a message compiler that uses JIT compilation mode.\n * In @intlify/core-base v10+, JIT mode is enabled by default, which generates\n * AST (Abstract Syntax Tree) instead of JavaScript code, making it compatible\n * with CSP-restricted environments like Cloudflare Workers.\n *\n * **IMPORTANT:** Call this function once at application startup before creating\n * any I18nService instances. Safe to call multiple times (only registers once).\n *\n * @example\n * ```typescript\n * // In application entry point (before app.initialize())\n * setupI18nCompiler()\n * ```\n */\nexport function setupI18nCompiler(): void {\n // Guard against multiple registrations\n if (isRegistered) {\n return\n }\n\n // Register the compile function from @intlify/core-base as the message compiler\n // In v11+, compile() uses JIT mode by default, generating AST instead of JavaScript code\n // This avoids CSP violations (no eval/new Function) in Cloudflare Workers\n registerMessageCompiler(compile)\n\n isRegistered = true\n}\n"],"mappings":";;;;;;;;;;AAWA,IAAI,eAAe;;;;;;;;;;;;;;;;;;AAmBnB,SAAgB,oBAA0B;AAExC,KAAI,aACF;AAMF,yBAAwB,QAAQ;AAEhC,gBAAe"}
1
+ {"version":3,"file":"setup-CtekcwuO.mjs","names":[],"sources":["../src/i18n/utils/setup.ts"],"sourcesContent":["/**\n * I18n Setup - Message Compiler Registration\n *\n * Registers a JIT (Just-In-Time) message compiler for @intlify/core-base\n * that works in Cloudflare Workers edge runtime.\n *\n * This must be called ONCE at application startup, before any I18nService instances are created.\n */\n\nimport { compile, registerMessageCompiler } from '@intlify/core-base'\n\nlet isRegistered = false\n\n/**\n * Setup JIT message compiler for i18n\n *\n * Registers a message compiler that uses JIT compilation mode.\n * In @intlify/core-base v10+, JIT mode is enabled by default, which generates\n * AST (Abstract Syntax Tree) instead of JavaScript code, making it compatible\n * with CSP-restricted environments like Cloudflare Workers.\n *\n * **IMPORTANT:** Call this function once at application startup before creating\n * any I18nService instances. Safe to call multiple times (only registers once).\n *\n * @example\n * ```typescript\n * // In application entry point (before app.initialize())\n * setupI18nCompiler()\n * ```\n */\nexport function setupI18nCompiler(): void {\n // Guard against multiple registrations\n if (isRegistered) {\n return\n }\n\n // Register the compile function from @intlify/core-base as the message compiler\n // In v11+, compile() uses JIT mode by default, generating AST instead of JavaScript code\n // This avoids CSP violations (no eval/new Function) in Cloudflare Workers\n registerMessageCompiler(compile)\n\n isRegistered = true\n}\n"],"mappings":";;;;;;;;;;AAWA,IAAI,eAAe;;;;;;;;;;;;;;;;;;AAmBnB,SAAgB,oBAA0B;AAExC,KAAI,aACF;AAMF,yBAAwB,QAAQ;AAEhC,gBAAe"}
@@ -0,0 +1,74 @@
1
+ //#region src/router/signed-url.ts
2
+ /**
3
+ * Import a signing key for HMAC-SHA256.
4
+ */
5
+ async function importKey(secret) {
6
+ const encoder = new TextEncoder();
7
+ return crypto.subtle.importKey("raw", encoder.encode(secret), {
8
+ name: "HMAC",
9
+ hash: "SHA-256"
10
+ }, false, ["sign", "verify"]);
11
+ }
12
+ /**
13
+ * Encode an ArrayBuffer as base64url (URL-safe base64).
14
+ */
15
+ function toBase64Url(buffer) {
16
+ const bytes = new Uint8Array(buffer);
17
+ let binary = "";
18
+ for (const byte of bytes) binary += String.fromCharCode(byte);
19
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
20
+ }
21
+ /**
22
+ * Sign a URL with HMAC-SHA256.
23
+ *
24
+ * Appends `signature` and optionally `expires` query parameters.
25
+ * The signature covers the pathname + search (excluding the signature params themselves).
26
+ *
27
+ * @param url - Full URL or path to sign
28
+ * @param secret - HMAC secret key (e.g., from env.APP_SECRET)
29
+ * @param options - Optional expiration
30
+ * @returns URL string with `signature` (and `expires`) query params appended
31
+ */
32
+ async function signUrl(url, secret, options) {
33
+ const parsedUrl = new URL(url, "https://placeholder.local");
34
+ const key = await importKey(secret);
35
+ if (options?.expiresIn) {
36
+ const expires = Math.floor(Date.now() / 1e3) + options.expiresIn;
37
+ parsedUrl.searchParams.set("expires", String(expires));
38
+ }
39
+ const dataToSign = `${parsedUrl.pathname}?${parsedUrl.searchParams.toString()}`;
40
+ const encoder = new TextEncoder();
41
+ const signature = toBase64Url(await crypto.subtle.sign("HMAC", key, encoder.encode(dataToSign)));
42
+ parsedUrl.searchParams.set("signature", signature);
43
+ return url.startsWith("http") ? parsedUrl.toString() : `${parsedUrl.pathname}?${parsedUrl.searchParams.toString()}`;
44
+ }
45
+ /**
46
+ * Verify a signed URL using `crypto.subtle.verify()` (timing-attack-safe).
47
+ *
48
+ * @param url - Full URL or path with signature query param
49
+ * @param secret - HMAC secret key (same key used for signing)
50
+ * @returns true if signature is valid and not expired
51
+ */
52
+ async function verifySignedUrl(url, secret) {
53
+ const parsedUrl = new URL(url, "https://placeholder.local");
54
+ const signature = parsedUrl.searchParams.get("signature");
55
+ if (!signature) return false;
56
+ const expires = parsedUrl.searchParams.get("expires");
57
+ if (expires) {
58
+ const expiryTime = parseInt(expires, 10);
59
+ if (isNaN(expiryTime) || Math.floor(Date.now() / 1e3) > expiryTime) return false;
60
+ }
61
+ parsedUrl.searchParams.delete("signature");
62
+ const dataToVerify = `${parsedUrl.pathname}?${parsedUrl.searchParams.toString()}`;
63
+ const base64 = signature.replace(/-/g, "+").replace(/_/g, "/");
64
+ const binaryStr = atob(base64);
65
+ const signatureBytes = new Uint8Array(binaryStr.length);
66
+ for (let i = 0; i < binaryStr.length; i++) signatureBytes[i] = binaryStr.charCodeAt(i);
67
+ const key = await importKey(secret);
68
+ const encoder = new TextEncoder();
69
+ return crypto.subtle.verify("HMAC", key, signatureBytes, encoder.encode(dataToVerify));
70
+ }
71
+ //#endregion
72
+ export { verifySignedUrl as n, signUrl as t };
73
+
74
+ //# sourceMappingURL=signed-url-COX7cCWR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signed-url-COX7cCWR.mjs","names":[],"sources":["../src/router/signed-url.ts"],"sourcesContent":["/**\n * Signed URL utilities using HMAC-SHA256 via Web Crypto API.\n *\n * Follows the Cloudflare Workers signing pattern:\n * https://developers.cloudflare.com/workers/examples/signing-requests/\n *\n * Uses `crypto.subtle.verify()` for timing-attack-safe comparison.\n */\n\n/**\n * Options for signing a URL.\n */\nexport interface SignedUrlOptions {\n /** Time-to-live in seconds. URL expires after this duration. */\n expiresIn?: number\n}\n\n/**\n * Import a signing key for HMAC-SHA256.\n */\nasync function importKey(secret: string): Promise<CryptoKey> {\n const encoder = new TextEncoder()\n return crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify']\n )\n}\n\n/**\n * Encode an ArrayBuffer as base64url (URL-safe base64).\n */\nfunction toBase64Url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n let binary = ''\n for (const byte of bytes) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\n/**\n * Sign a URL with HMAC-SHA256.\n *\n * Appends `signature` and optionally `expires` query parameters.\n * The signature covers the pathname + search (excluding the signature params themselves).\n *\n * @param url - Full URL or path to sign\n * @param secret - HMAC secret key (e.g., from env.APP_SECRET)\n * @param options - Optional expiration\n * @returns URL string with `signature` (and `expires`) query params appended\n */\nexport async function signUrl(url: string, secret: string, options?: SignedUrlOptions): Promise<string> {\n const parsedUrl = new URL(url, 'https://placeholder.local')\n const key = await importKey(secret)\n\n // Add expiry if specified\n if (options?.expiresIn) {\n const expires = Math.floor(Date.now() / 1000) + options.expiresIn\n parsedUrl.searchParams.set('expires', String(expires))\n }\n\n // Sign: pathname + sorted search params (without signature)\n const dataToSign = `${parsedUrl.pathname}?${parsedUrl.searchParams.toString()}`\n const encoder = new TextEncoder()\n const signatureBuffer = await crypto.subtle.sign('HMAC', key, encoder.encode(dataToSign))\n const signature = toBase64Url(signatureBuffer)\n\n parsedUrl.searchParams.set('signature', signature)\n // Return just the path + query for relative URLs, full URL for absolute\n return url.startsWith('http') ? parsedUrl.toString() : `${parsedUrl.pathname}?${parsedUrl.searchParams.toString()}`\n}\n\n/**\n * Verify a signed URL using `crypto.subtle.verify()` (timing-attack-safe).\n *\n * @param url - Full URL or path with signature query param\n * @param secret - HMAC secret key (same key used for signing)\n * @returns true if signature is valid and not expired\n */\nexport async function verifySignedUrl(url: string, secret: string): Promise<boolean> {\n const parsedUrl = new URL(url, 'https://placeholder.local')\n const signature = parsedUrl.searchParams.get('signature')\n if (!signature) return false\n\n // Check expiry\n const expires = parsedUrl.searchParams.get('expires')\n if (expires) {\n const expiryTime = parseInt(expires, 10)\n if (isNaN(expiryTime) || Math.floor(Date.now() / 1000) > expiryTime) {\n return false\n }\n }\n\n // Reconstruct the data that was signed (without signature param)\n parsedUrl.searchParams.delete('signature')\n const dataToVerify = `${parsedUrl.pathname}?${parsedUrl.searchParams.toString()}`\n\n // Decode base64url signature\n const base64 = signature.replace(/-/g, '+').replace(/_/g, '/')\n const binaryStr = atob(base64)\n const signatureBytes = new Uint8Array(binaryStr.length)\n for (let i = 0; i < binaryStr.length; i++) {\n signatureBytes[i] = binaryStr.charCodeAt(i)\n }\n\n const key = await importKey(secret)\n const encoder = new TextEncoder()\n\n // Use crypto.subtle.verify() for timing-attack-safe comparison\n return crypto.subtle.verify('HMAC', key, signatureBytes, encoder.encode(dataToVerify))\n}\n"],"mappings":";;;;AAoBA,eAAe,UAAU,QAAoC;CAC3D,MAAM,UAAU,IAAI,aAAa;AACjC,QAAO,OAAO,OAAO,UACnB,OACA,QAAQ,OAAO,OAAO,EACtB;EAAE,MAAM;EAAQ,MAAM;EAAW,EACjC,OACA,CAAC,QAAQ,SAAS,CACnB;;;;;AAMH,SAAS,YAAY,QAA6B;CAChD,MAAM,QAAQ,IAAI,WAAW,OAAO;CACpC,IAAI,SAAS;AACb,MAAK,MAAM,QAAQ,MACjB,WAAU,OAAO,aAAa,KAAK;AAErC,QAAO,KAAK,OAAO,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,GAAG;;;;;;;;;;;;;AAchF,eAAsB,QAAQ,KAAa,QAAgB,SAA6C;CACtG,MAAM,YAAY,IAAI,IAAI,KAAK,4BAA4B;CAC3D,MAAM,MAAM,MAAM,UAAU,OAAO;AAGnC,KAAI,SAAS,WAAW;EACtB,MAAM,UAAU,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK,GAAG,QAAQ;AACxD,YAAU,aAAa,IAAI,WAAW,OAAO,QAAQ,CAAC;;CAIxD,MAAM,aAAa,GAAG,UAAU,SAAS,GAAG,UAAU,aAAa,UAAU;CAC7E,MAAM,UAAU,IAAI,aAAa;CAEjC,MAAM,YAAY,YAAY,MADA,OAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,OAAO,WAAW,CAAC,CAC3C;AAE9C,WAAU,aAAa,IAAI,aAAa,UAAU;AAElD,QAAO,IAAI,WAAW,OAAO,GAAG,UAAU,UAAU,GAAG,GAAG,UAAU,SAAS,GAAG,UAAU,aAAa,UAAU;;;;;;;;;AAUnH,eAAsB,gBAAgB,KAAa,QAAkC;CACnF,MAAM,YAAY,IAAI,IAAI,KAAK,4BAA4B;CAC3D,MAAM,YAAY,UAAU,aAAa,IAAI,YAAY;AACzD,KAAI,CAAC,UAAW,QAAO;CAGvB,MAAM,UAAU,UAAU,aAAa,IAAI,UAAU;AACrD,KAAI,SAAS;EACX,MAAM,aAAa,SAAS,SAAS,GAAG;AACxC,MAAI,MAAM,WAAW,IAAI,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK,GAAG,WACvD,QAAO;;AAKX,WAAU,aAAa,OAAO,YAAY;CAC1C,MAAM,eAAe,GAAG,UAAU,SAAS,GAAG,UAAU,aAAa,UAAU;CAG/E,MAAM,SAAS,UAAU,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI;CAC9D,MAAM,YAAY,KAAK,OAAO;CAC9B,MAAM,iBAAiB,IAAI,WAAW,UAAU,OAAO;AACvD,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACpC,gBAAe,KAAK,UAAU,WAAW,EAAE;CAG7C,MAAM,MAAM,MAAM,UAAU,OAAO;CACnC,MAAM,UAAU,IAAI,aAAa;AAGjC,QAAO,OAAO,OAAO,OAAO,QAAQ,KAAK,gBAAgB,QAAQ,OAAO,aAAa,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { EmailSmtpConnectionFailedError, SmtpConfigurationMissingError, SmtpHostMissingError } from "./email/index.mjs";
2
- import { t as BaseEmailProvider } from "./base-email.provider-Cuw4OAB0.mjs";
2
+ import { t as BaseEmailProvider } from "./base-email.provider-mjynzewK.mjs";
3
3
  import * as nodemailer from "nodemailer";
4
4
  import { Readable } from "stream";
5
5
  //#region src/email/providers/smtp.provider.ts
@@ -72,4 +72,4 @@ var SmtpProvider = class extends BaseEmailProvider {
72
72
  //#endregion
73
73
  export { SmtpProvider };
74
74
 
75
- //# sourceMappingURL=smtp.provider-CAwpvzvD.mjs.map
75
+ //# sourceMappingURL=smtp.provider-B07yuARi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"smtp.provider-CAwpvzvD.mjs","names":[],"sources":["../src/email/providers/smtp.provider.ts"],"sourcesContent":["import type { Transporter } from 'nodemailer'\nimport * as nodemailer from 'nodemailer'\nimport type SMTPTransport from 'nodemailer/lib/smtp-transport'\nimport { Readable } from 'stream'\nimport type { ResolvedEmailAttachment, ResolvedEmailMessage } from '../contracts'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EmailSmtpConnectionFailedError, SmtpConfigurationMissingError, SmtpHostMissingError } from '../errors'\nimport { BaseEmailProvider } from './base-email.provider'\nimport type { EmailSendResult } from './email-provider.interface'\n\n/**\n * SMTP Email Provider\n *\n * Implementation of IEmailProvider using SMTP/nodemailer\n * Supports any SMTP server configured via SMTP_URL\n */\nexport class SmtpProvider extends BaseEmailProvider {\n private readonly transporter: Transporter<SMTPTransport.SentMessageInfo>\n private readonly defaultFrom: { name: string; email: string }\n\n constructor(\n private readonly options: EmailModuleOptions\n ) {\n super()\n\n // Validate SMTP configuration\n if (!this.options.smtp) {\n throw new SmtpConfigurationMissingError()\n }\n\n if (!this.options.smtp.host) {\n throw new SmtpHostMissingError()\n }\n\n // Create nodemailer transporter\n this.transporter = nodemailer.createTransport({\n host: this.options.smtp.host,\n port: this.options.smtp.port,\n secure: this.options.smtp.secure,\n auth: this.options.smtp.username && this.options.smtp.password\n ? {\n user: this.options.smtp.username,\n pass: this.options.smtp.password,\n }\n : undefined,\n })\n\n this.defaultFrom = this.options.from\n }\n\n async send(message: ResolvedEmailMessage): Promise<EmailSendResult> {\n try {\n const from = message.from\n ? `${message.from.name} <${message.from.email}>`\n : `${this.defaultFrom.name} <${this.defaultFrom.email}>`\n\n const info = await this.transporter.sendMail({\n from,\n to: Array.isArray(message.to) ? message.to.join(', ') : message.to,\n subject: message.subject,\n html: message.html,\n text: message.text,\n replyTo: message.replyTo,\n cc: message.cc?.join(', '),\n bcc: message.bcc?.join(', '),\n attachments: message.attachments?.map(attachment => ({\n filename: attachment.filename,\n content: this.toNodeStream(attachment.content),\n contentType: attachment.contentType,\n })),\n })\n\n return {\n messageId: info.messageId,\n accepted: true,\n metadata: {\n provider: 'smtp',\n response: info.response,\n },\n }\n } catch {\n throw new EmailSmtpConnectionFailedError(\n this.options.smtp?.host ?? '',\n this.options.smtp?.port ?? 587\n )\n }\n }\n\n /**\n * Convert attachment content to Node.js stream format\n *\n * Nodemailer expects Node.js Readable streams, not web ReadableStream.\n * Buffer is passed through as-is since nodemailer supports it directly.\n */\n private toNodeStream(content: ResolvedEmailAttachment['content']): Buffer | Readable {\n if (Buffer.isBuffer(content)) {\n return content\n }\n // Convert web ReadableStream to Node.js Readable\n return Readable.fromWeb(content as Parameters<typeof Readable.fromWeb>[0])\n }\n}\n"],"mappings":";;;;;;;;;;;AAgBA,IAAa,eAAb,cAAkC,kBAAkB;CAClD;CACA;CAEA,YACE,SACA;AACA,SAAO;AAFU,OAAA,UAAA;AAKjB,MAAI,CAAC,KAAK,QAAQ,KAChB,OAAM,IAAI,+BAA+B;AAG3C,MAAI,CAAC,KAAK,QAAQ,KAAK,KACrB,OAAM,IAAI,sBAAsB;AAIlC,OAAK,cAAc,WAAW,gBAAgB;GAC5C,MAAM,KAAK,QAAQ,KAAK;GACxB,MAAM,KAAK,QAAQ,KAAK;GACxB,QAAQ,KAAK,QAAQ,KAAK;GAC1B,MAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,QAAQ,KAAK,WAClD;IACA,MAAM,KAAK,QAAQ,KAAK;IACxB,MAAM,KAAK,QAAQ,KAAK;IACzB,GACC,KAAA;GACL,CAAC;AAEF,OAAK,cAAc,KAAK,QAAQ;;CAGlC,MAAM,KAAK,SAAyD;AAClE,MAAI;GACF,MAAM,OAAO,QAAQ,OACjB,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,KAC5C,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,YAAY,MAAM;GAExD,MAAM,OAAO,MAAM,KAAK,YAAY,SAAS;IAC3C;IACA,IAAI,MAAM,QAAQ,QAAQ,GAAG,GAAG,QAAQ,GAAG,KAAK,KAAK,GAAG,QAAQ;IAChE,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,SAAS,QAAQ;IACjB,IAAI,QAAQ,IAAI,KAAK,KAAK;IAC1B,KAAK,QAAQ,KAAK,KAAK,KAAK;IAC5B,aAAa,QAAQ,aAAa,KAAI,gBAAe;KACnD,UAAU,WAAW;KACrB,SAAS,KAAK,aAAa,WAAW,QAAQ;KAC9C,aAAa,WAAW;KACzB,EAAE;IACJ,CAAC;AAEF,UAAO;IACL,WAAW,KAAK;IAChB,UAAU;IACV,UAAU;KACR,UAAU;KACV,UAAU,KAAK;KAChB;IACF;UACK;AACN,SAAM,IAAI,+BACR,KAAK,QAAQ,MAAM,QAAQ,IAC3B,KAAK,QAAQ,MAAM,QAAQ,IAC5B;;;;;;;;;CAUL,aAAqB,SAAgE;AACnF,MAAI,OAAO,SAAS,QAAQ,CAC1B,QAAO;AAGT,SAAO,SAAS,QAAQ,QAAkD"}
1
+ {"version":3,"file":"smtp.provider-B07yuARi.mjs","names":[],"sources":["../src/email/providers/smtp.provider.ts"],"sourcesContent":["import type { Transporter } from 'nodemailer'\nimport * as nodemailer from 'nodemailer'\nimport type SMTPTransport from 'nodemailer/lib/smtp-transport'\nimport { Readable } from 'stream'\nimport type { ResolvedEmailAttachment, ResolvedEmailMessage } from '../contracts'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EmailSmtpConnectionFailedError, SmtpConfigurationMissingError, SmtpHostMissingError } from '../errors'\nimport { BaseEmailProvider } from './base-email.provider'\nimport type { EmailSendResult } from './email-provider.interface'\n\n/**\n * SMTP Email Provider\n *\n * Implementation of IEmailProvider using SMTP/nodemailer\n * Supports any SMTP server configured via SMTP_URL\n */\nexport class SmtpProvider extends BaseEmailProvider {\n private readonly transporter: Transporter<SMTPTransport.SentMessageInfo>\n private readonly defaultFrom: { name: string; email: string }\n\n constructor(\n private readonly options: EmailModuleOptions\n ) {\n super()\n\n // Validate SMTP configuration\n if (!this.options.smtp) {\n throw new SmtpConfigurationMissingError()\n }\n\n if (!this.options.smtp.host) {\n throw new SmtpHostMissingError()\n }\n\n // Create nodemailer transporter\n this.transporter = nodemailer.createTransport({\n host: this.options.smtp.host,\n port: this.options.smtp.port,\n secure: this.options.smtp.secure,\n auth: this.options.smtp.username && this.options.smtp.password\n ? {\n user: this.options.smtp.username,\n pass: this.options.smtp.password,\n }\n : undefined,\n })\n\n this.defaultFrom = this.options.from\n }\n\n async send(message: ResolvedEmailMessage): Promise<EmailSendResult> {\n try {\n const from = message.from\n ? `${message.from.name} <${message.from.email}>`\n : `${this.defaultFrom.name} <${this.defaultFrom.email}>`\n\n const info = await this.transporter.sendMail({\n from,\n to: Array.isArray(message.to) ? message.to.join(', ') : message.to,\n subject: message.subject,\n html: message.html,\n text: message.text,\n replyTo: message.replyTo,\n cc: message.cc?.join(', '),\n bcc: message.bcc?.join(', '),\n attachments: message.attachments?.map(attachment => ({\n filename: attachment.filename,\n content: this.toNodeStream(attachment.content),\n contentType: attachment.contentType,\n })),\n })\n\n return {\n messageId: info.messageId,\n accepted: true,\n metadata: {\n provider: 'smtp',\n response: info.response,\n },\n }\n } catch {\n throw new EmailSmtpConnectionFailedError(\n this.options.smtp?.host ?? '',\n this.options.smtp?.port ?? 587\n )\n }\n }\n\n /**\n * Convert attachment content to Node.js stream format\n *\n * Nodemailer expects Node.js Readable streams, not web ReadableStream.\n * Buffer is passed through as-is since nodemailer supports it directly.\n */\n private toNodeStream(content: ResolvedEmailAttachment['content']): Buffer | Readable {\n if (Buffer.isBuffer(content)) {\n return content\n }\n // Convert web ReadableStream to Node.js Readable\n return Readable.fromWeb(content as unknown as Parameters<typeof Readable.fromWeb>[0])\n }\n}\n"],"mappings":";;;;;;;;;;;AAgBA,IAAa,eAAb,cAAkC,kBAAkB;CAClD;CACA;CAEA,YACE,SACA;AACA,SAAO;AAFU,OAAA,UAAA;AAKjB,MAAI,CAAC,KAAK,QAAQ,KAChB,OAAM,IAAI,+BAA+B;AAG3C,MAAI,CAAC,KAAK,QAAQ,KAAK,KACrB,OAAM,IAAI,sBAAsB;AAIlC,OAAK,cAAc,WAAW,gBAAgB;GAC5C,MAAM,KAAK,QAAQ,KAAK;GACxB,MAAM,KAAK,QAAQ,KAAK;GACxB,QAAQ,KAAK,QAAQ,KAAK;GAC1B,MAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,QAAQ,KAAK,WAClD;IACA,MAAM,KAAK,QAAQ,KAAK;IACxB,MAAM,KAAK,QAAQ,KAAK;IACzB,GACC,KAAA;GACL,CAAC;AAEF,OAAK,cAAc,KAAK,QAAQ;;CAGlC,MAAM,KAAK,SAAyD;AAClE,MAAI;GACF,MAAM,OAAO,QAAQ,OACjB,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,KAC5C,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,YAAY,MAAM;GAExD,MAAM,OAAO,MAAM,KAAK,YAAY,SAAS;IAC3C;IACA,IAAI,MAAM,QAAQ,QAAQ,GAAG,GAAG,QAAQ,GAAG,KAAK,KAAK,GAAG,QAAQ;IAChE,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,MAAM,QAAQ;IACd,SAAS,QAAQ;IACjB,IAAI,QAAQ,IAAI,KAAK,KAAK;IAC1B,KAAK,QAAQ,KAAK,KAAK,KAAK;IAC5B,aAAa,QAAQ,aAAa,KAAI,gBAAe;KACnD,UAAU,WAAW;KACrB,SAAS,KAAK,aAAa,WAAW,QAAQ;KAC9C,aAAa,WAAW;KACzB,EAAE;IACJ,CAAC;AAEF,UAAO;IACL,WAAW,KAAK;IAChB,UAAU;IACV,UAAU;KACR,UAAU;KACV,UAAU,KAAK;KAChB;IACF;UACK;AACN,SAAM,IAAI,+BACR,KAAK,QAAQ,MAAM,QAAQ,IAC3B,KAAK,QAAQ,MAAM,QAAQ,IAC5B;;;;;;;;;CAUL,aAAqB,SAAgE;AACnF,MAAI,OAAO,SAAS,QAAQ,CAC1B,QAAO;AAGT,SAAO,SAAS,QAAQ,QAA6D"}