@happyvertical/smrt-messages 0.60.10 → 0.60.11

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 (31) hide show
  1. package/AGENTS.md +10 -0
  2. package/README.md +34 -0
  3. package/agents/notification-outbox.md +48 -2
  4. package/dist/chunks/{AccountCollection-By48bdwt.js → AccountCollection-BGzfQG2I.js} +2 -2
  5. package/dist/chunks/{AccountCollection-By48bdwt.js.map → AccountCollection-BGzfQG2I.js.map} +1 -1
  6. package/dist/chunks/{Email-CMUK3aWU.js → Email-CBIrfFLm.js} +3 -3
  7. package/dist/chunks/{Email-CMUK3aWU.js.map → Email-CBIrfFLm.js.map} +1 -1
  8. package/dist/chunks/{EmailFolder-DLJvqNqI.js → EmailFolder-Cz2JU5Z1.js} +2 -2
  9. package/dist/chunks/{EmailFolder-DLJvqNqI.js.map → EmailFolder-Cz2JU5Z1.js.map} +1 -1
  10. package/dist/chunks/{Message-DXpqot5f.js → Message-DiO_FvAR.js} +4 -4
  11. package/dist/chunks/{Message-DXpqot5f.js.map → Message-DiO_FvAR.js.map} +1 -1
  12. package/dist/chunks/{MessageCollection-CMGaSGBc.js → MessageCollection-uui10uhg.js} +2 -2
  13. package/dist/chunks/{MessageCollection-CMGaSGBc.js.map → MessageCollection-uui10uhg.js.map} +1 -1
  14. package/dist/chunks/__smrt-register__-DyTpUDMS.js +6 -0
  15. package/dist/chunks/__smrt-register__-DyTpUDMS.js.map +1 -0
  16. package/dist/collections/NotificationOptOutCollection.d.ts +6 -0
  17. package/dist/collections/NotificationOptOutCollection.d.ts.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +264 -55
  21. package/dist/index.js.map +1 -1
  22. package/dist/manifest.json +330 -1
  23. package/dist/models/NotificationOptOut.d.ts +44 -0
  24. package/dist/models/NotificationOptOut.d.ts.map +1 -0
  25. package/dist/services/NotificationOptOutService.d.ts +80 -0
  26. package/dist/services/NotificationOptOutService.d.ts.map +1 -0
  27. package/dist/smrt-knowledge.json +104 -10
  28. package/dist/smrt-knowledge.okf.md +4 -2
  29. package/package.json +9 -9
  30. package/dist/chunks/__smrt-register__-CaGLtiEU.js +0 -6
  31. package/dist/chunks/__smrt-register__-CaGLtiEU.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { t as Message } from "./Message-DXpqot5f.js";
2
+ import { t as Message } from "./Message-DiO_FvAR.js";
3
3
  import { SmrtCollection } from "@happyvertical/smrt-core";
4
4
  import { queryGlobal, queryWithGlobals } from "@happyvertical/smrt-tenancy";
5
5
  //#region src/collections/MessageCollection.ts
@@ -166,4 +166,4 @@ var MessageCollection = class extends SmrtCollection {
166
166
  //#endregion
167
167
  export { MessageCollection_exports as n, MessageCollection as t };
168
168
 
169
- //# sourceMappingURL=MessageCollection-CMGaSGBc.js.map
169
+ //# sourceMappingURL=MessageCollection-uui10uhg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageCollection-CMGaSGBc.js","names":[],"sources":["../../src/collections/MessageCollection.ts"],"sourcesContent":["/**\n * MessageCollection - Unified collection for polymorphic message queries\n *\n * Queries the messages table with STI, returning correct subclass instances.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Message } from '../models/Message';\nimport type { MessageSearchFilters } from '../types';\n\nexport class MessageCollection extends SmrtCollection<Message> {\n static readonly _itemClass = Message;\n\n /**\n * Search messages with filters\n */\n async search(\n query: string,\n filters?: MessageSearchFilters,\n ): Promise<Message[]> {\n let messages = await this.list({});\n\n // Filter by query\n if (query) {\n const lowerQuery = query.toLowerCase();\n messages = messages.filter(\n (m) =>\n m.subject?.toLowerCase().includes(lowerQuery) ||\n m.body?.toLowerCase().includes(lowerQuery) ||\n m.fromAddress?.toLowerCase().includes(lowerQuery) ||\n m.fromName?.toLowerCase().includes(lowerQuery),\n );\n }\n\n // Apply filters\n if (filters) {\n if (filters.accountIds && filters.accountIds.length > 0) {\n messages = messages.filter((m) =>\n filters.accountIds?.includes(m.accountId),\n );\n }\n if (filters.messageType) {\n messages = messages.filter((m) => {\n const metaType = (m as { _meta_type?: string })._meta_type || '';\n return metaType.includes(filters.messageType as string);\n });\n }\n if (filters.from) {\n const fromLower = filters.from.toLowerCase();\n messages = messages.filter(\n (m) =>\n m.fromAddress?.toLowerCase().includes(fromLower) ||\n m.fromName?.toLowerCase().includes(fromLower),\n );\n }\n if (filters.to) {\n const toLower = filters.to.toLowerCase();\n messages = messages.filter((m) =>\n m.toAddresses?.toLowerCase().includes(toLower),\n );\n }\n if (filters.isRead !== undefined) {\n messages = messages.filter((m) => m.isRead === filters.isRead);\n }\n if (filters.isFlagged !== undefined) {\n messages = messages.filter((m) => m.isFlagged === filters.isFlagged);\n }\n if (filters.sinceDate) {\n messages = messages.filter(\n (m) => m.date && m.date >= (filters.sinceDate as Date),\n );\n }\n if (filters.beforeDate) {\n messages = messages.filter(\n (m) => m.date && m.date < (filters.beforeDate as Date),\n );\n }\n if (filters.query) {\n const q = filters.query.toLowerCase();\n messages = messages.filter(\n (m) =>\n m.subject?.toLowerCase().includes(q) ||\n m.body?.toLowerCase().includes(q),\n );\n }\n }\n\n return messages;\n }\n\n /**\n * Get messages by multiple accounts\n */\n async getByAccounts(accountIds: string[]): Promise<Message[]> {\n const allMessages = await this.list({});\n return allMessages.filter((m) => accountIds.includes(m.accountId));\n }\n\n /**\n * Get messages by STI type.\n *\n * Accepts either the full discriminator (e.g. \"@happyvertical/smrt-messages:Email\")\n * or the short type name (e.g. \"Email\").\n */\n async getByType(messageType: string): Promise<Message[]> {\n if (messageType.includes(':') || messageType.startsWith('@')) {\n return await this.list({ where: { _meta_type: messageType } });\n }\n const allMessages = await this.list({});\n return allMessages.filter((m) => {\n const metaType = (m as { _meta_type?: string })._meta_type || '';\n return metaType.endsWith(`:${messageType}`);\n });\n }\n\n /**\n * Get unread messages\n */\n async getUnread(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { isRead: false };\n if (accountId) {\n where.accountId = accountId;\n }\n return await this.list({ where });\n }\n\n /**\n * Get flagged messages\n */\n async getFlagged(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { isFlagged: true };\n if (accountId) {\n where.accountId = accountId;\n }\n return await this.list({ where });\n }\n\n /**\n * Get recent messages\n */\n async getRecent(limit = 20, accountId?: string): Promise<Message[]> {\n const allMessages = await this.list({\n where: accountId ? { accountId } : undefined,\n });\n\n return allMessages\n .sort((a, b) => {\n const dateA = a.date?.getTime() || 0;\n const dateB = b.date?.getTime() || 0;\n return dateB - dateA;\n })\n .slice(0, limit);\n }\n\n /**\n * Get messages by thread\n */\n async getByThread(threadId: string): Promise<Message[]> {\n return await this.list({ where: { threadId } });\n }\n\n /**\n * Mark multiple messages as read\n */\n async markAllRead(messageIds: string[]): Promise<void> {\n for (const id of messageIds) {\n const message = await this.get({ id });\n if (message) {\n await message.markRead();\n }\n }\n }\n\n /**\n * Get message statistics for an account\n */\n async getAccountStats(accountId: string): Promise<{\n total: number;\n unread: number;\n flagged: number;\n byType: Record<string, number>;\n }> {\n const messages = await this.list({ where: { accountId } });\n const byType: Record<string, number> = {};\n\n for (const msg of messages) {\n const metaType = (msg as { _meta_type?: string })._meta_type || 'Unknown';\n const shortType = metaType.split(':').pop() || metaType;\n byType[shortType] = (byType[shortType] || 0) + 1;\n }\n\n return {\n total: messages.length,\n unread: messages.filter((m) => !m.isRead).length,\n flagged: messages.filter((m) => m.isFlagged).length,\n byType,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Send / Draft Queries\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Get draft messages\n */\n async getDrafts(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'draft' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get sent messages\n */\n async getSent(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'sent' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get scheduled messages\n */\n async getScheduled(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'scheduled' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get messages that failed to send\n */\n async getFailedSends(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'failed' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get outbox (pending + sending + scheduled)\n */\n async getOutbox(accountId?: string): Promise<Message[]> {\n const allMessages = await this.list({\n where: accountId ? { accountId } : undefined,\n });\n return allMessages.filter(\n (m) =>\n m.sendStatus === 'pending' ||\n m.sendStatus === 'sending' ||\n m.sendStatus === 'scheduled',\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Tenant Helper Methods\n // ─────────────────────────────────────────────────────────────────────────\n\n async findByTenant(tenantId: string): Promise<Message[]> {\n return this.list({ where: { tenantId } });\n }\n\n // Message is the @TenantScoped STI base, so an explicit `tenant_id IS NULL`\n // filter via list() throws and unflagged raw SQL is blocked under an active\n // tenant context (#1596). Route through the raw helpers — no `_meta_type`\n // scope so the base collection still returns ALL message subtypes.\n async findGlobal(): Promise<Message[]> {\n return queryGlobal<Message>(this);\n }\n\n async findWithGlobals(tenantId: string): Promise<Message[]> {\n return queryWithGlobals<Message>(this, tenantId, 'Message.findWithGlobals');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MessageCollection-uui10uhg.js","names":[],"sources":["../../src/collections/MessageCollection.ts"],"sourcesContent":["/**\n * MessageCollection - Unified collection for polymorphic message queries\n *\n * Queries the messages table with STI, returning correct subclass instances.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Message } from '../models/Message';\nimport type { MessageSearchFilters } from '../types';\n\nexport class MessageCollection extends SmrtCollection<Message> {\n static readonly _itemClass = Message;\n\n /**\n * Search messages with filters\n */\n async search(\n query: string,\n filters?: MessageSearchFilters,\n ): Promise<Message[]> {\n let messages = await this.list({});\n\n // Filter by query\n if (query) {\n const lowerQuery = query.toLowerCase();\n messages = messages.filter(\n (m) =>\n m.subject?.toLowerCase().includes(lowerQuery) ||\n m.body?.toLowerCase().includes(lowerQuery) ||\n m.fromAddress?.toLowerCase().includes(lowerQuery) ||\n m.fromName?.toLowerCase().includes(lowerQuery),\n );\n }\n\n // Apply filters\n if (filters) {\n if (filters.accountIds && filters.accountIds.length > 0) {\n messages = messages.filter((m) =>\n filters.accountIds?.includes(m.accountId),\n );\n }\n if (filters.messageType) {\n messages = messages.filter((m) => {\n const metaType = (m as { _meta_type?: string })._meta_type || '';\n return metaType.includes(filters.messageType as string);\n });\n }\n if (filters.from) {\n const fromLower = filters.from.toLowerCase();\n messages = messages.filter(\n (m) =>\n m.fromAddress?.toLowerCase().includes(fromLower) ||\n m.fromName?.toLowerCase().includes(fromLower),\n );\n }\n if (filters.to) {\n const toLower = filters.to.toLowerCase();\n messages = messages.filter((m) =>\n m.toAddresses?.toLowerCase().includes(toLower),\n );\n }\n if (filters.isRead !== undefined) {\n messages = messages.filter((m) => m.isRead === filters.isRead);\n }\n if (filters.isFlagged !== undefined) {\n messages = messages.filter((m) => m.isFlagged === filters.isFlagged);\n }\n if (filters.sinceDate) {\n messages = messages.filter(\n (m) => m.date && m.date >= (filters.sinceDate as Date),\n );\n }\n if (filters.beforeDate) {\n messages = messages.filter(\n (m) => m.date && m.date < (filters.beforeDate as Date),\n );\n }\n if (filters.query) {\n const q = filters.query.toLowerCase();\n messages = messages.filter(\n (m) =>\n m.subject?.toLowerCase().includes(q) ||\n m.body?.toLowerCase().includes(q),\n );\n }\n }\n\n return messages;\n }\n\n /**\n * Get messages by multiple accounts\n */\n async getByAccounts(accountIds: string[]): Promise<Message[]> {\n const allMessages = await this.list({});\n return allMessages.filter((m) => accountIds.includes(m.accountId));\n }\n\n /**\n * Get messages by STI type.\n *\n * Accepts either the full discriminator (e.g. \"@happyvertical/smrt-messages:Email\")\n * or the short type name (e.g. \"Email\").\n */\n async getByType(messageType: string): Promise<Message[]> {\n if (messageType.includes(':') || messageType.startsWith('@')) {\n return await this.list({ where: { _meta_type: messageType } });\n }\n const allMessages = await this.list({});\n return allMessages.filter((m) => {\n const metaType = (m as { _meta_type?: string })._meta_type || '';\n return metaType.endsWith(`:${messageType}`);\n });\n }\n\n /**\n * Get unread messages\n */\n async getUnread(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { isRead: false };\n if (accountId) {\n where.accountId = accountId;\n }\n return await this.list({ where });\n }\n\n /**\n * Get flagged messages\n */\n async getFlagged(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { isFlagged: true };\n if (accountId) {\n where.accountId = accountId;\n }\n return await this.list({ where });\n }\n\n /**\n * Get recent messages\n */\n async getRecent(limit = 20, accountId?: string): Promise<Message[]> {\n const allMessages = await this.list({\n where: accountId ? { accountId } : undefined,\n });\n\n return allMessages\n .sort((a, b) => {\n const dateA = a.date?.getTime() || 0;\n const dateB = b.date?.getTime() || 0;\n return dateB - dateA;\n })\n .slice(0, limit);\n }\n\n /**\n * Get messages by thread\n */\n async getByThread(threadId: string): Promise<Message[]> {\n return await this.list({ where: { threadId } });\n }\n\n /**\n * Mark multiple messages as read\n */\n async markAllRead(messageIds: string[]): Promise<void> {\n for (const id of messageIds) {\n const message = await this.get({ id });\n if (message) {\n await message.markRead();\n }\n }\n }\n\n /**\n * Get message statistics for an account\n */\n async getAccountStats(accountId: string): Promise<{\n total: number;\n unread: number;\n flagged: number;\n byType: Record<string, number>;\n }> {\n const messages = await this.list({ where: { accountId } });\n const byType: Record<string, number> = {};\n\n for (const msg of messages) {\n const metaType = (msg as { _meta_type?: string })._meta_type || 'Unknown';\n const shortType = metaType.split(':').pop() || metaType;\n byType[shortType] = (byType[shortType] || 0) + 1;\n }\n\n return {\n total: messages.length,\n unread: messages.filter((m) => !m.isRead).length,\n flagged: messages.filter((m) => m.isFlagged).length,\n byType,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Send / Draft Queries\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Get draft messages\n */\n async getDrafts(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'draft' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get sent messages\n */\n async getSent(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'sent' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get scheduled messages\n */\n async getScheduled(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'scheduled' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get messages that failed to send\n */\n async getFailedSends(accountId?: string): Promise<Message[]> {\n const where: Record<string, unknown> = { sendStatus: 'failed' };\n if (accountId) where.accountId = accountId;\n return await this.list({ where });\n }\n\n /**\n * Get outbox (pending + sending + scheduled)\n */\n async getOutbox(accountId?: string): Promise<Message[]> {\n const allMessages = await this.list({\n where: accountId ? { accountId } : undefined,\n });\n return allMessages.filter(\n (m) =>\n m.sendStatus === 'pending' ||\n m.sendStatus === 'sending' ||\n m.sendStatus === 'scheduled',\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Tenant Helper Methods\n // ─────────────────────────────────────────────────────────────────────────\n\n async findByTenant(tenantId: string): Promise<Message[]> {\n return this.list({ where: { tenantId } });\n }\n\n // Message is the @TenantScoped STI base, so an explicit `tenant_id IS NULL`\n // filter via list() throws and unflagged raw SQL is blocked under an active\n // tenant context (#1596). Route through the raw helpers — no `_meta_type`\n // scope so the base collection still returns ALL message subtypes.\n async findGlobal(): Promise<Message[]> {\n return queryGlobal<Message>(this);\n }\n\n async findWithGlobals(tenantId: string): Promise<Message[]> {\n return queryWithGlobals<Message>(this, tenantId, 'Message.findWithGlobals');\n }\n}\n"],"mappings":"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"}