@remit/backend 0.0.143 → 0.0.145

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 (279) hide show
  1. package/dist/dev-server/content-handler.d.ts +38 -0
  2. package/dist/dev-server/content-handler.js +46 -0
  3. package/dist/src/api.d.ts +5 -0
  4. package/dist/src/api.js +73 -0
  5. package/dist/src/auth.d.ts +24 -0
  6. package/dist/src/auth.js +81 -0
  7. package/dist/src/config/api-config.d.ts +20 -0
  8. package/dist/src/config/api-config.js +1 -0
  9. package/dist/src/config/from-env.d.ts +2 -0
  10. package/dist/src/config/from-env.js +65 -0
  11. package/dist/src/config/msoauth-secrets-manager.d.ts +2 -0
  12. package/dist/src/config/msoauth-secrets-manager.js +28 -0
  13. package/dist/src/config/msoauth.d.ts +19 -0
  14. package/dist/src/config/msoauth.js +6 -0
  15. package/{src/derive/autoMoved.ts → dist/src/derive/autoMoved.d.ts} +1 -29
  16. package/dist/src/derive/autoMoved.js +49 -0
  17. package/dist/src/derive/contentSignature.d.ts +40 -0
  18. package/{src/derive/contentSignature.ts → dist/src/derive/contentSignature.js} +23 -77
  19. package/dist/src/derive/contentUrl.d.ts +33 -0
  20. package/dist/src/derive/contentUrl.js +27 -0
  21. package/{src/derive/deriveMuted.ts → dist/src/derive/deriveMuted.d.ts} +1 -3
  22. package/dist/src/derive/deriveMuted.js +7 -0
  23. package/dist/src/derive/enrichThreadRows.d.ts +118 -0
  24. package/dist/src/derive/enrichThreadRows.js +151 -0
  25. package/dist/src/derive/filterThreadCriteria.d.ts +23 -0
  26. package/dist/src/derive/filterThreadCriteria.js +24 -0
  27. package/{src/derive/pendingMoveCounts.ts → dist/src/derive/pendingMoveCounts.d.ts} +5 -53
  28. package/dist/src/derive/pendingMoveCounts.js +53 -0
  29. package/{src/derive/senderTrust.ts → dist/src/derive/senderTrust.d.ts} +1 -9
  30. package/dist/src/derive/senderTrust.js +19 -0
  31. package/dist/src/error.d.ts +21 -0
  32. package/dist/src/error.js +85 -0
  33. package/dist/src/handlers/account-guards.d.ts +36 -0
  34. package/dist/src/handlers/account-guards.js +107 -0
  35. package/dist/src/handlers/account-oauth.d.ts +15 -0
  36. package/dist/src/handlers/account-oauth.js +334 -0
  37. package/dist/src/handlers/account-overrides.d.ts +53 -0
  38. package/dist/src/handlers/account-overrides.js +233 -0
  39. package/dist/src/handlers/account-signature.d.ts +28 -0
  40. package/dist/src/handlers/account-signature.js +85 -0
  41. package/dist/src/handlers/account.d.ts +7 -0
  42. package/dist/src/handlers/account.js +337 -0
  43. package/dist/src/handlers/address.d.ts +35 -0
  44. package/dist/src/handlers/address.js +133 -0
  45. package/dist/src/handlers/calendar-event.d.ts +70 -0
  46. package/dist/src/handlers/calendar-event.js +417 -0
  47. package/dist/src/handlers/calendar-feed.d.ts +47 -0
  48. package/dist/src/handlers/calendar-feed.js +184 -0
  49. package/dist/src/handlers/calendar-suggestion.d.ts +47 -0
  50. package/dist/src/handlers/calendar-suggestion.js +204 -0
  51. package/dist/src/handlers/calendar.d.ts +116 -0
  52. package/dist/src/handlers/calendar.js +323 -0
  53. package/dist/src/handlers/config.d.ts +43 -0
  54. package/dist/src/handlers/config.js +164 -0
  55. package/dist/src/handlers/ensure-account-config.d.ts +9 -0
  56. package/dist/src/handlers/ensure-account-config.js +27 -0
  57. package/dist/src/handlers/filter.d.ts +3 -0
  58. package/dist/src/handlers/filter.js +88 -0
  59. package/dist/src/handlers/folder-role-appointments.d.ts +68 -0
  60. package/dist/src/handlers/folder-role-appointments.js +168 -0
  61. package/dist/src/handlers/folder-role-labels.d.ts +26 -0
  62. package/dist/src/handlers/folder-role-labels.js +40 -0
  63. package/dist/src/handlers/folder-role.d.ts +9 -0
  64. package/dist/src/handlers/folder-role.js +37 -0
  65. package/dist/src/handlers/index.d.ts +88 -0
  66. package/dist/src/handlers/index.js +62 -0
  67. package/dist/src/handlers/label.d.ts +43 -0
  68. package/dist/src/handlers/label.js +107 -0
  69. package/dist/src/handlers/mailbox-bindings.d.ts +41 -0
  70. package/dist/src/handlers/mailbox-bindings.js +61 -0
  71. package/dist/src/handlers/mailbox.d.ts +23 -0
  72. package/dist/src/handlers/mailbox.js +211 -0
  73. package/dist/src/handlers/me.d.ts +2 -0
  74. package/dist/src/handlers/me.js +117 -0
  75. package/dist/src/handlers/message.d.ts +82 -0
  76. package/dist/src/handlers/message.js +595 -0
  77. package/dist/src/handlers/organize.d.ts +10 -0
  78. package/dist/src/handlers/organize.js +103 -0
  79. package/dist/src/handlers/outbox-attachment.d.ts +25 -0
  80. package/dist/src/handlers/outbox-attachment.js +77 -0
  81. package/dist/src/handlers/outbox.d.ts +4 -0
  82. package/dist/src/handlers/outbox.js +136 -0
  83. package/dist/src/handlers/sync-progress.d.ts +46 -0
  84. package/dist/src/handlers/sync-progress.js +63 -0
  85. package/dist/src/handlers/sync.d.ts +28 -0
  86. package/dist/src/handlers/sync.js +73 -0
  87. package/dist/src/handlers/system-update.d.ts +2 -0
  88. package/dist/src/handlers/system-update.js +159 -0
  89. package/dist/src/handlers/thread.d.ts +148 -0
  90. package/dist/src/handlers/thread.js +228 -0
  91. package/dist/src/handlers/unified-threads.d.ts +247 -0
  92. package/dist/src/handlers/unified-threads.js +406 -0
  93. package/{src/handlers/vip-suggestions.ts → dist/src/handlers/vip-suggestions.d.ts} +1 -13
  94. package/dist/src/handlers/vip-suggestions.js +16 -0
  95. package/dist/src/index.d.ts +5 -0
  96. package/dist/src/index.js +53 -0
  97. package/{src/json.ts → dist/src/json.d.ts} +1 -4
  98. package/dist/src/json.js +10 -0
  99. package/dist/src/jwt-auth.d.ts +15 -0
  100. package/dist/src/jwt-auth.js +45 -0
  101. package/dist/src/lambda.d.ts +13 -0
  102. package/dist/src/lambda.js +33 -0
  103. package/dist/src/request.d.ts +9 -0
  104. package/dist/src/request.js +17 -0
  105. package/dist/src/response.d.ts +44 -0
  106. package/dist/src/response.js +80 -0
  107. package/dist/src/serve.d.ts +31 -0
  108. package/dist/src/serve.js +117 -0
  109. package/dist/src/service/account-deletion.d.ts +20 -0
  110. package/dist/src/service/account-deletion.js +33 -0
  111. package/dist/src/service/compose-sqlite.d.ts +3 -0
  112. package/dist/src/service/compose-sqlite.js +13 -0
  113. package/dist/src/service/create-remit-client.d.ts +109 -0
  114. package/dist/src/service/create-remit-client.js +188 -0
  115. package/dist/src/service/data-client.d.ts +12 -0
  116. package/dist/src/service/data-client.js +42 -0
  117. package/dist/src/service/export-identity.d.ts +4 -0
  118. package/dist/src/service/export-identity.js +33 -0
  119. package/dist/src/service/fire-and-forget.d.ts +54 -0
  120. package/dist/src/service/fire-and-forget.js +37 -0
  121. package/dist/src/service/organize.d.ts +181 -0
  122. package/dist/src/service/organize.js +367 -0
  123. package/dist/src/service/queues.d.ts +12 -0
  124. package/dist/src/service/queues.js +1 -0
  125. package/dist/src/service/refusal.d.ts +4 -0
  126. package/dist/src/service/refusal.js +20 -0
  127. package/dist/src/service/sender-category.d.ts +51 -0
  128. package/{src/service/sender-category.ts → dist/src/service/sender-category.js} +40 -81
  129. package/dist/src/service/shared-deps.d.ts +6 -0
  130. package/dist/src/service/shared-deps.js +24 -0
  131. package/dist/src/service/trigger-sync.d.ts +57 -0
  132. package/dist/src/service/trigger-sync.js +31 -0
  133. package/dist/src/types.d.ts +72 -0
  134. package/dist/src/types.js +1 -0
  135. package/package.json +125 -98
  136. package/.env.test +0 -4
  137. package/dev-server/content-auth.test.ts +0 -113
  138. package/dev-server/content-auth.ts +0 -54
  139. package/dev-server/content-handler.test.ts +0 -105
  140. package/dev-server/content-handler.ts +0 -79
  141. package/dev-server/content-path.test.ts +0 -37
  142. package/dev-server/content-path.ts +0 -27
  143. package/dev-server/cors.test.ts +0 -48
  144. package/dev-server/cors.ts +0 -25
  145. package/dev-server/keep-alive.test.ts +0 -24
  146. package/dev-server/lambda-helpers.ts +0 -69
  147. package/dev-server/log-output.test.ts +0 -173
  148. package/dev-server/oauth-callback-route.test.ts +0 -66
  149. package/dev-server/relational-health.test.ts +0 -83
  150. package/dev-server/relational-health.ts +0 -39
  151. package/dev-server/server.ts +0 -384
  152. package/dev-server/sync-age.test.ts +0 -91
  153. package/dev-server/sync-age.ts +0 -55
  154. package/dev-server/upload-handler.ts +0 -132
  155. package/dev-server/upload-route.test.ts +0 -196
  156. package/scripts/backfill-classification.ts +0 -91
  157. package/scripts/backfill-list-id.ts +0 -83
  158. package/scripts/config-save.ts +0 -128
  159. package/src/auth.test.ts +0 -12
  160. package/src/auth.ts +0 -92
  161. package/src/config/msoauth.test.ts +0 -48
  162. package/src/config/msoauth.ts +0 -48
  163. package/src/data-backend.test.ts +0 -23
  164. package/src/data-backend.ts +0 -14
  165. package/src/derive/autoMoved.test.ts +0 -104
  166. package/src/derive/contentSignature.test.ts +0 -161
  167. package/src/derive/contentUrl.test.ts +0 -172
  168. package/src/derive/contentUrl.ts +0 -86
  169. package/src/derive/deriveMuted.test.ts +0 -34
  170. package/src/derive/enrichThreadRows.test.ts +0 -360
  171. package/src/derive/enrichThreadRows.ts +0 -234
  172. package/src/derive/filterThreadCriteria.test.ts +0 -126
  173. package/src/derive/filterThreadCriteria.ts +0 -51
  174. package/src/derive/senderTrust.test.ts +0 -67
  175. package/src/error.test.ts +0 -201
  176. package/src/error.ts +0 -123
  177. package/src/handlers/account-guards.test.ts +0 -67
  178. package/src/handlers/account-guards.ts +0 -169
  179. package/src/handlers/account-oauth-callback.test.ts +0 -155
  180. package/src/handlers/account-oauth-consent.test.ts +0 -336
  181. package/src/handlers/account-oauth-start.test.ts +0 -191
  182. package/src/handlers/account-oauth.test.ts +0 -439
  183. package/src/handlers/account-oauth.ts +0 -635
  184. package/src/handlers/account-overrides.test.ts +0 -88
  185. package/src/handlers/account-overrides.ts +0 -323
  186. package/src/handlers/account-ownership.ts +0 -25
  187. package/src/handlers/account-signature.ts +0 -117
  188. package/src/handlers/account-synced-services.test.ts +0 -245
  189. package/src/handlers/account.ts +0 -552
  190. package/src/handlers/address.test.ts +0 -381
  191. package/src/handlers/address.ts +0 -200
  192. package/src/handlers/calendar-event.test.ts +0 -685
  193. package/src/handlers/calendar-event.ts +0 -738
  194. package/src/handlers/calendar-feed-validators.test.ts +0 -228
  195. package/src/handlers/calendar-feed.test.ts +0 -439
  196. package/src/handlers/calendar-feed.ts +0 -291
  197. package/src/handlers/calendar-sqlite-fixture.ts +0 -36
  198. package/src/handlers/calendar-suggestion.test.ts +0 -1269
  199. package/src/handlers/calendar-suggestion.ts +0 -425
  200. package/src/handlers/calendar.test.ts +0 -1080
  201. package/src/handlers/calendar.ts +0 -517
  202. package/src/handlers/config.test.ts +0 -245
  203. package/src/handlers/config.ts +0 -332
  204. package/src/handlers/ensure-account-config.ts +0 -30
  205. package/src/handlers/filter.test.ts +0 -644
  206. package/src/handlers/filter.ts +0 -518
  207. package/src/handlers/folder-role-appointments.test.ts +0 -438
  208. package/src/handlers/folder-role-appointments.ts +0 -249
  209. package/src/handlers/folder-role-labels.ts +0 -63
  210. package/src/handlers/folder-role.test.ts +0 -209
  211. package/src/handlers/folder-role.ts +0 -80
  212. package/src/handlers/index.ts +0 -83
  213. package/src/handlers/label.test.ts +0 -112
  214. package/src/handlers/label.ts +0 -210
  215. package/src/handlers/mailbox-bindings.ts +0 -111
  216. package/src/handlers/mailbox-rename-refusal.test.ts +0 -247
  217. package/src/handlers/mailbox-settled-target.test.ts +0 -251
  218. package/src/handlers/mailbox.test.ts +0 -280
  219. package/src/handlers/mailbox.ts +0 -581
  220. package/src/handlers/me.test.ts +0 -81
  221. package/src/handlers/me.ts +0 -203
  222. package/src/handlers/message-read-backfill.test.ts +0 -103
  223. package/src/handlers/message.test.ts +0 -131
  224. package/src/handlers/message.ts +0 -1131
  225. package/src/handlers/organize.test.ts +0 -277
  226. package/src/handlers/organize.ts +0 -197
  227. package/src/handlers/outbox-attachment.test.ts +0 -765
  228. package/src/handlers/outbox-attachment.ts +0 -126
  229. package/src/handlers/outbox-list.test.ts +0 -179
  230. package/src/handlers/outbox.test.ts +0 -772
  231. package/src/handlers/outbox.ts +0 -258
  232. package/src/handlers/search.test.ts +0 -161
  233. package/src/handlers/search.ts +0 -105
  234. package/src/handlers/sync-progress.test.ts +0 -205
  235. package/src/handlers/sync-progress.ts +0 -99
  236. package/src/handlers/sync.test.ts +0 -168
  237. package/src/handlers/sync.ts +0 -118
  238. package/src/handlers/system-update.test.ts +0 -497
  239. package/src/handlers/system-update.ts +0 -230
  240. package/src/handlers/thread.test.ts +0 -396
  241. package/src/handlers/thread.ts +0 -427
  242. package/src/handlers/unified-threads-mail-sources.sqlite.test.ts +0 -197
  243. package/src/handlers/unified-threads.test.ts +0 -785
  244. package/src/handlers/unified-threads.ts +0 -669
  245. package/src/index.test.ts +0 -83
  246. package/src/index.ts +0 -228
  247. package/src/jwt-auth.test.ts +0 -152
  248. package/src/jwt-auth.ts +0 -132
  249. package/src/log-redaction.test.ts +0 -144
  250. package/src/openapi-spec.test.ts +0 -118
  251. package/src/request-context.test.ts +0 -61
  252. package/src/request-context.ts +0 -33
  253. package/src/request-pipeline.test.ts +0 -88
  254. package/src/request.ts +0 -10
  255. package/src/response.test.ts +0 -130
  256. package/src/response.ts +0 -144
  257. package/src/service/compose-sqlite.ts +0 -98
  258. package/src/service/config-import.ts +0 -39
  259. package/src/service/create-remit-client.ts +0 -522
  260. package/src/service/data-client.test.ts +0 -97
  261. package/src/service/data-client.ts +0 -58
  262. package/src/service/export-identity.ts +0 -39
  263. package/src/service/filter.ts +0 -40
  264. package/src/service/fire-and-forget.test.ts +0 -126
  265. package/src/service/fire-and-forget.ts +0 -79
  266. package/src/service/localhost-env-config.test.ts +0 -36
  267. package/src/service/organize-queue.ts +0 -98
  268. package/src/service/organize.test.ts +0 -1451
  269. package/src/service/organize.ts +0 -1025
  270. package/src/service/read-path-filter-config.test.ts +0 -243
  271. package/src/service/semantic-capability.test.ts +0 -78
  272. package/src/service/semantic-capability.ts +0 -95
  273. package/src/service/sender-category.test.ts +0 -235
  274. package/src/service/sqs.ts +0 -4
  275. package/src/service/trigger-sync.test.ts +0 -205
  276. package/src/service/trigger-sync.ts +0 -100
  277. package/src/stdout-capture-fixture.ts +0 -40
  278. package/src/types.ts +0 -271
  279. package/tsconfig.json +0 -7
@@ -0,0 +1,109 @@
1
+ import type { IAccountConfigRepository, IAccountExportRequestRepository, IAccountRepository, IAccountSettingRepository, IAddressRepository, ICalendarCollectionRepository, ICalendarEventIndexRepository, ICalendarFeedTokenRepository, ICalendarObjectRepository, ICalendarStore, ICalendarSuggestionRepository, IConfigImportRepository, IEnvelopeRepository, IFilterRepository, ILabelRepository, IMailboxLockRepository, IMailboxRepository, IMailboxSpecialUseRepository, IMessageFlagPushRepository, IMessageFlagRepository, IMessageLabelRepository, IMessagePlacementMoveRepository, IMessageRepository, IOrganizeJobRequestRepository, IOutboxAttachmentRepository, IOutboxMessageRepository, IQuarantineRepository, ISenderSignerStandingRepository, ISyncWarningRepository, IThreadMessageRepository } from "@remit/data-ports";
2
+ import { BodySyncQueueService, BodySyncService, type FilterConfig, FlagQueueService, type IImapConnection, MailboxQueueService, MessageMoveService, OutboxAttachmentService, OutboxQueueService, SpamReportService } from "@remit/mailbox-service";
3
+ import { type SecretsService } from "@remit/secrets-service";
4
+ import type { StorageService } from "@remit/storage-service";
5
+ import type { QueueName, RemitClientQueues } from "./queues.js";
6
+ export interface ConnectionScope {
7
+ getConnection: () => Promise<IImapConnection>;
8
+ disconnect: () => Promise<void>;
9
+ }
10
+ export interface RemitClientCore {
11
+ accountConfig: IAccountConfigRepository;
12
+ account: IAccountRepository;
13
+ accountSetting: IAccountSettingRepository;
14
+ address: IAddressRepository;
15
+ mailbox: IMailboxRepository;
16
+ mailboxSpecialUse: IMailboxSpecialUseRepository;
17
+ mailboxLock: IMailboxLockRepository;
18
+ message: IMessageRepository;
19
+ messageFlag: IMessageFlagRepository;
20
+ outboxMessage: IOutboxMessageRepository;
21
+ threadMessage: IThreadMessageRepository;
22
+ envelope: IEnvelopeRepository;
23
+ accountExportRequest: IAccountExportRequestRepository;
24
+ configImport: IConfigImportRepository;
25
+ quarantine: IQuarantineRepository;
26
+ syncWarning: ISyncWarningRepository;
27
+ organizeJobRequest: IOrganizeJobRequestRepository;
28
+ filter: IFilterRepository;
29
+ label: ILabelRepository;
30
+ messageLabel: IMessageLabelRepository;
31
+ senderSignerStanding: ISenderSignerStandingRepository;
32
+ calendarCollection: ICalendarCollectionRepository;
33
+ calendarObject: ICalendarObjectRepository;
34
+ calendarEventIndex: ICalendarEventIndexRepository;
35
+ calendarFeedToken: ICalendarFeedTokenRepository;
36
+ calendarSuggestion: ICalendarSuggestionRepository;
37
+ calendarStore: ICalendarStore;
38
+ storage: StorageService;
39
+ secrets: SecretsService;
40
+ placementMove: IMessagePlacementMoveRepository;
41
+ flagPush: IMessageFlagPushRepository;
42
+ outboxAttachment: OutboxAttachmentService;
43
+ createConnectionScope: (accountId: string) => Promise<ConnectionScope>;
44
+ }
45
+ export interface SyncQueueServices {
46
+ flagQueue: FlagQueueService;
47
+ mailboxQueue: MailboxQueueService;
48
+ messageMove: MessageMoveService;
49
+ spamReport: SpamReportService;
50
+ }
51
+ export interface SmtpQueueServices {
52
+ outboxQueue: OutboxQueueService;
53
+ }
54
+ export interface BodyQueueServices {
55
+ bodySyncQueue: BodySyncQueueService;
56
+ }
57
+ export interface ReadPathServices {
58
+ bodySync: BodySyncService;
59
+ }
60
+ export type RemitClient<K extends QueueName = QueueName> = RemitClientCore & {
61
+ queues: Pick<RemitClientQueues, K>;
62
+ } & ("sync" extends K ? SyncQueueServices : unknown) & ("smtp" extends K ? SmtpQueueServices : unknown) & ("body" extends K ? BodyQueueServices : unknown) & ("sync" | "messageManagement" extends K ? ReadPathServices : unknown);
63
+ export interface RemitClientRepositories {
64
+ accountConfig: IAccountConfigRepository;
65
+ account: IAccountRepository;
66
+ accountSetting: IAccountSettingRepository;
67
+ address: IAddressRepository;
68
+ mailbox: IMailboxRepository;
69
+ mailboxSpecialUse: IMailboxSpecialUseRepository;
70
+ mailboxLock: IMailboxLockRepository;
71
+ message: IMessageRepository;
72
+ messageFlag: IMessageFlagRepository;
73
+ outboxMessage: IOutboxMessageRepository;
74
+ outboxAttachment: IOutboxAttachmentRepository;
75
+ threadMessage: IThreadMessageRepository;
76
+ envelope: IEnvelopeRepository;
77
+ accountExportRequest: IAccountExportRequestRepository;
78
+ configImport: IConfigImportRepository;
79
+ quarantine: IQuarantineRepository;
80
+ syncWarning: ISyncWarningRepository;
81
+ organizeJobRequest: IOrganizeJobRequestRepository;
82
+ placementMove: IMessagePlacementMoveRepository;
83
+ flagPush: IMessageFlagPushRepository;
84
+ filter: IFilterRepository;
85
+ label: ILabelRepository;
86
+ messageLabel: IMessageLabelRepository;
87
+ senderSignerStanding: ISenderSignerStandingRepository;
88
+ calendarCollection: ICalendarCollectionRepository;
89
+ calendarObject: ICalendarObjectRepository;
90
+ calendarEventIndex: ICalendarEventIndexRepository;
91
+ calendarFeedToken: ICalendarFeedTokenRepository;
92
+ calendarSuggestion: ICalendarSuggestionRepository;
93
+ calendarStore: ICalendarStore;
94
+ }
95
+ export interface RemitClientSharedDeps {
96
+ storage: StorageService;
97
+ secrets: SecretsService;
98
+ queues: RemitClientQueues;
99
+ }
100
+ export interface RemitClientDeps extends RemitClientSharedDeps {
101
+ repositories: RemitClientRepositories;
102
+ }
103
+ export declare const buildReadPathFilterConfig: (repositories: Pick<RemitClientRepositories, "filter" | "messageLabel" | "mailbox" | "message" | "threadMessage" | "placementMove" | "address" | "mailboxSpecialUse">, queues: Pick<RemitClientQueues, "messageManagement">) => FilterConfig;
104
+ export declare const createRemitCore: (deps: Omit<RemitClientDeps, "queues">) => RemitClientCore;
105
+ export declare const composeSyncServices: (core: RemitClientCore, queues: Pick<RemitClientQueues, "sync">) => SyncQueueServices;
106
+ export declare const composeSmtpServices: (core: RemitClientCore, queues: Pick<RemitClientQueues, "smtp">) => SmtpQueueServices;
107
+ export declare const composeBodyServices: (queues: Pick<RemitClientQueues, "body">) => BodyQueueServices;
108
+ export declare const composeReadPathServices: (core: RemitClientCore, queues: Pick<RemitClientQueues, "messageManagement">, sync: Pick<SyncQueueServices, "flagQueue">) => ReadPathServices;
109
+ export declare const createRemitClient: (deps: RemitClientDeps) => RemitClient;
@@ -0,0 +1,188 @@
1
+ import { logger } from "@remit/logger";
2
+ import { BodySyncQueueService, BodySyncService, createConnection, FlagPushService, FlagQueueService, MailboxQueueService, MessageMoveService, OutboxAttachmentService, OutboxQueueService, PlacementMoveService, SpamReportService, SyncWarningService, } from "@remit/mailbox-service";
3
+ import { buildFilterConfig } from "@remit/mailbox-service/filter-config";
4
+ import { deserializeEncryptedPayload, } from "@remit/secrets-service";
5
+ const buildConnectionScope = (accountService, secretsService) => async (accountId) => {
6
+ const account = await accountService.get(accountId);
7
+ if (!account.passwordHash) {
8
+ throw new Error(`Account ${accountId} has no passwordHash — OAuth accounts cannot use this connection path`);
9
+ }
10
+ const password = await secretsService.decrypt(deserializeEncryptedPayload(JSON.parse(account.passwordHash)));
11
+ let connection = null;
12
+ let connectPromise = null;
13
+ const getConnection = async () => {
14
+ if (connectPromise) {
15
+ return connectPromise;
16
+ }
17
+ const conn = createConnection({
18
+ user: account.username,
19
+ credentials: { kind: "password", password },
20
+ host: account.imapHost,
21
+ port: account.imapPort,
22
+ tls: account.imapTls,
23
+ });
24
+ connection = conn;
25
+ connectPromise = conn.connect().then(() => conn);
26
+ return connectPromise;
27
+ };
28
+ const disconnect = async () => {
29
+ if (connection) {
30
+ await connection.disconnect();
31
+ connection = null;
32
+ connectPromise = null;
33
+ }
34
+ };
35
+ return { getConnection, disconnect };
36
+ };
37
+ // The read-path body backfill (describeMessage / getRawMessage →
38
+ // fetchAndGetBody) materializes a body that was never sync-synced, and must run
39
+ // the account's standing filters the same way the imap-worker's sync path does.
40
+ // Without this, a message a user opens before background body-sync is classified
41
+ // but never filter-evaluated — and once its body is stored the filter-capable
42
+ // sync path skips it, so an explicit standing filter silently never fires (issue
43
+ // #223). Only the explicit user-rule half is wired here; heuristic classifier
44
+ // placement stays on the sync path. It shares `buildFilterConfig` with
45
+ // `sync-message-body.ts`, so a filter fires on either (issue #298). Over the
46
+ // message-management queue's local-first PlacementMoveService.
47
+ export const buildReadPathFilterConfig = (repositories, queues) => {
48
+ const { messageManagement } = queues;
49
+ return buildFilterConfig({
50
+ filterService: repositories.filter,
51
+ messageLabelService: repositories.messageLabel,
52
+ mailboxService: repositories.mailbox,
53
+ placementMoveService: new PlacementMoveService({
54
+ messageService: repositories.message,
55
+ threadMessageService: repositories.threadMessage,
56
+ markerService: repositories.placementMove,
57
+ addressService: repositories.address,
58
+ mailboxSpecialUseService: repositories.mailboxSpecialUse,
59
+ mailboxService: repositories.mailbox,
60
+ queue: messageManagement,
61
+ }),
62
+ });
63
+ };
64
+ export const createRemitCore = (deps) => {
65
+ const { repositories, storage, secrets } = deps;
66
+ return {
67
+ accountConfig: repositories.accountConfig,
68
+ account: repositories.account,
69
+ accountSetting: repositories.accountSetting,
70
+ address: repositories.address,
71
+ mailbox: repositories.mailbox,
72
+ mailboxSpecialUse: repositories.mailboxSpecialUse,
73
+ mailboxLock: repositories.mailboxLock,
74
+ message: repositories.message,
75
+ messageFlag: repositories.messageFlag,
76
+ outboxMessage: repositories.outboxMessage,
77
+ threadMessage: repositories.threadMessage,
78
+ envelope: repositories.envelope,
79
+ accountExportRequest: repositories.accountExportRequest,
80
+ configImport: repositories.configImport,
81
+ quarantine: repositories.quarantine,
82
+ syncWarning: repositories.syncWarning,
83
+ organizeJobRequest: repositories.organizeJobRequest,
84
+ filter: repositories.filter,
85
+ label: repositories.label,
86
+ messageLabel: repositories.messageLabel,
87
+ senderSignerStanding: repositories.senderSignerStanding,
88
+ calendarCollection: repositories.calendarCollection,
89
+ calendarObject: repositories.calendarObject,
90
+ calendarEventIndex: repositories.calendarEventIndex,
91
+ calendarFeedToken: repositories.calendarFeedToken,
92
+ calendarSuggestion: repositories.calendarSuggestion,
93
+ calendarStore: repositories.calendarStore,
94
+ storage,
95
+ secrets,
96
+ placementMove: repositories.placementMove,
97
+ flagPush: repositories.flagPush,
98
+ // One instance, shared with the outbox queue service: the per-draft chain
99
+ // that makes the attachment cap safe under concurrency lives on it, and a
100
+ // discard racing an upload has to take the same lock the upload does.
101
+ outboxAttachment: new OutboxAttachmentService({
102
+ outboxMessageService: repositories.outboxMessage,
103
+ outboxAttachmentService: repositories.outboxAttachment,
104
+ storage,
105
+ }),
106
+ createConnectionScope: buildConnectionScope(repositories.account, secrets),
107
+ };
108
+ };
109
+ export const composeSyncServices = (core, queues) => {
110
+ const flagPushService = new FlagPushService({
111
+ markerService: core.flagPush,
112
+ queue: queues.sync,
113
+ logger,
114
+ });
115
+ const messageMove = new MessageMoveService({
116
+ messageService: core.message,
117
+ mailboxService: core.mailbox,
118
+ mailboxSpecialUseService: core.mailboxSpecialUse,
119
+ threadMessageService: core.threadMessage,
120
+ addressService: core.address,
121
+ queue: queues.sync,
122
+ logger,
123
+ });
124
+ return {
125
+ flagQueue: new FlagQueueService({
126
+ messageFlagService: core.messageFlag,
127
+ messageService: core.message,
128
+ threadMessageService: core.threadMessage,
129
+ flagPushService,
130
+ logger,
131
+ }),
132
+ mailboxQueue: new MailboxQueueService({
133
+ mailboxService: core.mailbox,
134
+ queue: queues.sync,
135
+ logger,
136
+ }),
137
+ messageMove,
138
+ spamReport: new SpamReportService({
139
+ messageService: core.message,
140
+ addressService: core.address,
141
+ accountService: core.account,
142
+ mailboxSpecialUseService: core.mailboxSpecialUse,
143
+ messageMoveService: messageMove,
144
+ flagPushService,
145
+ logger,
146
+ }),
147
+ };
148
+ };
149
+ export const composeSmtpServices = (core, queues) => ({
150
+ outboxQueue: new OutboxQueueService({
151
+ outboxMessageService: core.outboxMessage,
152
+ outboxAttachmentService: core.outboxAttachment,
153
+ accountService: core.account,
154
+ queue: queues.smtp,
155
+ logger,
156
+ }),
157
+ });
158
+ export const composeBodyServices = (queues) => ({
159
+ bodySyncQueue: new BodySyncQueueService({ queue: queues.body, logger }),
160
+ });
161
+ export const composeReadPathServices = (core, queues, sync) => ({
162
+ // The flag service is the one `flagQueue` is (issue #302): body sync
163
+ // auto-marks-read a message from an unsubscribed sender through the exact
164
+ // same `FlagQueueService.markAsRead` a manual mark-as-read goes through.
165
+ bodySync: new BodySyncService(core.message, core.storage, core.threadMessage, core.address, core.envelope, new SyncWarningService(core.syncWarning), logger, undefined, buildReadPathFilterConfig(core, queues), undefined, { flagQueueService: sync.flagQueue },
166
+ // The read-path backfill materializes a body the sync path never got to,
167
+ // so it is a message's first sight as much as the sync pass is — and an
168
+ // invitation the user opens before background sync must still get its
169
+ // card (issue #1033, the same argument as the filter wiring above).
170
+ {
171
+ calendarSuggestionService: core.calendarSuggestion,
172
+ calendarStore: core.calendarStore,
173
+ filterService: core.filter,
174
+ }),
175
+ });
176
+ export const createRemitClient = (deps) => {
177
+ const { queues } = deps;
178
+ const core = createRemitCore(deps);
179
+ const sync = composeSyncServices(core, queues);
180
+ return {
181
+ ...core,
182
+ queues,
183
+ ...sync,
184
+ ...composeSmtpServices(core, queues),
185
+ ...composeBodyServices(queues),
186
+ ...composeReadPathServices(core, queues, sync),
187
+ };
188
+ };
@@ -0,0 +1,12 @@
1
+ import type { RemitClient } from "./create-remit-client.js";
2
+ export type { ConnectionScope, RemitClient, } from "./create-remit-client.js";
3
+ /**
4
+ * Register the client from the composition root. An adapter that lives outside
5
+ * this shared, open-core module — DynamoDB, or a Postgres adapter in the
6
+ * repository that deploys it — is never imported here; its composition root
7
+ * calls this before handling a request.
8
+ */
9
+ export declare const setClient: (client: RemitClient) => void;
10
+ export declare const getClient: () => Promise<RemitClient>;
11
+ /** Reset the singleton and any injected client — test use only. */
12
+ export declare const _resetForTest: () => void;
@@ -0,0 +1,42 @@
1
+ let clientPromise = null;
2
+ let injected = null;
3
+ /**
4
+ * Register the client from the composition root. An adapter that lives outside
5
+ * this shared, open-core module — DynamoDB, or a Postgres adapter in the
6
+ * repository that deploys it — is never imported here; its composition root
7
+ * calls this before handling a request.
8
+ */
9
+ export const setClient = (client) => {
10
+ injected = client;
11
+ clientPromise = null;
12
+ };
13
+ /**
14
+ * The SQLite composition this build contains, reached only by a process that
15
+ * registered no client of its own. The `import()` is dynamic so a bundler
16
+ * following the entry graph of a process that injects its client never reaches
17
+ * `@remit/drizzle-service` or drizzle-orm (both `external` for the Lambda
18
+ * esbuild build).
19
+ *
20
+ * `SQLITE_APP_DB_PATH` is the precondition the composition needs, checked ahead of
21
+ * the import so its absence names `setClient` instead of failing on module
22
+ * resolution inside a bundle, or — worse — opening an empty database and
23
+ * serving an empty mailbox. It is a precondition, not a backend selection.
24
+ */
25
+ const buildRelationalClient = async () => {
26
+ if (!process.env.SQLITE_APP_DB_PATH) {
27
+ throw new Error("no client registered — register one with setClient() from your composition root");
28
+ }
29
+ const composition = await import("./compose-sqlite.js");
30
+ return composition.buildSqliteClient();
31
+ };
32
+ export const getClient = () => {
33
+ clientPromise ??= injected
34
+ ? Promise.resolve(injected)
35
+ : buildRelationalClient();
36
+ return clientPromise;
37
+ };
38
+ /** Reset the singleton and any injected client — test use only. */
39
+ export const _resetForTest = () => {
40
+ clientPromise = null;
41
+ injected = null;
42
+ };
@@ -0,0 +1,4 @@
1
+ import type { ConfigExportIdentity } from "@remit/config-transfer";
2
+ import type { ApiConfig } from "../config/api-config.js";
3
+ /** Who wrote a configuration file, and from where. Provenance only. */
4
+ export declare const exportIdentity: (config: Pick<ApiConfig, "updateControlDir" | "publicOrigin">, now?: Date) => ConfigExportIdentity;
@@ -0,0 +1,33 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /**
4
+ * The version a file says it was written by. The updater owns this fact: it
5
+ * reads the tag from `.env` and writes it into `state.json`, which is the only
6
+ * honest source across an update that rewrites that tag underneath a
7
+ * long-lived process. With no state file there is nothing authoritative to
8
+ * stamp, and an export says so rather than inventing a version an importing
9
+ * reader would then trust.
10
+ */
11
+ const runningVersion = (dir) => {
12
+ let raw;
13
+ try {
14
+ raw = readFileSync(join(dir, "state.json"), "utf8");
15
+ }
16
+ catch {
17
+ return "unknown";
18
+ }
19
+ const state = JSON.parse(raw);
20
+ if (typeof state !== "object" || state === null)
21
+ return "unknown";
22
+ const version = state.currentVersion;
23
+ return typeof version === "string" && version.length > 0
24
+ ? version
25
+ : "unknown";
26
+ };
27
+ /** Who wrote a configuration file, and from where. Provenance only. */
28
+ export const exportIdentity = (config, now = new Date()) => ({
29
+ app: "reader",
30
+ version: runningVersion(config.updateControlDir),
31
+ exportedAt: now.toISOString(),
32
+ instance: config.publicOrigin,
33
+ });
@@ -0,0 +1,54 @@
1
+ type StructuredLog = (fields: Record<string, unknown>, message: string) => void;
2
+ export interface FireAndForgetLogger {
3
+ error: StructuredLog;
4
+ }
5
+ export interface FireAndForgetContext {
6
+ /**
7
+ * Stable discriminator for the originating call site (e.g. `"config_load"`,
8
+ * `"account_create"`). Surfaces on the log line as `source` so a CloudWatch
9
+ * metric filter / alarm can attribute a leak to its origin.
10
+ */
11
+ source: string;
12
+ /**
13
+ * Message logged when the background work rejects. Should describe the work
14
+ * and reassure that the failure is contained (e.g. "...best-effort").
15
+ */
16
+ message: string;
17
+ /**
18
+ * Identifiers carried onto the structured error line (accountId,
19
+ * accountConfigId, …) so a failure is traceable without a stack dive.
20
+ */
21
+ ids?: Record<string, unknown>;
22
+ /**
23
+ * Alertable discriminator on the structured error line, the field CloudWatch
24
+ * metric filters key on. Defaults to `"sync_trigger_failed"` for the original
25
+ * sync-kick sites; a different fire-and-forget surface (e.g. a flag enqueue)
26
+ * must pass its own so it is not mis-attributed to the sync-trigger alarm.
27
+ */
28
+ alert?: string;
29
+ logger?: FireAndForgetLogger;
30
+ }
31
+ /**
32
+ * Run unawaited background work that MUST NOT be able to fail the request that
33
+ * spawned it.
34
+ *
35
+ * The dev/Lambda runtime shares one event loop across concurrent requests, so a
36
+ * rejection escaping an unawaited background promise (a `void trigger()`) lands
37
+ * on whatever request happens to be in flight when the deferred work finally
38
+ * fails — turning an unrelated READ into a spurious 500. That is exactly how an
39
+ * unreachable SQS queue 500'd `/mailboxes`, `/threads`, `/outbox` and `/config`.
40
+ *
41
+ * This helper is the single containment point: it awaits the work inside a
42
+ * try/catch, logs every rejection LOUDLY with the alertable structured fields
43
+ * (`alert` — defaulting to `"sync_trigger_failed"`, `source`, the SDK error
44
+ * name/code, the caller ids), and ALWAYS resolves to void. Callers `void
45
+ * fireAndForget(...)` with no
46
+ * chance of an unhandled rejection escaping. Routing all fire-and-forget sites
47
+ * through here keeps the containment from regressing one handler at a time.
48
+ *
49
+ * Use this ONLY for genuine side effects whose failure must not change the
50
+ * response. A failure the caller is responsible for (the real DynamoDB read, an
51
+ * enqueue that IS the request's purpose) must still propagate and 500.
52
+ */
53
+ export declare const fireAndForget: (work: () => Promise<unknown>, context: FireAndForgetContext) => Promise<void>;
54
+ export {};
@@ -0,0 +1,37 @@
1
+ import { logger } from "@remit/logger";
2
+ /**
3
+ * Run unawaited background work that MUST NOT be able to fail the request that
4
+ * spawned it.
5
+ *
6
+ * The dev/Lambda runtime shares one event loop across concurrent requests, so a
7
+ * rejection escaping an unawaited background promise (a `void trigger()`) lands
8
+ * on whatever request happens to be in flight when the deferred work finally
9
+ * fails — turning an unrelated READ into a spurious 500. That is exactly how an
10
+ * unreachable SQS queue 500'd `/mailboxes`, `/threads`, `/outbox` and `/config`.
11
+ *
12
+ * This helper is the single containment point: it awaits the work inside a
13
+ * try/catch, logs every rejection LOUDLY with the alertable structured fields
14
+ * (`alert` — defaulting to `"sync_trigger_failed"`, `source`, the SDK error
15
+ * name/code, the caller ids), and ALWAYS resolves to void. Callers `void
16
+ * fireAndForget(...)` with no
17
+ * chance of an unhandled rejection escaping. Routing all fire-and-forget sites
18
+ * through here keeps the containment from regressing one handler at a time.
19
+ *
20
+ * Use this ONLY for genuine side effects whose failure must not change the
21
+ * response. A failure the caller is responsible for (the real DynamoDB read, an
22
+ * enqueue that IS the request's purpose) must still propagate and 500.
23
+ */
24
+ export const fireAndForget = async (work, context) => {
25
+ await work().catch((error) => {
26
+ const log = context.logger ?? logger;
27
+ log.error({
28
+ alert: context.alert ?? "sync_trigger_failed",
29
+ source: context.source,
30
+ ...context.ids,
31
+ errorName: error?.name,
32
+ errorCode: error?.Code ??
33
+ error?.code,
34
+ error,
35
+ }, context.message);
36
+ });
37
+ };
@@ -0,0 +1,181 @@
1
+ import type { FilterItem, OrganizeJobRequestItem } from "@remit/data-ports";
2
+ import { type FilterMessage, type LiteralClauseNarrowing, PlacementMoveService } from "@remit/mailbox-service";
3
+ import type { RemitClient } from "./data-client.js";
4
+ /**
5
+ * Hard cap on both the previewed and the applied set. A back-apply is a
6
+ * data-heavy corpus pass (remit-data-heavy: frugal), bounded by counting
7
+ * MATCHES as it pages a store-side narrowed listing, and the final match set
8
+ * never exceeds this. It bounds the answer, never the corpus the question is
9
+ * asked of (#459).
10
+ */
11
+ export declare const ORGANIZE_MATCH_LIMIT = 500;
12
+ /**
13
+ * The predicate + action for one back-apply pass — the OrganizeInput fields,
14
+ * flattened. The action fields carry absence as the `"None"` sentinel (RFC 034
15
+ * Decision 3.1).
16
+ */
17
+ export interface OrganizePredicate {
18
+ matchOperator: FilterItem["matchOperator"];
19
+ literalClauses: FilterItem["literalClauses"];
20
+ actionLabelId: string;
21
+ actionMailboxId: string;
22
+ }
23
+ /**
24
+ * The predicate a back-apply job snapshotted onto its row, read back verbatim so
25
+ * the worker runs exactly what the request asked for.
26
+ */
27
+ export declare const predicateFromJob: (job: OrganizeJobRequestItem) => OrganizePredicate;
28
+ /**
29
+ * One corpus message projected onto the fields a literal clause matches
30
+ * against, paired with its id. Sourced from the core thread rows.
31
+ */
32
+ export interface OrganizeCandidate {
33
+ messageId: string;
34
+ message: FilterMessage;
35
+ }
36
+ /**
37
+ * One page of the narrowed corpus, cursored exactly as the underlying listing
38
+ * is: an absent `continuationToken` means the store has no more rows matching
39
+ * the terms, never that a read window ran out.
40
+ */
41
+ export interface OrganizeCandidatePage {
42
+ items: OrganizeCandidate[];
43
+ continuationToken?: string;
44
+ }
45
+ /** What to ask the store for, and where to resume. */
46
+ export interface OrganizeCandidateQuery extends LiteralClauseNarrowing {
47
+ limit: number;
48
+ continuationToken?: string;
49
+ mailboxIds?: readonly string[];
50
+ }
51
+ /**
52
+ * Where a pass looks and which standing filter it runs for. A filter
53
+ * back-apply narrows the corpus to the inbox and matches a body-content clause
54
+ * on the stored preview; an "all like these" pass sets neither.
55
+ */
56
+ export interface OrganizeMatchScope {
57
+ filterId?: string;
58
+ mailboxIds?: readonly string[];
59
+ }
60
+ export interface OrganizeMatchDeps {
61
+ /**
62
+ * One page of the account's messages as literal-match candidates, with the
63
+ * clause terms evaluated by the store. Paged rather than capped, so the
64
+ * caller's bound falls on matches instead of on how far back the read
65
+ * reached (#459).
66
+ */
67
+ listAccountFilterMessages: (accountConfigId: string, query: OrganizeCandidateQuery) => Promise<OrganizeCandidatePage>;
68
+ }
69
+ /**
70
+ * Why the matcher refused a predicate. A refusal is an expected outcome of
71
+ * asking for a rule this deployment cannot evaluate, not a fault: the HTTP
72
+ * boundary words it as a 400 and the back-apply worker fails the job row on it,
73
+ * neither of which may treat it as the infrastructure failure a throw would
74
+ * make it (reader #463).
75
+ */
76
+ export interface OrganizeRejection {
77
+ reason: "BodyContentUnsupported" | "FilterNotActive" | "FilterMissing" | "FolderMissing";
78
+ message: string;
79
+ }
80
+ /** The matched ids. */
81
+ export interface OrganizeMatched {
82
+ rejected: null;
83
+ messageIds: string[];
84
+ }
85
+ /** The predicate was refused: nothing matched, and nothing may be applied. */
86
+ export interface OrganizeMatchRejected {
87
+ rejected: OrganizeRejection;
88
+ }
89
+ export type OrganizeMatchResult = OrganizeMatched | OrganizeMatchRejected;
90
+ /**
91
+ * A `HasWords` clause matches the message body (RFC 031, `clauseMatches`). The
92
+ * live index-time filter evaluates it against the full parsed body
93
+ * (`body-sync.ts` `toFilterMessage`); the corpus slice carries only
94
+ * `From`/`Subject` at full fidelity and no faithful body, so it cannot serve a
95
+ * body-content clause without silently narrowing the match to whatever preview
96
+ * happened to be stored. A body-content clause on an "all like these" pass is
97
+ * refused instead of returning a wrong set.
98
+ */
99
+ export declare const BODY_CONTENT_REJECTION_MESSAGE = "Organize cannot match a body-content (HasWords) clause over the mailbox: it reads only each message's preview, not its body";
100
+ /**
101
+ * The refusal a predicate carries on its own, decidable without reading the
102
+ * corpus: a body-content clause is refused up front. `createOrganizeJob` runs
103
+ * this before enqueueing and {@link matchOrganize} runs it too, so preview, the
104
+ * worker and the request boundary all refuse the same rule (reader #463).
105
+ */
106
+ export declare const organizePredicateRejection: (predicate: OrganizePredicate) => OrganizeRejection | null;
107
+ /**
108
+ * The matcher shared by preview and apply — the previewed set equals the applied
109
+ * set for the same input. Read-only. Returns the matching message ids, bounded.
110
+ *
111
+ * - An ad-hoc predicate with no clause matches nothing: a run-now request
112
+ * carries none, and an empty predicate must never sweep the whole mailbox.
113
+ * - A predicate the corpus read cannot evaluate (a body-content clause on an
114
+ * "all like these" pass, see {@link bodyContentRejection}) comes back as a
115
+ * rejection, so a caller can tell a refused rule from a broken deployment
116
+ * (reader #463).
117
+ */
118
+ export declare const matchOrganize: (deps: OrganizeMatchDeps, accountConfigId: string, predicate: OrganizePredicate, limit?: number, scope?: OrganizeMatchScope) => Promise<OrganizeMatchResult>;
119
+ /** The matcher deps for the running backend/worker. */
120
+ export declare const buildOrganizeMatchDeps: (client: RemitClient<never>) => OrganizeMatchDeps;
121
+ /**
122
+ * The exclusive-move arm of a back-apply, wired exactly like the body-sync
123
+ * placement mover (`sync-message-body.ts`): the local-first
124
+ * {@link PlacementMoveService} over the shared message-management queue. A move
125
+ * commits locally (marker + ThreadMessage + Message row) and enqueues a
126
+ * `PLACEMENT_MOVE_PUSH` for the reconciler; its marker state engine makes a
127
+ * redelivered job idempotent (mirrors #1297).
128
+ */
129
+ export declare const buildOrganizeMoveService: (client: RemitClient<"messageManagement">) => PlacementMoveService;
130
+ export interface ApplyOrganizeDeps {
131
+ client: RemitClient<never>;
132
+ moveService: PlacementMoveService;
133
+ }
134
+ export interface ApplyOrganizeResult {
135
+ applied: number;
136
+ failed: number;
137
+ /** Why each failed message failed, as the job reports it. */
138
+ failures: string[];
139
+ }
140
+ /**
141
+ * Apply the back-apply action to every matched message, reusing the index-time
142
+ * apply plumbing: an idempotent MessageLabel upsert (additive) with
143
+ * `appliedByFilterId` deliberately ABSENT — this path attributes to no filter,
144
+ * exactly like a hand-applied label (RFC 034 Decision 3.3) — and an idempotent
145
+ * folder move (exclusive). One poisoned message never fails the batch; it is
146
+ * counted as failed and the pass continues.
147
+ *
148
+ * Every requested move happens. A standing filter acts at exactly two moments —
149
+ * when a message is first seen, and when the user presses "run rules now" — and
150
+ * holds no claim on that message afterwards, so it never arbitrates against a
151
+ * later user-initiated apply (reader #497). Cross-filter precedence stays where
152
+ * two filters collide over the same message at the same trigger
153
+ * (`FilterPipeline`/`selectMoveWinner`), and `applied` therefore counts moves
154
+ * that actually happened.
155
+ */
156
+ export declare const applyOrganize: (deps: ApplyOrganizeDeps, accountConfigId: string, messageIds: readonly string[], predicate: OrganizePredicate) => Promise<ApplyOrganizeResult>;
157
+ export interface FilterBackApplyResult {
158
+ rejected: null;
159
+ matched: number;
160
+ applied: number;
161
+ failed: number;
162
+ failures: string[];
163
+ }
164
+ export interface FilterBackApplyDeps {
165
+ client: RemitClient<never>;
166
+ matchDeps: OrganizeMatchDeps;
167
+ moveService: PlacementMoveService;
168
+ }
169
+ /**
170
+ * Run one standing filter over the inbox as if each matching message had just
171
+ * arrived. Every message the matcher returns is a match: its label always
172
+ * applies, being additive. The index-time {@link FilterPipeline} only orders
173
+ * the exclusive move, so the move is skipped where it names another active
174
+ * filter. The pipeline sees the stored preview rather than the full body, so
175
+ * its verdict on whether this filter matches is not asked; and an evaluate it
176
+ * could not finish names no other filter, so the move goes ahead rather than
177
+ * silently dropping out.
178
+ */
179
+ export declare const backApplyFilter: (deps: FilterBackApplyDeps, accountConfigId: string, filterId: string) => Promise<FilterBackApplyResult | OrganizeMatchRejected>;
180
+ /** Whether a job row runs a standing filter rather than its own snapshot. */
181
+ export declare const isFilterJob: (job: Pick<OrganizeJobRequestItem, "filterId">) => boolean;