@happyvertical/smrt-messages 0.55.3 → 0.55.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/chunks/{AccountCollection-BSWXrQmD.js → AccountCollection-DMQINcFA.js} +2 -2
  3. package/dist/chunks/{AccountCollection-BSWXrQmD.js.map → AccountCollection-DMQINcFA.js.map} +1 -1
  4. package/dist/chunks/{Email-CnRGofys.js → Email-8tcs4wQO.js} +3 -3
  5. package/dist/chunks/{Email-CnRGofys.js.map → Email-8tcs4wQO.js.map} +1 -1
  6. package/dist/chunks/{EmailFolder-aXGEmdf5.js → EmailFolder-CK256GYp.js} +2 -2
  7. package/dist/chunks/{EmailFolder-aXGEmdf5.js.map → EmailFolder-CK256GYp.js.map} +1 -1
  8. package/dist/chunks/{Message-DtY3YWLk.js → Message-BcLnXZOq.js} +4 -4
  9. package/dist/chunks/{Message-DtY3YWLk.js.map → Message-BcLnXZOq.js.map} +1 -1
  10. package/dist/chunks/{MessageCollection-yc460tXd.js → MessageCollection-BzdBp-OQ.js} +2 -2
  11. package/dist/chunks/{MessageCollection-yc460tXd.js.map → MessageCollection-BzdBp-OQ.js.map} +1 -1
  12. package/dist/chunks/{__smrt-register__-DwAFiEyb.js → __smrt-register__-BxV1pHMK.js} +2 -2
  13. package/dist/chunks/{__smrt-register__-DwAFiEyb.js.map → __smrt-register__-BxV1pHMK.js.map} +1 -1
  14. package/dist/chunks/intake-snapshot-B03bDtSW.js +55 -0
  15. package/dist/chunks/intake-snapshot-B03bDtSW.js.map +1 -0
  16. package/dist/index.js +31 -9
  17. package/dist/index.js.map +1 -1
  18. package/dist/intake-snapshot.d.ts +27 -0
  19. package/dist/intake-snapshot.d.ts.map +1 -0
  20. package/dist/manifest.json +29 -1
  21. package/dist/models/EmailAccount.d.ts +10 -0
  22. package/dist/models/EmailAccount.d.ts.map +1 -1
  23. package/dist/smrt-knowledge.json +23 -5
  24. package/package.json +15 -15
@@ -0,0 +1,27 @@
1
+ import type { EmailMessage } from '@happyvertical/email';
2
+ /** Full-message byte snapshot for an already authorized, configured account. */
3
+ export interface EmailIntakeSnapshot {
4
+ accountId: string;
5
+ messageId: string;
6
+ threadId: string;
7
+ inReplyTo: string;
8
+ subject: string;
9
+ text: string;
10
+ html: string;
11
+ date: string;
12
+ attachments: Array<{
13
+ partId: string;
14
+ filename: string;
15
+ mediaType: string;
16
+ bytes: Uint8Array;
17
+ }>;
18
+ }
19
+ /** Safe machine-readable snapshot failure; never contains provider error details. */
20
+ export declare class EmailIntakeSnapshotError extends Error {
21
+ readonly category: 'invalid' | 'limit' | 'unavailable_bytes';
22
+ readonly name = "EmailIntakeSnapshotError";
23
+ constructor(category: 'invalid' | 'limit' | 'unavailable_bytes', message: string);
24
+ }
25
+ /** Copies actual provider bytes; missing bytes fail closed, never read provider-supplied paths. */
26
+ export declare function snapshotEmailForIntake(accountId: string, message: EmailMessage, maxBytes: number): EmailIntakeSnapshot;
27
+ //# sourceMappingURL=intake-snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intake-snapshot.d.ts","sourceRoot":"","sources":["../src/intake-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,KAAK,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC,CAAC;CACJ;AAED,qFAAqF;AACrF,qBAAa,wBAAyB,SAAQ,KAAK;aAG/B,QAAQ,EAAE,SAAS,GAAG,OAAO,GAAG,mBAAmB;IAFrE,SAAkB,IAAI,8BAA8B;gBAElC,QAAQ,EAAE,SAAS,GAAG,OAAO,GAAG,mBAAmB,EACnE,OAAO,EAAE,MAAM;CAIlB;AAED,mGAAmG;AACnG,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,GACf,mBAAmB,CAwDrB"}
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-messages",
5
- "packageVersion": "0.55.3",
5
+ "packageVersion": "0.55.5",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-messages:AccountCollection": {
8
8
  "name": "accountcollection",
@@ -5667,6 +5667,34 @@
5667
5667
  "isStatic": false,
5668
5668
  "isPublic": true
5669
5669
  },
5670
+ "readIntakeMessage": {
5671
+ "name": "readIntakeMessage",
5672
+ "async": true,
5673
+ "parameters": [
5674
+ {
5675
+ "name": "locator",
5676
+ "type": "string",
5677
+ "optional": false
5678
+ },
5679
+ {
5680
+ "name": "maxBytes",
5681
+ "type": "number",
5682
+ "optional": false
5683
+ },
5684
+ {
5685
+ "name": "imapIdentity",
5686
+ "type": "object",
5687
+ "optional": true,
5688
+ "memberTypes": [
5689
+ "string",
5690
+ "number"
5691
+ ]
5692
+ }
5693
+ ],
5694
+ "returnType": "any",
5695
+ "isStatic": false,
5696
+ "isPublic": true
5697
+ },
5670
5698
  "syncFrom": {
5671
5699
  "name": "syncFrom",
5672
5700
  "async": true,
@@ -35,6 +35,16 @@ export declare class EmailAccount extends Account {
35
35
  * '/providers/all') once during startup.
36
36
  */
37
37
  createClient(): Promise<import("@happyvertical/email").EmailClient>;
38
+ /**
39
+ * Read an immutable body/attachment-byte snapshot for an authorized intake host.
40
+ * Does not sync, acknowledge, mark read or advance mailbox checkpoints.
41
+ * Metadata-only providers fail explicitly; provider paths are never dereferenced.
42
+ */
43
+ readIntakeMessage(locator: string, maxBytes: number, imapIdentity?: {
44
+ folder: string;
45
+ uidValidity: number;
46
+ uid: string;
47
+ }): Promise<import("../intake-snapshot.js").EmailIntakeSnapshot>;
38
48
  /**
39
49
  * Sync emails from the email server to the database
40
50
  */
@@ -1 +1 @@
1
- {"version":3,"file":"EmailAccount.d.ts","sourceRoot":"","sources":["../../src/models/EmailAccount.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EAEtB,WAAW,EACX,UAAU,EACX,MAAM,aAAU,CAAC;AAClB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAW,CAAC;AAEpC,qBAMa,YAAa,SAAQ,OAAO;IACvC,KAAK,SAAM;IACX,mBAAmB,SAAM;gBAEb,OAAO,GAAE,mBAAwB;IAW7C;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACY,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAO9D;;;;;;;;;;OAUG;IACG,YAAY;IAwBlB;;OAEG;IACG,QAAQ,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAuL9D;;OAEG;IACG,UAAU;IAShB;;OAEG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM;IAc9B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAOvC;;OAEG;IACY,cAAc,CAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,GAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd,GACL,OAAO,CAAC,IAAI,CAAC;IAOhB;;;;;;;OAOG;IACG,OAAO,CACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAU5D;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;CAcxC"}
1
+ {"version":3,"file":"EmailAccount.d.ts","sourceRoot":"","sources":["../../src/models/EmailAccount.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EAEtB,WAAW,EACX,UAAU,EACX,MAAM,aAAU,CAAC;AAClB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAW,CAAC;AAEpC,qBAMa,YAAa,SAAQ,OAAO;IACvC,KAAK,SAAM;IACX,mBAAmB,SAAM;gBAEb,OAAO,GAAE,mBAAwB;IAW7C;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACY,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAO9D;;;;;;;;;;OAUG;IACG,YAAY;IAwBlB;;;;OAIG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IAkCrE;;OAEG;IACG,QAAQ,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAuL9D;;OAEG;IACG,UAAU;IAShB;;OAEG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM;IAc9B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAOvC;;OAEG;IACY,cAAc,CAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,GAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd,GACL,OAAO,CAAC,IAAI,CAAC;IAOhB;;;;;;;OAOG;IACG,OAAO,CACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAU5D;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;CAcxC"}
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-messages",
6
- "packageVersion": "0.55.3",
6
+ "packageVersion": "0.55.5",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "1229498422b6e3541805d04c9e752a7bd433e6e1ae6809b438b863176064e585",
11
- "packageJson": "f97c01a78059d13b0ca4e91bc6ee9233e45c839a3bdd6e5729d91612c2d721bd",
12
- "agents": "36405a8a9cefd4f831381d3670572dc3489d43fbc1787fb12b70814cc1e59f34"
10
+ "manifest": "5672edddd4dfef9fa73368527a89e1c0ea1c9168c2906a946145456d1b669ce1",
11
+ "packageJson": "92d210f731628d547679b02a07544fc89be7219551fb91a5431e6327faec57d6",
12
+ "agents": "f2bbd8f6d74a68c2ab949153f2051c7a906bd2e4ba547873b3641d0779491c59"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -5814,6 +5814,7 @@
5814
5814
  "getSettings",
5815
5815
  "getUnreadCount",
5816
5816
  "migrateToSecrets",
5817
+ "readIntakeMessage",
5817
5818
  "setConfiguration",
5818
5819
  "setCredentials",
5819
5820
  "setSettings",
@@ -5877,6 +5878,16 @@
5877
5878
  "async": true,
5878
5879
  "returns": "Promise<void>"
5879
5880
  },
5881
+ {
5882
+ "name": "readIntakeMessage",
5883
+ "async": true,
5884
+ "params": [
5885
+ "locator: string",
5886
+ "maxBytes: number",
5887
+ "imapIdentity?: object"
5888
+ ],
5889
+ "returns": "any"
5890
+ },
5880
5891
  {
5881
5892
  "name": "setConfiguration",
5882
5893
  "params": [
@@ -6038,6 +6049,13 @@
6038
6049
  "reason": "not listed in api.include",
6039
6050
  "objectName": "@happyvertical/smrt-messages:EmailAccount"
6040
6051
  },
6052
+ {
6053
+ "kind": "api",
6054
+ "operation": "readIntakeMessage",
6055
+ "code": "not-included",
6056
+ "reason": "not listed in api.include",
6057
+ "objectName": "@happyvertical/smrt-messages:EmailAccount"
6058
+ },
6041
6059
  {
6042
6060
  "kind": "api",
6043
6061
  "operation": "setConfiguration",
@@ -15138,5 +15156,5 @@
15138
15156
  "polymorphicAssociations": 0,
15139
15157
  "uuidColumns": 96
15140
15158
  },
15141
- "agentDoc": "# @happyvertical/smrt-messages\n\nMulti-channel messaging with STI hierarchies for both messages and accounts. Credential encryption via smrt-secrets.\n\n## STI Hierarchies\n\n**Messages** (share `messages` table):\n- `Message` (base): accountId, personaId, endpointId, correlationId, threadId, subject, body, fromAddress, toAddresses (JSON), sendStatus (draft/sending/sent/failed), retryCount\n- `Email`: messageId (RFC 822), inReplyTo, ccAddresses, bccAddresses, htmlBody, textBody, folderId, labels, headers\n- `Tweet`: tweetId, retweetCount, likeCount, mediaUrls, hashtags, mentions\n- `SlackMessage`: channelId, slackTs, slackThreadTs, reactions, blocks\n\n**Accounts** (share `accounts` table):\n- `Account` (base): channelType, providerType, public configuration,\n credentialSecretId, isActive, lastSyncAt, deprecated sensitive settings\n- `EmailAccount`, `TwitterAccount`, `SlackAccount`, `ZulipAccount`,\n `TelegramAccount`: provider-specific identity/sender behavior\n\n## Persona messaging\n\n- `MessagingEndpoint`: tenant-owned, write-only destination JSON with masked\n display helper.\n- `PersonaMessageRoute`: persona → account + endpoint, selected by purpose and\n descending priority.\n- `PersonaMessagingService`: persists and sends through an enabled route.\n- Provider registry: public setup descriptors + optional sender factory. Keep\n channel (`sms`) separate from provider (`twilio`).\n- Permissions: `messages.send`, `messages.manage-routes`,\n `messages.manage-credentials`.\n- `MessagingSettingsService` is the permissioned server boundary;\n its host-supplied `resolvePersonaTenantId` proves cross-package ownership.\n `MessagingSettingsPanel` is the callback-driven Svelte surface.\n- Account, endpoint, and route generated surfaces are read-only. Do not add\n generated mutations around the settings service or make secret fields writable.\n\n## In-app notifications (`UserNotification`)\n\nThe in-app channel: one stored row per person, with read state.\n`user_notifications`: `tenantId` (required), `recipientUserId`\n(`@crossPackageRef` smrt-users:User, uuid), `kind`, `title`, `body`, `href`,\n`severity` (info/success/warning/error), `sourceRef`, `occurredAt`, `readAt`,\n`dismissedAt`. Conflict identity `(tenant_id, recipient_user_id, source_ref)`.\n\n- `UserNotificationService` is the only access path: `notify()` /\n `notifyMany()` (dedupe on `sourceRef`, existing row returned untouched;\n a unique ref is generated when omitted), `listForUser()` / `countUnread()`\n across explicit `tenantIds` (each read under `withTenant`), `markRead()`,\n `dismiss()` (also marks read), `markAllRead()`. Every write filters on the\n recipient, so another user's id matches nothing.\n- Generated REST/MCP/CLI are off (`api`/`mcp`/`cli: false`): a tenant-scoped\n list would expose every member's rows. Hosts expose their own routes that\n pass the signed-in user's id, never a request-chosen one.\n- Hosts decide who is told (e.g. `SocialPost.createdByUserId` for a failed\n post). New table: consumers run `db:migrate`.\n\n## Credential Security\n\nAccount credentials stored via tenant-bound `credentialSecretId` →\nsmrt-secrets. Use `setCredentials()`/`getCredentials()` — never store passwords\ndirectly. Credential descriptor schemas contain field definitions, never values.\n\n## Send Lifecycle\n\n`message.send()`: resolves account → creates provider sender → updates sendStatus. Retry support with `maxRetries` budget.\n\n## Provider entry points (bundle boundary, #1979)\n\nThe package root is provider-neutral: models, collections, services,\npermissions, and the provider registry carry **zero provider-SDK weight**.\nProvider SDK wrappers (`@happyvertical/email`, `@happyvertical/messages`) are\nreachable only through explicit entry points, imported once at server startup:\n\n```ts\nimport '@happyvertical/smrt-messages/providers/email'; // smtp/imap/pop3/gmail\nimport '@happyvertical/smrt-messages/providers/slack';\nimport '@happyvertical/smrt-messages/providers/twitter';\nimport '@happyvertical/smrt-messages/providers/all'; // all of the above\n```\n\n- Without the matching entry, `EmailAccount.createClient()` (send + mailbox\n sync) throws an actionable error and `SlackSender`/`TweetSender` return a\n failure result naming the entry to import. Telegram and Zulip are\n fetch-based and always work from the root.\n- Builtin registry definitions stay metadata-only (setup fields for UIs); the\n entries upgrade them with `createEmailClient`/`createMessageClient`/\n `createSender` hooks. Never import a provider SDK from any module reachable\n from `src/index.ts` — the consumer bundle gate (`packages/bundle-gate`)\n fails CI when one becomes reachable.\n- `Attachment.readContent()` loads `@happyvertical/files` through core's\n `importOptionalDependency` boundary and keeps its null-on-failure contract\n in builds where the SDK is absent.\n\n## Gotchas\n\n- **Manifest before decorators**: every decorated model imports\n `../__smrt-register__.js`, and `package.json#sideEffects` lists\n `./src/__smrt-register__.ts`. Without both, the library build dropped or\n reordered the self-registration, so dist classes registered with only their\n decorated fields and could adopt smrt-ledgers' same-named `Account` (#3098).\n- **`Account` is not unique**: smrt-ledgers also declares `Account` (on\n `ledger_accounts`). Resolve by constructor or qualified name; the\n coexistence tests (`ledger-account-identity-*`, and the PostgreSQL lane\n `pnpm test:postgres`) load both packages in both orders.\n- **STI `_meta_type`**: qualified format `@happyvertical/smrt-messages:Email`\n- **JSON fields**: all address/metadata fields are JSON strings with `getX()`/`setX()` helpers\n- **RFC 822 threading**: Email uses `inReplyTo`/`messageId`/`references` — manual management required\n- **Attachment.messageId**: use this field (not `emailId`); old getter/setter mapped via deprecation wrapper\n- **Optional tenancy**: `@TenantScoped({ mode: 'optional' })` on Message, Account, Attachment\n- **Required tenancy**: endpoints and persona routes are always tenant scoped\n- **Secrets are write-only**: never serialize credentials or endpoint.address to an app\n"
15159
+ "agentDoc": "# @happyvertical/smrt-messages\n\nMulti-channel messaging with STI hierarchies for both messages and accounts. Credential encryption via smrt-secrets.\n\n## STI Hierarchies\n\n**Messages** (share `messages` table):\n- `Message` (base): accountId, personaId, endpointId, correlationId, threadId, subject, body, fromAddress, toAddresses (JSON), sendStatus (draft/sending/sent/failed), retryCount\n- `Email`: messageId (RFC 822), inReplyTo, ccAddresses, bccAddresses, htmlBody, textBody, folderId, labels, headers\n- `Tweet`: tweetId, retweetCount, likeCount, mediaUrls, hashtags, mentions\n- `SlackMessage`: channelId, slackTs, slackThreadTs, reactions, blocks\n\n**Accounts** (share `accounts` table):\n- `Account` (base): channelType, providerType, public configuration,\n credentialSecretId, isActive, lastSyncAt, deprecated sensitive settings\n- `EmailAccount`, `TwitterAccount`, `SlackAccount`, `ZulipAccount`,\n `TelegramAccount`: provider-specific identity/sender behavior\n\n## Persona messaging\n\n- `MessagingEndpoint`: tenant-owned, write-only destination JSON with masked\n display helper.\n- `PersonaMessageRoute`: persona → account + endpoint, selected by purpose and\n descending priority.\n- `PersonaMessagingService`: persists and sends through an enabled route.\n- Provider registry: public setup descriptors + optional sender factory. Keep\n channel (`sms`) separate from provider (`twilio`).\n- Permissions: `messages.send`, `messages.manage-routes`,\n `messages.manage-credentials`.\n- `MessagingSettingsService` is the permissioned server boundary;\n its host-supplied `resolvePersonaTenantId` proves cross-package ownership.\n `MessagingSettingsPanel` is the callback-driven Svelte surface.\n- Account, endpoint, and route generated surfaces are read-only. Do not add\n generated mutations around the settings service or make secret fields writable.\n\n## In-app notifications (`UserNotification`)\n\nThe in-app channel: one stored row per person, with read state.\n`user_notifications`: `tenantId` (required), `recipientUserId`\n(`@crossPackageRef` smrt-users:User, uuid), `kind`, `title`, `body`, `href`,\n`severity` (info/success/warning/error), `sourceRef`, `occurredAt`, `readAt`,\n`dismissedAt`. Conflict identity `(tenant_id, recipient_user_id, source_ref)`.\n\n- `UserNotificationService` is the only access path: `notify()` /\n `notifyMany()` (dedupe on `sourceRef`, existing row returned untouched;\n a unique ref is generated when omitted), `listForUser()` / `countUnread()`\n across explicit `tenantIds` (each read under `withTenant`), `markRead()`,\n `dismiss()` (also marks read), `markAllRead()`. Every write filters on the\n recipient, so another user's id matches nothing.\n- Generated REST/MCP/CLI are off (`api`/`mcp`/`cli: false`): a tenant-scoped\n list would expose every member's rows. Hosts expose their own routes that\n pass the signed-in user's id, never a request-chosen one.\n- Hosts decide who is told (e.g. `SocialPost.createdByUserId` for a failed\n post). New table: consumers run `db:migrate`.\n\n## Credential Security\n\nAccount credentials stored via tenant-bound `credentialSecretId` →\nsmrt-secrets. Use `setCredentials()`/`getCredentials()` — never store passwords\ndirectly. Credential descriptor schemas contain field definitions, never values.\n\n## Send Lifecycle\n\n`message.send()`: resolves account → creates provider sender → updates sendStatus. Retry support with `maxRetries` budget.\n\n## Provider entry points (bundle boundary, #1979)\n\nThe package root is provider-neutral: models, collections, services,\npermissions, and the provider registry carry **zero provider-SDK weight**.\nProvider SDK wrappers (`@happyvertical/email`, `@happyvertical/messages`) are\nreachable only through explicit entry points, imported once at server startup:\n\n```ts\nimport '@happyvertical/smrt-messages/providers/email'; // smtp/imap/pop3/gmail\nimport '@happyvertical/smrt-messages/providers/slack';\nimport '@happyvertical/smrt-messages/providers/twitter';\nimport '@happyvertical/smrt-messages/providers/all'; // all of the above\n```\n\n- Without the matching entry, `EmailAccount.createClient()` (send + mailbox\n sync) throws an actionable error and `SlackSender`/`TweetSender` return a\n failure result naming the entry to import. Telegram and Zulip are\n fetch-based and always work from the root.\n- Builtin registry definitions stay metadata-only (setup fields for UIs); the\n entries upgrade them with `createEmailClient`/`createMessageClient`/\n `createSender` hooks. Never import a provider SDK from any module reachable\n from `src/index.ts` — the consumer bundle gate (`packages/bundle-gate`)\n fails CI when one becomes reachable.\n- `Attachment.readContent()` loads `@happyvertical/files` through core's\n `importOptionalDependency` boundary and keeps its null-on-failure contract\n in builds where the SDK is absent.\n\n## Gotchas\n\n- **Manifest before decorators**: every decorated model imports\n `../__smrt-register__.js`, and `package.json#sideEffects` lists\n `./src/__smrt-register__.ts`. Without both, the library build dropped or\n reordered the self-registration, so dist classes registered with only their\n decorated fields and could adopt smrt-ledgers' same-named `Account` (#3098).\n- **`Account` is not unique**: smrt-ledgers also declares `Account` (on\n `ledger_accounts`). Resolve by constructor or qualified name; the\n coexistence tests (`ledger-account-identity-*`, and the PostgreSQL lane\n `pnpm test:postgres`) load both packages in both orders.\n- **STI `_meta_type`**: qualified format `@happyvertical/smrt-messages:Email`\n- **JSON fields**: all address/metadata fields are JSON strings with `getX()`/`setX()` helpers\n- **RFC 822 threading**: Email uses `inReplyTo`/`messageId`/`references` — manual management required\n- **Attachment.messageId**: use this field (not `emailId`); old getter/setter mapped via deprecation wrapper\n- **Optional tenancy**: `@TenantScoped({ mode: 'optional' })` on Message, Account, Attachment\n- **Required tenancy**: endpoints and persona routes are always tenant scoped\n- **Secrets are write-only**: never serialize credentials or endpoint.address to an app\n\n## Intake byte snapshots\n\n`EmailAccount.readIntakeMessage(providerLookup, maxBytes, imapIdentity?)` is the authorized host seam for\nopt-in ingestion. It copies full-message attachment bytes from the registered\nemail provider and fails when only attachment metadata is available; it never\nreads provider-supplied paths or advances sync checkpoints. Keep this path\nprovider-neutral and exclude arbitrary headers/credentials from its snapshot.\nIMAP lookup must include folder/UIDVALIDITY/UID verification; sender Message-ID\nis a lookup hint, never the durable delivery identity.\n"
15142
15160
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-messages",
3
- "version": "0.55.3",
3
+ "version": "0.55.5",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Unified multi-channel messaging with STI-based hierarchies",
6
6
  "type": "module",
@@ -58,18 +58,18 @@
58
58
  "./dist/svelte/index.js"
59
59
  ],
60
60
  "dependencies": {
61
- "@happyvertical/email": "^0.102.2",
62
- "@happyvertical/files": "^0.102.2",
63
- "@happyvertical/logger": "^0.102.2",
64
- "@happyvertical/messages": "^0.102.2",
65
- "@happyvertical/smrt-core": "0.55.3",
66
- "@happyvertical/smrt-types": "0.55.3",
67
- "@happyvertical/smrt-secrets": "0.55.3",
68
- "@happyvertical/smrt-tenancy": "0.55.3",
69
- "@happyvertical/smrt-ui": "0.55.3",
70
- "@happyvertical/smrt-users": "0.55.3",
71
- "@happyvertical/sql": "^0.102.2",
72
- "@happyvertical/utils": "^0.102.2"
61
+ "@happyvertical/email": "^0.102.4",
62
+ "@happyvertical/files": "^0.102.4",
63
+ "@happyvertical/logger": "^0.102.4",
64
+ "@happyvertical/messages": "^0.102.4",
65
+ "@happyvertical/smrt-core": "0.55.5",
66
+ "@happyvertical/smrt-types": "0.55.5",
67
+ "@happyvertical/smrt-secrets": "0.55.5",
68
+ "@happyvertical/smrt-tenancy": "0.55.5",
69
+ "@happyvertical/smrt-ui": "0.55.5",
70
+ "@happyvertical/smrt-users": "0.55.5",
71
+ "@happyvertical/sql": "^0.102.4",
72
+ "@happyvertical/utils": "^0.102.4"
73
73
  },
74
74
  "peerDependencies": {
75
75
  "@happyvertical/encryption": "*",
@@ -84,8 +84,8 @@
84
84
  }
85
85
  },
86
86
  "devDependencies": {
87
- "@happyvertical/smrt-ledgers": "0.55.3",
88
- "@happyvertical/smrt-vitest": "0.55.3",
87
+ "@happyvertical/smrt-ledgers": "0.55.5",
88
+ "@happyvertical/smrt-vitest": "0.55.5",
89
89
  "@sveltejs/package": "^2.5.8",
90
90
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
91
91
  "@types/node": "24.13.6",