@happyvertical/smrt-messages 0.30.0

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 (153) hide show
  1. package/AGENTS.md +31 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +103 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/collections/AccountCollection.d.ts +42 -0
  8. package/dist/collections/AccountCollection.d.ts.map +1 -0
  9. package/dist/collections/AttachmentCollection.d.ts +67 -0
  10. package/dist/collections/AttachmentCollection.d.ts.map +1 -0
  11. package/dist/collections/BlacklistCollection.d.ts +14 -0
  12. package/dist/collections/BlacklistCollection.d.ts.map +1 -0
  13. package/dist/collections/EmailAccountCollection.d.ts +74 -0
  14. package/dist/collections/EmailAccountCollection.d.ts.map +1 -0
  15. package/dist/collections/EmailAttachmentCollection.d.ts +38 -0
  16. package/dist/collections/EmailAttachmentCollection.d.ts.map +1 -0
  17. package/dist/collections/EmailCollection.d.ts +81 -0
  18. package/dist/collections/EmailCollection.d.ts.map +1 -0
  19. package/dist/collections/EmailFolderCollection.d.ts +85 -0
  20. package/dist/collections/EmailFolderCollection.d.ts.map +1 -0
  21. package/dist/collections/MessageCollection.d.ts +74 -0
  22. package/dist/collections/MessageCollection.d.ts.map +1 -0
  23. package/dist/collections/WhitelistCollection.d.ts +18 -0
  24. package/dist/collections/WhitelistCollection.d.ts.map +1 -0
  25. package/dist/index.d.ts +27 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +3068 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/manifest.json +10576 -0
  30. package/dist/models/Account.d.ts +47 -0
  31. package/dist/models/Account.d.ts.map +1 -0
  32. package/dist/models/Attachment.d.ts +48 -0
  33. package/dist/models/Attachment.d.ts.map +1 -0
  34. package/dist/models/Blacklist.d.ts +21 -0
  35. package/dist/models/Blacklist.d.ts.map +1 -0
  36. package/dist/models/Email.d.ts +98 -0
  37. package/dist/models/Email.d.ts.map +1 -0
  38. package/dist/models/EmailAccount.d.ts +65 -0
  39. package/dist/models/EmailAccount.d.ts.map +1 -0
  40. package/dist/models/EmailAttachment.d.ts +19 -0
  41. package/dist/models/EmailAttachment.d.ts.map +1 -0
  42. package/dist/models/EmailFolder.d.ts +65 -0
  43. package/dist/models/EmailFolder.d.ts.map +1 -0
  44. package/dist/models/Message.d.ts +105 -0
  45. package/dist/models/Message.d.ts.map +1 -0
  46. package/dist/models/SlackAccount.d.ts +13 -0
  47. package/dist/models/SlackAccount.d.ts.map +1 -0
  48. package/dist/models/SlackMessage.d.ts +34 -0
  49. package/dist/models/SlackMessage.d.ts.map +1 -0
  50. package/dist/models/Tweet.d.ts +31 -0
  51. package/dist/models/Tweet.d.ts.map +1 -0
  52. package/dist/models/TwitterAccount.d.ts +12 -0
  53. package/dist/models/TwitterAccount.d.ts.map +1 -0
  54. package/dist/models/Whitelist.d.ts +21 -0
  55. package/dist/models/Whitelist.d.ts.map +1 -0
  56. package/dist/playground.d.ts +2 -0
  57. package/dist/playground.d.ts.map +1 -0
  58. package/dist/playground.js +176 -0
  59. package/dist/playground.js.map +1 -0
  60. package/dist/senders/EmailSender.d.ts +13 -0
  61. package/dist/senders/EmailSender.d.ts.map +1 -0
  62. package/dist/senders/SlackSender.d.ts +11 -0
  63. package/dist/senders/SlackSender.d.ts.map +1 -0
  64. package/dist/senders/TweetSender.d.ts +11 -0
  65. package/dist/senders/TweetSender.d.ts.map +1 -0
  66. package/dist/smrt-knowledge.json +4234 -0
  67. package/dist/svelte/components/AccountAvatar.svelte +107 -0
  68. package/dist/svelte/components/AccountAvatar.svelte.d.ts +12 -0
  69. package/dist/svelte/components/AccountAvatar.svelte.d.ts.map +1 -0
  70. package/dist/svelte/components/AccountCard.svelte +173 -0
  71. package/dist/svelte/components/AccountCard.svelte.d.ts +12 -0
  72. package/dist/svelte/components/AccountCard.svelte.d.ts.map +1 -0
  73. package/dist/svelte/components/AccountList.svelte +90 -0
  74. package/dist/svelte/components/AccountList.svelte.d.ts +12 -0
  75. package/dist/svelte/components/AccountList.svelte.d.ts.map +1 -0
  76. package/dist/svelte/components/AttachmentChip.svelte +99 -0
  77. package/dist/svelte/components/AttachmentChip.svelte.d.ts +12 -0
  78. package/dist/svelte/components/AttachmentChip.svelte.d.ts.map +1 -0
  79. package/dist/svelte/components/AttachmentUpload.svelte +160 -0
  80. package/dist/svelte/components/AttachmentUpload.svelte.d.ts +11 -0
  81. package/dist/svelte/components/AttachmentUpload.svelte.d.ts.map +1 -0
  82. package/dist/svelte/components/ComposeForm.svelte +387 -0
  83. package/dist/svelte/components/ComposeForm.svelte.d.ts +13 -0
  84. package/dist/svelte/components/ComposeForm.svelte.d.ts.map +1 -0
  85. package/dist/svelte/components/EmailAccountManager.svelte +690 -0
  86. package/dist/svelte/components/EmailAccountManager.svelte.d.ts +15 -0
  87. package/dist/svelte/components/EmailAccountManager.svelte.d.ts.map +1 -0
  88. package/dist/svelte/components/EmailFilterManager.svelte +687 -0
  89. package/dist/svelte/components/EmailFilterManager.svelte.d.ts +14 -0
  90. package/dist/svelte/components/EmailFilterManager.svelte.d.ts.map +1 -0
  91. package/dist/svelte/components/FolderNav.svelte +171 -0
  92. package/dist/svelte/components/FolderNav.svelte.d.ts +11 -0
  93. package/dist/svelte/components/FolderNav.svelte.d.ts.map +1 -0
  94. package/dist/svelte/components/ForwardForm.svelte +166 -0
  95. package/dist/svelte/components/ForwardForm.svelte.d.ts +10 -0
  96. package/dist/svelte/components/ForwardForm.svelte.d.ts.map +1 -0
  97. package/dist/svelte/components/MessageCard.svelte +336 -0
  98. package/dist/svelte/components/MessageCard.svelte.d.ts +20 -0
  99. package/dist/svelte/components/MessageCard.svelte.d.ts.map +1 -0
  100. package/dist/svelte/components/MessageDetail.svelte +309 -0
  101. package/dist/svelte/components/MessageDetail.svelte.d.ts +18 -0
  102. package/dist/svelte/components/MessageDetail.svelte.d.ts.map +1 -0
  103. package/dist/svelte/components/MessageFilters.svelte +228 -0
  104. package/dist/svelte/components/MessageFilters.svelte.d.ts +13 -0
  105. package/dist/svelte/components/MessageFilters.svelte.d.ts.map +1 -0
  106. package/dist/svelte/components/MessageList.svelte +101 -0
  107. package/dist/svelte/components/MessageList.svelte.d.ts +23 -0
  108. package/dist/svelte/components/MessageList.svelte.d.ts.map +1 -0
  109. package/dist/svelte/components/MessageStatusIndicator.svelte +82 -0
  110. package/dist/svelte/components/MessageStatusIndicator.svelte.d.ts +11 -0
  111. package/dist/svelte/components/MessageStatusIndicator.svelte.d.ts.map +1 -0
  112. package/dist/svelte/components/MessageToolbar.svelte +131 -0
  113. package/dist/svelte/components/MessageToolbar.svelte.d.ts +14 -0
  114. package/dist/svelte/components/MessageToolbar.svelte.d.ts.map +1 -0
  115. package/dist/svelte/components/MessageTypeBadge.svelte +59 -0
  116. package/dist/svelte/components/MessageTypeBadge.svelte.d.ts +9 -0
  117. package/dist/svelte/components/MessageTypeBadge.svelte.d.ts.map +1 -0
  118. package/dist/svelte/components/RecipientInput.svelte +150 -0
  119. package/dist/svelte/components/RecipientInput.svelte.d.ts +11 -0
  120. package/dist/svelte/components/RecipientInput.svelte.d.ts.map +1 -0
  121. package/dist/svelte/components/ReplyForm.svelte +159 -0
  122. package/dist/svelte/components/ReplyForm.svelte.d.ts +11 -0
  123. package/dist/svelte/components/ReplyForm.svelte.d.ts.map +1 -0
  124. package/dist/svelte/components/SendStatusBadge.svelte +64 -0
  125. package/dist/svelte/components/SendStatusBadge.svelte.d.ts +8 -0
  126. package/dist/svelte/components/SendStatusBadge.svelte.d.ts.map +1 -0
  127. package/dist/svelte/components/ThreadView.svelte +240 -0
  128. package/dist/svelte/components/ThreadView.svelte.d.ts +12 -0
  129. package/dist/svelte/components/ThreadView.svelte.d.ts.map +1 -0
  130. package/dist/svelte/i18n.d.ts +42 -0
  131. package/dist/svelte/i18n.d.ts.map +1 -0
  132. package/dist/svelte/i18n.js +60 -0
  133. package/dist/svelte/i18n.messages.d.ts +32 -0
  134. package/dist/svelte/i18n.messages.d.ts.map +1 -0
  135. package/dist/svelte/i18n.messages.js +46 -0
  136. package/dist/svelte/index.d.ts +54 -0
  137. package/dist/svelte/index.d.ts.map +1 -0
  138. package/dist/svelte/index.js +44 -0
  139. package/dist/svelte/playground.d.ts +341 -0
  140. package/dist/svelte/playground.d.ts.map +1 -0
  141. package/dist/svelte/playground.js +171 -0
  142. package/dist/svelte/types.d.ts +195 -0
  143. package/dist/svelte/types.d.ts.map +1 -0
  144. package/dist/svelte/types.js +6 -0
  145. package/dist/types.d.ts +316 -0
  146. package/dist/types.d.ts.map +1 -0
  147. package/dist/types.js +2 -0
  148. package/dist/types.js.map +1 -0
  149. package/dist/ui.d.ts +4 -0
  150. package/dist/ui.d.ts.map +1 -0
  151. package/dist/ui.js +103 -0
  152. package/dist/ui.js.map +1 -0
  153. package/package.json +104 -0
package/dist/index.js ADDED
@@ -0,0 +1,3068 @@
1
+ import { ObjectRegistry, field, smrt, SmrtObject, SmrtCollection, foreignKey } from "@happyvertical/smrt-core";
2
+ import { tenantId, TenantScoped } from "@happyvertical/smrt-tenancy";
3
+ ObjectRegistry.registerPackageManifest(
4
+ new URL("./manifest.json", import.meta.url)
5
+ );
6
+ var __defProp$3 = Object.defineProperty;
7
+ var __getOwnPropDesc$b = Object.getOwnPropertyDescriptor;
8
+ var __decorateClass$b = (decorators, target, key, kind) => {
9
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$b(target, key) : target;
10
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
+ if (decorator = decorators[i])
12
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
13
+ if (kind && result) __defProp$3(target, key, result);
14
+ return result;
15
+ };
16
+ let Account = class extends SmrtObject {
17
+ tenantId = null;
18
+ name = "";
19
+ providerType = "";
20
+ // credentialSecretId stores the Secret's NAME (keyed by name + tenant
21
+ // context in smrt-secrets), NOT its primary-key id — see setCredentials()/
22
+ // getCredentials() which call secretService.store/retrieve by name. So it is
23
+ // deliberately NOT a @crossPackageRef id FK.
24
+ credentialSecretId = null;
25
+ isActive = true;
26
+ lastSyncAt = null;
27
+ settings = "";
28
+ // JSON
29
+ // Timestamps
30
+ createdAt = /* @__PURE__ */ new Date();
31
+ updatedAt = /* @__PURE__ */ new Date();
32
+ constructor(options = {}) {
33
+ super(options);
34
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
35
+ if (options.name !== void 0) this.name = options.name;
36
+ if (options.providerType !== void 0)
37
+ this.providerType = options.providerType;
38
+ if (options.credentialSecretId !== void 0)
39
+ this.credentialSecretId = options.credentialSecretId || null;
40
+ if (options.isActive !== void 0) this.isActive = options.isActive;
41
+ if (options.lastSyncAt !== void 0)
42
+ this.lastSyncAt = options.lastSyncAt || null;
43
+ if (options.settings !== void 0) this.settings = options.settings;
44
+ if (options.createdAt) this.createdAt = options.createdAt;
45
+ if (options.updatedAt) this.updatedAt = options.updatedAt;
46
+ }
47
+ /**
48
+ * Get settings as parsed object
49
+ */
50
+ getSettings() {
51
+ if (!this.settings) return {};
52
+ try {
53
+ return JSON.parse(this.settings);
54
+ } catch {
55
+ return {};
56
+ }
57
+ }
58
+ /**
59
+ * Set settings from object
60
+ */
61
+ setSettings(settings) {
62
+ this.settings = JSON.stringify(settings);
63
+ }
64
+ /**
65
+ * Activate account
66
+ */
67
+ async activate() {
68
+ this.isActive = true;
69
+ this.updatedAt = /* @__PURE__ */ new Date();
70
+ await this.save();
71
+ }
72
+ /**
73
+ * Deactivate account
74
+ */
75
+ async deactivate() {
76
+ this.isActive = false;
77
+ this.updatedAt = /* @__PURE__ */ new Date();
78
+ await this.save();
79
+ }
80
+ /**
81
+ * Store credentials securely using smrt-secrets
82
+ */
83
+ async setCredentials(credentials, options = {}) {
84
+ const { SecretService } = await import("@happyvertical/smrt-secrets");
85
+ const secretService = await SecretService.create({ db: this.db });
86
+ const secretName = `account-${this.id}`;
87
+ const secretValue = JSON.stringify(credentials);
88
+ if (this.credentialSecretId) {
89
+ await secretService.store(this.credentialSecretId, secretValue, {
90
+ description: options.description || `Credentials for ${this.name}`,
91
+ category: options.category || "messaging"
92
+ });
93
+ } else {
94
+ await secretService.store(secretName, secretValue, {
95
+ description: options.description || `Credentials for ${this.name}`,
96
+ category: options.category || "messaging"
97
+ });
98
+ this.credentialSecretId = secretName;
99
+ this.updatedAt = /* @__PURE__ */ new Date();
100
+ await this.save();
101
+ }
102
+ }
103
+ /**
104
+ * Create a sender for this account.
105
+ * Subclasses must override to return a concrete sender.
106
+ */
107
+ async createSender() {
108
+ throw new Error(
109
+ `createSender() not implemented for account type '${this.providerType}'`
110
+ );
111
+ }
112
+ /**
113
+ * Retrieve stored credentials
114
+ */
115
+ async getCredentials() {
116
+ if (!this.credentialSecretId) {
117
+ return this.getSettings();
118
+ }
119
+ const { SecretService } = await import("@happyvertical/smrt-secrets");
120
+ const secretService = await SecretService.create({ db: this.db });
121
+ try {
122
+ const secret = await secretService.retrieve(this.credentialSecretId);
123
+ return JSON.parse(secret.value);
124
+ } catch {
125
+ return null;
126
+ }
127
+ }
128
+ };
129
+ __decorateClass$b([
130
+ tenantId({ nullable: true })
131
+ ], Account.prototype, "tenantId", 2);
132
+ __decorateClass$b([
133
+ field({ sensitive: true })
134
+ ], Account.prototype, "settings", 2);
135
+ Account = __decorateClass$b([
136
+ TenantScoped({ mode: "optional" }),
137
+ smrt({
138
+ tableStrategy: "sti",
139
+ api: { include: ["list", "get", "create", "update", "delete"] },
140
+ mcp: { include: ["list", "get"] },
141
+ cli: true
142
+ })
143
+ ], Account);
144
+ class AccountCollection extends SmrtCollection {
145
+ static _itemClass = Account;
146
+ /**
147
+ * Get active accounts
148
+ */
149
+ async getActive() {
150
+ return await this.list({ where: { isActive: true } });
151
+ }
152
+ /**
153
+ * Get inactive accounts
154
+ */
155
+ async getInactive() {
156
+ return await this.list({ where: { isActive: false } });
157
+ }
158
+ /**
159
+ * Get accounts by provider type
160
+ */
161
+ async getByProviderType(providerType) {
162
+ return await this.list({ where: { providerType } });
163
+ }
164
+ /**
165
+ * Get accounts by STI type.
166
+ *
167
+ * Accepts either the full discriminator (e.g. "@happyvertical/smrt-messages:EmailAccount")
168
+ * or the short type name (e.g. "EmailAccount").
169
+ */
170
+ async getByType(accountType) {
171
+ if (accountType.includes(":") || accountType.startsWith("@")) {
172
+ return await this.list({ where: { _meta_type: accountType } });
173
+ }
174
+ const allAccounts = await this.list({});
175
+ return allAccounts.filter((a) => {
176
+ const metaType = a._meta_type || "";
177
+ return metaType.endsWith(`:${accountType}`);
178
+ });
179
+ }
180
+ /**
181
+ * Search accounts with filters
182
+ */
183
+ async search(query, filters) {
184
+ let accounts = await this.list({});
185
+ if (query) {
186
+ const lowerQuery = query.toLowerCase();
187
+ accounts = accounts.filter(
188
+ (a) => a.name?.toLowerCase().includes(lowerQuery)
189
+ );
190
+ }
191
+ if (filters) {
192
+ if (filters.providerType) {
193
+ accounts = accounts.filter(
194
+ (a) => a.providerType === filters.providerType
195
+ );
196
+ }
197
+ if (filters.isActive !== void 0) {
198
+ accounts = accounts.filter((a) => a.isActive === filters.isActive);
199
+ }
200
+ if (filters.accountType) {
201
+ accounts = accounts.filter((a) => {
202
+ const metaType = a._meta_type || "";
203
+ return metaType.includes(filters.accountType);
204
+ });
205
+ }
206
+ }
207
+ return accounts;
208
+ }
209
+ /**
210
+ * Get account statistics
211
+ */
212
+ async getStats() {
213
+ const accounts = await this.list({});
214
+ const byType = {};
215
+ for (const account of accounts) {
216
+ const metaType = account._meta_type || "Unknown";
217
+ const shortType = metaType.split(":").pop() || metaType;
218
+ byType[shortType] = (byType[shortType] || 0) + 1;
219
+ }
220
+ return {
221
+ total: accounts.length,
222
+ active: accounts.filter((a) => a.isActive).length,
223
+ inactive: accounts.filter((a) => !a.isActive).length,
224
+ byType
225
+ };
226
+ }
227
+ // ─────────────────────────────────────────────────────────────────────────
228
+ // Tenant Helper Methods
229
+ // ─────────────────────────────────────────────────────────────────────────
230
+ async findByTenant(tenantId2) {
231
+ return this.list({ where: { tenantId: tenantId2 } });
232
+ }
233
+ async findGlobal() {
234
+ return this.list({ where: { tenantId: null } });
235
+ }
236
+ async findWithGlobals(tenantId2) {
237
+ return this.query(
238
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
239
+ [tenantId2]
240
+ );
241
+ }
242
+ }
243
+ const AccountCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
244
+ __proto__: null,
245
+ AccountCollection
246
+ }, Symbol.toStringTag, { value: "Module" }));
247
+ var __defProp$2 = Object.defineProperty;
248
+ var __getOwnPropDesc$a = Object.getOwnPropertyDescriptor;
249
+ var __decorateClass$a = (decorators, target, key, kind) => {
250
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$a(target, key) : target;
251
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
252
+ if (decorator = decorators[i])
253
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
254
+ if (kind && result) __defProp$2(target, key, result);
255
+ return result;
256
+ };
257
+ let Attachment = class extends SmrtObject {
258
+ tenantId = null;
259
+ messageId = "";
260
+ filename = "";
261
+ contentType = "";
262
+ size = 0;
263
+ contentId = "";
264
+ // For inline images (<img src="cid:...">)
265
+ contentDisposition = "attachment";
266
+ filePath = "";
267
+ // External file storage path
268
+ sourceUrl = "";
269
+ // For non-email attachments (media URLs, etc.)
270
+ // Timestamps
271
+ createdAt = /* @__PURE__ */ new Date();
272
+ constructor(options = {}) {
273
+ super(options);
274
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
275
+ if (options.messageId !== void 0) this.messageId = options.messageId;
276
+ if (options.filename !== void 0) this.filename = options.filename;
277
+ if (options.contentType !== void 0)
278
+ this.contentType = options.contentType;
279
+ if (options.size !== void 0) this.size = options.size;
280
+ if (options.contentId !== void 0) this.contentId = options.contentId;
281
+ if (options.contentDisposition !== void 0)
282
+ this.contentDisposition = options.contentDisposition;
283
+ if (options.filePath !== void 0) this.filePath = options.filePath;
284
+ if (options.sourceUrl !== void 0) this.sourceUrl = options.sourceUrl;
285
+ if (options.createdAt) this.createdAt = options.createdAt;
286
+ }
287
+ /**
288
+ * Get the message this attachment belongs to
289
+ */
290
+ async getMessage() {
291
+ if (!this.messageId) return null;
292
+ const { MessageCollection: MessageCollection2 } = await Promise.resolve().then(() => MessageCollection$1);
293
+ const collection = await MessageCollection2.create(this.options);
294
+ return await collection.get({ id: this.messageId });
295
+ }
296
+ /**
297
+ * Check if attachment is an image
298
+ */
299
+ isImage() {
300
+ return this.contentType.startsWith("image/");
301
+ }
302
+ /**
303
+ * Check if attachment is a PDF
304
+ */
305
+ isPdf() {
306
+ return this.contentType === "application/pdf";
307
+ }
308
+ /**
309
+ * Check if attachment is inline (embedded in message body)
310
+ */
311
+ isInline() {
312
+ return this.contentDisposition === "inline";
313
+ }
314
+ /**
315
+ * Get file extension from filename
316
+ */
317
+ getExtension() {
318
+ if (!this.filename) return "";
319
+ const parts = this.filename.split(".");
320
+ return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : "";
321
+ }
322
+ /**
323
+ * Get human-readable file size
324
+ */
325
+ getFormattedSize() {
326
+ const units = ["B", "KB", "MB", "GB"];
327
+ let size = this.size;
328
+ let unitIndex = 0;
329
+ while (size >= 1024 && unitIndex < units.length - 1) {
330
+ size /= 1024;
331
+ unitIndex++;
332
+ }
333
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
334
+ }
335
+ /**
336
+ * Check if file is stored externally
337
+ */
338
+ hasExternalFile() {
339
+ return !!this.filePath;
340
+ }
341
+ /**
342
+ * Read file content (if stored externally)
343
+ */
344
+ async readContent() {
345
+ if (!this.filePath) return null;
346
+ try {
347
+ const { getFilesystem } = await import("@happyvertical/files");
348
+ const files = await getFilesystem({ type: "local" });
349
+ const data = await files.read(this.filePath);
350
+ return data instanceof Buffer ? data : Buffer.from(data);
351
+ } catch {
352
+ return null;
353
+ }
354
+ }
355
+ };
356
+ __decorateClass$a([
357
+ tenantId({ nullable: true })
358
+ ], Attachment.prototype, "tenantId", 2);
359
+ __decorateClass$a([
360
+ foreignKey("Message")
361
+ ], Attachment.prototype, "messageId", 2);
362
+ Attachment = __decorateClass$a([
363
+ TenantScoped({ mode: "optional" }),
364
+ smrt({
365
+ api: { include: ["list", "get", "create", "delete"] },
366
+ mcp: { include: ["list", "get"] },
367
+ cli: true
368
+ })
369
+ ], Attachment);
370
+ class AttachmentCollection extends SmrtCollection {
371
+ static _itemClass = Attachment;
372
+ /**
373
+ * Get attachments for a message
374
+ */
375
+ async getByMessage(messageId) {
376
+ return await this.list({ where: { messageId } });
377
+ }
378
+ /**
379
+ * Get attachments by content type
380
+ */
381
+ async getByContentType(contentType) {
382
+ return await this.list({ where: { contentType } });
383
+ }
384
+ /**
385
+ * Get image attachments
386
+ */
387
+ async getImages(messageId) {
388
+ const attachments = messageId ? await this.getByMessage(messageId) : await this.list({});
389
+ return attachments.filter((a) => a.isImage());
390
+ }
391
+ /**
392
+ * Get PDF attachments
393
+ */
394
+ async getPdfs(messageId) {
395
+ const attachments = messageId ? await this.getByMessage(messageId) : await this.list({});
396
+ return attachments.filter((a) => a.isPdf());
397
+ }
398
+ /**
399
+ * Get inline attachments (embedded in message body)
400
+ */
401
+ async getInline(messageId) {
402
+ const attachments = await this.getByMessage(messageId);
403
+ return attachments.filter((a) => a.isInline());
404
+ }
405
+ /**
406
+ * Get regular attachments (not inline)
407
+ */
408
+ async getRegular(messageId) {
409
+ const attachments = await this.getByMessage(messageId);
410
+ return attachments.filter((a) => !a.isInline());
411
+ }
412
+ /**
413
+ * Get attachments with external files
414
+ */
415
+ async getWithExternalFiles() {
416
+ const attachments = await this.list({});
417
+ return attachments.filter((a) => a.hasExternalFile());
418
+ }
419
+ /**
420
+ * Get total size of attachments for a message
421
+ */
422
+ async getTotalSize(messageId) {
423
+ const attachments = await this.getByMessage(messageId);
424
+ return attachments.reduce((sum, a) => sum + a.size, 0);
425
+ }
426
+ /**
427
+ * Get largest attachments
428
+ */
429
+ async getLargest(limit = 10) {
430
+ const attachments = await this.list({});
431
+ return attachments.sort((a, b) => b.size - a.size).slice(0, limit);
432
+ }
433
+ /**
434
+ * Search attachments by filename
435
+ */
436
+ async searchByFilename(query) {
437
+ const attachments = await this.list({});
438
+ const lowerQuery = query.toLowerCase();
439
+ return attachments.filter(
440
+ (a) => a.filename?.toLowerCase().includes(lowerQuery)
441
+ );
442
+ }
443
+ /**
444
+ * Get attachments by extension
445
+ */
446
+ async getByExtension(extension) {
447
+ const attachments = await this.list({});
448
+ const lowerExt = extension.toLowerCase().replace(/^\./, "");
449
+ return attachments.filter((a) => a.getExtension() === lowerExt);
450
+ }
451
+ /**
452
+ * Get attachment statistics
453
+ */
454
+ async getStats() {
455
+ const attachments = await this.list({});
456
+ const byType = {};
457
+ for (const attachment of attachments) {
458
+ const type = attachment.contentType.split("/")[0] || "other";
459
+ byType[type] = (byType[type] || 0) + 1;
460
+ }
461
+ return {
462
+ total: attachments.length,
463
+ totalSize: attachments.reduce((sum, a) => sum + a.size, 0),
464
+ byType,
465
+ inline: attachments.filter((a) => a.isInline()).length,
466
+ regular: attachments.filter((a) => !a.isInline()).length
467
+ };
468
+ }
469
+ /**
470
+ * Delete all attachments for a message
471
+ */
472
+ async deleteByMessage(messageId) {
473
+ const attachments = await this.getByMessage(messageId);
474
+ let count = 0;
475
+ for (const attachment of attachments) {
476
+ await attachment.delete();
477
+ count++;
478
+ }
479
+ return count;
480
+ }
481
+ // ─────────────────────────────────────────────────────────────────────────
482
+ // Tenant Helper Methods
483
+ // ─────────────────────────────────────────────────────────────────────────
484
+ async findByTenant(tenantId2) {
485
+ return this.list({ where: { tenantId: tenantId2 } });
486
+ }
487
+ async findGlobal() {
488
+ return this.list({ where: { tenantId: null } });
489
+ }
490
+ async findWithGlobals(tenantId2) {
491
+ return this.query(
492
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
493
+ [tenantId2]
494
+ );
495
+ }
496
+ }
497
+ const AttachmentCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
498
+ __proto__: null,
499
+ AttachmentCollection
500
+ }, Symbol.toStringTag, { value: "Module" }));
501
+ var __defProp$1 = Object.defineProperty;
502
+ var __getOwnPropDesc$9 = Object.getOwnPropertyDescriptor;
503
+ var __decorateClass$9 = (decorators, target, key, kind) => {
504
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$9(target, key) : target;
505
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
506
+ if (decorator = decorators[i])
507
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
508
+ if (kind && result) __defProp$1(target, key, result);
509
+ return result;
510
+ };
511
+ let Message = class extends SmrtObject {
512
+ tenantId = null;
513
+ accountId = "";
514
+ threadId = "";
515
+ subject = "";
516
+ body = "";
517
+ // Normalized plain text
518
+ fromAddress = "";
519
+ fromName = "";
520
+ toAddresses = "";
521
+ // JSON array of {address, name}
522
+ date = null;
523
+ isRead = false;
524
+ isFlagged = false;
525
+ hasAttachments = false;
526
+ size = 0;
527
+ metadata = "";
528
+ // JSON extension bag
529
+ // Send lifecycle fields
530
+ sendStatus = "draft";
531
+ sentAt = null;
532
+ sendError = "";
533
+ retryCount = 0;
534
+ maxRetries = 3;
535
+ scheduledSendAt = null;
536
+ inReplyToMessageId = "";
537
+ // Timestamps
538
+ createdAt = /* @__PURE__ */ new Date();
539
+ updatedAt = /* @__PURE__ */ new Date();
540
+ constructor(options = {}) {
541
+ super(options);
542
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
543
+ if (options.accountId !== void 0) this.accountId = options.accountId;
544
+ if (options.threadId !== void 0) this.threadId = options.threadId;
545
+ if (options.subject !== void 0) this.subject = options.subject;
546
+ if (options.body !== void 0) this.body = options.body;
547
+ if (options.fromAddress !== void 0)
548
+ this.fromAddress = options.fromAddress;
549
+ if (options.fromName !== void 0) this.fromName = options.fromName;
550
+ if (options.toAddresses !== void 0)
551
+ this.toAddresses = options.toAddresses;
552
+ if (options.date !== void 0) this.date = options.date || null;
553
+ if (options.isRead !== void 0) this.isRead = options.isRead;
554
+ if (options.isFlagged !== void 0) this.isFlagged = options.isFlagged;
555
+ if (options.hasAttachments !== void 0)
556
+ this.hasAttachments = options.hasAttachments;
557
+ if (options.size !== void 0) this.size = options.size;
558
+ if (options.metadata !== void 0) this.metadata = options.metadata;
559
+ if (options.sendStatus !== void 0) this.sendStatus = options.sendStatus;
560
+ if (options.sentAt !== void 0) this.sentAt = options.sentAt || null;
561
+ if (options.sendError !== void 0) this.sendError = options.sendError;
562
+ if (options.retryCount !== void 0) this.retryCount = options.retryCount;
563
+ if (options.maxRetries !== void 0) this.maxRetries = options.maxRetries;
564
+ if (options.scheduledSendAt !== void 0)
565
+ this.scheduledSendAt = options.scheduledSendAt || null;
566
+ if (options.inReplyToMessageId !== void 0)
567
+ this.inReplyToMessageId = options.inReplyToMessageId;
568
+ if (options.createdAt) this.createdAt = options.createdAt;
569
+ if (options.updatedAt) this.updatedAt = options.updatedAt;
570
+ }
571
+ /**
572
+ * Get to addresses as parsed array
573
+ */
574
+ getToAddresses() {
575
+ if (!this.toAddresses) return [];
576
+ try {
577
+ return JSON.parse(this.toAddresses);
578
+ } catch {
579
+ return [];
580
+ }
581
+ }
582
+ /**
583
+ * Set to addresses from array
584
+ */
585
+ setToAddresses(addresses) {
586
+ this.toAddresses = JSON.stringify(addresses);
587
+ }
588
+ /**
589
+ * Get metadata as parsed object
590
+ */
591
+ getMetadata() {
592
+ if (!this.metadata) return {};
593
+ try {
594
+ return JSON.parse(this.metadata);
595
+ } catch {
596
+ return {};
597
+ }
598
+ }
599
+ /**
600
+ * Set metadata from object
601
+ */
602
+ setMetadata(data) {
603
+ this.metadata = JSON.stringify(data);
604
+ }
605
+ /**
606
+ * Mark message as read
607
+ */
608
+ async markRead() {
609
+ this.isRead = true;
610
+ this.updatedAt = /* @__PURE__ */ new Date();
611
+ await this.save();
612
+ }
613
+ /**
614
+ * Mark message as unread
615
+ */
616
+ async markUnread() {
617
+ this.isRead = false;
618
+ this.updatedAt = /* @__PURE__ */ new Date();
619
+ await this.save();
620
+ }
621
+ /**
622
+ * Toggle flagged status
623
+ */
624
+ async toggleFlagged() {
625
+ this.isFlagged = !this.isFlagged;
626
+ this.updatedAt = /* @__PURE__ */ new Date();
627
+ await this.save();
628
+ }
629
+ /**
630
+ * Check if message is unread
631
+ */
632
+ isUnread() {
633
+ return !this.isRead;
634
+ }
635
+ /**
636
+ * Get a short preview of the message body
637
+ */
638
+ getPreview(maxLength = 200) {
639
+ const text = this.body || "";
640
+ if (text.length <= maxLength) return text;
641
+ return `${text.slice(0, maxLength)}...`;
642
+ }
643
+ /**
644
+ * Get the account for this message
645
+ */
646
+ async getAccount() {
647
+ if (!this.accountId) return null;
648
+ const { AccountCollection: AccountCollection2 } = await Promise.resolve().then(() => AccountCollection$1);
649
+ const collection = await AccountCollection2.create(this.options);
650
+ return await collection.get({ id: this.accountId });
651
+ }
652
+ /**
653
+ * Get messages in the same thread
654
+ */
655
+ async getThreadMessages() {
656
+ if (!this.threadId) return [this];
657
+ const { MessageCollection: MessageCollection2 } = await Promise.resolve().then(() => MessageCollection$1);
658
+ const collection = await MessageCollection2.create(this.options);
659
+ return await collection.list({ where: { threadId: this.threadId } });
660
+ }
661
+ /**
662
+ * Get attachments for this message
663
+ */
664
+ async getAttachments() {
665
+ const { AttachmentCollection: AttachmentCollection2 } = await Promise.resolve().then(() => AttachmentCollection$1);
666
+ const collection = await AttachmentCollection2.create(this.options);
667
+ return await collection.list({ where: { messageId: this.id } });
668
+ }
669
+ // ─────────────────────────────────────────────────────────────────────────
670
+ // Send Lifecycle
671
+ // ─────────────────────────────────────────────────────────────────────────
672
+ /**
673
+ * Send this message via its account's sender
674
+ */
675
+ async send(options) {
676
+ const account = await this.getAccount();
677
+ if (!account) {
678
+ const result = {
679
+ success: false,
680
+ error: "No account associated with this message",
681
+ sentAt: /* @__PURE__ */ new Date()
682
+ };
683
+ this.sendStatus = "failed";
684
+ this.sendError = result.error ?? "";
685
+ this.updatedAt = /* @__PURE__ */ new Date();
686
+ await this.save();
687
+ return result;
688
+ }
689
+ const sender = await account.createSender();
690
+ this.sendStatus = "sending";
691
+ this.updatedAt = /* @__PURE__ */ new Date();
692
+ await this.save();
693
+ try {
694
+ const result = await sender.send(this, options);
695
+ if (result.success) {
696
+ this.sendStatus = "sent";
697
+ this.sentAt = result.sentAt;
698
+ this.sendError = "";
699
+ } else {
700
+ this.sendStatus = "failed";
701
+ this.sendError = result.error ?? "Send failed";
702
+ }
703
+ this.updatedAt = /* @__PURE__ */ new Date();
704
+ await this.save();
705
+ return result;
706
+ } catch (error) {
707
+ const errorMessage = error instanceof Error ? error.message : String(error);
708
+ this.sendStatus = "failed";
709
+ this.sendError = errorMessage;
710
+ this.updatedAt = /* @__PURE__ */ new Date();
711
+ await this.save();
712
+ return {
713
+ success: false,
714
+ error: errorMessage,
715
+ sentAt: /* @__PURE__ */ new Date()
716
+ };
717
+ }
718
+ }
719
+ /**
720
+ * Retry sending a failed message
721
+ */
722
+ async retrySend(options) {
723
+ if (this.sendStatus !== "failed") {
724
+ return {
725
+ success: false,
726
+ error: `Cannot retry: message status is '${this.sendStatus}', expected 'failed'`,
727
+ sentAt: /* @__PURE__ */ new Date()
728
+ };
729
+ }
730
+ if (this.retryCount >= this.maxRetries) {
731
+ return {
732
+ success: false,
733
+ error: `Retry budget exhausted (${this.retryCount}/${this.maxRetries})`,
734
+ sentAt: /* @__PURE__ */ new Date()
735
+ };
736
+ }
737
+ this.retryCount++;
738
+ this.updatedAt = /* @__PURE__ */ new Date();
739
+ await this.save();
740
+ return this.send(options);
741
+ }
742
+ /**
743
+ * Create a reply to this message (returns unsaved draft)
744
+ */
745
+ createReply(_options) {
746
+ const reply = new this.constructor({
747
+ ...this.options,
748
+ id: void 0,
749
+ accountId: this.accountId,
750
+ threadId: this.threadId || this.id || "",
751
+ subject: this.subject.startsWith("Re:") ? this.subject : `Re: ${this.subject}`,
752
+ toAddresses: JSON.stringify([
753
+ { address: this.fromAddress, name: this.fromName }
754
+ ]),
755
+ fromAddress: "",
756
+ fromName: "",
757
+ body: this.buildQuotedBody(),
758
+ inReplyToMessageId: this.id || "",
759
+ sendStatus: "draft",
760
+ isRead: true,
761
+ date: null,
762
+ createdAt: void 0,
763
+ updatedAt: void 0
764
+ });
765
+ return reply;
766
+ }
767
+ /**
768
+ * Create a forward of this message (returns unsaved draft)
769
+ */
770
+ createForward() {
771
+ const forward = new this.constructor({
772
+ ...this.options,
773
+ id: void 0,
774
+ accountId: this.accountId,
775
+ threadId: "",
776
+ subject: this.subject.startsWith("Fwd:") ? this.subject : `Fwd: ${this.subject}`,
777
+ toAddresses: "[]",
778
+ fromAddress: "",
779
+ fromName: "",
780
+ body: this.buildQuotedBody(),
781
+ hasAttachments: this.hasAttachments,
782
+ inReplyToMessageId: "",
783
+ sendStatus: "draft",
784
+ isRead: true,
785
+ date: null,
786
+ createdAt: void 0,
787
+ updatedAt: void 0
788
+ });
789
+ return forward;
790
+ }
791
+ /**
792
+ * Build quoted body for reply/forward
793
+ */
794
+ buildQuotedBody() {
795
+ const dateStr = this.date ? this.date.toLocaleString() : "unknown date";
796
+ const from = this.fromName ? `${this.fromName} <${this.fromAddress}>` : this.fromAddress;
797
+ const quotedLines = (this.body || "").split("\n").map((line) => `> ${line}`).join("\n");
798
+ return `
799
+
800
+ On ${dateStr}, ${from} wrote:
801
+ ${quotedLines}`;
802
+ }
803
+ };
804
+ __decorateClass$9([
805
+ tenantId({ nullable: true })
806
+ ], Message.prototype, "tenantId", 2);
807
+ __decorateClass$9([
808
+ foreignKey("Account")
809
+ ], Message.prototype, "accountId", 2);
810
+ __decorateClass$9([
811
+ foreignKey("Message")
812
+ ], Message.prototype, "inReplyToMessageId", 2);
813
+ Message = __decorateClass$9([
814
+ TenantScoped({ mode: "optional" }),
815
+ smrt({
816
+ tableStrategy: "sti",
817
+ api: { include: ["list", "get"] },
818
+ mcp: { include: ["list", "get"] },
819
+ cli: true
820
+ })
821
+ ], Message);
822
+ class MessageCollection extends SmrtCollection {
823
+ static _itemClass = Message;
824
+ /**
825
+ * Search messages with filters
826
+ */
827
+ async search(query, filters) {
828
+ let messages = await this.list({});
829
+ if (query) {
830
+ const lowerQuery = query.toLowerCase();
831
+ messages = messages.filter(
832
+ (m) => m.subject?.toLowerCase().includes(lowerQuery) || m.body?.toLowerCase().includes(lowerQuery) || m.fromAddress?.toLowerCase().includes(lowerQuery) || m.fromName?.toLowerCase().includes(lowerQuery)
833
+ );
834
+ }
835
+ if (filters) {
836
+ if (filters.accountIds && filters.accountIds.length > 0) {
837
+ messages = messages.filter(
838
+ (m) => filters.accountIds?.includes(m.accountId)
839
+ );
840
+ }
841
+ if (filters.messageType) {
842
+ messages = messages.filter((m) => {
843
+ const metaType = m._meta_type || "";
844
+ return metaType.includes(filters.messageType);
845
+ });
846
+ }
847
+ if (filters.from) {
848
+ const fromLower = filters.from.toLowerCase();
849
+ messages = messages.filter(
850
+ (m) => m.fromAddress?.toLowerCase().includes(fromLower) || m.fromName?.toLowerCase().includes(fromLower)
851
+ );
852
+ }
853
+ if (filters.to) {
854
+ const toLower = filters.to.toLowerCase();
855
+ messages = messages.filter(
856
+ (m) => m.toAddresses?.toLowerCase().includes(toLower)
857
+ );
858
+ }
859
+ if (filters.isRead !== void 0) {
860
+ messages = messages.filter((m) => m.isRead === filters.isRead);
861
+ }
862
+ if (filters.isFlagged !== void 0) {
863
+ messages = messages.filter((m) => m.isFlagged === filters.isFlagged);
864
+ }
865
+ if (filters.sinceDate) {
866
+ messages = messages.filter(
867
+ (m) => m.date && m.date >= filters.sinceDate
868
+ );
869
+ }
870
+ if (filters.beforeDate) {
871
+ messages = messages.filter(
872
+ (m) => m.date && m.date < filters.beforeDate
873
+ );
874
+ }
875
+ if (filters.query) {
876
+ const q = filters.query.toLowerCase();
877
+ messages = messages.filter(
878
+ (m) => m.subject?.toLowerCase().includes(q) || m.body?.toLowerCase().includes(q)
879
+ );
880
+ }
881
+ }
882
+ return messages;
883
+ }
884
+ /**
885
+ * Get messages by multiple accounts
886
+ */
887
+ async getByAccounts(accountIds) {
888
+ const allMessages = await this.list({});
889
+ return allMessages.filter((m) => accountIds.includes(m.accountId));
890
+ }
891
+ /**
892
+ * Get messages by STI type.
893
+ *
894
+ * Accepts either the full discriminator (e.g. "@happyvertical/smrt-messages:Email")
895
+ * or the short type name (e.g. "Email").
896
+ */
897
+ async getByType(messageType) {
898
+ if (messageType.includes(":") || messageType.startsWith("@")) {
899
+ return await this.list({ where: { _meta_type: messageType } });
900
+ }
901
+ const allMessages = await this.list({});
902
+ return allMessages.filter((m) => {
903
+ const metaType = m._meta_type || "";
904
+ return metaType.endsWith(`:${messageType}`);
905
+ });
906
+ }
907
+ /**
908
+ * Get unread messages
909
+ */
910
+ async getUnread(accountId) {
911
+ const where = { isRead: false };
912
+ if (accountId) {
913
+ where.accountId = accountId;
914
+ }
915
+ return await this.list({ where });
916
+ }
917
+ /**
918
+ * Get flagged messages
919
+ */
920
+ async getFlagged(accountId) {
921
+ const where = { isFlagged: true };
922
+ if (accountId) {
923
+ where.accountId = accountId;
924
+ }
925
+ return await this.list({ where });
926
+ }
927
+ /**
928
+ * Get recent messages
929
+ */
930
+ async getRecent(limit = 20, accountId) {
931
+ const allMessages = await this.list({
932
+ where: accountId ? { accountId } : void 0
933
+ });
934
+ return allMessages.sort((a, b) => {
935
+ const dateA = a.date?.getTime() || 0;
936
+ const dateB = b.date?.getTime() || 0;
937
+ return dateB - dateA;
938
+ }).slice(0, limit);
939
+ }
940
+ /**
941
+ * Get messages by thread
942
+ */
943
+ async getByThread(threadId) {
944
+ return await this.list({ where: { threadId } });
945
+ }
946
+ /**
947
+ * Mark multiple messages as read
948
+ */
949
+ async markAllRead(messageIds) {
950
+ for (const id of messageIds) {
951
+ const message = await this.get({ id });
952
+ if (message) {
953
+ await message.markRead();
954
+ }
955
+ }
956
+ }
957
+ /**
958
+ * Get message statistics for an account
959
+ */
960
+ async getAccountStats(accountId) {
961
+ const messages = await this.list({ where: { accountId } });
962
+ const byType = {};
963
+ for (const msg of messages) {
964
+ const metaType = msg._meta_type || "Unknown";
965
+ const shortType = metaType.split(":").pop() || metaType;
966
+ byType[shortType] = (byType[shortType] || 0) + 1;
967
+ }
968
+ return {
969
+ total: messages.length,
970
+ unread: messages.filter((m) => !m.isRead).length,
971
+ flagged: messages.filter((m) => m.isFlagged).length,
972
+ byType
973
+ };
974
+ }
975
+ // ─────────────────────────────────────────────────────────────────────────
976
+ // Send / Draft Queries
977
+ // ─────────────────────────────────────────────────────────────────────────
978
+ /**
979
+ * Get draft messages
980
+ */
981
+ async getDrafts(accountId) {
982
+ const where = { sendStatus: "draft" };
983
+ if (accountId) where.accountId = accountId;
984
+ return await this.list({ where });
985
+ }
986
+ /**
987
+ * Get sent messages
988
+ */
989
+ async getSent(accountId) {
990
+ const where = { sendStatus: "sent" };
991
+ if (accountId) where.accountId = accountId;
992
+ return await this.list({ where });
993
+ }
994
+ /**
995
+ * Get scheduled messages
996
+ */
997
+ async getScheduled(accountId) {
998
+ const where = { sendStatus: "scheduled" };
999
+ if (accountId) where.accountId = accountId;
1000
+ return await this.list({ where });
1001
+ }
1002
+ /**
1003
+ * Get messages that failed to send
1004
+ */
1005
+ async getFailedSends(accountId) {
1006
+ const where = { sendStatus: "failed" };
1007
+ if (accountId) where.accountId = accountId;
1008
+ return await this.list({ where });
1009
+ }
1010
+ /**
1011
+ * Get outbox (pending + sending + scheduled)
1012
+ */
1013
+ async getOutbox(accountId) {
1014
+ const allMessages = await this.list({
1015
+ where: accountId ? { accountId } : void 0
1016
+ });
1017
+ return allMessages.filter(
1018
+ (m) => m.sendStatus === "pending" || m.sendStatus === "sending" || m.sendStatus === "scheduled"
1019
+ );
1020
+ }
1021
+ // ─────────────────────────────────────────────────────────────────────────
1022
+ // Tenant Helper Methods
1023
+ // ─────────────────────────────────────────────────────────────────────────
1024
+ async findByTenant(tenantId2) {
1025
+ return this.list({ where: { tenantId: tenantId2 } });
1026
+ }
1027
+ async findGlobal() {
1028
+ return this.list({ where: { tenantId: null } });
1029
+ }
1030
+ async findWithGlobals(tenantId2) {
1031
+ return this.query(
1032
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
1033
+ [tenantId2]
1034
+ );
1035
+ }
1036
+ }
1037
+ const MessageCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1038
+ __proto__: null,
1039
+ MessageCollection
1040
+ }, Symbol.toStringTag, { value: "Module" }));
1041
+ var __getOwnPropDesc$8 = Object.getOwnPropertyDescriptor;
1042
+ var __decorateClass$8 = (decorators, target, key, kind) => {
1043
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$8(target, key) : target;
1044
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
1045
+ if (decorator = decorators[i])
1046
+ result = decorator(result) || result;
1047
+ return result;
1048
+ };
1049
+ let EmailAccount = class extends Account {
1050
+ email = "";
1051
+ syncIntervalMinutes = 60;
1052
+ constructor(options = {}) {
1053
+ super(options);
1054
+ if (options.email !== void 0) this.email = options.email;
1055
+ if (options.providerType !== void 0)
1056
+ this.providerType = options.providerType;
1057
+ if (options.syncIntervalMinutes !== void 0)
1058
+ this.syncIntervalMinutes = options.syncIntervalMinutes;
1059
+ }
1060
+ /**
1061
+ * Options for child rows (folders/emails) created during sync: carries the DB
1062
+ * connection + tenant context from this account, but strips this account's own
1063
+ * identity fields. When this account was hydrated from the DB, `this.options`
1064
+ * holds the account row's `id`/`slug`/`_skipLoad`; spreading those into a new
1065
+ * child would make every synced row inherit the account's primary key and
1066
+ * upsert over each other.
1067
+ */
1068
+ childOptions() {
1069
+ const rest = { ...this.options };
1070
+ delete rest.id;
1071
+ delete rest.slug;
1072
+ delete rest._skipLoad;
1073
+ return rest;
1074
+ }
1075
+ /**
1076
+ * Create a sender for this email account
1077
+ */
1078
+ async createSender() {
1079
+ const client = await this.createClient();
1080
+ await client.connect();
1081
+ const { EmailSender: EmailSender2 } = await Promise.resolve().then(() => EmailSender$1);
1082
+ return new EmailSender2(client, this);
1083
+ }
1084
+ /**
1085
+ * Create an EmailClient from stored settings
1086
+ * Retrieves credentials from smrt-secrets if credentialSecretId is set
1087
+ */
1088
+ async createClient() {
1089
+ const { getEmailClient } = await import("@happyvertical/email");
1090
+ let settings;
1091
+ if (this.credentialSecretId) {
1092
+ const { SecretService } = await import("@happyvertical/smrt-secrets");
1093
+ const secretService = await SecretService.create({ db: this.db });
1094
+ const secret = await secretService.retrieve(this.credentialSecretId);
1095
+ settings = JSON.parse(secret.value);
1096
+ } else {
1097
+ settings = this.getSettings();
1098
+ }
1099
+ return await getEmailClient({
1100
+ type: this.providerType,
1101
+ ...settings
1102
+ });
1103
+ }
1104
+ /**
1105
+ * Sync emails from the email server to the database
1106
+ */
1107
+ async syncFrom(options = {}) {
1108
+ const startTime = Date.now();
1109
+ const result = {
1110
+ folders: [],
1111
+ messagesProcessed: 0,
1112
+ messagesDownloaded: 0,
1113
+ messagesSkipped: 0,
1114
+ errors: [],
1115
+ duration: 0
1116
+ };
1117
+ try {
1118
+ const client = await this.createClient();
1119
+ await client.connect();
1120
+ const foldersToSync = options.folders || ["INBOX"];
1121
+ result.folders = foldersToSync;
1122
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
1123
+ const { EmailFolderCollection: EmailFolderCollection2 } = await Promise.resolve().then(() => EmailFolderCollection$1);
1124
+ const emailCollection = await EmailCollection2.create(
1125
+ this.options
1126
+ );
1127
+ const folderCollection = await EmailFolderCollection2.create(
1128
+ this.options
1129
+ );
1130
+ for (const folderName of foldersToSync) {
1131
+ try {
1132
+ const accountId = this.id ?? "";
1133
+ let folder = await folderCollection.getByPath(accountId, folderName);
1134
+ if (!folder) {
1135
+ const { EmailFolder: EmailFolder2 } = await Promise.resolve().then(() => EmailFolder$1);
1136
+ folder = new EmailFolder2({
1137
+ ...this.childOptions(),
1138
+ accountId,
1139
+ name: folderName,
1140
+ path: folderName
1141
+ });
1142
+ await folder.initialize();
1143
+ await folder.save();
1144
+ }
1145
+ const fetchOptions = {
1146
+ folder: folderName,
1147
+ limit: options.batchSize || 100
1148
+ };
1149
+ if (options.since) {
1150
+ fetchOptions.since = options.since;
1151
+ }
1152
+ if (options.before) {
1153
+ fetchOptions.before = options.before;
1154
+ }
1155
+ const messages = await client.fetch(fetchOptions);
1156
+ for (const msg of messages) {
1157
+ result.messagesProcessed++;
1158
+ try {
1159
+ const existingEmail = await emailCollection.getByMessageId(
1160
+ this.id,
1161
+ msg.messageId || ""
1162
+ );
1163
+ if (existingEmail && !options.fullSync) {
1164
+ result.messagesSkipped++;
1165
+ continue;
1166
+ }
1167
+ const { Email: Email2 } = await Promise.resolve().then(() => Email$1);
1168
+ let email = existingEmail;
1169
+ if (!email) {
1170
+ email = new Email2({
1171
+ ...this.childOptions(),
1172
+ accountId
1173
+ });
1174
+ await email.initialize();
1175
+ }
1176
+ email.messageId = msg.messageId || "";
1177
+ email.threadId = msg.threadId || "";
1178
+ email.inReplyTo = msg.inReplyTo || "";
1179
+ email.fromAddress = msg.from?.address || "";
1180
+ email.fromName = msg.from?.name || "";
1181
+ email.toAddresses = JSON.stringify(msg.to || []);
1182
+ email.ccAddresses = JSON.stringify(msg.cc || []);
1183
+ email.bccAddresses = JSON.stringify(msg.bcc || []);
1184
+ email.replyToAddress = msg.replyTo?.address || "";
1185
+ email.replyToName = msg.replyTo?.name || "";
1186
+ email.subject = msg.subject || "";
1187
+ email.date = msg.date || null;
1188
+ email.textBody = msg.text || "";
1189
+ email.htmlBody = msg.html || "";
1190
+ email.body = msg.text || "";
1191
+ email.folderId = folder.id;
1192
+ email.folderPath = folderName;
1193
+ email.labels = JSON.stringify(msg.labels || []);
1194
+ email.flags = JSON.stringify(msg.flags || []);
1195
+ email.hasAttachments = msg.attachments && msg.attachments.length > 0 || false;
1196
+ email.size = msg.size || 0;
1197
+ email.headers = JSON.stringify(msg.headers || {});
1198
+ email.updatedAt = /* @__PURE__ */ new Date();
1199
+ await email.save();
1200
+ result.messagesDownloaded++;
1201
+ if (options.onProgress) {
1202
+ options.onProgress({
1203
+ folder: folderName,
1204
+ processed: result.messagesProcessed,
1205
+ total: messages.length,
1206
+ downloaded: result.messagesDownloaded,
1207
+ skipped: result.messagesSkipped,
1208
+ errors: result.errors.length
1209
+ });
1210
+ }
1211
+ } catch (error) {
1212
+ const err = error instanceof Error ? error : new Error(String(error));
1213
+ result.errors.push(err);
1214
+ if (options.onError) {
1215
+ options.onError(err, msg);
1216
+ }
1217
+ }
1218
+ }
1219
+ folder.messageCount = await emailCollection.countByFolder(folder.id);
1220
+ folder.unreadCount = await emailCollection.countUnreadByFolder(
1221
+ folder.id
1222
+ );
1223
+ await folder.save();
1224
+ } catch (error) {
1225
+ const err = error instanceof Error ? error : new Error(String(error));
1226
+ result.errors.push(err);
1227
+ if (options.onError) {
1228
+ options.onError(err);
1229
+ }
1230
+ }
1231
+ }
1232
+ this.lastSyncAt = /* @__PURE__ */ new Date();
1233
+ this.updatedAt = /* @__PURE__ */ new Date();
1234
+ await this.save();
1235
+ await client.disconnect();
1236
+ } catch (error) {
1237
+ const err = error instanceof Error ? error : new Error(String(error));
1238
+ result.errors.push(err);
1239
+ if (options.onError) {
1240
+ options.onError(err);
1241
+ }
1242
+ }
1243
+ result.duration = Date.now() - startTime;
1244
+ return result;
1245
+ }
1246
+ /**
1247
+ * Get all folders for this account
1248
+ */
1249
+ async getFolders() {
1250
+ const { EmailFolderCollection: EmailFolderCollection2 } = await Promise.resolve().then(() => EmailFolderCollection$1);
1251
+ const collection = await EmailFolderCollection2.create(
1252
+ this.options
1253
+ );
1254
+ return await collection.list({ where: { accountId: this.id } });
1255
+ }
1256
+ /**
1257
+ * Get all emails for this account
1258
+ */
1259
+ async getEmails(limit) {
1260
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
1261
+ const collection = await EmailCollection2.create(this.options);
1262
+ const options = { where: { accountId: this.id } };
1263
+ if (limit) {
1264
+ options.limit = limit;
1265
+ }
1266
+ return await collection.list(options);
1267
+ }
1268
+ /**
1269
+ * Get unread email count
1270
+ */
1271
+ async getUnreadCount() {
1272
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
1273
+ const collection = await EmailCollection2.create(this.options);
1274
+ return await collection.countUnreadByAccount(this.id);
1275
+ }
1276
+ /**
1277
+ * Store credentials securely using smrt-secrets (email-specific)
1278
+ */
1279
+ async setCredentials(credentials, options = {}) {
1280
+ const { SecretService } = await import("@happyvertical/smrt-secrets");
1281
+ const secretService = await SecretService.create({ db: this.db });
1282
+ const secretName = `email-account-${this.id}`;
1283
+ const secretValue = JSON.stringify(credentials);
1284
+ if (this.credentialSecretId) {
1285
+ await secretService.store(this.credentialSecretId, secretValue, {
1286
+ description: options.description || `IMAP credentials for ${this.name}`,
1287
+ category: options.category || "email"
1288
+ });
1289
+ } else {
1290
+ await secretService.store(secretName, secretValue, {
1291
+ description: options.description || `IMAP credentials for ${this.name}`,
1292
+ category: options.category || "email"
1293
+ });
1294
+ this.credentialSecretId = secretName;
1295
+ this.updatedAt = /* @__PURE__ */ new Date();
1296
+ await this.save();
1297
+ }
1298
+ }
1299
+ /**
1300
+ * Sync all active email accounts (for job runner)
1301
+ *
1302
+ * NOTE: This is a class-wide operation, not per-instance. It syncs ALL active
1303
+ * accounts, ignoring `this` instance. It exists as an instance method because
1304
+ * the TaskRunner dispatches via `objectType: 'EmailAccount', method: 'syncAll'`
1305
+ * which requires an instance method signature.
1306
+ */
1307
+ async syncAll(args) {
1308
+ const { EmailAccountCollection: EmailAccountCollection2 } = await Promise.resolve().then(() => EmailAccountCollection$1);
1309
+ const collection = await EmailAccountCollection2.create({
1310
+ db: this.db
1311
+ });
1312
+ return collection.syncAll(args);
1313
+ }
1314
+ /**
1315
+ * Migrate from plain-text settings to secrets
1316
+ */
1317
+ async migrateToSecrets() {
1318
+ if (this.credentialSecretId) {
1319
+ return;
1320
+ }
1321
+ const settings = this.getSettings();
1322
+ if (Object.keys(settings).length === 0) {
1323
+ return;
1324
+ }
1325
+ await this.setCredentials(settings, {
1326
+ description: `Migrated IMAP credentials for ${this.name}`
1327
+ });
1328
+ }
1329
+ };
1330
+ EmailAccount = __decorateClass$8([
1331
+ smrt({
1332
+ tableStrategy: "sti",
1333
+ api: { include: ["list", "get", "create", "update", "delete"] },
1334
+ mcp: { include: ["list", "get"] },
1335
+ cli: true
1336
+ })
1337
+ ], EmailAccount);
1338
+ class EmailAccountCollection extends AccountCollection {
1339
+ static _itemClass = EmailAccount;
1340
+ /**
1341
+ * Get account by email address
1342
+ */
1343
+ async getByEmail(email) {
1344
+ const accounts = await this.list({ where: { email } });
1345
+ return accounts[0] || null;
1346
+ }
1347
+ /**
1348
+ * Get accounts by email provider type
1349
+ */
1350
+ async getByEmailProviderType(providerType) {
1351
+ return await this.list({ where: { providerType } });
1352
+ }
1353
+ /**
1354
+ * Get active email accounts
1355
+ */
1356
+ async getActive() {
1357
+ return await this.list({ where: { isActive: true } });
1358
+ }
1359
+ /**
1360
+ * Get inactive email accounts
1361
+ */
1362
+ async getInactive() {
1363
+ return await this.list({ where: { isActive: false } });
1364
+ }
1365
+ /**
1366
+ * Get accounts that need syncing
1367
+ */
1368
+ async getNeedingSync(maxAgeMinutes = 60) {
1369
+ const allAccounts = await this.getActive();
1370
+ const cutoffTime = new Date(Date.now() - maxAgeMinutes * 60 * 1e3);
1371
+ return allAccounts.filter(
1372
+ (account) => !account.lastSyncAt || account.lastSyncAt < cutoffTime
1373
+ );
1374
+ }
1375
+ /**
1376
+ * Search email accounts with filters.
1377
+ * Alias: `search()` for backward compatibility.
1378
+ */
1379
+ async search(query, filters) {
1380
+ return this.searchEmailAccounts(query, filters);
1381
+ }
1382
+ /**
1383
+ * Get accounts by email provider type.
1384
+ * Alias: `getByProviderType()` for backward compatibility.
1385
+ */
1386
+ async getByProviderType(providerType) {
1387
+ return this.getByEmailProviderType(providerType);
1388
+ }
1389
+ /**
1390
+ * Get email account statistics.
1391
+ * Alias: `getStats()` for backward compatibility.
1392
+ */
1393
+ async getStats() {
1394
+ const stats = await this.getEmailStats();
1395
+ return {
1396
+ total: stats.total,
1397
+ active: stats.active,
1398
+ inactive: stats.inactive,
1399
+ byType: stats.byProvider
1400
+ };
1401
+ }
1402
+ /**
1403
+ * Search email accounts with filters
1404
+ */
1405
+ async searchEmailAccounts(query, filters) {
1406
+ let accounts = await this.list({});
1407
+ if (query) {
1408
+ const lowerQuery = query.toLowerCase();
1409
+ accounts = accounts.filter(
1410
+ (a) => a.name?.toLowerCase().includes(lowerQuery) || a.email?.toLowerCase().includes(lowerQuery)
1411
+ );
1412
+ }
1413
+ if (filters) {
1414
+ if (filters.providerType) {
1415
+ accounts = accounts.filter(
1416
+ (a) => a.providerType === filters.providerType
1417
+ );
1418
+ }
1419
+ if (filters.email) {
1420
+ const emailLower = filters.email.toLowerCase();
1421
+ accounts = accounts.filter(
1422
+ (a) => a.email?.toLowerCase().includes(emailLower)
1423
+ );
1424
+ }
1425
+ if (filters.isActive !== void 0) {
1426
+ accounts = accounts.filter((a) => a.isActive === filters.isActive);
1427
+ }
1428
+ }
1429
+ return accounts;
1430
+ }
1431
+ /**
1432
+ * Sync all active email accounts
1433
+ */
1434
+ async syncAll(options) {
1435
+ const results = /* @__PURE__ */ new Map();
1436
+ const accounts = await this.getActive();
1437
+ for (const account of accounts) {
1438
+ const ea = account;
1439
+ const accountId = ea.id ?? ea.email ?? "unknown";
1440
+ try {
1441
+ await ea.syncFrom(options);
1442
+ results.set(accountId, { success: true });
1443
+ } catch (error) {
1444
+ results.set(accountId, {
1445
+ success: false,
1446
+ error: error instanceof Error ? error : new Error(String(error))
1447
+ });
1448
+ }
1449
+ }
1450
+ return results;
1451
+ }
1452
+ /**
1453
+ * Get total unread count across all email accounts
1454
+ */
1455
+ async getTotalUnreadCount() {
1456
+ const accounts = await this.getActive();
1457
+ let total = 0;
1458
+ for (const account of accounts) {
1459
+ total += await account.getUnreadCount();
1460
+ }
1461
+ return total;
1462
+ }
1463
+ /**
1464
+ * Get email account statistics
1465
+ */
1466
+ async getEmailStats() {
1467
+ const accounts = await this.list({});
1468
+ const byProvider = {
1469
+ smtp: 0,
1470
+ imap: 0,
1471
+ pop3: 0,
1472
+ gmail: 0
1473
+ };
1474
+ for (const account of accounts) {
1475
+ const pt = account.providerType;
1476
+ if (pt in byProvider) {
1477
+ byProvider[pt]++;
1478
+ }
1479
+ }
1480
+ return {
1481
+ total: accounts.length,
1482
+ active: accounts.filter((a) => a.isActive).length,
1483
+ inactive: accounts.filter((a) => !a.isActive).length,
1484
+ byProvider
1485
+ };
1486
+ }
1487
+ // ─────────────────────────────────────────────────────────────────────────
1488
+ // Tenant Helper Methods
1489
+ // ─────────────────────────────────────────────────────────────────────────
1490
+ async findByTenant(tenantId2) {
1491
+ return this.list({ where: { tenantId: tenantId2 } });
1492
+ }
1493
+ async findGlobal() {
1494
+ return this.list({ where: { tenantId: null } });
1495
+ }
1496
+ async findWithGlobals(tenantId2) {
1497
+ return this.query(
1498
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
1499
+ [tenantId2]
1500
+ );
1501
+ }
1502
+ }
1503
+ const EmailAccountCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1504
+ __proto__: null,
1505
+ EmailAccountCollection
1506
+ }, Symbol.toStringTag, { value: "Module" }));
1507
+ class EmailAttachment extends Attachment {
1508
+ /**
1509
+ * Legacy emailId field — maps to messageId
1510
+ */
1511
+ get emailId() {
1512
+ return this.messageId;
1513
+ }
1514
+ set emailId(value) {
1515
+ this.messageId = value;
1516
+ }
1517
+ constructor(options = {}) {
1518
+ const mappedOptions = {
1519
+ ...options,
1520
+ messageId: options.emailId || options.messageId || ""
1521
+ };
1522
+ super(mappedOptions);
1523
+ }
1524
+ /**
1525
+ * Get the email this attachment belongs to
1526
+ * @deprecated Use getMessage() instead
1527
+ */
1528
+ async getEmail() {
1529
+ if (!this.messageId) return null;
1530
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
1531
+ const collection = await EmailCollection2.create(this.options);
1532
+ return await collection.get({ id: this.messageId });
1533
+ }
1534
+ }
1535
+ class EmailAttachmentCollection extends AttachmentCollection {
1536
+ static _itemClass = EmailAttachment;
1537
+ /**
1538
+ * Get attachments for an email
1539
+ * @deprecated Use getByMessage() instead
1540
+ */
1541
+ async getByEmail(emailId) {
1542
+ return await this.getByMessage(emailId);
1543
+ }
1544
+ /**
1545
+ * Get image attachments
1546
+ */
1547
+ async getImages(emailId) {
1548
+ return await super.getImages(emailId);
1549
+ }
1550
+ /**
1551
+ * Get PDF attachments
1552
+ */
1553
+ async getPdfs(emailId) {
1554
+ return await super.getPdfs(emailId);
1555
+ }
1556
+ /**
1557
+ * Get inline attachments
1558
+ */
1559
+ async getInline(emailId) {
1560
+ return await super.getInline(emailId);
1561
+ }
1562
+ /**
1563
+ * Get regular attachments
1564
+ */
1565
+ async getRegular(emailId) {
1566
+ return await super.getRegular(emailId);
1567
+ }
1568
+ /**
1569
+ * Delete all attachments for an email
1570
+ * @deprecated Use deleteByMessage() instead
1571
+ */
1572
+ async deleteByEmail(emailId) {
1573
+ return await this.deleteByMessage(emailId);
1574
+ }
1575
+ // ─────────────────────────────────────────────────────────────────────────
1576
+ // Tenant Helper Methods
1577
+ // ─────────────────────────────────────────────────────────────────────────
1578
+ async findByTenant(tenantId2) {
1579
+ return this.list({ where: { tenantId: tenantId2 } });
1580
+ }
1581
+ async findGlobal() {
1582
+ return this.list({ where: { tenantId: null } });
1583
+ }
1584
+ async findWithGlobals(tenantId2) {
1585
+ return this.query(
1586
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
1587
+ [tenantId2]
1588
+ );
1589
+ }
1590
+ }
1591
+ var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
1592
+ var __decorateClass$7 = (decorators, target, key, kind) => {
1593
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
1594
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
1595
+ if (decorator = decorators[i])
1596
+ result = decorator(result) || result;
1597
+ return result;
1598
+ };
1599
+ let Email = class extends Message {
1600
+ // RFC 822 fields
1601
+ messageId = "";
1602
+ // RFC 822 Message-ID header
1603
+ inReplyTo = "";
1604
+ // Additional recipients
1605
+ ccAddresses = "";
1606
+ bccAddresses = "";
1607
+ replyToAddress = "";
1608
+ replyToName = "";
1609
+ // Email-specific content
1610
+ textBody = "";
1611
+ htmlBody = "";
1612
+ // Location
1613
+ folderId = "";
1614
+ folderPath = "";
1615
+ labels = "";
1616
+ // JSON array (Gmail)
1617
+ flags = "";
1618
+ // JSON array (IMAP)
1619
+ // Email-specific status flags
1620
+ isAnswered = false;
1621
+ isDraft = false;
1622
+ // Raw data
1623
+ rawMessage = "";
1624
+ headers = "";
1625
+ // JSON object
1626
+ constructor(options = {}) {
1627
+ super(options);
1628
+ if (options.messageId !== void 0) this.messageId = options.messageId;
1629
+ if (options.inReplyTo !== void 0) this.inReplyTo = options.inReplyTo;
1630
+ if (options.ccAddresses !== void 0)
1631
+ this.ccAddresses = options.ccAddresses;
1632
+ if (options.bccAddresses !== void 0)
1633
+ this.bccAddresses = options.bccAddresses;
1634
+ if (options.replyToAddress !== void 0)
1635
+ this.replyToAddress = options.replyToAddress;
1636
+ if (options.replyToName !== void 0)
1637
+ this.replyToName = options.replyToName;
1638
+ if (options.textBody !== void 0) this.textBody = options.textBody;
1639
+ if (options.htmlBody !== void 0) this.htmlBody = options.htmlBody;
1640
+ if (options.folderId !== void 0) this.folderId = options.folderId;
1641
+ if (options.folderPath !== void 0) this.folderPath = options.folderPath;
1642
+ if (options.labels !== void 0) this.labels = options.labels;
1643
+ if (options.flags !== void 0) this.flags = options.flags;
1644
+ if (options.isAnswered !== void 0) this.isAnswered = options.isAnswered;
1645
+ if (options.isDraft !== void 0) this.isDraft = options.isDraft;
1646
+ if (options.rawMessage !== void 0) this.rawMessage = options.rawMessage;
1647
+ if (options.headers !== void 0) this.headers = options.headers;
1648
+ if (options.textBody && !options.body) {
1649
+ this.body = options.textBody;
1650
+ }
1651
+ }
1652
+ /**
1653
+ * Get CC addresses as parsed array
1654
+ */
1655
+ getCcAddresses() {
1656
+ if (!this.ccAddresses) return [];
1657
+ try {
1658
+ return JSON.parse(this.ccAddresses);
1659
+ } catch {
1660
+ return [];
1661
+ }
1662
+ }
1663
+ /**
1664
+ * Get BCC addresses as parsed array
1665
+ */
1666
+ getBccAddresses() {
1667
+ if (!this.bccAddresses) return [];
1668
+ try {
1669
+ return JSON.parse(this.bccAddresses);
1670
+ } catch {
1671
+ return [];
1672
+ }
1673
+ }
1674
+ /**
1675
+ * Get labels as parsed array
1676
+ */
1677
+ getLabels() {
1678
+ if (!this.labels) return [];
1679
+ try {
1680
+ return JSON.parse(this.labels);
1681
+ } catch {
1682
+ return [];
1683
+ }
1684
+ }
1685
+ /**
1686
+ * Set labels from array
1687
+ */
1688
+ setLabels(labels) {
1689
+ this.labels = JSON.stringify(labels);
1690
+ }
1691
+ /**
1692
+ * Get flags as parsed array
1693
+ */
1694
+ getFlags() {
1695
+ if (!this.flags) return [];
1696
+ try {
1697
+ return JSON.parse(this.flags);
1698
+ } catch {
1699
+ return [];
1700
+ }
1701
+ }
1702
+ /**
1703
+ * Set flags from array
1704
+ */
1705
+ setFlags(flags) {
1706
+ this.flags = JSON.stringify(flags);
1707
+ }
1708
+ /**
1709
+ * Get headers as parsed object
1710
+ */
1711
+ getHeaders() {
1712
+ if (!this.headers) return {};
1713
+ try {
1714
+ return JSON.parse(this.headers);
1715
+ } catch {
1716
+ return {};
1717
+ }
1718
+ }
1719
+ /**
1720
+ * Set headers from object
1721
+ */
1722
+ setHeaders(headers) {
1723
+ this.headers = JSON.stringify(headers);
1724
+ }
1725
+ /**
1726
+ * Get the email account (typed as EmailAccount)
1727
+ */
1728
+ async getAccount() {
1729
+ if (!this.accountId) return null;
1730
+ const { EmailAccountCollection: EmailAccountCollection2 } = await Promise.resolve().then(() => EmailAccountCollection$1);
1731
+ const collection = await EmailAccountCollection2.create(
1732
+ this.options
1733
+ );
1734
+ return await collection.get({ id: this.accountId });
1735
+ }
1736
+ /**
1737
+ * Get the folder
1738
+ */
1739
+ async getFolder() {
1740
+ if (!this.folderId) return null;
1741
+ const { EmailFolderCollection: EmailFolderCollection2 } = await Promise.resolve().then(() => EmailFolderCollection$1);
1742
+ const collection = await EmailFolderCollection2.create(
1743
+ this.options
1744
+ );
1745
+ return await collection.get({ id: this.folderId });
1746
+ }
1747
+ /**
1748
+ * Get emails in the same thread
1749
+ */
1750
+ async getThreadEmails() {
1751
+ if (!this.threadId) return [this];
1752
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
1753
+ const collection = await EmailCollection2.create(this.options);
1754
+ return await collection.list({ where: { threadId: this.threadId } });
1755
+ }
1756
+ /**
1757
+ * Get a short preview of the email body
1758
+ */
1759
+ getPreview(maxLength = 200) {
1760
+ const body = this.textBody || this.htmlBody?.replace(/<[^>]*>/g, "") || "";
1761
+ if (body.length <= maxLength) return body;
1762
+ return `${body.slice(0, maxLength)}...`;
1763
+ }
1764
+ /**
1765
+ * Get References header values as array
1766
+ */
1767
+ getReferences() {
1768
+ const headers = this.getHeaders();
1769
+ const refs = headers.references;
1770
+ if (!refs) return [];
1771
+ if (Array.isArray(refs)) return refs;
1772
+ return refs.split(/\s+/).filter(Boolean);
1773
+ }
1774
+ /**
1775
+ * Create a reply to this email with RFC 822 threading
1776
+ */
1777
+ createReply(options) {
1778
+ const reply = new Email({
1779
+ ...this.options,
1780
+ id: void 0,
1781
+ accountId: this.accountId,
1782
+ threadId: this.threadId || this.id || void 0,
1783
+ subject: this.subject.startsWith("Re:") ? this.subject : `Re: ${this.subject}`,
1784
+ fromAddress: "",
1785
+ fromName: "",
1786
+ inReplyToMessageId: this.id || void 0,
1787
+ sendStatus: "draft",
1788
+ isRead: true,
1789
+ isDraft: true,
1790
+ date: null,
1791
+ createdAt: void 0,
1792
+ updatedAt: void 0,
1793
+ // RFC 822 threading
1794
+ inReplyTo: this.messageId,
1795
+ // To: original sender
1796
+ toAddresses: JSON.stringify([
1797
+ { address: this.fromAddress, name: this.fromName }
1798
+ ])
1799
+ });
1800
+ const refs = [...this.getReferences()];
1801
+ if (this.messageId && !refs.includes(this.messageId)) {
1802
+ refs.push(this.messageId);
1803
+ }
1804
+ reply.setHeaders({ references: refs.join(" ") });
1805
+ if (options?.replyAll) {
1806
+ const allRecipients = [
1807
+ ...this.getToAddresses(),
1808
+ ...this.getCcAddresses()
1809
+ ];
1810
+ const seen = /* @__PURE__ */ new Set([this.fromAddress.toLowerCase()]);
1811
+ const ccAddresses = allRecipients.filter((r) => {
1812
+ const addr = r.address.toLowerCase();
1813
+ if (seen.has(addr)) return false;
1814
+ seen.add(addr);
1815
+ return true;
1816
+ });
1817
+ reply.ccAddresses = JSON.stringify(ccAddresses);
1818
+ }
1819
+ reply.body = this.buildQuotedBody();
1820
+ reply.textBody = reply.body;
1821
+ return reply;
1822
+ }
1823
+ /**
1824
+ * Create a forward of this email
1825
+ */
1826
+ createForward() {
1827
+ const forwardBody = this.buildForwardBody();
1828
+ const forward = new Email({
1829
+ ...this.options,
1830
+ id: void 0,
1831
+ accountId: this.accountId,
1832
+ threadId: "",
1833
+ subject: this.subject.startsWith("Fwd:") ? this.subject : `Fwd: ${this.subject}`,
1834
+ toAddresses: "[]",
1835
+ ccAddresses: "[]",
1836
+ bccAddresses: "[]",
1837
+ fromAddress: "",
1838
+ fromName: "",
1839
+ body: forwardBody,
1840
+ textBody: forwardBody,
1841
+ hasAttachments: this.hasAttachments,
1842
+ inReplyToMessageId: "",
1843
+ inReplyTo: "",
1844
+ sendStatus: "draft",
1845
+ isDraft: true,
1846
+ isRead: true,
1847
+ date: null,
1848
+ createdAt: void 0,
1849
+ updatedAt: void 0
1850
+ });
1851
+ return forward;
1852
+ }
1853
+ /**
1854
+ * Build email-specific quoted body for replies
1855
+ */
1856
+ buildQuotedBody() {
1857
+ const dateStr = this.date ? this.date.toLocaleString() : "unknown date";
1858
+ const from = this.fromName ? `${this.fromName} <${this.fromAddress}>` : this.fromAddress;
1859
+ const bodyText = this.textBody || this.body || "";
1860
+ const quotedLines = bodyText.split("\n").map((line) => `> ${line}`).join("\n");
1861
+ return `
1862
+
1863
+ On ${dateStr}, ${from} wrote:
1864
+ ${quotedLines}`;
1865
+ }
1866
+ /**
1867
+ * Build forwarded message body
1868
+ */
1869
+ buildForwardBody() {
1870
+ const dateStr = this.date ? this.date.toLocaleString() : "unknown date";
1871
+ const from = this.fromName ? `${this.fromName} <${this.fromAddress}>` : this.fromAddress;
1872
+ const toStr = this.getToAddresses().map((r) => r.name ? `${r.name} <${r.address}>` : r.address).join(", ");
1873
+ const bodyText = this.textBody || this.body || "";
1874
+ return [
1875
+ "",
1876
+ "",
1877
+ "---------- Forwarded message ----------",
1878
+ `From: ${from}`,
1879
+ `Date: ${dateStr}`,
1880
+ `Subject: ${this.subject}`,
1881
+ `To: ${toStr}`,
1882
+ "",
1883
+ bodyText
1884
+ ].join("\n");
1885
+ }
1886
+ };
1887
+ Email = __decorateClass$7([
1888
+ smrt({
1889
+ tableStrategy: "sti",
1890
+ api: { include: ["list", "get", "create", "update", "delete"] },
1891
+ mcp: { include: ["list", "get"] },
1892
+ cli: true
1893
+ })
1894
+ ], Email);
1895
+ const Email$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1896
+ __proto__: null,
1897
+ get Email() {
1898
+ return Email;
1899
+ }
1900
+ }, Symbol.toStringTag, { value: "Module" }));
1901
+ class EmailCollection extends MessageCollection {
1902
+ static _itemClass = Email;
1903
+ /**
1904
+ * Get email by RFC 822 Message-ID
1905
+ */
1906
+ async getByMessageId(accountId, messageId) {
1907
+ const emails = await this.list({
1908
+ where: { accountId, messageId },
1909
+ limit: 1
1910
+ });
1911
+ return emails[0] || null;
1912
+ }
1913
+ /**
1914
+ * Get emails by account
1915
+ */
1916
+ async getByAccount(accountId) {
1917
+ return await this.list({ where: { accountId } });
1918
+ }
1919
+ /**
1920
+ * Get emails by folder
1921
+ */
1922
+ async getByFolder(folderId) {
1923
+ return await this.list({ where: { folderId } });
1924
+ }
1925
+ /**
1926
+ * Get emails by thread
1927
+ */
1928
+ async getByThread(threadId) {
1929
+ return await this.list({ where: { threadId } });
1930
+ }
1931
+ /**
1932
+ * Get unread emails
1933
+ */
1934
+ async getUnread(accountId) {
1935
+ const where = { isRead: false };
1936
+ if (accountId) {
1937
+ where.accountId = accountId;
1938
+ }
1939
+ return await this.list({ where });
1940
+ }
1941
+ /**
1942
+ * Get flagged emails
1943
+ */
1944
+ async getFlagged(accountId) {
1945
+ const where = { isFlagged: true };
1946
+ if (accountId) {
1947
+ where.accountId = accountId;
1948
+ }
1949
+ return await this.list({ where });
1950
+ }
1951
+ /**
1952
+ * Get emails with attachments
1953
+ */
1954
+ async getWithAttachments(accountId) {
1955
+ const where = { hasAttachments: true };
1956
+ if (accountId) {
1957
+ where.accountId = accountId;
1958
+ }
1959
+ return await this.list({ where });
1960
+ }
1961
+ /**
1962
+ * Get recent emails
1963
+ */
1964
+ async getRecent(limit = 20, accountId) {
1965
+ const allEmails = await this.list({
1966
+ where: accountId ? { accountId } : void 0
1967
+ });
1968
+ return allEmails.sort((a, b) => {
1969
+ const dateA = a.date?.getTime() || 0;
1970
+ const dateB = b.date?.getTime() || 0;
1971
+ return dateB - dateA;
1972
+ }).slice(0, limit);
1973
+ }
1974
+ /**
1975
+ * Count emails in a folder
1976
+ */
1977
+ async countByFolder(folderId) {
1978
+ const emails = await this.list({ where: { folderId } });
1979
+ return emails.length;
1980
+ }
1981
+ /**
1982
+ * Count unread emails in a folder
1983
+ */
1984
+ async countUnreadByFolder(folderId) {
1985
+ const emails = await this.list({ where: { folderId, isRead: false } });
1986
+ return emails.length;
1987
+ }
1988
+ /**
1989
+ * Count unread emails for an account
1990
+ */
1991
+ async countUnreadByAccount(accountId) {
1992
+ const emails = await this.list({ where: { accountId, isRead: false } });
1993
+ return emails.length;
1994
+ }
1995
+ /**
1996
+ * Search emails with email-specific filters.
1997
+ * Alias: `search()` for backward compatibility.
1998
+ */
1999
+ async search(query, filters) {
2000
+ return this.searchEmails(query, filters);
2001
+ }
2002
+ /**
2003
+ * Search emails with email-specific filters
2004
+ */
2005
+ async searchEmails(query, filters) {
2006
+ let emails = await this.list({});
2007
+ if (query) {
2008
+ const lowerQuery = query.toLowerCase();
2009
+ emails = emails.filter(
2010
+ (e) => e.subject?.toLowerCase().includes(lowerQuery) || e.textBody?.toLowerCase().includes(lowerQuery) || e.fromAddress?.toLowerCase().includes(lowerQuery) || e.fromName?.toLowerCase().includes(lowerQuery)
2011
+ );
2012
+ }
2013
+ if (filters) {
2014
+ if (filters.accountId) {
2015
+ emails = emails.filter((e) => e.accountId === filters.accountId);
2016
+ }
2017
+ if (filters.folderId) {
2018
+ emails = emails.filter((e) => e.folderId === filters.folderId);
2019
+ }
2020
+ if (filters.threadId) {
2021
+ emails = emails.filter((e) => e.threadId === filters.threadId);
2022
+ }
2023
+ if (filters.from) {
2024
+ const fromLower = filters.from.toLowerCase();
2025
+ emails = emails.filter(
2026
+ (e) => e.fromAddress?.toLowerCase().includes(fromLower) || e.fromName?.toLowerCase().includes(fromLower)
2027
+ );
2028
+ }
2029
+ if (filters.to) {
2030
+ const toLower = filters.to.toLowerCase();
2031
+ emails = emails.filter(
2032
+ (e) => e.toAddresses?.toLowerCase().includes(toLower)
2033
+ );
2034
+ }
2035
+ if (filters.subject) {
2036
+ const subjectLower = filters.subject.toLowerCase();
2037
+ emails = emails.filter(
2038
+ (e) => e.subject?.toLowerCase().includes(subjectLower)
2039
+ );
2040
+ }
2041
+ if (filters.isRead !== void 0) {
2042
+ emails = emails.filter((e) => e.isRead === filters.isRead);
2043
+ }
2044
+ if (filters.isFlagged !== void 0) {
2045
+ emails = emails.filter((e) => e.isFlagged === filters.isFlagged);
2046
+ }
2047
+ if (filters.hasAttachments !== void 0) {
2048
+ emails = emails.filter(
2049
+ (e) => e.hasAttachments === filters.hasAttachments
2050
+ );
2051
+ }
2052
+ if (filters.sincDate) {
2053
+ emails = emails.filter(
2054
+ (e) => e.date && e.date >= filters.sincDate
2055
+ );
2056
+ }
2057
+ if (filters.beforeDate) {
2058
+ emails = emails.filter(
2059
+ (e) => e.date && e.date < filters.beforeDate
2060
+ );
2061
+ }
2062
+ }
2063
+ return emails;
2064
+ }
2065
+ /**
2066
+ * Mark all emails in a folder as read
2067
+ */
2068
+ async markFolderRead(folderId) {
2069
+ const emails = await this.getUnread();
2070
+ const folderEmails = emails.filter((e) => e.folderId === folderId);
2071
+ for (const email of folderEmails) {
2072
+ await email.markRead();
2073
+ }
2074
+ }
2075
+ /**
2076
+ * Delete emails by folder
2077
+ */
2078
+ async deleteByFolder(folderId) {
2079
+ const emails = await this.getByFolder(folderId);
2080
+ let count = 0;
2081
+ for (const email of emails) {
2082
+ await email.delete();
2083
+ count++;
2084
+ }
2085
+ return count;
2086
+ }
2087
+ /**
2088
+ * Get email statistics for an account
2089
+ */
2090
+ async getAccountStats(accountId) {
2091
+ const emails = await this.getByAccount(accountId);
2092
+ return {
2093
+ total: emails.length,
2094
+ unread: emails.filter((e) => !e.isRead).length,
2095
+ flagged: emails.filter((e) => e.isFlagged).length,
2096
+ withAttachments: emails.filter((e) => e.hasAttachments).length,
2097
+ byType: { Email: emails.length }
2098
+ };
2099
+ }
2100
+ // ─────────────────────────────────────────────────────────────────────────
2101
+ // Tenant Helper Methods
2102
+ // ─────────────────────────────────────────────────────────────────────────
2103
+ async findByTenant(tenantId2) {
2104
+ return this.list({ where: { tenantId: tenantId2 } });
2105
+ }
2106
+ async findGlobal() {
2107
+ return this.list({ where: { tenantId: null } });
2108
+ }
2109
+ async findWithGlobals(tenantId2) {
2110
+ return this.query(
2111
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
2112
+ [tenantId2]
2113
+ );
2114
+ }
2115
+ }
2116
+ const EmailCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2117
+ __proto__: null,
2118
+ EmailCollection
2119
+ }, Symbol.toStringTag, { value: "Module" }));
2120
+ var __defProp = Object.defineProperty;
2121
+ var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
2122
+ var __decorateClass$6 = (decorators, target, key, kind) => {
2123
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
2124
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2125
+ if (decorator = decorators[i])
2126
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
2127
+ if (kind && result) __defProp(target, key, result);
2128
+ return result;
2129
+ };
2130
+ let EmailFolder = class extends SmrtObject {
2131
+ tenantId = null;
2132
+ accountId = "";
2133
+ name = "";
2134
+ path = "";
2135
+ delimiter = "/";
2136
+ specialUse = "";
2137
+ // '\\Inbox', '\\Sent', '\\Drafts', etc.
2138
+ messageCount = 0;
2139
+ unreadCount = 0;
2140
+ subscribed = true;
2141
+ // Timestamps
2142
+ createdAt = /* @__PURE__ */ new Date();
2143
+ updatedAt = /* @__PURE__ */ new Date();
2144
+ constructor(options = {}) {
2145
+ super(options);
2146
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
2147
+ if (options.accountId !== void 0) this.accountId = options.accountId;
2148
+ if (options.name !== void 0) this.name = options.name;
2149
+ if (options.path !== void 0) this.path = options.path;
2150
+ if (options.delimiter !== void 0) this.delimiter = options.delimiter;
2151
+ if (options.specialUse !== void 0) this.specialUse = options.specialUse;
2152
+ if (options.messageCount !== void 0)
2153
+ this.messageCount = options.messageCount;
2154
+ if (options.unreadCount !== void 0)
2155
+ this.unreadCount = options.unreadCount;
2156
+ if (options.subscribed !== void 0) this.subscribed = options.subscribed;
2157
+ if (options.createdAt) this.createdAt = options.createdAt;
2158
+ if (options.updatedAt) this.updatedAt = options.updatedAt;
2159
+ }
2160
+ /**
2161
+ * Get the email account
2162
+ */
2163
+ async getAccount() {
2164
+ if (!this.accountId) return null;
2165
+ const { EmailAccountCollection: EmailAccountCollection2 } = await Promise.resolve().then(() => EmailAccountCollection$1);
2166
+ const collection = await EmailAccountCollection2.create(
2167
+ this.options
2168
+ );
2169
+ return await collection.get({ id: this.accountId });
2170
+ }
2171
+ /**
2172
+ * Get all emails in this folder
2173
+ */
2174
+ async getEmails(limit) {
2175
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
2176
+ const collection = await EmailCollection2.create(this.options);
2177
+ const options = { where: { folderId: this.id } };
2178
+ if (limit) {
2179
+ options.limit = limit;
2180
+ }
2181
+ return await collection.list(options);
2182
+ }
2183
+ /**
2184
+ * Get unread emails in this folder
2185
+ */
2186
+ async getUnreadEmails(limit) {
2187
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
2188
+ const collection = await EmailCollection2.create(this.options);
2189
+ const options = {
2190
+ where: { folderId: this.id, isRead: false }
2191
+ };
2192
+ if (limit) {
2193
+ options.limit = limit;
2194
+ }
2195
+ return await collection.list(options);
2196
+ }
2197
+ /**
2198
+ * Update message counts from database
2199
+ */
2200
+ async refreshCounts() {
2201
+ const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
2202
+ const collection = await EmailCollection2.create(this.options);
2203
+ this.messageCount = await collection.countByFolder(this.id);
2204
+ this.unreadCount = await collection.countUnreadByFolder(this.id);
2205
+ this.updatedAt = /* @__PURE__ */ new Date();
2206
+ await this.save();
2207
+ }
2208
+ /**
2209
+ * Check if this is the inbox folder
2210
+ */
2211
+ isInbox() {
2212
+ return this.specialUse === "\\Inbox" || this.path.toLowerCase() === "inbox";
2213
+ }
2214
+ /**
2215
+ * Check if this is the sent folder
2216
+ */
2217
+ isSent() {
2218
+ return this.specialUse === "\\Sent" || this.path.toLowerCase() === "sent";
2219
+ }
2220
+ /**
2221
+ * Check if this is the drafts folder
2222
+ */
2223
+ isDrafts() {
2224
+ return this.specialUse === "\\Drafts" || this.path.toLowerCase() === "drafts";
2225
+ }
2226
+ /**
2227
+ * Check if this is the trash folder
2228
+ */
2229
+ isTrash() {
2230
+ return this.specialUse === "\\Trash" || this.path.toLowerCase() === "trash";
2231
+ }
2232
+ /**
2233
+ * Check if this is the spam/junk folder
2234
+ */
2235
+ isSpam() {
2236
+ return this.specialUse === "\\Junk" || this.path.toLowerCase() === "spam" || this.path.toLowerCase() === "junk";
2237
+ }
2238
+ /**
2239
+ * Check if this is a system folder
2240
+ */
2241
+ isSystemFolder() {
2242
+ return !!this.specialUse;
2243
+ }
2244
+ /**
2245
+ * Subscribe to folder
2246
+ */
2247
+ async subscribe() {
2248
+ this.subscribed = true;
2249
+ this.updatedAt = /* @__PURE__ */ new Date();
2250
+ await this.save();
2251
+ }
2252
+ /**
2253
+ * Unsubscribe from folder
2254
+ */
2255
+ async unsubscribe() {
2256
+ this.subscribed = false;
2257
+ this.updatedAt = /* @__PURE__ */ new Date();
2258
+ await this.save();
2259
+ }
2260
+ };
2261
+ __decorateClass$6([
2262
+ tenantId({ nullable: true })
2263
+ ], EmailFolder.prototype, "tenantId", 2);
2264
+ __decorateClass$6([
2265
+ foreignKey("Account")
2266
+ ], EmailFolder.prototype, "accountId", 2);
2267
+ EmailFolder = __decorateClass$6([
2268
+ TenantScoped({ mode: "optional" }),
2269
+ smrt({
2270
+ api: { include: ["list", "get", "create", "update", "delete"] },
2271
+ mcp: { include: ["list", "get"] },
2272
+ cli: true
2273
+ })
2274
+ ], EmailFolder);
2275
+ const EmailFolder$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2276
+ __proto__: null,
2277
+ get EmailFolder() {
2278
+ return EmailFolder;
2279
+ }
2280
+ }, Symbol.toStringTag, { value: "Module" }));
2281
+ class EmailFolderCollection extends SmrtCollection {
2282
+ static _itemClass = EmailFolder;
2283
+ /**
2284
+ * Get folder by path for an account
2285
+ */
2286
+ async getByPath(accountId, path) {
2287
+ const folders = await this.list({ where: { accountId, path } });
2288
+ return folders[0] || null;
2289
+ }
2290
+ /**
2291
+ * Get folders by account
2292
+ */
2293
+ async getByAccount(accountId) {
2294
+ return await this.list({ where: { accountId } });
2295
+ }
2296
+ /**
2297
+ * Get inbox folder for an account
2298
+ */
2299
+ async getInbox(accountId) {
2300
+ const folders = await this.getByAccount(accountId);
2301
+ return folders.find((f) => f.isInbox()) || null;
2302
+ }
2303
+ /**
2304
+ * Get sent folder for an account
2305
+ */
2306
+ async getSent(accountId) {
2307
+ const folders = await this.getByAccount(accountId);
2308
+ return folders.find((f) => f.isSent()) || null;
2309
+ }
2310
+ /**
2311
+ * Get drafts folder for an account
2312
+ */
2313
+ async getDrafts(accountId) {
2314
+ const folders = await this.getByAccount(accountId);
2315
+ return folders.find((f) => f.isDrafts()) || null;
2316
+ }
2317
+ /**
2318
+ * Get trash folder for an account
2319
+ */
2320
+ async getTrash(accountId) {
2321
+ const folders = await this.getByAccount(accountId);
2322
+ return folders.find((f) => f.isTrash()) || null;
2323
+ }
2324
+ /**
2325
+ * Get spam folder for an account
2326
+ */
2327
+ async getSpam(accountId) {
2328
+ const folders = await this.getByAccount(accountId);
2329
+ return folders.find((f) => f.isSpam()) || null;
2330
+ }
2331
+ /**
2332
+ * Get system folders for an account
2333
+ */
2334
+ async getSystemFolders(accountId) {
2335
+ const folders = await this.getByAccount(accountId);
2336
+ return folders.filter((f) => f.isSystemFolder());
2337
+ }
2338
+ /**
2339
+ * Get user-created folders for an account
2340
+ */
2341
+ async getUserFolders(accountId) {
2342
+ const folders = await this.getByAccount(accountId);
2343
+ return folders.filter((f) => !f.isSystemFolder());
2344
+ }
2345
+ /**
2346
+ * Get subscribed folders
2347
+ */
2348
+ async getSubscribed(accountId) {
2349
+ const where = { subscribed: true };
2350
+ if (accountId) {
2351
+ where.accountId = accountId;
2352
+ }
2353
+ return await this.list({ where });
2354
+ }
2355
+ /**
2356
+ * Get folders with unread messages
2357
+ */
2358
+ async getWithUnread(accountId) {
2359
+ const folders = accountId ? await this.getByAccount(accountId) : await this.list({});
2360
+ return folders.filter((f) => f.unreadCount > 0);
2361
+ }
2362
+ /**
2363
+ * Search folders with filters
2364
+ */
2365
+ async search(query, filters) {
2366
+ let folders = await this.list({});
2367
+ if (query) {
2368
+ const lowerQuery = query.toLowerCase();
2369
+ folders = folders.filter(
2370
+ (f) => f.name?.toLowerCase().includes(lowerQuery) || f.path?.toLowerCase().includes(lowerQuery)
2371
+ );
2372
+ }
2373
+ if (filters) {
2374
+ if (filters.accountId) {
2375
+ folders = folders.filter((f) => f.accountId === filters.accountId);
2376
+ }
2377
+ if (filters.specialUse) {
2378
+ folders = folders.filter((f) => f.specialUse === filters.specialUse);
2379
+ }
2380
+ if (filters.subscribed !== void 0) {
2381
+ folders = folders.filter((f) => f.subscribed === filters.subscribed);
2382
+ }
2383
+ }
2384
+ return folders;
2385
+ }
2386
+ /**
2387
+ * Refresh counts for all folders in an account
2388
+ */
2389
+ async refreshAllCounts(accountId) {
2390
+ const folders = await this.getByAccount(accountId);
2391
+ for (const folder of folders) {
2392
+ await folder.refreshCounts();
2393
+ }
2394
+ }
2395
+ /**
2396
+ * Get folder statistics for an account
2397
+ */
2398
+ async getAccountStats(accountId) {
2399
+ const folders = await this.getByAccount(accountId);
2400
+ return {
2401
+ totalFolders: folders.length,
2402
+ totalMessages: folders.reduce((sum, f) => sum + f.messageCount, 0),
2403
+ totalUnread: folders.reduce((sum, f) => sum + f.unreadCount, 0),
2404
+ systemFolders: folders.filter((f) => f.isSystemFolder()).length,
2405
+ userFolders: folders.filter((f) => !f.isSystemFolder()).length
2406
+ };
2407
+ }
2408
+ /**
2409
+ * Create standard system folders for an account
2410
+ */
2411
+ async createSystemFolders(accountId) {
2412
+ const standardFolders = [
2413
+ { name: "INBOX", path: "INBOX", specialUse: "\\Inbox" },
2414
+ { name: "Sent", path: "Sent", specialUse: "\\Sent" },
2415
+ { name: "Drafts", path: "Drafts", specialUse: "\\Drafts" },
2416
+ { name: "Trash", path: "Trash", specialUse: "\\Trash" },
2417
+ { name: "Spam", path: "Spam", specialUse: "\\Junk" }
2418
+ ];
2419
+ for (const folderData of standardFolders) {
2420
+ const existing = await this.getByPath(accountId, folderData.path);
2421
+ if (!existing) {
2422
+ const folder = await this.create({
2423
+ accountId,
2424
+ ...folderData
2425
+ });
2426
+ await folder.save();
2427
+ }
2428
+ }
2429
+ }
2430
+ // ─────────────────────────────────────────────────────────────────────────
2431
+ // Tenant Helper Methods
2432
+ // ─────────────────────────────────────────────────────────────────────────
2433
+ /**
2434
+ * Find all email folders belonging to a specific tenant
2435
+ */
2436
+ async findByTenant(tenantId2) {
2437
+ return this.list({ where: { tenantId: tenantId2 } });
2438
+ }
2439
+ /**
2440
+ * Find all global email folders (no tenant)
2441
+ */
2442
+ async findGlobal() {
2443
+ return this.list({ where: { tenantId: null } });
2444
+ }
2445
+ /**
2446
+ * Find email folders for a tenant including global folders
2447
+ */
2448
+ async findWithGlobals(tenantId2) {
2449
+ return this.query(
2450
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
2451
+ [tenantId2]
2452
+ );
2453
+ }
2454
+ }
2455
+ const EmailFolderCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2456
+ __proto__: null,
2457
+ EmailFolderCollection
2458
+ }, Symbol.toStringTag, { value: "Module" }));
2459
+ var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
2460
+ var __decorateClass$5 = (decorators, target, key, kind) => {
2461
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
2462
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2463
+ if (decorator = decorators[i])
2464
+ result = decorator(result) || result;
2465
+ return result;
2466
+ };
2467
+ let Blacklist = class extends SmrtObject {
2468
+ pattern = "";
2469
+ type = "email";
2470
+ reason = "";
2471
+ autoArchive = true;
2472
+ constructor(options = {}) {
2473
+ super(options);
2474
+ if (options.pattern !== void 0) {
2475
+ this.pattern = options.pattern;
2476
+ }
2477
+ if (options.type !== void 0) {
2478
+ this.type = options.type;
2479
+ }
2480
+ }
2481
+ /**
2482
+ * Check if an email address matches this blacklist entry
2483
+ */
2484
+ matches(email) {
2485
+ const normalizedEmail = email.toLowerCase().trim();
2486
+ switch (this.type) {
2487
+ case "email":
2488
+ return normalizedEmail === this.pattern.toLowerCase().trim();
2489
+ case "domain": {
2490
+ const domain = normalizedEmail.split("@")[1];
2491
+ return domain === this.pattern.toLowerCase().trim();
2492
+ }
2493
+ case "regex": {
2494
+ const pattern = this.pattern.trim();
2495
+ if (!pattern) {
2496
+ return false;
2497
+ }
2498
+ try {
2499
+ const regex = new RegExp(pattern, "i");
2500
+ return regex.test(normalizedEmail);
2501
+ } catch {
2502
+ return false;
2503
+ }
2504
+ }
2505
+ default:
2506
+ return false;
2507
+ }
2508
+ }
2509
+ };
2510
+ Blacklist = __decorateClass$5([
2511
+ smrt({
2512
+ api: { include: ["list", "get", "create", "update", "delete"] },
2513
+ cli: true,
2514
+ tenantScoped: true
2515
+ })
2516
+ ], Blacklist);
2517
+ class BlacklistCollection extends SmrtCollection {
2518
+ static _itemClass = Blacklist;
2519
+ /**
2520
+ * Check if an email is blacklisted
2521
+ */
2522
+ async isBlacklisted(email) {
2523
+ const entries = await this.list({});
2524
+ for (const entry of entries) {
2525
+ if (entry.matches(email)) {
2526
+ return true;
2527
+ }
2528
+ }
2529
+ return false;
2530
+ }
2531
+ /**
2532
+ * Get the matching blacklist entry for an email
2533
+ */
2534
+ async getMatchingEntry(email) {
2535
+ const entries = await this.list({});
2536
+ for (const entry of entries) {
2537
+ if (entry.matches(email)) {
2538
+ return entry;
2539
+ }
2540
+ }
2541
+ return null;
2542
+ }
2543
+ }
2544
+ var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
2545
+ var __decorateClass$4 = (decorators, target, key, kind) => {
2546
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
2547
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2548
+ if (decorator = decorators[i])
2549
+ result = decorator(result) || result;
2550
+ return result;
2551
+ };
2552
+ let Whitelist = class extends SmrtObject {
2553
+ pattern = "";
2554
+ type = "email";
2555
+ category = null;
2556
+ description = "";
2557
+ constructor(options = {}) {
2558
+ super(options);
2559
+ if (options.pattern !== void 0) {
2560
+ this.pattern = options.pattern;
2561
+ }
2562
+ if (options.type !== void 0) {
2563
+ this.type = options.type;
2564
+ }
2565
+ }
2566
+ /**
2567
+ * Check if an email address matches this whitelist entry
2568
+ */
2569
+ matches(email) {
2570
+ const normalizedEmail = email.toLowerCase().trim();
2571
+ switch (this.type) {
2572
+ case "email":
2573
+ return normalizedEmail === this.pattern.toLowerCase().trim();
2574
+ case "domain": {
2575
+ const domain = normalizedEmail.split("@")[1];
2576
+ return domain === this.pattern.toLowerCase().trim();
2577
+ }
2578
+ case "regex": {
2579
+ const pattern = this.pattern.trim();
2580
+ if (!pattern) {
2581
+ return false;
2582
+ }
2583
+ try {
2584
+ const regex = new RegExp(pattern, "i");
2585
+ return regex.test(normalizedEmail);
2586
+ } catch {
2587
+ return false;
2588
+ }
2589
+ }
2590
+ default:
2591
+ return false;
2592
+ }
2593
+ }
2594
+ };
2595
+ Whitelist = __decorateClass$4([
2596
+ smrt({
2597
+ api: { include: ["list", "get", "create", "update", "delete"] },
2598
+ cli: true,
2599
+ tenantScoped: true
2600
+ })
2601
+ ], Whitelist);
2602
+ class WhitelistCollection extends SmrtCollection {
2603
+ static _itemClass = Whitelist;
2604
+ /**
2605
+ * Check if an email is whitelisted for a specific category
2606
+ */
2607
+ async isWhitelisted(email, category) {
2608
+ const entries = await this.list({});
2609
+ for (const entry of entries) {
2610
+ if (!entry.matches(email)) {
2611
+ continue;
2612
+ }
2613
+ if (!category) {
2614
+ return true;
2615
+ }
2616
+ if (entry.category === category || entry.category === null) {
2617
+ return true;
2618
+ }
2619
+ }
2620
+ return false;
2621
+ }
2622
+ /**
2623
+ * Get the matching whitelist entry for an email
2624
+ */
2625
+ async getMatchingEntry(email) {
2626
+ const entries = await this.list({});
2627
+ for (const entry of entries) {
2628
+ if (entry.matches(email)) {
2629
+ return entry;
2630
+ }
2631
+ }
2632
+ return null;
2633
+ }
2634
+ /**
2635
+ * Get whitelist entries by category
2636
+ */
2637
+ async getByCategory(category) {
2638
+ return await this.list({
2639
+ where: { category }
2640
+ });
2641
+ }
2642
+ }
2643
+ var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
2644
+ var __decorateClass$3 = (decorators, target, key, kind) => {
2645
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
2646
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2647
+ if (decorator = decorators[i])
2648
+ result = decorator(result) || result;
2649
+ return result;
2650
+ };
2651
+ let SlackAccount = class extends Account {
2652
+ workspaceId = "";
2653
+ workspaceName = "";
2654
+ botUserId = "";
2655
+ constructor(options = {}) {
2656
+ super(options);
2657
+ if (options.workspaceId !== void 0)
2658
+ this.workspaceId = options.workspaceId;
2659
+ if (options.workspaceName !== void 0)
2660
+ this.workspaceName = options.workspaceName;
2661
+ if (options.botUserId !== void 0) this.botUserId = options.botUserId;
2662
+ if (!this.providerType) this.providerType = "slack";
2663
+ }
2664
+ /**
2665
+ * Create a sender for this Slack account
2666
+ */
2667
+ async createSender() {
2668
+ const { SlackSender: SlackSender2 } = await Promise.resolve().then(() => SlackSender$1);
2669
+ return new SlackSender2(this);
2670
+ }
2671
+ };
2672
+ SlackAccount = __decorateClass$3([
2673
+ smrt({
2674
+ tableStrategy: "sti",
2675
+ api: { include: ["list", "get", "create", "update", "delete"] },
2676
+ mcp: { include: ["list", "get"] },
2677
+ cli: true
2678
+ })
2679
+ ], SlackAccount);
2680
+ var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
2681
+ var __decorateClass$2 = (decorators, target, key, kind) => {
2682
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
2683
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2684
+ if (decorator = decorators[i])
2685
+ result = decorator(result) || result;
2686
+ return result;
2687
+ };
2688
+ let SlackMessage = class extends Message {
2689
+ channelId = "";
2690
+ channelName = "";
2691
+ slackTs = "";
2692
+ // Slack message timestamp (unique ID)
2693
+ slackThreadTs = "";
2694
+ // Thread parent timestamp
2695
+ reactions = "";
2696
+ // JSON array of {name, count, users[]}
2697
+ isEdited = false;
2698
+ messageType = "";
2699
+ // 'message', 'bot_message', etc.
2700
+ blocks = "";
2701
+ // JSON - Slack Block Kit blocks
2702
+ constructor(options = {}) {
2703
+ super(options);
2704
+ if (options.channelId !== void 0) this.channelId = options.channelId;
2705
+ if (options.channelName !== void 0)
2706
+ this.channelName = options.channelName;
2707
+ if (options.slackTs !== void 0) this.slackTs = options.slackTs;
2708
+ if (options.slackThreadTs !== void 0)
2709
+ this.slackThreadTs = options.slackThreadTs;
2710
+ if (options.reactions !== void 0) this.reactions = options.reactions;
2711
+ if (options.isEdited !== void 0) this.isEdited = options.isEdited;
2712
+ if (options.messageType !== void 0)
2713
+ this.messageType = options.messageType;
2714
+ if (options.blocks !== void 0) this.blocks = options.blocks;
2715
+ }
2716
+ /**
2717
+ * Get reactions as parsed array
2718
+ */
2719
+ getReactions() {
2720
+ if (!this.reactions) return [];
2721
+ try {
2722
+ return JSON.parse(this.reactions);
2723
+ } catch {
2724
+ return [];
2725
+ }
2726
+ }
2727
+ /**
2728
+ * Get blocks as parsed array
2729
+ */
2730
+ getBlocks() {
2731
+ if (!this.blocks) return [];
2732
+ try {
2733
+ return JSON.parse(this.blocks);
2734
+ } catch {
2735
+ return [];
2736
+ }
2737
+ }
2738
+ /**
2739
+ * Check if message is in a thread
2740
+ */
2741
+ isInThread() {
2742
+ return !!this.slackThreadTs && this.slackThreadTs !== this.slackTs;
2743
+ }
2744
+ /**
2745
+ * Get total reaction count
2746
+ */
2747
+ getTotalReactions() {
2748
+ return this.getReactions().reduce((sum, r) => sum + r.count, 0);
2749
+ }
2750
+ };
2751
+ SlackMessage = __decorateClass$2([
2752
+ smrt({
2753
+ tableStrategy: "sti",
2754
+ api: { include: ["list", "get", "create"] },
2755
+ mcp: { include: ["list", "get"] },
2756
+ cli: true
2757
+ })
2758
+ ], SlackMessage);
2759
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
2760
+ var __decorateClass$1 = (decorators, target, key, kind) => {
2761
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
2762
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2763
+ if (decorator = decorators[i])
2764
+ result = decorator(result) || result;
2765
+ return result;
2766
+ };
2767
+ let Tweet = class extends Message {
2768
+ tweetId = "";
2769
+ retweetCount = 0;
2770
+ likeCount = 0;
2771
+ replyCount = 0;
2772
+ isRetweet = false;
2773
+ isReply = false;
2774
+ mediaUrls = "";
2775
+ // JSON array
2776
+ hashtags = "";
2777
+ // JSON array
2778
+ mentions = "";
2779
+ // JSON array
2780
+ constructor(options = {}) {
2781
+ super(options);
2782
+ if (options.tweetId !== void 0) this.tweetId = options.tweetId;
2783
+ if (options.retweetCount !== void 0)
2784
+ this.retweetCount = options.retweetCount;
2785
+ if (options.likeCount !== void 0) this.likeCount = options.likeCount;
2786
+ if (options.replyCount !== void 0) this.replyCount = options.replyCount;
2787
+ if (options.isRetweet !== void 0) this.isRetweet = options.isRetweet;
2788
+ if (options.isReply !== void 0) this.isReply = options.isReply;
2789
+ if (options.mediaUrls !== void 0) this.mediaUrls = options.mediaUrls;
2790
+ if (options.hashtags !== void 0) this.hashtags = options.hashtags;
2791
+ if (options.mentions !== void 0) this.mentions = options.mentions;
2792
+ }
2793
+ /**
2794
+ * Get media URLs as parsed array
2795
+ */
2796
+ getMediaUrls() {
2797
+ if (!this.mediaUrls) return [];
2798
+ try {
2799
+ return JSON.parse(this.mediaUrls);
2800
+ } catch {
2801
+ return [];
2802
+ }
2803
+ }
2804
+ /**
2805
+ * Get hashtags as parsed array
2806
+ */
2807
+ getHashtags() {
2808
+ if (!this.hashtags) return [];
2809
+ try {
2810
+ return JSON.parse(this.hashtags);
2811
+ } catch {
2812
+ return [];
2813
+ }
2814
+ }
2815
+ /**
2816
+ * Get mentions as parsed array
2817
+ */
2818
+ getMentions() {
2819
+ if (!this.mentions) return [];
2820
+ try {
2821
+ return JSON.parse(this.mentions);
2822
+ } catch {
2823
+ return [];
2824
+ }
2825
+ }
2826
+ /**
2827
+ * Get total engagement count
2828
+ */
2829
+ getEngagement() {
2830
+ return this.retweetCount + this.likeCount + this.replyCount;
2831
+ }
2832
+ };
2833
+ Tweet = __decorateClass$1([
2834
+ smrt({
2835
+ tableStrategy: "sti",
2836
+ api: { include: ["list", "get", "create"] },
2837
+ mcp: { include: ["list", "get"] },
2838
+ cli: true
2839
+ })
2840
+ ], Tweet);
2841
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
2842
+ var __decorateClass = (decorators, target, key, kind) => {
2843
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
2844
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2845
+ if (decorator = decorators[i])
2846
+ result = decorator(result) || result;
2847
+ return result;
2848
+ };
2849
+ let TwitterAccount = class extends Account {
2850
+ handle = "";
2851
+ twitterUserId = "";
2852
+ constructor(options = {}) {
2853
+ super(options);
2854
+ if (options.handle !== void 0) this.handle = options.handle;
2855
+ if (options.twitterUserId !== void 0)
2856
+ this.twitterUserId = options.twitterUserId;
2857
+ if (!this.providerType) this.providerType = "twitter";
2858
+ }
2859
+ /**
2860
+ * Create a sender for this Twitter account
2861
+ */
2862
+ async createSender() {
2863
+ const { TweetSender: TweetSender2 } = await Promise.resolve().then(() => TweetSender$1);
2864
+ return new TweetSender2(this);
2865
+ }
2866
+ };
2867
+ TwitterAccount = __decorateClass([
2868
+ smrt({
2869
+ tableStrategy: "sti",
2870
+ api: { include: ["list", "get", "create", "update", "delete"] },
2871
+ mcp: { include: ["list", "get"] },
2872
+ cli: true
2873
+ })
2874
+ ], TwitterAccount);
2875
+ class EmailSender {
2876
+ providerType = "email";
2877
+ client;
2878
+ account;
2879
+ constructor(client, account) {
2880
+ this.client = client;
2881
+ this.account = account;
2882
+ }
2883
+ isReady() {
2884
+ return this.client.isConnected();
2885
+ }
2886
+ async send(message, _options) {
2887
+ const toAddresses = message.getToAddresses();
2888
+ const ccAddresses = message.getCcAddresses();
2889
+ const bccAddresses = message.getBccAddresses();
2890
+ const emailMessage = {
2891
+ from: {
2892
+ name: message.fromName || this.account.name,
2893
+ address: message.fromAddress || this.account.email
2894
+ },
2895
+ to: toAddresses.map((r) => ({
2896
+ name: r.name,
2897
+ address: r.address
2898
+ })),
2899
+ cc: ccAddresses.length > 0 ? ccAddresses.map((r) => ({ name: r.name, address: r.address })) : void 0,
2900
+ bcc: bccAddresses.length > 0 ? bccAddresses.map((r) => ({ name: r.name, address: r.address })) : void 0,
2901
+ subject: message.subject,
2902
+ text: message.textBody || message.body,
2903
+ html: message.htmlBody || void 0,
2904
+ inReplyTo: message.inReplyTo || void 0
2905
+ };
2906
+ try {
2907
+ const result = await this.client.send(emailMessage);
2908
+ return {
2909
+ success: true,
2910
+ providerMessageId: result.messageId,
2911
+ accepted: result.accepted,
2912
+ rejected: result.rejected,
2913
+ providerResponse: { response: result.response },
2914
+ sentAt: /* @__PURE__ */ new Date()
2915
+ };
2916
+ } catch (error) {
2917
+ return {
2918
+ success: false,
2919
+ error: error instanceof Error ? error.message : String(error),
2920
+ sentAt: /* @__PURE__ */ new Date()
2921
+ };
2922
+ }
2923
+ }
2924
+ }
2925
+ const EmailSender$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2926
+ __proto__: null,
2927
+ EmailSender
2928
+ }, Symbol.toStringTag, { value: "Module" }));
2929
+ class SlackSender {
2930
+ providerType = "slack";
2931
+ account;
2932
+ constructor(account) {
2933
+ this.account = account;
2934
+ }
2935
+ isReady() {
2936
+ return this.account.isActive;
2937
+ }
2938
+ async send(message, _options) {
2939
+ const { getMessageClient } = await import("@happyvertical/messages");
2940
+ const credentials = await this.account.getCredentials();
2941
+ if (!credentials?.botToken) {
2942
+ return {
2943
+ success: false,
2944
+ error: "No botToken found in account credentials",
2945
+ sentAt: /* @__PURE__ */ new Date()
2946
+ };
2947
+ }
2948
+ const client = await getMessageClient({
2949
+ type: "slack",
2950
+ botToken: credentials.botToken
2951
+ });
2952
+ try {
2953
+ await client.connect();
2954
+ const result = await client.send(
2955
+ {
2956
+ from: { id: this.account.botUserId, name: this.account.name },
2957
+ channelId: message.channelId,
2958
+ content: message.body
2959
+ },
2960
+ {
2961
+ replyTo: message.slackThreadTs || void 0
2962
+ }
2963
+ );
2964
+ await client.disconnect();
2965
+ return {
2966
+ success: result.success,
2967
+ providerMessageId: result.messageId,
2968
+ providerResponse: result.providerResponse,
2969
+ sentAt: result.timestamp
2970
+ };
2971
+ } catch (error) {
2972
+ return {
2973
+ success: false,
2974
+ error: error instanceof Error ? error.message : String(error),
2975
+ sentAt: /* @__PURE__ */ new Date()
2976
+ };
2977
+ }
2978
+ }
2979
+ }
2980
+ const SlackSender$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2981
+ __proto__: null,
2982
+ SlackSender
2983
+ }, Symbol.toStringTag, { value: "Module" }));
2984
+ class TweetSender {
2985
+ providerType = "twitter";
2986
+ account;
2987
+ constructor(account) {
2988
+ this.account = account;
2989
+ }
2990
+ isReady() {
2991
+ return this.account.isActive;
2992
+ }
2993
+ async send(message, _options) {
2994
+ const { getMessageClient } = await import("@happyvertical/messages");
2995
+ const credentials = await this.account.getCredentials();
2996
+ if (!credentials?.apiKey || !credentials?.apiSecret || !credentials?.accessToken || !credentials?.accessSecret) {
2997
+ return {
2998
+ success: false,
2999
+ error: "Missing Twitter API credentials in account",
3000
+ sentAt: /* @__PURE__ */ new Date()
3001
+ };
3002
+ }
3003
+ const client = await getMessageClient({
3004
+ type: "twitter",
3005
+ apiKey: credentials.apiKey,
3006
+ apiSecret: credentials.apiSecret,
3007
+ accessToken: credentials.accessToken,
3008
+ accessSecret: credentials.accessSecret
3009
+ });
3010
+ try {
3011
+ const sendOptions = {};
3012
+ if (message.isReply && message.inReplyToMessageId) {
3013
+ sendOptions.replyTo = message.inReplyToMessageId;
3014
+ }
3015
+ const result = await client.send(
3016
+ {
3017
+ from: { id: this.account.twitterUserId, name: this.account.handle },
3018
+ content: message.body
3019
+ },
3020
+ sendOptions.replyTo ? { replyTo: sendOptions.replyTo } : void 0
3021
+ );
3022
+ return {
3023
+ success: result.success,
3024
+ providerMessageId: result.messageId,
3025
+ providerResponse: result.providerResponse,
3026
+ sentAt: result.timestamp
3027
+ };
3028
+ } catch (error) {
3029
+ return {
3030
+ success: false,
3031
+ error: error instanceof Error ? error.message : String(error),
3032
+ sentAt: /* @__PURE__ */ new Date()
3033
+ };
3034
+ }
3035
+ }
3036
+ }
3037
+ const TweetSender$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3038
+ __proto__: null,
3039
+ TweetSender
3040
+ }, Symbol.toStringTag, { value: "Module" }));
3041
+ export {
3042
+ Account,
3043
+ AccountCollection,
3044
+ Attachment,
3045
+ AttachmentCollection,
3046
+ Blacklist,
3047
+ BlacklistCollection,
3048
+ Email,
3049
+ EmailAccount,
3050
+ EmailAccountCollection,
3051
+ EmailAttachment,
3052
+ EmailAttachmentCollection,
3053
+ EmailCollection,
3054
+ EmailFolder,
3055
+ EmailFolderCollection,
3056
+ EmailSender,
3057
+ Message,
3058
+ MessageCollection,
3059
+ SlackAccount,
3060
+ SlackMessage,
3061
+ SlackSender,
3062
+ Tweet,
3063
+ TweetSender,
3064
+ TwitterAccount,
3065
+ Whitelist,
3066
+ WhitelistCollection
3067
+ };
3068
+ //# sourceMappingURL=index.js.map