@happyvertical/smrt-messages 0.51.24 → 0.51.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -0
- package/dist/chunks/{AccountCollection-5IchK-_S.js → AccountCollection-BmV1i0h_.js} +2 -1
- package/dist/chunks/AccountCollection-BmV1i0h_.js.map +1 -0
- package/dist/chunks/{Email-BIdbpXym.js → Email-DgzIxL59.js} +3 -2
- package/dist/chunks/Email-DgzIxL59.js.map +1 -0
- package/dist/chunks/{EmailFolder-D0o1z0_T.js → EmailFolder-CSOFlXiv.js} +2 -1
- package/dist/chunks/EmailFolder-CSOFlXiv.js.map +1 -0
- package/dist/chunks/{Message-BtsJ06xH.js → Message-BqkK3cZS.js} +4 -3
- package/dist/chunks/Message-BqkK3cZS.js.map +1 -0
- package/dist/chunks/{MessageCollection-CYEFYNMh.js → MessageCollection-BRCkkIyh.js} +2 -2
- package/dist/chunks/{MessageCollection-CYEFYNMh.js.map → MessageCollection-BRCkkIyh.js.map} +1 -1
- package/dist/chunks/__smrt-register__-B_Zw2Bpn.js +6 -0
- package/dist/chunks/__smrt-register__-B_Zw2Bpn.js.map +1 -0
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -1
- package/dist/models/Account.d.ts.map +1 -1
- package/dist/models/Attachment.d.ts +1 -1
- package/dist/models/Attachment.d.ts.map +1 -1
- package/dist/models/Blacklist.d.ts.map +1 -1
- package/dist/models/Email.d.ts +2 -2
- package/dist/models/Email.d.ts.map +1 -1
- package/dist/models/EmailAccount.d.ts +1 -1
- package/dist/models/EmailAccount.d.ts.map +1 -1
- package/dist/models/EmailFolder.d.ts +3 -3
- package/dist/models/EmailFolder.d.ts.map +1 -1
- package/dist/models/Message.d.ts +3 -3
- package/dist/models/Message.d.ts.map +1 -1
- package/dist/models/MessagingEndpoint.d.ts.map +1 -1
- package/dist/models/PersonaMessageRoute.d.ts.map +1 -1
- package/dist/models/SlackAccount.d.ts.map +1 -1
- package/dist/models/SlackMessage.d.ts.map +1 -1
- package/dist/models/TelegramAccount.d.ts.map +1 -1
- package/dist/models/Tweet.d.ts.map +1 -1
- package/dist/models/TwitterAccount.d.ts.map +1 -1
- package/dist/models/Whitelist.d.ts.map +1 -1
- package/dist/models/ZulipAccount.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +7 -5
- package/package.json +11 -8
- package/dist/chunks/AccountCollection-5IchK-_S.js.map +0 -1
- package/dist/chunks/Email-BIdbpXym.js.map +0 -1
- package/dist/chunks/EmailFolder-D0o1z0_T.js.map +0 -1
- package/dist/chunks/Message-BtsJ06xH.js.map +0 -1
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-messages",
|
|
5
|
-
"packageVersion": "0.51.
|
|
5
|
+
"packageVersion": "0.51.26",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-messages:AccountCollection": {
|
|
8
8
|
"name": "accountcollection",
|
|
@@ -10747,6 +10747,7 @@
|
|
|
10747
10747
|
"moduleType": "smrt",
|
|
10748
10748
|
"smrtDependencies": [
|
|
10749
10749
|
"@happyvertical/smrt-core",
|
|
10750
|
+
"@happyvertical/smrt-ledgers",
|
|
10750
10751
|
"@happyvertical/smrt-secrets",
|
|
10751
10752
|
"@happyvertical/smrt-tenancy",
|
|
10752
10753
|
"@happyvertical/smrt-users"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Account.d.ts","sourceRoot":"","sources":["../../src/models/Account.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"Account.d.ts","sourceRoot":"","sources":["../../src/models/Account.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,yBAAyB,CAAC;AACjC,OAAO,EAAS,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAOnE,OAAO,KAAK,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvE,qBAOa,OAAQ,SAAQ,UAAU;IAErC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,IAAI,SAAM;IACV,YAAY,SAAM;IAClB,WAAW,SAAM;IAMjB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAQ;IACzC,QAAQ,UAAQ;IAChB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAK/B,QAAQ,SAAM;IACd,kEAAkE;IAClE,aAAa,SAAQ;IAGrB,SAAS,OAAc;IACvB,SAAS,OAAc;gBAEX,OAAO,GAAE,cAAmB;IAqBxC;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAStC;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIpD,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQ3C,gBAAgB,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI9D,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACG,cAAc,CAClB,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;IA0BhB;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAWrD;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAoB/D,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,yBAAyB;YAQnB,2BAA2B;CAO1C"}
|
|
@@ -15,7 +15,7 @@ export declare class Attachment extends SmrtObject {
|
|
|
15
15
|
/**
|
|
16
16
|
* Get the message this attachment belongs to
|
|
17
17
|
*/
|
|
18
|
-
getMessage(): Promise<import('./Message').Message | null>;
|
|
18
|
+
getMessage(): Promise<import('./Message.js').Message | null>;
|
|
19
19
|
/**
|
|
20
20
|
* Check if attachment is an image
|
|
21
21
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attachment.d.ts","sourceRoot":"","sources":["../../src/models/Attachment.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"Attachment.d.ts","sourceRoot":"","sources":["../../src/models/Attachment.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,yBAAyB,CAAC;AACjC,OAAO,EAGL,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElD,qBAMa,UAAW,SAAQ,UAAU;IAExC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,SAAS,SAAM;IACf,QAAQ,SAAM;IACd,WAAW,SAAM;IACjB,IAAI,SAAK;IACT,SAAS,SAAM;IACf,kBAAkB,EAAE,YAAY,GAAG,QAAQ,CAAgB;IAC3D,QAAQ,SAAM;IACd,SAAS,SAAM;IAGf,SAAS,OAAc;gBAEX,OAAO,GAAE,iBAAsB;IAiB3C;;OAEG;IACG,UAAU;IAWhB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,KAAK,IAAI,OAAO;IAIhB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,YAAY,IAAI,MAAM;IAMtB;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAa1B;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;;;;;OAMG;IACG,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAe5C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Blacklist.d.ts","sourceRoot":"","sources":["../../src/models/Blacklist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"Blacklist.d.ts","sourceRoot":"","sources":["../../src/models/Blacklist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAG5D,OAAO,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;;;GAOG;AACH,qBAKa,SAAU,SAAQ,UAAU;IACvC,OAAO,EAAE,MAAM,CAAM;IACrB,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAW;IAC7C,MAAM,EAAE,MAAM,CAAM;IACpB,WAAW,EAAE,OAAO,CAAQ;gBAEhB,OAAO,GAAE,gBAAqB;IAW1C;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CA6BhC"}
|
package/dist/models/Email.d.ts
CHANGED
|
@@ -59,11 +59,11 @@ export declare class Email extends Message {
|
|
|
59
59
|
/**
|
|
60
60
|
* Get the email account (typed as EmailAccount)
|
|
61
61
|
*/
|
|
62
|
-
getAccount(): Promise<import('./Account').Account | null>;
|
|
62
|
+
getAccount(): Promise<import('./Account.js').Account | null>;
|
|
63
63
|
/**
|
|
64
64
|
* Get the folder
|
|
65
65
|
*/
|
|
66
|
-
getFolder(): Promise<import('./EmailFolder').EmailFolder | null>;
|
|
66
|
+
getFolder(): Promise<import('./EmailFolder.js').EmailFolder | null>;
|
|
67
67
|
/**
|
|
68
68
|
* Get emails in the same thread
|
|
69
69
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Email.d.ts","sourceRoot":"","sources":["../../src/models/Email.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"Email.d.ts","sourceRoot":"","sources":["../../src/models/Email.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,qBAMa,KAAM,SAAQ,OAAO;IAEhC,SAAS,SAAM;IACf,SAAS,SAAM;IAGf,WAAW,SAAM;IACjB,YAAY,SAAM;IAClB,cAAc,SAAM;IACpB,WAAW,SAAM;IAGjB,QAAQ,SAAM;IACd,QAAQ,SAAM;IAGd,QAAQ,SAAM;IACd,UAAU,SAAM;IAChB,MAAM,SAAM;IACZ,KAAK,SAAM;IAGX,UAAU,UAAS;IACnB,OAAO,UAAS;IAGhB,UAAU,SAAM;IAChB,OAAO,SAAM;gBAED,OAAO,GAAE,YAAiB;IA8BtC;;OAEG;IACH,cAAc,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAS3D;;OAEG;IACH,eAAe,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAS5D;;OAEG;IACH,SAAS,IAAI,MAAM,EAAE;IASrB;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAIjC;;OAEG;IACH,QAAQ,IAAI,MAAM,EAAE;IASpB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/B;;OAEG;IACH,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAS/C;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI;IAI5D;;OAEG;IACY,UAAU;IAWzB;;OAEG;IACG,SAAS;IAWf;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IASzC;;OAEG;IACM,UAAU,CAAC,SAAS,SAAM,GAAG,MAAM;IAM5C;;OAEG;IACH,aAAa,IAAI,MAAM,EAAE;IAQzB;;OAEG;IACM,WAAW,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,KAAK;IA0D7D;;OAEG;IACM,aAAa,IAAI,KAAK;IAgC/B;;OAEG;cACgB,eAAe,IAAI,MAAM;IAe5C;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAwBzB"}
|
|
@@ -40,7 +40,7 @@ export declare class EmailAccount extends Account {
|
|
|
40
40
|
/**
|
|
41
41
|
* Get all emails for this account
|
|
42
42
|
*/
|
|
43
|
-
getEmails(limit?: number): Promise<import('./Message').Message[]>;
|
|
43
|
+
getEmails(limit?: number): Promise<import('./Message.js').Message[]>;
|
|
44
44
|
/**
|
|
45
45
|
* Get unread email count
|
|
46
46
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmailAccount.d.ts","sourceRoot":"","sources":["../../src/models/EmailAccount.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EAEtB,WAAW,EACX,UAAU,EACX,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,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,UAAU,CAAC;AAClB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,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"}
|
|
@@ -16,15 +16,15 @@ export declare class EmailFolder extends SmrtObject {
|
|
|
16
16
|
/**
|
|
17
17
|
* Get the email account
|
|
18
18
|
*/
|
|
19
|
-
getAccount(): Promise<import('./Account').Account | null>;
|
|
19
|
+
getAccount(): Promise<import('./Account.js').Account | null>;
|
|
20
20
|
/**
|
|
21
21
|
* Get all emails in this folder
|
|
22
22
|
*/
|
|
23
|
-
getEmails(limit?: number): Promise<import('./Message').Message[]>;
|
|
23
|
+
getEmails(limit?: number): Promise<import('./Message.js').Message[]>;
|
|
24
24
|
/**
|
|
25
25
|
* Get unread emails in this folder
|
|
26
26
|
*/
|
|
27
|
-
getUnreadEmails(limit?: number): Promise<import('./Message').Message[]>;
|
|
27
|
+
getUnreadEmails(limit?: number): Promise<import('./Message.js').Message[]>;
|
|
28
28
|
/**
|
|
29
29
|
* Update message counts from database
|
|
30
30
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmailFolder.d.ts","sourceRoot":"","sources":["../../src/models/EmailFolder.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"EmailFolder.d.ts","sourceRoot":"","sources":["../../src/models/EmailFolder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,yBAAyB,CAAC;AACjC,OAAO,EAAc,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,qBAMa,WAAY,SAAQ,UAAU;IAEzC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,SAAS,SAAM;IACf,IAAI,SAAM;IACV,IAAI,SAAM;IACV,SAAS,SAAO;IAChB,UAAU,SAAM;IAChB,YAAY,SAAK;IACjB,WAAW,SAAK;IAChB,UAAU,UAAQ;IAGlB,SAAS,OAAc;IACvB,SAAS,OAAc;gBAEX,OAAO,GAAE,kBAAuB;IAkB5C;;OAEG;IACG,UAAU;IAWhB;;OAEG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM;IAc9B;;OAEG;IACG,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM;IAcpC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBpC;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAMnB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,MAAM,IAAI,OAAO;IAQjB;;OAEG;IACH,cAAc,IAAI,OAAO;IAIzB;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAMhC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAKnC"}
|
package/dist/models/Message.d.ts
CHANGED
|
@@ -74,8 +74,8 @@ export declare class Message extends SmrtObject {
|
|
|
74
74
|
/**
|
|
75
75
|
* Get the account for this message
|
|
76
76
|
*/
|
|
77
|
-
getAccount(): Promise<import('./Account').Account | null>;
|
|
78
|
-
getEndpoint(): Promise<import('./MessagingEndpoint').MessagingEndpoint | null>;
|
|
77
|
+
getAccount(): Promise<import('./Account.js').Account | null>;
|
|
78
|
+
getEndpoint(): Promise<import('./MessagingEndpoint.js').MessagingEndpoint | null>;
|
|
79
79
|
/**
|
|
80
80
|
* Get messages in the same thread
|
|
81
81
|
*/
|
|
@@ -83,7 +83,7 @@ export declare class Message extends SmrtObject {
|
|
|
83
83
|
/**
|
|
84
84
|
* Get attachments for this message
|
|
85
85
|
*/
|
|
86
|
-
getAttachments(): Promise<import('./Attachment').Attachment[]>;
|
|
86
|
+
getAttachments(): Promise<import('./Attachment.js').Attachment[]>;
|
|
87
87
|
/**
|
|
88
88
|
* Send this message via its account's sender
|
|
89
89
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/models/Message.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/models/Message.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,yBAAyB,CAAC;AACjC,OAAO,EAGL,UAAU,EAIX,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACX,MAAM,UAAU,CAAC;AAElB,qBAOa,OAAQ,SAAQ,UAAU;IAErC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,SAAS,SAAM;IAIf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEhC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC,aAAa,SAAM;IACnB,QAAQ,SAAM;IACd,OAAO,SAAM;IACb,IAAI,SAAM;IACV,WAAW,SAAM;IACjB,QAAQ,SAAM;IACd,WAAW,SAAM;IACjB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAQ;IACzB,MAAM,UAAS;IACf,SAAS,UAAS;IAClB,cAAc,UAAS;IACvB,IAAI,SAAK;IACT,QAAQ,SAAM;IAGd,UAAU,EAAE,UAAU,CAAW;IACjC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAQ;IAC3B,SAAS,SAAM;IACf,UAAU,SAAK;IACf,UAAU,SAAK;IACf,eAAe,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEpC,kBAAkB,SAAM;IAGxB,SAAS,OAAc;IACvB,SAAS,OAAc;gBAEX,OAAO,GAAE,cAAmB;YAqC1B,qBAAqB;IAuDnC;;OAEG;IACH,cAAc,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAS3D;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAI1E;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAStC;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIhD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,UAAU,CAAC,SAAS,SAAM,GAAG,MAAM;IAMnC;;OAEG;IACG,UAAU;IAWV,WAAW;IASjB;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAW7C;;OAEG;IACG,cAAc;IAapB;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkJpE;;OAEG;IACG,SAAS,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAwBzE;;;;;;;;OAQG;IACH,SAAS,CAAC,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IASjD;;OAEG;IACH,WAAW,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO;IA0BvD;;OAEG;IACH,aAAa,IAAI,OAAO;IAyBxB;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,MAAM;CAapC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessagingEndpoint.d.ts","sourceRoot":"","sources":["../../src/models/MessagingEndpoint.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessagingEndpoint.d.ts","sourceRoot":"","sources":["../../src/models/MessagingEndpoint.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;AACjC,OAAO,EAGL,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE9E,qBAQa,iBAAkB,SAAQ,UAAU;IAE/C,QAAQ,SAAM;IAGd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGhC,KAAK,SAAM;IAGX,OAAO,EAAE,gBAAgB,CAAW;IAEpC,+EAA+E;IAE/E,OAAO,SAAQ;IAEf,QAAQ,UAAQ;IAChB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAC/B,SAAS,OAAc;IACvB,SAAS,OAAc;gBAEX,OAAO,GAAE,wBAA6B;IAalD,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC;IAQ5E,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIlD,gBAAgB,IAAI,MAAM;CAiB3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PersonaMessageRoute.d.ts","sourceRoot":"","sources":["../../src/models/PersonaMessageRoute.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,UAAU,EAEX,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"PersonaMessageRoute.d.ts","sourceRoot":"","sources":["../../src/models/PersonaMessageRoute.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAGlC,OAAO,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAE9D,qBAca,mBAAoB,SAAQ,UAAU;IAEjD,QAAQ,SAAM;IAKd,SAAS,SAAM;IAGf,SAAS,SAAM;IAGf,UAAU,SAAM;IAGhB,OAAO,SAAa;IAEpB,QAAQ,SAAK;IACb,OAAO,UAAQ;IACf,SAAS,OAAc;IACvB,SAAS,OAAc;gBAEX,OAAO,GAAE,0BAA+B;CAYrD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SlackAccount.d.ts","sourceRoot":"","sources":["../../src/models/SlackAccount.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"SlackAccount.d.ts","sourceRoot":"","sources":["../../src/models/SlackAccount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,qBAMa,YAAa,SAAQ,OAAO;IACvC,WAAW,SAAM;IACjB,aAAa,SAAM;IACnB,SAAS,SAAM;gBAEH,OAAO,GAAE,mBAAwB;IAc7C;;OAEG;IACY,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC;CAI/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SlackMessage.d.ts","sourceRoot":"","sources":["../../src/models/SlackMessage.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"SlackMessage.d.ts","sourceRoot":"","sources":["../../src/models/SlackMessage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,qBAMa,YAAa,SAAQ,OAAO;IACvC,SAAS,SAAM;IACf,WAAW,SAAM;IACjB,OAAO,SAAM;IACb,aAAa,SAAM;IACnB,SAAS,SAAM;IACf,QAAQ,UAAS;IACjB,WAAW,SAAM;IACjB,MAAM,SAAM;gBAEA,OAAO,GAAE,mBAAwB;IAgB7C;;OAEG;IACH,YAAY,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IASxE;;OAEG;IACH,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;IAStC;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,iBAAiB,IAAI,MAAM;CAG5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TelegramAccount.d.ts","sourceRoot":"","sources":["../../src/models/TelegramAccount.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TelegramAccount.d.ts","sourceRoot":"","sources":["../../src/models/TelegramAccount.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,qBAMa,eAAgB,SAAQ,OAAO;gBAC9B,OAAO,GAAE,cAAmB;CAKzC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tweet.d.ts","sourceRoot":"","sources":["../../src/models/Tweet.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"Tweet.d.ts","sourceRoot":"","sources":["../../src/models/Tweet.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,qBAMa,KAAM,SAAQ,OAAO;IAChC,OAAO,SAAM;IACb,YAAY,SAAK;IACjB,SAAS,SAAK;IACd,UAAU,SAAK;IACf,SAAS,UAAS;IAClB,OAAO,UAAS;IAChB,SAAS,SAAM;IACf,QAAQ,SAAM;IACd,QAAQ,SAAM;gBAEF,OAAO,GAAE,YAAiB;IAetC;;OAEG;IACH,YAAY,IAAI,MAAM,EAAE;IASxB;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;IASvB;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;IASvB;;OAEG;IACH,aAAa,IAAI,MAAM;CAGxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TwitterAccount.d.ts","sourceRoot":"","sources":["../../src/models/TwitterAccount.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"TwitterAccount.d.ts","sourceRoot":"","sources":["../../src/models/TwitterAccount.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,qBAMa,cAAe,SAAQ,OAAO;IACzC,MAAM,SAAM;IACZ,aAAa,SAAM;gBAEP,OAAO,GAAE,qBAA0B;IAY/C;;OAEG;IACY,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC;CAI/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Whitelist.d.ts","sourceRoot":"","sources":["../../src/models/Whitelist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"Whitelist.d.ts","sourceRoot":"","sources":["../../src/models/Whitelist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAG5D,OAAO,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;;;GAOG;AACH,qBAKa,SAAU,SAAQ,UAAU;IACvC,OAAO,EAAE,MAAM,CAAM;IACrB,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAW;IAC7C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC/B,WAAW,EAAE,MAAM,CAAM;gBAEb,OAAO,GAAE,gBAAqB;IAW1C;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CA6BhC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ZulipAccount.d.ts","sourceRoot":"","sources":["../../src/models/ZulipAccount.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ZulipAccount.d.ts","sourceRoot":"","sources":["../../src/models/ZulipAccount.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,qBAMa,YAAa,SAAQ,OAAO;gBAC3B,OAAO,GAAE,cAAmB;CAKzC"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -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.51.
|
|
6
|
+
"packageVersion": "0.51.26",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "9ef9266176fc1087930fcfdf983fb7581cf05af63147b5f3e18581801e6bbf24",
|
|
11
|
+
"packageJson": "803b83b34bd4dcc8f1ad47b4d01b9b0b68b396cbff26f495a7aaebb37f82d851",
|
|
12
|
+
"agents": "ceb0ee5313abe0316f7b4dbfc0ec8c94e3ec0e10826851145d4da91b6b764f38"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@happyvertical/smrt-users": "workspace:*",
|
|
37
37
|
"@happyvertical/sql": "catalog:",
|
|
38
38
|
"@happyvertical/utils": "catalog:",
|
|
39
|
+
"@happyvertical/smrt-ledgers": "workspace:*",
|
|
39
40
|
"@happyvertical/smrt-vitest": "workspace:*",
|
|
40
41
|
"@sveltejs/package": "^2.5.8",
|
|
41
42
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
},
|
|
50
51
|
"smrtDependencies": [
|
|
51
52
|
"@happyvertical/smrt-core",
|
|
53
|
+
"@happyvertical/smrt-ledgers",
|
|
52
54
|
"@happyvertical/smrt-secrets",
|
|
53
55
|
"@happyvertical/smrt-tenancy",
|
|
54
56
|
"@happyvertical/smrt-types",
|
|
@@ -15005,5 +15007,5 @@
|
|
|
15005
15007
|
"polymorphicAssociations": 0,
|
|
15006
15008
|
"uuidColumns": 90
|
|
15007
15009
|
},
|
|
15008
|
-
"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## 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- **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"
|
|
15010
|
+
"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## 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"
|
|
15009
15011
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-messages",
|
|
3
|
-
"version": "0.51.
|
|
3
|
+
"version": "0.51.26",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"description": "Unified multi-channel messaging with STI-based hierarchies",
|
|
6
6
|
"type": "module",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"sideEffects": [
|
|
53
53
|
"./dist/index.js",
|
|
54
54
|
"./dist/__smrt-register__.js",
|
|
55
|
+
"./src/__smrt-register__.ts",
|
|
55
56
|
"./dist/providers/*.js",
|
|
56
57
|
"./dist/chunks/*.js",
|
|
57
58
|
"./dist/svelte/index.js"
|
|
@@ -61,12 +62,12 @@
|
|
|
61
62
|
"@happyvertical/files": "^0.90.0",
|
|
62
63
|
"@happyvertical/logger": "^0.90.0",
|
|
63
64
|
"@happyvertical/messages": "^0.90.0",
|
|
64
|
-
"@happyvertical/smrt-core": "0.51.
|
|
65
|
-
"@happyvertical/smrt-types": "0.51.
|
|
66
|
-
"@happyvertical/smrt-secrets": "0.51.
|
|
67
|
-
"@happyvertical/smrt-tenancy": "0.51.
|
|
68
|
-
"@happyvertical/smrt-ui": "0.51.
|
|
69
|
-
"@happyvertical/smrt-users": "0.51.
|
|
65
|
+
"@happyvertical/smrt-core": "0.51.26",
|
|
66
|
+
"@happyvertical/smrt-types": "0.51.26",
|
|
67
|
+
"@happyvertical/smrt-secrets": "0.51.26",
|
|
68
|
+
"@happyvertical/smrt-tenancy": "0.51.26",
|
|
69
|
+
"@happyvertical/smrt-ui": "0.51.26",
|
|
70
|
+
"@happyvertical/smrt-users": "0.51.26",
|
|
70
71
|
"@happyvertical/sql": "^0.90.0",
|
|
71
72
|
"@happyvertical/utils": "^0.90.0"
|
|
72
73
|
},
|
|
@@ -83,7 +84,8 @@
|
|
|
83
84
|
}
|
|
84
85
|
},
|
|
85
86
|
"devDependencies": {
|
|
86
|
-
"@happyvertical/smrt-
|
|
87
|
+
"@happyvertical/smrt-ledgers": "0.51.26",
|
|
88
|
+
"@happyvertical/smrt-vitest": "0.51.26",
|
|
87
89
|
"@sveltejs/package": "^2.5.8",
|
|
88
90
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
89
91
|
"@types/node": "24.13.6",
|
|
@@ -122,6 +124,7 @@
|
|
|
122
124
|
"check": "pnpm exec svelte-check --tsconfig ./tsconfig.svelte.json",
|
|
123
125
|
"pretest": "[ -f ../cli/dist/index.js ] && node ../cli/dist/index.js test --manifest-only || true",
|
|
124
126
|
"test": "vitest run",
|
|
127
|
+
"test:postgres": "node ../../scripts/run-with-ci-postgres.mjs -- pnpm exec vitest run optional.test.ts",
|
|
125
128
|
"test:watch": "vitest",
|
|
126
129
|
"typecheck": "tsc --noEmit && node ../../scripts/svelte-check-a11y.mjs --tsconfig ./tsconfig.svelte.json",
|
|
127
130
|
"verify:pack": "node ../../scripts/verify-package-types-exports.js ."
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AccountCollection-5IchK-_S.js","names":["tenantId"],"sources":["../../src/models/Account.ts","../../src/collections/AccountCollection.ts"],"sourcesContent":["/**\n * Account model - Base class for all messaging account types (STI)\n *\n * Common fields shared across email, Slack, Twitter accounts, etc.\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport {\n getCurrentTenant,\n TenantScoped,\n tenantId,\n withTenant,\n} from '@happyvertical/smrt-tenancy';\nimport type { AccountOptions, MessageSenderInterface } from '../types';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class Account extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n name = '';\n providerType = '';\n channelType = '';\n // credentialSecretId stores the Secret's NAME (keyed by name + tenant\n // context in smrt-secrets), NOT its primary-key id — see setCredentials()/\n // getCredentials() which call tenant-bound SecretService operations by name.\n // So it is deliberately NOT a @crossPackageRef id FK.\n @field({ sensitive: true, readonly: true })\n credentialSecretId: string | null = null;\n isActive = true;\n lastSyncAt: Date | null = null;\n // JSON. Sensitive (#1540): may hold a plaintext credential fallback, so it is\n // excluded from generated API/MCP responses and rejected as a `where` filter\n // key.\n @field({ sensitive: true, readonly: true })\n settings = ''; // JSON\n /** Non-secret provider configuration such as a Zulip site URL. */\n configuration = '{}';\n\n // Timestamps\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: AccountOptions = {}) {\n super(options);\n\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.providerType !== undefined)\n this.providerType = options.providerType;\n if (options.channelType !== undefined)\n this.channelType = options.channelType;\n if (options.credentialSecretId !== undefined)\n this.credentialSecretId = options.credentialSecretId || null;\n if (options.isActive !== undefined) this.isActive = options.isActive;\n if (options.lastSyncAt !== undefined)\n this.lastSyncAt = options.lastSyncAt || null;\n if (options.settings !== undefined) this.settings = options.settings;\n if (options.configuration !== undefined)\n this.configuration = options.configuration;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n }\n\n /**\n * Get settings as parsed object\n */\n getSettings(): Record<string, unknown> {\n if (!this.settings) return {};\n try {\n return JSON.parse(this.settings);\n } catch {\n return {};\n }\n }\n\n /**\n * Set settings from object\n */\n setSettings(settings: Record<string, unknown>): void {\n this.settings = JSON.stringify(settings);\n }\n\n getConfiguration(): Record<string, unknown> {\n try {\n return JSON.parse(this.configuration || '{}');\n } catch {\n return {};\n }\n }\n\n setConfiguration(configuration: Record<string, unknown>): void {\n this.configuration = JSON.stringify(configuration);\n }\n\n get hasCredentials(): boolean {\n return Boolean(this.credentialSecretId || this.settings);\n }\n\n /**\n * Activate account\n */\n async activate(): Promise<void> {\n this.isActive = true;\n this.updatedAt = new Date();\n await this.save();\n }\n\n /**\n * Deactivate account\n */\n async deactivate(): Promise<void> {\n this.isActive = false;\n this.updatedAt = new Date();\n await this.save();\n }\n\n /**\n * Store credentials securely using smrt-secrets\n */\n async setCredentials(\n credentials: Record<string, unknown>,\n options: {\n description?: string;\n category?: string;\n } = {},\n ): Promise<void> {\n if (!this.id) {\n throw new Error('Account must be persisted before storing credentials.');\n }\n const { SecretService } = await import('@happyvertical/smrt-secrets');\n const secretService = await SecretService.create({ db: this.db });\n\n const secretName = `${this.channelType || 'messaging'}-account-${this.id}`;\n const secretValue = JSON.stringify(credentials);\n const tenantId = this.requireCredentialTenantId('store credentials');\n\n await secretService.storeForTenant(\n tenantId,\n this.credentialSecretId || secretName,\n secretValue,\n {\n description: options.description || `Credentials for ${this.name}`,\n category: options.category || 'messaging',\n },\n );\n\n this.credentialSecretId ||= secretName;\n this.updatedAt = new Date();\n await this.withCredentialTenantContext(() => this.save());\n }\n\n /**\n * Create a sender for this account.\n * Subclasses must override to return a concrete sender.\n */\n async createSender(): Promise<MessageSenderInterface> {\n const { getMessagingProvider } = await import('../providers.js');\n const provider = getMessagingProvider(this.providerType);\n if (!provider?.createSender) {\n throw new Error(\n `createSender() not implemented for account type '${this.providerType}'`,\n );\n }\n return provider.createSender(this);\n }\n\n /**\n * Retrieve stored credentials\n */\n async getCredentials(): Promise<Record<string, unknown> | null> {\n if (!this.credentialSecretId) {\n return this.getSettings();\n }\n\n const tenantId = this.requireCredentialTenantId('retrieve credentials');\n const { SecretService } = await import('@happyvertical/smrt-secrets');\n const secretService = await SecretService.create({ db: this.db });\n\n try {\n const secret = await secretService.retrieveForTenant(\n tenantId,\n this.credentialSecretId,\n );\n return JSON.parse(secret.value);\n } catch {\n return null;\n }\n }\n\n private getCredentialTenantId(): string | null {\n return this.tenantId || getCurrentTenant()?.tenantId || null;\n }\n\n private requireCredentialTenantId(action: string): string {\n const tenantId = this.getCredentialTenantId();\n if (!tenantId) {\n throw new Error(`Tenant context required to ${action}.`);\n }\n return tenantId;\n }\n\n private async withCredentialTenantContext<T>(\n fn: () => Promise<T>,\n ): Promise<T> {\n const tenantId = this.getCredentialTenantId();\n if (!tenantId || getCurrentTenant()?.tenantId === tenantId) return fn();\n return withTenant({ tenantId }, fn);\n }\n}\n","/**\n * AccountCollection - Unified collection for polymorphic account queries\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Account } from '../models/Account';\nimport type { AccountSearchFilters } from '../types';\n\nexport class AccountCollection extends SmrtCollection<Account> {\n static readonly _itemClass = Account;\n\n /**\n * Get active accounts\n */\n async getActive(): Promise<Account[]> {\n return await this.list({ where: { isActive: true } });\n }\n\n /**\n * Get inactive accounts\n */\n async getInactive(): Promise<Account[]> {\n return await this.list({ where: { isActive: false } });\n }\n\n /**\n * Get accounts by provider type\n */\n async getByProviderType(providerType: string): Promise<Account[]> {\n return await this.list({ where: { providerType } });\n }\n\n /**\n * Get accounts by STI type.\n *\n * Accepts either the full discriminator (e.g. \"@happyvertical/smrt-messages:EmailAccount\")\n * or the short type name (e.g. \"EmailAccount\").\n */\n async getByType(accountType: string): Promise<Account[]> {\n if (accountType.includes(':') || accountType.startsWith('@')) {\n return await this.list({ where: { _meta_type: accountType } });\n }\n const allAccounts = await this.list({});\n return allAccounts.filter((a) => {\n const metaType = (a as { _meta_type?: string })._meta_type || '';\n return metaType.endsWith(`:${accountType}`);\n });\n }\n\n /**\n * Search accounts with filters\n */\n async search(\n query: string,\n filters?: AccountSearchFilters,\n ): Promise<Account[]> {\n let accounts = await this.list({});\n\n if (query) {\n const lowerQuery = query.toLowerCase();\n accounts = accounts.filter((a) =>\n a.name?.toLowerCase().includes(lowerQuery),\n );\n }\n\n if (filters) {\n if (filters.providerType) {\n accounts = accounts.filter(\n (a) => a.providerType === filters.providerType,\n );\n }\n if (filters.isActive !== undefined) {\n accounts = accounts.filter((a) => a.isActive === filters.isActive);\n }\n if (filters.accountType) {\n accounts = accounts.filter((a) => {\n const metaType = (a as { _meta_type?: string })._meta_type || '';\n return metaType.includes(filters.accountType as string);\n });\n }\n }\n\n return accounts;\n }\n\n /**\n * Get account statistics\n */\n async getStats(): Promise<{\n total: number;\n active: number;\n inactive: number;\n byType: Record<string, number>;\n }> {\n const accounts = await this.list({});\n const byType: Record<string, number> = {};\n\n for (const account of accounts) {\n const metaType =\n (account 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: accounts.length,\n active: accounts.filter((a) => a.isActive).length,\n inactive: accounts.filter((a) => !a.isActive).length,\n byType,\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Tenant Helper Methods\n // ─────────────────────────────────────────────────────────────────────────\n\n async findByTenant(tenantId: string): Promise<Account[]> {\n return this.list({ where: { tenantId } });\n }\n\n // Account is the @TenantScoped STI base — see MessageCollection. Route through\n // the raw helpers; no `_meta_type` so the base returns ALL account subtypes.\n async findGlobal(): Promise<Account[]> {\n return queryGlobal<Account>(this);\n }\n\n async findWithGlobals(tenantId: string): Promise<Account[]> {\n return queryWithGlobals<Account>(this, tenantId, 'Account.findWithGlobals');\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Email-BIdbpXym.js","names":[],"sources":["../../src/models/Email.ts"],"sourcesContent":["/**\n * Email model - Email message extending the Message STI base\n *\n * Retains all email-specific fields (RFC 822 compliance, folders, etc.)\n * while inheriting common message fields from Message.\n */\n\nimport { smrt } from '@happyvertical/smrt-core';\nimport type { EmailOptions } from '../types';\nimport { Message } from './Message';\n\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class Email extends Message {\n // RFC 822 fields\n messageId = ''; // RFC 822 Message-ID header\n inReplyTo = '';\n\n // Additional recipients\n ccAddresses = '';\n bccAddresses = '';\n replyToAddress = '';\n replyToName = '';\n\n // Email-specific content\n textBody = '';\n htmlBody = '';\n\n // Location\n folderId = '';\n folderPath = '';\n labels = ''; // JSON array (Gmail)\n flags = ''; // JSON array (IMAP)\n\n // Email-specific status flags\n isAnswered = false;\n isDraft = false;\n\n // Raw data\n rawMessage = '';\n headers = ''; // JSON object\n\n constructor(options: EmailOptions = {}) {\n super(options);\n\n if (options.messageId !== undefined) this.messageId = options.messageId;\n if (options.inReplyTo !== undefined) this.inReplyTo = options.inReplyTo;\n if (options.ccAddresses !== undefined)\n this.ccAddresses = options.ccAddresses;\n if (options.bccAddresses !== undefined)\n this.bccAddresses = options.bccAddresses;\n if (options.replyToAddress !== undefined)\n this.replyToAddress = options.replyToAddress;\n if (options.replyToName !== undefined)\n this.replyToName = options.replyToName;\n if (options.textBody !== undefined) this.textBody = options.textBody;\n if (options.htmlBody !== undefined) this.htmlBody = options.htmlBody;\n if (options.folderId !== undefined) this.folderId = options.folderId;\n if (options.folderPath !== undefined) this.folderPath = options.folderPath;\n if (options.labels !== undefined) this.labels = options.labels;\n if (options.flags !== undefined) this.flags = options.flags;\n if (options.isAnswered !== undefined) this.isAnswered = options.isAnswered;\n if (options.isDraft !== undefined) this.isDraft = options.isDraft;\n if (options.rawMessage !== undefined) this.rawMessage = options.rawMessage;\n if (options.headers !== undefined) this.headers = options.headers;\n\n // Populate base body from textBody for unified access\n if (options.textBody && !options.body) {\n this.body = options.textBody;\n }\n }\n\n /**\n * Get CC addresses as parsed array\n */\n getCcAddresses(): Array<{ address: string; name?: string }> {\n if (!this.ccAddresses) return [];\n try {\n return JSON.parse(this.ccAddresses);\n } catch {\n return [];\n }\n }\n\n /**\n * Get BCC addresses as parsed array\n */\n getBccAddresses(): Array<{ address: string; name?: string }> {\n if (!this.bccAddresses) return [];\n try {\n return JSON.parse(this.bccAddresses);\n } catch {\n return [];\n }\n }\n\n /**\n * Get labels as parsed array\n */\n getLabels(): string[] {\n if (!this.labels) return [];\n try {\n return JSON.parse(this.labels);\n } catch {\n return [];\n }\n }\n\n /**\n * Set labels from array\n */\n setLabels(labels: string[]): void {\n this.labels = JSON.stringify(labels);\n }\n\n /**\n * Get flags as parsed array\n */\n getFlags(): string[] {\n if (!this.flags) return [];\n try {\n return JSON.parse(this.flags);\n } catch {\n return [];\n }\n }\n\n /**\n * Set flags from array\n */\n setFlags(flags: string[]): void {\n this.flags = JSON.stringify(flags);\n }\n\n /**\n * Get headers as parsed object\n */\n getHeaders(): Record<string, string | string[]> {\n if (!this.headers) return {};\n try {\n return JSON.parse(this.headers);\n } catch {\n return {};\n }\n }\n\n /**\n * Set headers from object\n */\n setHeaders(headers: Record<string, string | string[]>): void {\n this.headers = JSON.stringify(headers);\n }\n\n /**\n * Get the email account (typed as EmailAccount)\n */\n override async getAccount() {\n if (!this.accountId) return null;\n\n const { EmailAccountCollection } = await import(\n '../collections/EmailAccountCollection'\n );\n const collection = await EmailAccountCollection.create(this.options);\n\n return await collection.get({ id: this.accountId });\n }\n\n /**\n * Get the folder\n */\n async getFolder() {\n if (!this.folderId) return null;\n\n const { EmailFolderCollection } = await import(\n '../collections/EmailFolderCollection'\n );\n const collection = await EmailFolderCollection.create(this.options);\n\n return await collection.get({ id: this.folderId });\n }\n\n /**\n * Get emails in the same thread\n */\n async getThreadEmails(): Promise<Email[]> {\n if (!this.threadId) return [this];\n\n const { EmailCollection } = await import('../collections/EmailCollection');\n const collection = await EmailCollection.create(this.options);\n\n return await collection.getByThread(this.threadId);\n }\n\n /**\n * Get a short preview of the email body\n */\n override getPreview(maxLength = 200): string {\n const body = this.textBody || this.htmlBody?.replace(/<[^>]*>/g, '') || '';\n if (body.length <= maxLength) return body;\n return `${body.slice(0, maxLength)}...`;\n }\n\n /**\n * Get References header values as array\n */\n getReferences(): string[] {\n const headers = this.getHeaders();\n const refs = headers.references;\n if (!refs) return [];\n if (Array.isArray(refs)) return refs;\n return refs.split(/\\s+/).filter(Boolean);\n }\n\n /**\n * Create a reply to this email with RFC 822 threading\n */\n override createReply(options?: { replyAll?: boolean }): Email {\n const reply = new Email({\n ...this.draftOptions(),\n id: undefined,\n accountId: this.accountId,\n threadId: this.threadId || this.id || undefined,\n subject: this.subject.startsWith('Re:')\n ? this.subject\n : `Re: ${this.subject}`,\n fromAddress: '',\n fromName: '',\n inReplyToMessageId: this.id || undefined,\n sendStatus: 'draft',\n isRead: true,\n isDraft: true,\n date: null,\n createdAt: undefined,\n updatedAt: undefined,\n\n // RFC 822 threading\n inReplyTo: this.messageId,\n\n // To: original sender\n toAddresses: JSON.stringify([\n { address: this.fromAddress, name: this.fromName },\n ]),\n });\n\n // RFC 822 References: original references + original Message-ID\n const refs = [...this.getReferences()];\n if (this.messageId && !refs.includes(this.messageId)) {\n refs.push(this.messageId);\n }\n reply.setHeaders({ references: refs.join(' ') });\n\n // Reply-All: add original To/CC as CC (excluding self)\n if (options?.replyAll) {\n const allRecipients = [\n ...this.getToAddresses(),\n ...this.getCcAddresses(),\n ];\n // Remove original sender (already in To:) and any duplicates\n const seen = new Set([this.fromAddress.toLowerCase()]);\n const ccAddresses = allRecipients.filter((r) => {\n const addr = r.address.toLowerCase();\n if (seen.has(addr)) return false;\n seen.add(addr);\n return true;\n });\n reply.ccAddresses = JSON.stringify(ccAddresses);\n }\n\n reply.body = this.buildQuotedBody();\n reply.textBody = reply.body;\n\n return reply;\n }\n\n /**\n * Create a forward of this email\n */\n override createForward(): Email {\n const forwardBody = this.buildForwardBody();\n\n const forward = new Email({\n ...this.draftOptions(),\n id: undefined,\n accountId: this.accountId,\n threadId: '',\n subject: this.subject.startsWith('Fwd:')\n ? this.subject\n : `Fwd: ${this.subject}`,\n toAddresses: '[]',\n ccAddresses: '[]',\n bccAddresses: '[]',\n fromAddress: '',\n fromName: '',\n body: forwardBody,\n textBody: forwardBody,\n hasAttachments: this.hasAttachments,\n inReplyToMessageId: '',\n inReplyTo: '',\n sendStatus: 'draft',\n isDraft: true,\n isRead: true,\n date: null,\n createdAt: undefined,\n updatedAt: undefined,\n });\n\n return forward;\n }\n\n /**\n * Build email-specific quoted body for replies\n */\n protected override buildQuotedBody(): string {\n const dateStr = this.date ? this.date.toLocaleString() : 'unknown date';\n const from = this.fromName\n ? `${this.fromName} <${this.fromAddress}>`\n : this.fromAddress;\n\n const bodyText = this.textBody || this.body || '';\n const quotedLines = bodyText\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n');\n\n return `\\n\\nOn ${dateStr}, ${from} wrote:\\n${quotedLines}`;\n }\n\n /**\n * Build forwarded message body\n */\n private buildForwardBody(): string {\n const dateStr = this.date ? this.date.toLocaleString() : 'unknown date';\n const from = this.fromName\n ? `${this.fromName} <${this.fromAddress}>`\n : this.fromAddress;\n\n const toStr = this.getToAddresses()\n .map((r) => (r.name ? `${r.name} <${r.address}>` : r.address))\n .join(', ');\n\n const bodyText = this.textBody || this.body || '';\n\n return [\n '',\n '',\n '---------- Forwarded message ----------',\n `From: ${from}`,\n `Date: ${dateStr}`,\n `Subject: ${this.subject}`,\n `To: ${toStr}`,\n '',\n bodyText,\n ].join('\\n');\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EmailFolder-D0o1z0_T.js","names":[],"sources":["../../src/models/EmailFolder.ts"],"sourcesContent":["/**\n * EmailFolder model - Folder/label tracking\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { EmailFolderOptions } from '../types';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class EmailFolder extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('Account')\n accountId = '';\n name = '';\n path = '';\n delimiter = '/';\n specialUse = ''; // '\\\\Inbox', '\\\\Sent', '\\\\Drafts', etc.\n messageCount = 0;\n unreadCount = 0;\n subscribed = true;\n\n // Timestamps\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: EmailFolderOptions = {}) {\n super(options);\n\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.accountId !== undefined) this.accountId = options.accountId;\n if (options.name !== undefined) this.name = options.name;\n if (options.path !== undefined) this.path = options.path;\n if (options.delimiter !== undefined) this.delimiter = options.delimiter;\n if (options.specialUse !== undefined) this.specialUse = options.specialUse;\n if (options.messageCount !== undefined)\n this.messageCount = options.messageCount;\n if (options.unreadCount !== undefined)\n this.unreadCount = options.unreadCount;\n if (options.subscribed !== undefined) this.subscribed = options.subscribed;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n }\n\n /**\n * Get the email account\n */\n async getAccount() {\n if (!this.accountId) return null;\n\n const { EmailAccountCollection } = await import(\n '../collections/EmailAccountCollection'\n );\n const collection = await EmailAccountCollection.create(this.options);\n\n return await collection.get({ id: this.accountId });\n }\n\n /**\n * Get all emails in this folder\n */\n async getEmails(limit?: number) {\n const { EmailCollection } = await import('../collections/EmailCollection');\n const collection = await EmailCollection.create(this.options);\n\n const options: { where: Record<string, unknown>; limit?: number } = {\n where: { folderId: this.id },\n };\n if (limit) {\n options.limit = limit;\n }\n\n return await collection.list(options);\n }\n\n /**\n * Get unread emails in this folder\n */\n async getUnreadEmails(limit?: number) {\n const { EmailCollection } = await import('../collections/EmailCollection');\n const collection = await EmailCollection.create(this.options);\n\n const options: { where: Record<string, unknown>; limit?: number } = {\n where: { folderId: this.id, isRead: false },\n };\n if (limit) {\n options.limit = limit;\n }\n\n return await collection.list(options);\n }\n\n /**\n * Update message counts from database\n */\n async refreshCounts(): Promise<void> {\n if (!this.id) {\n throw new Error(\n 'EmailFolder.refreshCounts requires a persisted folder (missing id)',\n );\n }\n const folderId = this.id;\n const { EmailCollection } = await import('../collections/EmailCollection');\n const collection = await EmailCollection.create(this.options);\n\n this.messageCount = await collection.countByFolder(folderId);\n this.unreadCount = await collection.countUnreadByFolder(folderId);\n this.updatedAt = new Date();\n await this.save();\n }\n\n /**\n * Check if this is the inbox folder\n */\n isInbox(): boolean {\n return this.specialUse === '\\\\Inbox' || this.path.toLowerCase() === 'inbox';\n }\n\n /**\n * Check if this is the sent folder\n */\n isSent(): boolean {\n return this.specialUse === '\\\\Sent' || this.path.toLowerCase() === 'sent';\n }\n\n /**\n * Check if this is the drafts folder\n */\n isDrafts(): boolean {\n return (\n this.specialUse === '\\\\Drafts' || this.path.toLowerCase() === 'drafts'\n );\n }\n\n /**\n * Check if this is the trash folder\n */\n isTrash(): boolean {\n return this.specialUse === '\\\\Trash' || this.path.toLowerCase() === 'trash';\n }\n\n /**\n * Check if this is the spam/junk folder\n */\n isSpam(): boolean {\n return (\n this.specialUse === '\\\\Junk' ||\n this.path.toLowerCase() === 'spam' ||\n this.path.toLowerCase() === 'junk'\n );\n }\n\n /**\n * Check if this is a system folder\n */\n isSystemFolder(): boolean {\n return !!this.specialUse;\n }\n\n /**\n * Subscribe to folder\n */\n async subscribe(): Promise<void> {\n this.subscribed = true;\n this.updatedAt = new Date();\n await this.save();\n }\n\n /**\n * Unsubscribe from folder\n */\n async unsubscribe(): Promise<void> {\n this.subscribed = false;\n this.updatedAt = new Date();\n await this.save();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Message-BtsJ06xH.js","names":["result"],"sources":["../../src/models/Message.ts"],"sourcesContent":["/**\n * Message model - Base class for all message types (STI)\n *\n * Common fields shared across email, tweets, slack messages, etc.\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n usesEmbeddedRevisionFallback,\n withEmbeddedWriteQueue,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n MessageOptions,\n MessageSendResult,\n SendMessageOptions,\n SendStatus,\n} from '../types';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class Message extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('Account')\n accountId = '';\n @crossPackageRef('@happyvertical/smrt-personas:AgentPersona', {\n nullable: true,\n })\n personaId: string | null = null;\n @foreignKey('MessagingEndpoint')\n endpointId: string | null = null;\n correlationId = '';\n threadId = '';\n subject = '';\n body = ''; // Normalized plain text\n fromAddress = '';\n fromName = '';\n toAddresses = ''; // JSON array of {address, name}\n date: Date | null = null;\n isRead = false;\n isFlagged = false;\n hasAttachments = false;\n size = 0;\n metadata = ''; // JSON extension bag\n\n // Send lifecycle fields\n sendStatus: SendStatus = 'draft';\n sentAt: Date | null = null;\n sendError = '';\n retryCount = 0;\n maxRetries = 3;\n scheduledSendAt: Date | null = null;\n @foreignKey('Message')\n inReplyToMessageId = '';\n\n // Timestamps\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: MessageOptions = {}) {\n super(options);\n\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.accountId !== undefined) this.accountId = options.accountId;\n if (options.personaId !== undefined) this.personaId = options.personaId;\n if (options.endpointId !== undefined) this.endpointId = options.endpointId;\n if (options.correlationId !== undefined)\n this.correlationId = options.correlationId;\n if (options.threadId !== undefined) this.threadId = options.threadId;\n if (options.subject !== undefined) this.subject = options.subject;\n if (options.body !== undefined) this.body = options.body;\n if (options.fromAddress !== undefined)\n this.fromAddress = options.fromAddress;\n if (options.fromName !== undefined) this.fromName = options.fromName;\n if (options.toAddresses !== undefined)\n this.toAddresses = options.toAddresses;\n if (options.date !== undefined) this.date = options.date || null;\n if (options.isRead !== undefined) this.isRead = options.isRead;\n if (options.isFlagged !== undefined) this.isFlagged = options.isFlagged;\n if (options.hasAttachments !== undefined)\n this.hasAttachments = options.hasAttachments;\n if (options.size !== undefined) this.size = options.size;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n if (options.sendStatus !== undefined) this.sendStatus = options.sendStatus;\n if (options.sentAt !== undefined) this.sentAt = options.sentAt || null;\n if (options.sendError !== undefined) this.sendError = options.sendError;\n if (options.retryCount !== undefined) this.retryCount = options.retryCount;\n if (options.maxRetries !== undefined) this.maxRetries = options.maxRetries;\n if (options.scheduledSendAt !== undefined)\n this.scheduledSendAt = options.scheduledSendAt || null;\n if (options.inReplyToMessageId !== undefined)\n this.inReplyToMessageId = options.inReplyToMessageId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n }\n\n private async finalizeSendLifecycle(\n result: MessageSendResult,\n ): Promise<void> {\n if (!this.id) throw new Error('Cannot finalize an unsaved message send');\n\n const sendStatus: SendStatus = result.success ? 'sent' : 'failed';\n const sendError = result.success ? '' : (result.error ?? 'Send failed');\n const useEmbeddedFallback = usesEmbeddedRevisionFallback(this.db);\n for (let attempt = 0; attempt < 8; attempt += 1) {\n let nextRevision: Date | undefined;\n await withEmbeddedWriteQueue(this.db, useEmbeddedFallback, async () => {\n const current = await this.getCanonicalPersistedRow({ id: this.id });\n if (current?.send_status !== 'sending') return;\n const currentRevision = current.updated_at;\n const currentRevisionMs = new Date(String(currentRevision)).getTime();\n if (!Number.isFinite(currentRevisionMs)) return;\n nextRevision = new Date(Math.max(Date.now(), currentRevisionMs + 1));\n const values = {\n send_status: sendStatus,\n sent_at: result.success ? result.sentAt : null,\n send_error: sendError,\n updated_at: nextRevision,\n };\n if (useEmbeddedFallback) {\n await this.db.upsert(this.tableName, ['id'], {\n ...current,\n ...values,\n });\n return;\n }\n await this.db.update(\n this.tableName,\n {\n id: this.id,\n send_status: 'sending',\n updated_at:\n currentRevision instanceof Date\n ? currentRevision.toISOString()\n : currentRevision,\n },\n values,\n );\n });\n // DuckDB's generic update result reports one affected row even when the\n // predicate matched none. Verify the durable lifecycle value instead of\n // trusting adapter row-count metadata before declaring finalization.\n const verified = await this.getCanonicalPersistedRow({ id: this.id });\n if (verified?.send_status === sendStatus) {\n await this.completePersistedUpdate(verified);\n return;\n }\n }\n throw new Error('Cannot finalize: message revision kept changing');\n }\n\n /**\n * Get to addresses as parsed array\n */\n getToAddresses(): Array<{ address: string; name?: string }> {\n if (!this.toAddresses) return [];\n try {\n return JSON.parse(this.toAddresses);\n } catch {\n return [];\n }\n }\n\n /**\n * Set to addresses from array\n */\n setToAddresses(addresses: Array<{ address: string; name?: string }>): void {\n this.toAddresses = JSON.stringify(addresses);\n }\n\n /**\n * Get metadata as parsed object\n */\n getMetadata(): Record<string, unknown> {\n if (!this.metadata) return {};\n try {\n return JSON.parse(this.metadata);\n } catch {\n return {};\n }\n }\n\n /**\n * Set metadata from object\n */\n setMetadata(data: Record<string, unknown>): void {\n this.metadata = JSON.stringify(data);\n }\n\n /**\n * Mark message as read\n */\n async markRead(): Promise<void> {\n this.isRead = true;\n this.updatedAt = new Date();\n await this.save();\n }\n\n /**\n * Mark message as unread\n */\n async markUnread(): Promise<void> {\n this.isRead = false;\n this.updatedAt = new Date();\n await this.save();\n }\n\n /**\n * Toggle flagged status\n */\n async toggleFlagged(): Promise<void> {\n this.isFlagged = !this.isFlagged;\n this.updatedAt = new Date();\n await this.save();\n }\n\n /**\n * Check if message is unread\n */\n isUnread(): boolean {\n return !this.isRead;\n }\n\n /**\n * Get a short preview of the message body\n */\n getPreview(maxLength = 200): string {\n const text = this.body || '';\n if (text.length <= maxLength) return text;\n return `${text.slice(0, maxLength)}...`;\n }\n\n /**\n * Get the account for this message\n */\n async getAccount() {\n if (!this.accountId) return null;\n\n const { AccountCollection } = await import(\n '../collections/AccountCollection'\n );\n const collection = await AccountCollection.create(this.options);\n\n return await collection.get({ id: this.accountId });\n }\n\n async getEndpoint() {\n if (!this.endpointId) return null;\n const { MessagingEndpointCollection } = await import(\n '../collections/MessagingEndpointCollection.js'\n );\n const collection = await MessagingEndpointCollection.create(this.options);\n return collection.get({ id: this.endpointId });\n }\n\n /**\n * Get messages in the same thread\n */\n async getThreadMessages(): Promise<Message[]> {\n if (!this.threadId) return [this];\n\n const { MessageCollection } = await import(\n '../collections/MessageCollection'\n );\n const collection = await MessageCollection.create(this.options);\n\n return await collection.list({ where: { threadId: this.threadId } });\n }\n\n /**\n * Get attachments for this message\n */\n async getAttachments() {\n const { AttachmentCollection } = await import(\n '../collections/AttachmentCollection'\n );\n const collection = await AttachmentCollection.create(this.options);\n\n return await collection.list({ where: { messageId: this.id } });\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Send Lifecycle\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Send this message via its account's sender\n */\n async send(options?: SendMessageOptions): Promise<MessageSendResult> {\n // Entry guard: a message that is already in flight or delivered must not be\n // sent again. Catches same-instance double-clicks before any work happens.\n if (this.sendStatus === 'sending' || this.sendStatus === 'sent') {\n return {\n success: false,\n error: `Cannot send: message is already '${this.sendStatus}'`,\n sentAt: new Date(),\n };\n }\n\n // Resolve account\n const account = await this.getAccount();\n if (!account) {\n const result: MessageSendResult = {\n success: false,\n error: 'No account associated with this message',\n sentAt: new Date(),\n };\n this.sendStatus = 'failed';\n this.sendError = result.error ?? '';\n this.updatedAt = new Date();\n await this.save();\n return result;\n }\n\n if (!account.isActive) {\n const result: MessageSendResult = {\n success: false,\n error: 'Messaging account is inactive',\n sentAt: new Date(),\n };\n this.sendStatus = 'failed';\n this.sendError = result.error ?? '';\n this.updatedAt = new Date();\n await this.save();\n return result;\n }\n\n // Get sender from account\n let sender: Awaited<ReturnType<typeof account.createSender>>;\n try {\n sender = await account.createSender();\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n this.sendStatus = 'failed';\n this.sendError = errorMessage;\n this.updatedAt = new Date();\n await this.save();\n return {\n success: false,\n error: errorMessage,\n sentAt: new Date(),\n };\n }\n\n // Claim the send. For a persisted row, do a compare-and-set so only one\n // concurrent sender wins: flip send_status to 'sending' atomically, gated on\n // the status we observed. If no row matched, another sender already claimed\n // it — abort without delivering (prevents duplicate sends). For an unsaved\n // draft there is no row yet, so the in-memory transition + save() (INSERT)\n // serves as the claim.\n const claimFromStatus = this.sendStatus;\n if (this.isPersisted && this.id) {\n const loadedRevision = this.updated_at;\n if (!loadedRevision) {\n return {\n success: false,\n error: 'Cannot send: persisted message has no loaded revision',\n sentAt: new Date(),\n };\n }\n const claimedRevision = new Date(\n Math.max(Date.now(), loadedRevision.getTime() + 1),\n );\n const useEmbeddedFallback = usesEmbeddedRevisionFallback(this.db);\n let claimed = false;\n await withEmbeddedWriteQueue(this.db, useEmbeddedFallback, async () => {\n if (useEmbeddedFallback) {\n const current = await this.getCanonicalPersistedRow({ id: this.id });\n const storedRevision = current?.updated_at;\n const currentRevision =\n storedRevision instanceof Date\n ? storedRevision.getTime()\n : typeof storedRevision === 'string'\n ? Date.parse(storedRevision)\n : Number.NaN;\n if (\n !current ||\n current.send_status !== claimFromStatus ||\n !Number.isFinite(currentRevision) ||\n currentRevision !== loadedRevision.getTime()\n )\n return;\n await this.db.upsert(this.tableName, ['id'], {\n ...current,\n send_status: 'sending',\n updated_at: claimedRevision,\n });\n claimed = true;\n return;\n }\n const claim = await this.db.update(\n this.tableName,\n {\n id: this.id,\n send_status: claimFromStatus,\n updated_at: loadedRevision.toISOString(),\n },\n { send_status: 'sending', updated_at: claimedRevision },\n );\n claimed = claim?.affected === 1;\n });\n if (!claimed) {\n return {\n success: false,\n error: 'Cannot send: message is already being sent',\n sentAt: new Date(),\n };\n }\n this.sendStatus = 'sending';\n this.updated_at = claimedRevision;\n this.updatedAt = new Date();\n } else {\n this.sendStatus = 'sending';\n this.updatedAt = new Date();\n await this.save();\n }\n\n let result: MessageSendResult;\n try {\n result = await sender.send(this, options);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n result = {\n success: false,\n error: errorMessage,\n sentAt: new Date(),\n };\n }\n await this.finalizeSendLifecycle(result);\n return result;\n }\n\n /**\n * Retry sending a failed message\n */\n async retrySend(options?: SendMessageOptions): Promise<MessageSendResult> {\n if (this.sendStatus !== 'failed') {\n return {\n success: false,\n error: `Cannot retry: message status is '${this.sendStatus}', expected 'failed'`,\n sentAt: new Date(),\n };\n }\n\n if (this.retryCount >= this.maxRetries) {\n return {\n success: false,\n error: `Retry budget exhausted (${this.retryCount}/${this.maxRetries})`,\n sentAt: new Date(),\n };\n }\n\n this.retryCount++;\n this.updatedAt = new Date();\n await this.save();\n\n return this.send(options);\n }\n\n /**\n * Options for a derived draft (reply/forward) built from this message. Carries\n * the DB connection + tenant context from `this.options`, but strips this\n * message's own identity fields. When this message was hydrated from the DB,\n * `this.options` holds the row's `id`/`slug`/`context`/`_skipLoad`; spreading\n * those into a new draft would make `draft.save()` upsert onto the natural-key\n * conflict columns (`slug`/`context`/`_meta_type`) and overwrite the ORIGINAL\n * message instead of inserting a new row. See EmailAccount.childOptions().\n */\n protected draftOptions(): Record<string, unknown> {\n const rest = { ...(this.options as Record<string, unknown>) };\n delete rest.id;\n delete rest.slug;\n delete rest.context;\n delete rest._skipLoad;\n return rest;\n }\n\n /**\n * Create a reply to this message (returns unsaved draft)\n */\n createReply(_options?: { replyAll?: boolean }): Message {\n const reply = new (this.constructor as typeof Message)({\n ...this.draftOptions(),\n id: undefined,\n accountId: this.accountId,\n threadId: this.threadId || this.id || '',\n subject: this.subject.startsWith('Re:')\n ? this.subject\n : `Re: ${this.subject}`,\n toAddresses: JSON.stringify([\n { address: this.fromAddress, name: this.fromName },\n ]),\n fromAddress: '',\n fromName: '',\n body: this.buildQuotedBody(),\n inReplyToMessageId: this.id || '',\n sendStatus: 'draft',\n isRead: true,\n date: null,\n createdAt: undefined,\n updatedAt: undefined,\n });\n\n return reply;\n }\n\n /**\n * Create a forward of this message (returns unsaved draft)\n */\n createForward(): Message {\n const forward = new (this.constructor as typeof Message)({\n ...this.draftOptions(),\n id: undefined,\n accountId: this.accountId,\n threadId: '',\n subject: this.subject.startsWith('Fwd:')\n ? this.subject\n : `Fwd: ${this.subject}`,\n toAddresses: '[]',\n fromAddress: '',\n fromName: '',\n body: this.buildQuotedBody(),\n hasAttachments: this.hasAttachments,\n inReplyToMessageId: '',\n sendStatus: 'draft',\n isRead: true,\n date: null,\n createdAt: undefined,\n updatedAt: undefined,\n });\n\n return forward;\n }\n\n /**\n * Build quoted body for reply/forward\n */\n protected buildQuotedBody(): string {\n const dateStr = this.date ? this.date.toLocaleString() : 'unknown date';\n const from = this.fromName\n ? `${this.fromName} <${this.fromAddress}>`\n : this.fromAddress;\n\n const quotedLines = (this.body || '')\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n');\n\n return `\\n\\nOn ${dateStr}, ${from} wrote:\\n${quotedLines}`;\n }\n}\n"],"mappings":";;;;;;;;;;;AA6BO,IAAM,UAAN,cAAsB,WAAW;CAEtC,WAA0B;CAG1B,YAAY;CAIZ,YAA2B;CAE3B,aAA4B;CAC5B,gBAAgB;CAChB,WAAW;CACX,UAAU;CACV,OAAO;CACP,cAAc;CACd,WAAW;CACX,cAAc;CACd,OAAoB;CACpB,SAAS;CACT,YAAY;CACZ,iBAAiB;CACjB,OAAO;CACP,WAAW;CAGX,aAAyB;CACzB,SAAsB;CACtB,YAAY;CACZ,aAAa;CACb,aAAa;CACb,kBAA+B;CAE/B,qBAAqB;CAGrB,4BAAY,IAAI,KAAK;CACrB,4BAAY,IAAI,KAAK;CAErB,YAAY,UAA0B,CAAC,GAAG;EACxC,MAAM,OAAO;EAEb,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;EAC9D,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;EAC9D,IAAI,QAAQ,eAAe,KAAA,GAAW,KAAK,aAAa,QAAQ;EAChE,IAAI,QAAQ,kBAAkB,KAAA,GAC5B,KAAK,gBAAgB,QAAQ;EAC/B,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,OAAO,QAAQ;EACpD,IAAI,QAAQ,gBAAgB,KAAA,GAC1B,KAAK,cAAc,QAAQ;EAC7B,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,gBAAgB,KAAA,GAC1B,KAAK,cAAc,QAAQ;EAC7B,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,OAAO,QAAQ,QAAQ;EAC5D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;EACxD,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;EAC9D,IAAI,QAAQ,mBAAmB,KAAA,GAC7B,KAAK,iBAAiB,QAAQ;EAChC,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,OAAO,QAAQ;EACpD,IAAI,QAAQ,aAAa,KAAA,GAAW,KAAK,WAAW,QAAQ;EAC5D,IAAI,QAAQ,eAAe,KAAA,GAAW,KAAK,aAAa,QAAQ;EAChE,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ,UAAU;EAClE,IAAI,QAAQ,cAAc,KAAA,GAAW,KAAK,YAAY,QAAQ;EAC9D,IAAI,QAAQ,eAAe,KAAA,GAAW,KAAK,aAAa,QAAQ;EAChE,IAAI,QAAQ,eAAe,KAAA,GAAW,KAAK,aAAa,QAAQ;EAChE,IAAI,QAAQ,oBAAoB,KAAA,GAC9B,KAAK,kBAAkB,QAAQ,mBAAmB;EACpD,IAAI,QAAQ,uBAAuB,KAAA,GACjC,KAAK,qBAAqB,QAAQ;EACpC,IAAI,QAAQ,WAAW,KAAK,YAAY,QAAQ;EAChD,IAAI,QAAQ,WAAW,KAAK,YAAY,QAAQ;CAClD;CAEA,MAAc,sBACZ,QACe;EACf,IAAI,CAAC,KAAK,IAAI,MAAM,IAAI,MAAM,yCAAyC;EAEvE,MAAM,aAAyB,OAAO,UAAU,SAAS;EACzD,MAAM,YAAY,OAAO,UAAU,KAAM,OAAO,SAAS;EACzD,MAAM,sBAAsB,6BAA6B,KAAK,EAAE;EAChE,KAAA,IAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;GAC/C,IAAI;GACJ,MAAM,uBAAuB,KAAK,IAAI,qBAAqB,YAAY;IACrE,MAAM,UAAU,MAAM,KAAK,yBAAyB,EAAE,IAAI,KAAK,GAAG,CAAC;IACnE,IAAI,SAAS,gBAAgB,WAAW;IACxC,MAAM,kBAAkB,QAAQ;IAChC,MAAM,oBAAoB,IAAI,KAAK,OAAO,eAAe,CAAC,CAAA,CAAE,QAAQ;IACpE,IAAI,CAAC,OAAO,SAAS,iBAAiB,GAAG;IACzC,eAAe,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,GAAG,oBAAoB,CAAC,CAAC;IACnE,MAAM,SAAS;KACb,aAAa;KACb,SAAS,OAAO,UAAU,OAAO,SAAS;KAC1C,YAAY;KACZ,YAAY;IACd;IACA,IAAI,qBAAqB;KACvB,MAAM,KAAK,GAAG,OAAO,KAAK,WAAW,CAAC,IAAI,GAAG;MAC3C,GAAG;MACH,GAAG;KACL,CAAC;KACD;IACF;IACA,MAAM,KAAK,GAAG,OACZ,KAAK,WACL;KACE,IAAI,KAAK;KACT,aAAa;KACb,YACE,2BAA2B,OACvB,gBAAgB,YAAY,IAC5B;IACR,GACA,MACF;GACF,CAAC;GAID,MAAM,WAAW,MAAM,KAAK,yBAAyB,EAAE,IAAI,KAAK,GAAG,CAAC;GACpE,IAAI,UAAU,gBAAgB,YAAY;IACxC,MAAM,KAAK,wBAAwB,QAAQ;IAC3C;GACF;EACF;EACA,MAAM,IAAI,MAAM,iDAAiD;CACnE;;;;CAKA,iBAA4D;EAC1D,IAAI,CAAC,KAAK,aAAa,OAAO,CAAC;EAC/B,IAAI;GACF,OAAO,KAAK,MAAM,KAAK,WAAW;EACpC,QAAQ;GACN,OAAO,CAAC;EACV;CACF;;;;CAKA,eAAe,WAA4D;EACzE,KAAK,cAAc,KAAK,UAAU,SAAS;CAC7C;;;;CAKA,cAAuC;EACrC,IAAI,CAAC,KAAK,UAAU,OAAO,CAAC;EAC5B,IAAI;GACF,OAAO,KAAK,MAAM,KAAK,QAAQ;EACjC,QAAQ;GACN,OAAO,CAAC;EACV;CACF;;;;CAKA,YAAY,MAAqC;EAC/C,KAAK,WAAW,KAAK,UAAU,IAAI;CACrC;;;;CAKA,MAAM,WAA0B;EAC9B,KAAK,SAAS;EACd,KAAK,4BAAY,IAAI,KAAK;EAC1B,MAAM,KAAK,KAAK;CAClB;;;;CAKA,MAAM,aAA4B;EAChC,KAAK,SAAS;EACd,KAAK,4BAAY,IAAI,KAAK;EAC1B,MAAM,KAAK,KAAK;CAClB;;;;CAKA,MAAM,gBAA+B;EACnC,KAAK,YAAY,CAAC,KAAK;EACvB,KAAK,4BAAY,IAAI,KAAK;EAC1B,MAAM,KAAK,KAAK;CAClB;;;;CAKA,WAAoB;EAClB,OAAO,CAAC,KAAK;CACf;;;;CAKA,WAAW,YAAY,KAAa;EAClC,MAAM,OAAO,KAAK,QAAQ;EAC1B,IAAI,KAAK,UAAU,WAAW,OAAO;EACrC,OAAO,GAAG,KAAK,MAAM,GAAG,SAAS,EAAC;CACpC;;;;CAKA,MAAM,aAAa;EACjB,IAAI,CAAC,KAAK,WAAW,OAAO;EAE5B,MAAM,EAAE,sBAAsB,MAAM,OAClC,kCAAA,CAAA,MAAA,MAAA,EAAA,CAAA;EAIF,OAAO,OAAM,MAFY,kBAAkB,OAAO,KAAK,OAAO,EAAA,CAEtC,IAAI,EAAE,IAAI,KAAK,UAAU,CAAC;CACpD;CAEA,MAAM,cAAc;EAClB,IAAI,CAAC,KAAK,YAAY,OAAO;EAC7B,MAAM,EAAE,gCAAgC,MAAM,OAC5C,cAAA,CAAA,MAAA,MAAA,EAAA,CAAA;EAGF,QAAO,MADkB,4BAA4B,OAAO,KAAK,OAAO,EAAA,CACtD,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC;CAC/C;;;;CAKA,MAAM,oBAAwC;EAC5C,IAAI,CAAC,KAAK,UAAU,OAAO,CAAC,IAAI;EAEhC,MAAM,EAAE,sBAAsB,MAAM,OAClC,kCAAA,CAAA,MAAA,MAAA,EAAA,CAAA;EAIF,OAAO,OAAM,MAFY,kBAAkB,OAAO,KAAK,OAAO,EAAA,CAEtC,KAAK,EAAE,OAAO,EAAE,UAAU,KAAK,SAAS,EAAE,CAAC;CACrE;;;;CAKA,MAAM,iBAAiB;EACrB,MAAM,EAAE,yBAAyB,MAAM,OACrC,cAAA,CAAA,MAAA,MAAA,EAAA,CAAA;EAIF,OAAO,OAAM,MAFY,qBAAqB,OAAO,KAAK,OAAO,EAAA,CAEzC,KAAK,EAAE,OAAO,EAAE,WAAW,KAAK,GAAG,EAAE,CAAC;CAChE;;;;CASA,MAAM,KAAK,SAA0D;EAGnE,IAAI,KAAK,eAAe,aAAa,KAAK,eAAe,QACvD,OAAO;GACL,SAAS;GACT,OAAO,oCAAoC,KAAK,WAAU;GAC1D,wBAAQ,IAAI,KAAK;EACnB;EAIF,MAAM,UAAU,MAAM,KAAK,WAAW;EACtC,IAAI,CAAC,SAAS;GACZ,MAAMA,UAA4B;IAChC,SAAS;IACT,OAAO;IACP,wBAAQ,IAAI,KAAK;GACnB;GACA,KAAK,aAAa;GAClB,KAAK,YAAYA,QAAO,SAAS;GACjC,KAAK,4BAAY,IAAI,KAAK;GAC1B,MAAM,KAAK,KAAK;GAChB,OAAOA;EACT;EAEA,IAAI,CAAC,QAAQ,UAAU;GACrB,MAAMA,UAA4B;IAChC,SAAS;IACT,OAAO;IACP,wBAAQ,IAAI,KAAK;GACnB;GACA,KAAK,aAAa;GAClB,KAAK,YAAYA,QAAO,SAAS;GACjC,KAAK,4BAAY,IAAI,KAAK;GAC1B,MAAM,KAAK,KAAK;GAChB,OAAOA;EACT;EAGA,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,QAAQ,aAAa;EACtC,SAAS,OAAO;GACd,MAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACvD,KAAK,aAAa;GAClB,KAAK,YAAY;GACjB,KAAK,4BAAY,IAAI,KAAK;GAC1B,MAAM,KAAK,KAAK;GAChB,OAAO;IACL,SAAS;IACT,OAAO;IACP,wBAAQ,IAAI,KAAK;GACnB;EACF;EAQA,MAAM,kBAAkB,KAAK;EAC7B,IAAI,KAAK,eAAe,KAAK,IAAI;GAC/B,MAAM,iBAAiB,KAAK;GAC5B,IAAI,CAAC,gBACH,OAAO;IACL,SAAS;IACT,OAAO;IACP,wBAAQ,IAAI,KAAK;GACnB;GAEF,MAAM,kBAAkB,IAAI,KAC1B,KAAK,IAAI,KAAK,IAAI,GAAG,eAAe,QAAQ,IAAI,CAAC,CACnD;GACA,MAAM,sBAAsB,6BAA6B,KAAK,EAAE;GAChE,IAAI,UAAU;GACd,MAAM,uBAAuB,KAAK,IAAI,qBAAqB,YAAY;IACrE,IAAI,qBAAqB;KACvB,MAAM,UAAU,MAAM,KAAK,yBAAyB,EAAE,IAAI,KAAK,GAAG,CAAC;KACnE,MAAM,iBAAiB,SAAS;KAChC,MAAM,kBACJ,0BAA0B,OACtB,eAAe,QAAQ,IACvB,OAAO,mBAAmB,WACxB,KAAK,MAAM,cAAc,IACzB;KACR,IACE,CAAC,WACD,QAAQ,gBAAgB,mBACxB,CAAC,OAAO,SAAS,eAAe,KAChC,oBAAoB,eAAe,QAAQ,GAE3C;KACF,MAAM,KAAK,GAAG,OAAO,KAAK,WAAW,CAAC,IAAI,GAAG;MAC3C,GAAG;MACH,aAAa;MACb,YAAY;KACd,CAAC;KACD,UAAU;KACV;IACF;IAUA,WAAU,MATU,KAAK,GAAG,OAC1B,KAAK,WACL;KACE,IAAI,KAAK;KACT,aAAa;KACb,YAAY,eAAe,YAAY;IACzC,GACA;KAAE,aAAa;KAAW,YAAY;IAAgB,CACxD,EAAA,EACiB,aAAa;GAChC,CAAC;GACD,IAAI,CAAC,SACH,OAAO;IACL,SAAS;IACT,OAAO;IACP,wBAAQ,IAAI,KAAK;GACnB;GAEF,KAAK,aAAa;GAClB,KAAK,aAAa;GAClB,KAAK,4BAAY,IAAI,KAAK;EAC5B,OAAO;GACL,KAAK,aAAa;GAClB,KAAK,4BAAY,IAAI,KAAK;GAC1B,MAAM,KAAK,KAAK;EAClB;EAEA,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,OAAO,KAAK,MAAM,OAAO;EAC1C,SAAS,OAAO;GAGd,SAAS;IACP,SAAS;IACT,OAHA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAIrD,wBAAQ,IAAI,KAAK;GACnB;EACF;EACA,MAAM,KAAK,sBAAsB,MAAM;EACvC,OAAO;CACT;;;;CAKA,MAAM,UAAU,SAA0D;EACxE,IAAI,KAAK,eAAe,UACtB,OAAO;GACL,SAAS;GACT,OAAO,oCAAoC,KAAK,WAAU;GAC1D,wBAAQ,IAAI,KAAK;EACnB;EAGF,IAAI,KAAK,cAAc,KAAK,YAC1B,OAAO;GACL,SAAS;GACT,OAAO,2BAA2B,KAAK,WAAU,GAAI,KAAK,WAAU;GACpE,wBAAQ,IAAI,KAAK;EACnB;EAGF,KAAK;EACL,KAAK,4BAAY,IAAI,KAAK;EAC1B,MAAM,KAAK,KAAK;EAEhB,OAAO,KAAK,KAAK,OAAO;CAC1B;;;;;;;;;;CAWU,eAAwC;EAChD,MAAM,OAAO,EAAE,GAAI,KAAK,QAAoC;EAC5D,OAAO,KAAK;EACZ,OAAO,KAAK;EACZ,OAAO,KAAK;EACZ,OAAO,KAAK;EACZ,OAAO;CACT;;;;CAKA,YAAY,UAA4C;EAuBtD,OAAO,IAtBY,KAAK,YAA+B;GACrD,GAAG,KAAK,aAAa;GACrB,IAAI,KAAA;GACJ,WAAW,KAAK;GAChB,UAAU,KAAK,YAAY,KAAK,MAAM;GACtC,SAAS,KAAK,QAAQ,WAAW,KAAK,IAClC,KAAK,UACL,OAAO,KAAK;GAChB,aAAa,KAAK,UAAU,CAC1B;IAAE,SAAS,KAAK;IAAa,MAAM,KAAK;GAAS,CACnD,CAAC;GACD,aAAa;GACb,UAAU;GACV,MAAM,KAAK,gBAAgB;GAC3B,oBAAoB,KAAK,MAAM;GAC/B,YAAY;GACZ,QAAQ;GACR,MAAM;GACN,WAAW,KAAA;GACX,WAAW,KAAA;EACb,CAEO;CACT;;;;CAKA,gBAAyB;EAsBvB,OAAO,IArBc,KAAK,YAA+B;GACvD,GAAG,KAAK,aAAa;GACrB,IAAI,KAAA;GACJ,WAAW,KAAK;GAChB,UAAU;GACV,SAAS,KAAK,QAAQ,WAAW,MAAM,IACnC,KAAK,UACL,QAAQ,KAAK;GACjB,aAAa;GACb,aAAa;GACb,UAAU;GACV,MAAM,KAAK,gBAAgB;GAC3B,gBAAgB,KAAK;GACrB,oBAAoB;GACpB,YAAY;GACZ,QAAQ;GACR,MAAM;GACN,WAAW,KAAA;GACX,WAAW,KAAA;EACb,CAEO;CACT;;;;CAKU,kBAA0B;EAWlC,OAAO;;KAVS,KAAK,OAAO,KAAK,KAAK,eAAe,IAAI,eAUjC,IATX,KAAK,WACd,GAAG,KAAK,SAAQ,IAAK,KAAK,YAAW,KACrC,KAAK,YAOwB;GALZ,KAAK,QAAQ,GAAA,CAC/B,MAAM,IAAI,CAAA,CACV,KAAK,SAAS,KAAK,MAAM,CAAA,CACzB,KAAK,IAEqC;CAC/C;AACF;AAnhBE,gBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GADjB,QAEX,WAAA,YAAA,CAAA;AAGA,gBAAA,CADC,WAAW,SAAS,CAAA,GAJV,QAKX,WAAA,aAAA,CAAA;AAIA,gBAAA,CAHC,gBAAgB,6CAA6C,EAC5D,UAAU,KACZ,CAAC,CAAA,GARU,QASX,WAAA,aAAA,CAAA;AAEA,gBAAA,CADC,WAAW,mBAAmB,CAAA,GAVpB,QAWX,WAAA,cAAA,CAAA;AAuBA,gBAAA,CADC,WAAW,SAAS,CAAA,GAjCV,QAkCX,WAAA,sBAAA,CAAA;AAlCW,UAAN,gBAAA,CAPN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,eAAe;CACf,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAE;CAChC,KAAK,EAAE,cAAc,KAAK;AAC5B,CAAC,CAAA,GACY,OAAA"}
|