@remit/backend 0.0.143 → 0.0.144

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 (269) 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 +7 -0
  4. package/dist/src/api.js +144 -0
  5. package/dist/src/auth.d.ts +30 -0
  6. package/dist/src/auth.js +73 -0
  7. package/dist/src/config/msoauth.d.ts +30 -0
  8. package/dist/src/config/msoauth.js +30 -0
  9. package/{src/data-backend.ts → dist/src/data-backend.d.ts} +2 -4
  10. package/dist/src/data-backend.js +12 -0
  11. package/{src/derive/autoMoved.ts → dist/src/derive/autoMoved.d.ts} +1 -29
  12. package/dist/src/derive/autoMoved.js +49 -0
  13. package/dist/src/derive/contentSignature.d.ts +41 -0
  14. package/{src/derive/contentSignature.ts → dist/src/derive/contentSignature.js} +27 -72
  15. package/dist/src/derive/contentUrl.d.ts +46 -0
  16. package/dist/src/derive/contentUrl.js +46 -0
  17. package/{src/derive/deriveMuted.ts → dist/src/derive/deriveMuted.d.ts} +1 -3
  18. package/dist/src/derive/deriveMuted.js +7 -0
  19. package/dist/src/derive/enrichThreadRows.d.ts +118 -0
  20. package/dist/src/derive/enrichThreadRows.js +151 -0
  21. package/dist/src/derive/filterThreadCriteria.d.ts +23 -0
  22. package/dist/src/derive/filterThreadCriteria.js +24 -0
  23. package/{src/derive/pendingMoveCounts.ts → dist/src/derive/pendingMoveCounts.d.ts} +5 -53
  24. package/dist/src/derive/pendingMoveCounts.js +53 -0
  25. package/{src/derive/senderTrust.ts → dist/src/derive/senderTrust.d.ts} +1 -9
  26. package/dist/src/derive/senderTrust.js +19 -0
  27. package/dist/src/error.d.ts +21 -0
  28. package/dist/src/error.js +85 -0
  29. package/dist/src/handlers/account-guards.d.ts +36 -0
  30. package/dist/src/handlers/account-guards.js +107 -0
  31. package/dist/src/handlers/account-oauth.d.ts +14 -0
  32. package/dist/src/handlers/account-oauth.js +387 -0
  33. package/dist/src/handlers/account-overrides.d.ts +53 -0
  34. package/dist/src/handlers/account-overrides.js +233 -0
  35. package/dist/src/handlers/account-signature.d.ts +28 -0
  36. package/dist/src/handlers/account-signature.js +85 -0
  37. package/dist/src/handlers/account.d.ts +6 -0
  38. package/dist/src/handlers/account.js +350 -0
  39. package/dist/src/handlers/address.d.ts +35 -0
  40. package/dist/src/handlers/address.js +141 -0
  41. package/dist/src/handlers/calendar-event.d.ts +70 -0
  42. package/dist/src/handlers/calendar-event.js +425 -0
  43. package/dist/src/handlers/calendar-feed.d.ts +47 -0
  44. package/dist/src/handlers/calendar-feed.js +192 -0
  45. package/dist/src/handlers/calendar-suggestion.d.ts +47 -0
  46. package/dist/src/handlers/calendar-suggestion.js +212 -0
  47. package/dist/src/handlers/calendar.d.ts +116 -0
  48. package/dist/src/handlers/calendar.js +330 -0
  49. package/dist/src/handlers/config.d.ts +43 -0
  50. package/dist/src/handlers/config.js +176 -0
  51. package/dist/src/handlers/ensure-account-config.d.ts +9 -0
  52. package/dist/src/handlers/ensure-account-config.js +27 -0
  53. package/dist/src/handlers/filter.d.ts +3 -0
  54. package/dist/src/handlers/filter.js +103 -0
  55. package/dist/src/handlers/folder-role-appointments.d.ts +68 -0
  56. package/dist/src/handlers/folder-role-appointments.js +168 -0
  57. package/dist/src/handlers/folder-role-labels.d.ts +26 -0
  58. package/dist/src/handlers/folder-role-labels.js +40 -0
  59. package/dist/src/handlers/folder-role.d.ts +9 -0
  60. package/dist/src/handlers/folder-role.js +40 -0
  61. package/dist/src/handlers/index.d.ts +2 -0
  62. package/dist/src/handlers/index.js +60 -0
  63. package/dist/src/handlers/label.d.ts +43 -0
  64. package/dist/src/handlers/label.js +114 -0
  65. package/dist/src/handlers/mailbox-bindings.d.ts +41 -0
  66. package/dist/src/handlers/mailbox-bindings.js +61 -0
  67. package/dist/src/handlers/mailbox.d.ts +23 -0
  68. package/dist/src/handlers/mailbox.js +219 -0
  69. package/dist/src/handlers/me.d.ts +2 -0
  70. package/dist/src/handlers/me.js +138 -0
  71. package/dist/src/handlers/message.d.ts +84 -0
  72. package/dist/src/handlers/message.js +607 -0
  73. package/dist/src/handlers/organize.d.ts +10 -0
  74. package/dist/src/handlers/organize.js +112 -0
  75. package/dist/src/handlers/outbox-attachment.d.ts +23 -0
  76. package/dist/src/handlers/outbox-attachment.js +81 -0
  77. package/dist/src/handlers/outbox.d.ts +4 -0
  78. package/dist/src/handlers/outbox.js +144 -0
  79. package/dist/src/handlers/sync-progress.d.ts +46 -0
  80. package/dist/src/handlers/sync-progress.js +63 -0
  81. package/dist/src/handlers/sync.d.ts +28 -0
  82. package/dist/src/handlers/sync.js +82 -0
  83. package/dist/src/handlers/system-update.d.ts +2 -0
  84. package/dist/src/handlers/system-update.js +172 -0
  85. package/dist/src/handlers/thread.d.ts +148 -0
  86. package/dist/src/handlers/thread.js +233 -0
  87. package/dist/src/handlers/unified-threads.d.ts +247 -0
  88. package/dist/src/handlers/unified-threads.js +409 -0
  89. package/{src/handlers/vip-suggestions.ts → dist/src/handlers/vip-suggestions.d.ts} +1 -13
  90. package/dist/src/handlers/vip-suggestions.js +16 -0
  91. package/dist/src/index.d.ts +2 -0
  92. package/dist/src/index.js +37 -0
  93. package/{src/json.ts → dist/src/json.d.ts} +1 -4
  94. package/dist/src/json.js +10 -0
  95. package/dist/src/jwt-auth.d.ts +18 -0
  96. package/dist/src/jwt-auth.js +97 -0
  97. package/dist/src/request-context.d.ts +9 -0
  98. package/dist/src/request-context.js +22 -0
  99. package/dist/src/request.d.ts +3 -0
  100. package/dist/src/request.js +7 -0
  101. package/dist/src/response.d.ts +27 -0
  102. package/dist/src/response.js +87 -0
  103. package/dist/src/service/account-deletion.d.ts +22 -0
  104. package/dist/src/service/account-deletion.js +39 -0
  105. package/dist/src/service/compose-sqlite.d.ts +2 -0
  106. package/dist/src/service/compose-sqlite.js +11 -0
  107. package/dist/src/service/create-remit-client.d.ts +95 -0
  108. package/dist/src/service/create-remit-client.js +207 -0
  109. package/dist/src/service/data-client.d.ts +12 -0
  110. package/dist/src/service/data-client.js +44 -0
  111. package/dist/src/service/export-identity.d.ts +3 -0
  112. package/dist/src/service/export-identity.js +35 -0
  113. package/dist/src/service/fire-and-forget.d.ts +54 -0
  114. package/dist/src/service/fire-and-forget.js +38 -0
  115. package/dist/src/service/organize.d.ts +184 -0
  116. package/dist/src/service/organize.js +384 -0
  117. package/dist/src/service/refusal.d.ts +4 -0
  118. package/dist/src/service/refusal.js +20 -0
  119. package/dist/src/service/sender-category.d.ts +51 -0
  120. package/{src/service/sender-category.ts → dist/src/service/sender-category.js} +40 -81
  121. package/dist/src/service/sqs.d.ts +1 -0
  122. package/{src/service/sqs.ts → dist/src/service/sqs.js} +0 -1
  123. package/dist/src/service/trigger-sync.d.ts +51 -0
  124. package/dist/src/service/trigger-sync.js +39 -0
  125. package/dist/src/types.d.ts +49 -0
  126. package/dist/src/types.js +1 -0
  127. package/package.json +113 -98
  128. package/.env.test +0 -4
  129. package/dev-server/content-auth.test.ts +0 -113
  130. package/dev-server/content-auth.ts +0 -54
  131. package/dev-server/content-handler.test.ts +0 -105
  132. package/dev-server/content-handler.ts +0 -79
  133. package/dev-server/content-path.test.ts +0 -37
  134. package/dev-server/content-path.ts +0 -27
  135. package/dev-server/cors.test.ts +0 -48
  136. package/dev-server/cors.ts +0 -25
  137. package/dev-server/keep-alive.test.ts +0 -24
  138. package/dev-server/lambda-helpers.ts +0 -69
  139. package/dev-server/log-output.test.ts +0 -173
  140. package/dev-server/oauth-callback-route.test.ts +0 -66
  141. package/dev-server/relational-health.test.ts +0 -83
  142. package/dev-server/relational-health.ts +0 -39
  143. package/dev-server/server.ts +0 -384
  144. package/dev-server/sync-age.test.ts +0 -91
  145. package/dev-server/sync-age.ts +0 -55
  146. package/dev-server/upload-handler.ts +0 -132
  147. package/dev-server/upload-route.test.ts +0 -196
  148. package/scripts/backfill-classification.ts +0 -91
  149. package/scripts/backfill-list-id.ts +0 -83
  150. package/scripts/config-save.ts +0 -128
  151. package/src/auth.test.ts +0 -12
  152. package/src/auth.ts +0 -92
  153. package/src/config/msoauth.test.ts +0 -48
  154. package/src/config/msoauth.ts +0 -48
  155. package/src/data-backend.test.ts +0 -23
  156. package/src/derive/autoMoved.test.ts +0 -104
  157. package/src/derive/contentSignature.test.ts +0 -161
  158. package/src/derive/contentUrl.test.ts +0 -172
  159. package/src/derive/contentUrl.ts +0 -86
  160. package/src/derive/deriveMuted.test.ts +0 -34
  161. package/src/derive/enrichThreadRows.test.ts +0 -360
  162. package/src/derive/enrichThreadRows.ts +0 -234
  163. package/src/derive/filterThreadCriteria.test.ts +0 -126
  164. package/src/derive/filterThreadCriteria.ts +0 -51
  165. package/src/derive/senderTrust.test.ts +0 -67
  166. package/src/error.test.ts +0 -201
  167. package/src/error.ts +0 -123
  168. package/src/handlers/account-guards.test.ts +0 -67
  169. package/src/handlers/account-guards.ts +0 -169
  170. package/src/handlers/account-oauth-callback.test.ts +0 -155
  171. package/src/handlers/account-oauth-consent.test.ts +0 -336
  172. package/src/handlers/account-oauth-start.test.ts +0 -191
  173. package/src/handlers/account-oauth.test.ts +0 -439
  174. package/src/handlers/account-oauth.ts +0 -635
  175. package/src/handlers/account-overrides.test.ts +0 -88
  176. package/src/handlers/account-overrides.ts +0 -323
  177. package/src/handlers/account-ownership.ts +0 -25
  178. package/src/handlers/account-signature.ts +0 -117
  179. package/src/handlers/account-synced-services.test.ts +0 -245
  180. package/src/handlers/account.ts +0 -552
  181. package/src/handlers/address.test.ts +0 -381
  182. package/src/handlers/address.ts +0 -200
  183. package/src/handlers/calendar-event.test.ts +0 -685
  184. package/src/handlers/calendar-event.ts +0 -738
  185. package/src/handlers/calendar-feed-validators.test.ts +0 -228
  186. package/src/handlers/calendar-feed.test.ts +0 -439
  187. package/src/handlers/calendar-feed.ts +0 -291
  188. package/src/handlers/calendar-sqlite-fixture.ts +0 -36
  189. package/src/handlers/calendar-suggestion.test.ts +0 -1269
  190. package/src/handlers/calendar-suggestion.ts +0 -425
  191. package/src/handlers/calendar.test.ts +0 -1080
  192. package/src/handlers/calendar.ts +0 -517
  193. package/src/handlers/config.test.ts +0 -245
  194. package/src/handlers/config.ts +0 -332
  195. package/src/handlers/ensure-account-config.ts +0 -30
  196. package/src/handlers/filter.test.ts +0 -644
  197. package/src/handlers/filter.ts +0 -518
  198. package/src/handlers/folder-role-appointments.test.ts +0 -438
  199. package/src/handlers/folder-role-appointments.ts +0 -249
  200. package/src/handlers/folder-role-labels.ts +0 -63
  201. package/src/handlers/folder-role.test.ts +0 -209
  202. package/src/handlers/folder-role.ts +0 -80
  203. package/src/handlers/index.ts +0 -83
  204. package/src/handlers/label.test.ts +0 -112
  205. package/src/handlers/label.ts +0 -210
  206. package/src/handlers/mailbox-bindings.ts +0 -111
  207. package/src/handlers/mailbox-rename-refusal.test.ts +0 -247
  208. package/src/handlers/mailbox-settled-target.test.ts +0 -251
  209. package/src/handlers/mailbox.test.ts +0 -280
  210. package/src/handlers/mailbox.ts +0 -581
  211. package/src/handlers/me.test.ts +0 -81
  212. package/src/handlers/me.ts +0 -203
  213. package/src/handlers/message-read-backfill.test.ts +0 -103
  214. package/src/handlers/message.test.ts +0 -131
  215. package/src/handlers/message.ts +0 -1131
  216. package/src/handlers/organize.test.ts +0 -277
  217. package/src/handlers/organize.ts +0 -197
  218. package/src/handlers/outbox-attachment.test.ts +0 -765
  219. package/src/handlers/outbox-attachment.ts +0 -126
  220. package/src/handlers/outbox-list.test.ts +0 -179
  221. package/src/handlers/outbox.test.ts +0 -772
  222. package/src/handlers/outbox.ts +0 -258
  223. package/src/handlers/search.test.ts +0 -161
  224. package/src/handlers/search.ts +0 -105
  225. package/src/handlers/sync-progress.test.ts +0 -205
  226. package/src/handlers/sync-progress.ts +0 -99
  227. package/src/handlers/sync.test.ts +0 -168
  228. package/src/handlers/sync.ts +0 -118
  229. package/src/handlers/system-update.test.ts +0 -497
  230. package/src/handlers/system-update.ts +0 -230
  231. package/src/handlers/thread.test.ts +0 -396
  232. package/src/handlers/thread.ts +0 -427
  233. package/src/handlers/unified-threads-mail-sources.sqlite.test.ts +0 -197
  234. package/src/handlers/unified-threads.test.ts +0 -785
  235. package/src/handlers/unified-threads.ts +0 -669
  236. package/src/index.test.ts +0 -83
  237. package/src/index.ts +0 -228
  238. package/src/jwt-auth.test.ts +0 -152
  239. package/src/jwt-auth.ts +0 -132
  240. package/src/log-redaction.test.ts +0 -144
  241. package/src/openapi-spec.test.ts +0 -118
  242. package/src/request-context.test.ts +0 -61
  243. package/src/request-context.ts +0 -33
  244. package/src/request-pipeline.test.ts +0 -88
  245. package/src/request.ts +0 -10
  246. package/src/response.test.ts +0 -130
  247. package/src/response.ts +0 -144
  248. package/src/service/compose-sqlite.ts +0 -98
  249. package/src/service/config-import.ts +0 -39
  250. package/src/service/create-remit-client.ts +0 -522
  251. package/src/service/data-client.test.ts +0 -97
  252. package/src/service/data-client.ts +0 -58
  253. package/src/service/export-identity.ts +0 -39
  254. package/src/service/filter.ts +0 -40
  255. package/src/service/fire-and-forget.test.ts +0 -126
  256. package/src/service/fire-and-forget.ts +0 -79
  257. package/src/service/localhost-env-config.test.ts +0 -36
  258. package/src/service/organize-queue.ts +0 -98
  259. package/src/service/organize.test.ts +0 -1451
  260. package/src/service/organize.ts +0 -1025
  261. package/src/service/read-path-filter-config.test.ts +0 -243
  262. package/src/service/semantic-capability.test.ts +0 -78
  263. package/src/service/semantic-capability.ts +0 -95
  264. package/src/service/sender-category.test.ts +0 -235
  265. package/src/service/trigger-sync.test.ts +0 -205
  266. package/src/service/trigger-sync.ts +0 -100
  267. package/src/stdout-capture-fixture.ts +0 -40
  268. package/src/types.ts +0 -271
  269. package/tsconfig.json +0 -7
@@ -0,0 +1,39 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { SendMessageCommand } from "@aws-sdk/client-sqs";
3
+ const isFifoQueue = (queueUrl) => queueUrl.endsWith(".fifo");
4
+ /**
5
+ * Build the scheduler's own dedup namespace, bucketed by tick interval so
6
+ * consecutive ticks each get a fresh id (never colliding with each other)
7
+ * while still deduping a genuine double-invocation of the same tick (e.g. a
8
+ * retried Lambda) — see `dedupId` above.
9
+ */
10
+ export const buildScheduledSyncDedupId = (accountId, now, bucketMs) => {
11
+ const bucket = Math.floor(now / bucketMs);
12
+ return `SYNC_MAILBOXES:scheduled:${accountId}:${bucket}`;
13
+ };
14
+ export const buildSyncMailboxesCommand = (input) => {
15
+ const { queueUrl, accountId, dedupId, explicitRequest } = input;
16
+ const event = {
17
+ type: "SYNC_MAILBOXES",
18
+ eventId: randomUUID(),
19
+ timestamp: Date.now(),
20
+ accountId,
21
+ ...(explicitRequest && { explicitRequest }),
22
+ };
23
+ const useFifo = isFifoQueue(queueUrl);
24
+ return new SendMessageCommand({
25
+ QueueUrl: queueUrl,
26
+ MessageBody: JSON.stringify(event),
27
+ ...(useFifo && {
28
+ MessageGroupId: accountId,
29
+ MessageDeduplicationId: dedupId ?? event.eventId,
30
+ }),
31
+ });
32
+ };
33
+ export const triggerAccountSync = async (input) => {
34
+ const command = buildSyncMailboxesCommand(input);
35
+ await input.sqsClient.send(command);
36
+ const body = command.input.MessageBody ?? "{}";
37
+ const parsed = JSON.parse(body);
38
+ return { eventId: parsed.eventId };
39
+ };
@@ -0,0 +1,49 @@
1
+ import type { Context } from "openapi-backend";
2
+ type MatchPrefix<Prefix extends string, S extends string> = S extends `${Prefix}${infer _}` ? S : never;
3
+ export type OperationIds = "MeOperations_deleteMe" | "MeOperations_listVipSuggestions" | "MeOperations_createExport" | "MeOperations_getExport" | "MeOperations_listQuarantine" | "ConfigOperations_getConfig" | "ConfigOperations_exportConfig" | "ConfigOperations_importConfig" | "SystemOperations_getSystemUpdate" | "SystemOperations_applySystemUpdate" | "AccountOperations_createAccount" | "AccountOperations_testConnection" | "AccountDetailOperations_updateAccount" | "AccountDetailOperations_deleteAccount" | "MicrosoftOAuthOperations_microsoftOAuthStart" | "MicrosoftOAuthOperations_microsoftOAuthCallback" | "MailboxOperations_listMailboxes" | "MailboxOperations_createMailbox" | "MailboxDetailOperations_getMailbox" | "MailboxDetailOperations_renameMailbox" | "MailboxDetailOperations_deleteMailbox" | "FolderRoleOperations_appointFolderRole" | "FilterOperations_listFilters" | "FilterOperations_createFilter" | "FilterDetailOperations_getFilter" | "FilterDetailOperations_updateFilter" | "FilterDetailOperations_deleteFilter" | "LabelOperations_listLabels" | "LabelOperations_createLabel" | "LabelDetailOperations_getLabel" | "LabelDetailOperations_updateLabel" | "LabelDetailOperations_deleteLabel" | "OrganizeOperations_createOrganizeJob" | "OrganizeOperations_previewOrganize" | "OrganizeJobDetailOperations_getOrganizeJob" | "SyncOperations_triggerSync" | "SyncOperations_getSyncStatus" | "ThreadDetailOperations_listThreadMessages" | "UnifiedThreadOperations_listAllThreads" | "ThreadOperations_listThreads" | "ThreadOperations_searchThreads" | "MessageOperations_describeMessage" | "MessageOperations_getRawMessage" | "MessageOperations_updateMessageFlags" | "MessageBulkOperations_deleteMessages" | "MessageBulkOperations_moveMessages" | "MessageBulkOperations_updateFlags" | "MessageBulkOperations_copyMessages" | "MessageBulkOperations_updateMessageLabels" | "MessageBulkOperations_reportSpam" | "MessageBulkOperations_notSpam" | "CalendarOperations_listCalendars" | "CalendarOperations_createCalendar" | "CalendarDetailOperations_getCalendar" | "CalendarDetailOperations_updateCalendar" | "CalendarDetailOperations_deleteCalendar" | "CalendarDetailOperations_getCalendarFeed" | "CalendarDetailOperations_putCalendarFeed" | "CalendarDetailOperations_revokeCalendarFeed" | "CalendarFeedOperations_getCalendarFeedIcal" | "CalendarEventOperations_listCalendarEvents" | "CalendarEventOperations_createCalendarEvent" | "CalendarEventDetailOperations_getCalendarEvent" | "CalendarEventDetailOperations_updateCalendarEvent" | "CalendarEventDetailOperations_deleteCalendarEvent" | "CalendarFreeBusyOperations_listCalendarFreeBusy" | "TrashOperations_emptyTrash" | "OutboxOperations_createOutboxMessage" | "OutboxOperations_listOutboxMessages" | "OutboxDetailOperations_getOutboxMessage" | "OutboxDetailOperations_updateOutboxMessage" | "OutboxDetailOperations_deleteOutboxMessage" | "OutboxDetailOperations_sendOutboxMessage" | "OutboxDetailOperations_mintOutboxAttachment" | "OutboxAttachmentOperations_completeOutboxAttachment" | "AddressOperations_searchAddresses" | "AddressDetailOperations_updateAddress" | "CalendarSuggestionOperations_listCalendarSuggestions" | "MessageCalendarSuggestionOperations_listMessageCalendarSuggestions" | "CalendarSuggestionActionOperations_acceptCalendarSuggestion" | "CalendarSuggestionActionOperations_declineCalendarSuggestion" | "CalendarSuggestionActionOperations_dismissCalendarSuggestion" | "CalendarSuggestionActionOperations_reopenCalendarSuggestion";
4
+ export type MeOperationIds = MatchPrefix<"MeOperations_", OperationIds>;
5
+ export type ConfigOperationIds = MatchPrefix<"ConfigOperations_", OperationIds>;
6
+ export type SystemOperationIds = MatchPrefix<"SystemOperations_", OperationIds>;
7
+ export type AccountOperationIds = MatchPrefix<"AccountOperations_", OperationIds>;
8
+ export type AccountDetailOperationIds = MatchPrefix<"AccountDetailOperations_", OperationIds>;
9
+ export type MailboxOperationIds = MatchPrefix<"MailboxOperations_", OperationIds>;
10
+ export type MailboxDetailOperationIds = MatchPrefix<"MailboxDetailOperations_", OperationIds>;
11
+ export type FolderRoleOperationIds = MatchPrefix<"FolderRoleOperations_", OperationIds>;
12
+ export type FilterOperationIds = MatchPrefix<"FilterOperations_", OperationIds>;
13
+ export type FilterDetailOperationIds = MatchPrefix<"FilterDetailOperations_", OperationIds>;
14
+ export type LabelOperationIds = MatchPrefix<"LabelOperations_", OperationIds>;
15
+ export type LabelDetailOperationIds = MatchPrefix<"LabelDetailOperations_", OperationIds>;
16
+ export type OrganizeOperationIds = MatchPrefix<"OrganizeOperations_", OperationIds>;
17
+ export type OrganizeJobDetailOperationIds = MatchPrefix<"OrganizeJobDetailOperations_", OperationIds>;
18
+ export type SyncOperationIds = MatchPrefix<"SyncOperations_", OperationIds>;
19
+ export type ThreadDetailOperationIds = MatchPrefix<"ThreadDetailOperations_", OperationIds>;
20
+ export type UnifiedThreadOperationIds = MatchPrefix<"UnifiedThreadOperations_", OperationIds>;
21
+ export type ThreadOperationIds = MatchPrefix<"ThreadOperations_", OperationIds>;
22
+ export type MessageOperationIds = MatchPrefix<"MessageOperations_", OperationIds>;
23
+ export type MessageBulkOperationIds = MatchPrefix<"MessageBulkOperations_", OperationIds>;
24
+ export type CalendarOperationIds = MatchPrefix<"CalendarOperations_", OperationIds>;
25
+ export type CalendarDetailOperationIds = MatchPrefix<"CalendarDetailOperations_", OperationIds>;
26
+ /**
27
+ * The feed sub-resource of `/calendars/{calendarId}`, handled beside the
28
+ * collection itself rather than inside it: minting a credential and serving one
29
+ * calendar's settings are different jobs on the same path.
30
+ */
31
+ export type CalendarFeedDetailOperationIds = "CalendarDetailOperations_getCalendarFeed" | "CalendarDetailOperations_putCalendarFeed" | "CalendarDetailOperations_revokeCalendarFeed";
32
+ export type CalendarCollectionDetailOperationIds = Exclude<CalendarDetailOperationIds, CalendarFeedDetailOperationIds>;
33
+ export type CalendarFeedOperationIds = MatchPrefix<"CalendarFeedOperations_", OperationIds>;
34
+ export type CalendarEventOperationIds = MatchPrefix<"CalendarEventOperations_", OperationIds>;
35
+ export type CalendarEventDetailOperationIds = MatchPrefix<"CalendarEventDetailOperations_", OperationIds>;
36
+ export type CalendarFreeBusyOperationIds = MatchPrefix<"CalendarFreeBusyOperations_", OperationIds>;
37
+ export type TrashOperationIds = MatchPrefix<"TrashOperations_", OperationIds>;
38
+ export type OutboxOperationIds = MatchPrefix<"OutboxOperations_", OperationIds>;
39
+ export type OutboxDetailOperationIds = MatchPrefix<"OutboxDetailOperations_", OperationIds>;
40
+ export type OutboxAttachmentOperationIds = MatchPrefix<"OutboxAttachmentOperations_", OperationIds>;
41
+ export type AddressOperationIds = MatchPrefix<"AddressOperations_", OperationIds>;
42
+ export type AddressDetailOperationIds = MatchPrefix<"AddressDetailOperations_", OperationIds>;
43
+ export type MicrosoftOAuthOperationIds = MatchPrefix<"MicrosoftOAuthOperations_", OperationIds>;
44
+ export type CalendarSuggestionOperationIds = MatchPrefix<"CalendarSuggestionOperations_", OperationIds>;
45
+ export type MessageCalendarSuggestionOperationIds = MatchPrefix<"MessageCalendarSuggestionOperations_", OperationIds>;
46
+ export type CalendarSuggestionActionOperationIds = MatchPrefix<"CalendarSuggestionActionOperations_", OperationIds>;
47
+ type HandlerResponse = Record<string, any>;
48
+ export type OperationHandler<_T extends OperationIds = OperationIds> = (context: Context) => Promise<HandlerResponse>;
49
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,100 +1,115 @@
1
1
  {
2
- "name": "@remit/backend",
3
- "version": "0.0.143",
4
- "description": "Remit Mail Inspector API backend",
5
- "license": "MIT",
6
- "author": "",
7
- "type": "module",
8
- "main": "src/index.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./src/index.ts",
12
- "default": "./src/index.ts"
13
- },
14
- "./client": {
15
- "types": "./src/service/data-client.ts",
16
- "default": "./src/service/data-client.ts"
17
- },
18
- "./create-remit-client": {
19
- "types": "./src/service/create-remit-client.ts",
20
- "default": "./src/service/create-remit-client.ts"
21
- },
22
- "./folder-role-appointments": {
23
- "types": "./src/handlers/folder-role-appointments.ts",
24
- "default": "./src/handlers/folder-role-appointments.ts"
25
- },
26
- "./folder-role-labels": {
27
- "types": "./src/handlers/folder-role-labels.ts",
28
- "default": "./src/handlers/folder-role-labels.ts"
29
- },
30
- "./organize": {
31
- "types": "./src/service/organize.ts",
32
- "default": "./src/service/organize.ts"
33
- },
34
- "./sender-category": {
35
- "types": "./src/service/sender-category.ts",
36
- "default": "./src/service/sender-category.ts"
37
- },
38
- "./trigger-sync": {
39
- "types": "./src/service/trigger-sync.ts",
40
- "default": "./src/service/trigger-sync.ts"
41
- },
42
- "./content-handler": {
43
- "types": "./dev-server/content-handler.ts",
44
- "default": "./dev-server/content-handler.ts"
45
- }
46
- },
47
- "scripts": {
48
- "test:typecheck": "tsgo --noEmit",
49
- "test:run": "node $NODE_TEST_FLAGS --env-file .env.test --import tsx --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-exclude='src/**/*.test.ts' --test-coverage-exclude='src/**/*.test.tsx' --test-coverage-lines=76 --test 'src/**/*.test.ts' 'dev-server/**/*.test.ts'",
50
- "test": "npm run test:typecheck && npm run test:run",
51
- "bundle": "esbuild 'src/index.ts' --sourcemap --bundle --platform=node --outfile=dist/index.js",
52
- "dev": "node --env-file .env.test --watch --import tsx dev-server/server.ts",
53
- "dev:remote": "node --env-file ../../.env.dev --watch --import tsx dev-server/server.ts",
54
- "serve": "node --import tsx dev-server/server.ts"
55
- },
56
- "devDependencies": {
57
- "@remit/apisix": "*",
58
- "@types/better-sqlite3": "^7.6.13",
59
- "@types/express": "^5.0.3",
60
- "@types/swagger-ui-express": "^4.1.8",
61
- "better-sqlite3": "^12.11.1",
62
- "esbuild": "^0.28.1",
63
- "express": "^5.1.0",
64
- "swagger-ui-express": "^5.0.1",
65
- "tsx": "*"
66
- },
67
- "dependencies": {
68
- "@remit/config-format": "*",
69
- "@remit/config-transfer": "*",
70
- "@remit/calendar-service": "*",
71
- "@remit/data-ports": "*",
72
- "@remit/domain-enums": "*",
73
- "@remit/api-openapi-types": "*",
74
- "@remit/logger-lambda": "*",
75
- "@remit/mail-oauth-service": "*",
76
- "@remit/secrets-service": "*",
77
- "@remit/mailbox-service": "*",
78
- "@remit/search-service": "*",
79
- "@remit/storage-service": "*",
80
- "@remit/auth-service": "*",
81
- "@remit/drizzle-service": "*",
82
- "@remit/sqs-client": "*",
83
- "@aws-sdk/client-secrets-manager": "*",
84
- "@aws-sdk/client-sqs": "*",
85
- "better-sqlite3": "^12.11.1",
86
- "expect-env": "*",
87
- "openapi-backend": "*",
88
- "p-map": "*",
89
- "@types/aws-lambda": "*",
90
- "@types/better-sqlite3": "^7.6.13"
91
- },
92
- "publishConfig": {
93
- "access": "public"
94
- },
95
- "repository": {
96
- "type": "git",
97
- "url": "git+https://github.com/remit-mail/reader.git",
98
- "directory": "packages/backend"
99
- }
2
+ "name": "@remit/backend",
3
+ "version": "0.0.144",
4
+ "description": "Remit Mail Inspector API backend",
5
+ "license": "MIT",
6
+ "author": "",
7
+ "type": "module",
8
+ "main": "dist/src/index.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/src/index.d.ts",
12
+ "default": "./dist/src/index.js"
13
+ },
14
+ "./api": {
15
+ "types": "./dist/src/api.d.ts",
16
+ "default": "./dist/src/api.js"
17
+ },
18
+ "./client": {
19
+ "types": "./dist/src/service/data-client.d.ts",
20
+ "default": "./dist/src/service/data-client.js"
21
+ },
22
+ "./create-remit-client": {
23
+ "types": "./dist/src/service/create-remit-client.d.ts",
24
+ "default": "./dist/src/service/create-remit-client.js"
25
+ },
26
+ "./folder-role-appointments": {
27
+ "types": "./dist/src/handlers/folder-role-appointments.d.ts",
28
+ "default": "./dist/src/handlers/folder-role-appointments.js"
29
+ },
30
+ "./folder-role-labels": {
31
+ "types": "./dist/src/handlers/folder-role-labels.d.ts",
32
+ "default": "./dist/src/handlers/folder-role-labels.js"
33
+ },
34
+ "./organize": {
35
+ "types": "./dist/src/service/organize.d.ts",
36
+ "default": "./dist/src/service/organize.js"
37
+ },
38
+ "./sender-category": {
39
+ "types": "./dist/src/service/sender-category.d.ts",
40
+ "default": "./dist/src/service/sender-category.js"
41
+ },
42
+ "./account-deletion": {
43
+ "types": "./dist/src/service/account-deletion.d.ts",
44
+ "default": "./dist/src/service/account-deletion.js"
45
+ },
46
+ "./trigger-sync": {
47
+ "types": "./dist/src/service/trigger-sync.d.ts",
48
+ "default": "./dist/src/service/trigger-sync.js"
49
+ },
50
+ "./content-handler": {
51
+ "types": "./dist/dev-server/content-handler.d.ts",
52
+ "default": "./dist/dev-server/content-handler.js"
53
+ }
54
+ },
55
+ "scripts": {
56
+ "prepack": "node ../../npm-scripts/pack-dist.mjs prepack",
57
+ "postpack": "node ../../npm-scripts/pack-dist.mjs postpack",
58
+ "test:typecheck": "tsgo --noEmit",
59
+ "test:run": "node $NODE_TEST_FLAGS --env-file .env.test --import tsx --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-exclude='src/**/*.test.ts' --test-coverage-exclude='src/**/*.test.tsx' --test-coverage-lines=76 --test 'src/**/*.test.ts' 'dev-server/**/*.test.ts'",
60
+ "test": "npm run test:typecheck && npm run test:run",
61
+ "bundle": "esbuild 'src/index.ts' --sourcemap --bundle --platform=node --outfile=dist/index.js",
62
+ "dev": "node --env-file .env.test --watch --import tsx dev-server/server.ts",
63
+ "dev:remote": "node --env-file ../../.env.dev --watch --import tsx dev-server/server.ts",
64
+ "serve": "node --import tsx dev-server/server.ts"
65
+ },
66
+ "devDependencies": {
67
+ "@remit/apisix": "*",
68
+ "@types/better-sqlite3": "^7.6.13",
69
+ "@types/express": "^5.0.3",
70
+ "@types/swagger-ui-express": "^4.1.8",
71
+ "better-sqlite3": "^12.11.1",
72
+ "esbuild": "^0.28.1",
73
+ "express": "^5.1.0",
74
+ "swagger-ui-express": "^5.0.1",
75
+ "tsx": "*"
76
+ },
77
+ "dependencies": {
78
+ "@remit/config-format": "*",
79
+ "@remit/config-transfer": "*",
80
+ "@remit/calendar-service": "*",
81
+ "@remit/data-ports": "*",
82
+ "@remit/domain-enums": "*",
83
+ "@remit/api-openapi-types": "*",
84
+ "@remit/logger-lambda": "*",
85
+ "@remit/mail-oauth-service": "*",
86
+ "@remit/secrets-service": "*",
87
+ "@remit/mailbox-service": "*",
88
+ "@remit/storage-service": "*",
89
+ "@remit/auth-service": "*",
90
+ "@remit/drizzle-service": "*",
91
+ "@remit/sqs-client": "*",
92
+ "@aws-sdk/client-secrets-manager": "*",
93
+ "@aws-sdk/client-sqs": "*",
94
+ "better-sqlite3": "^12.11.1",
95
+ "expect-env": "*",
96
+ "openapi-backend": "*",
97
+ "p-map": "*",
98
+ "@types/aws-lambda": "*",
99
+ "@types/better-sqlite3": "^7.6.13",
100
+ "@remit/logger": "*",
101
+ "@remit/api-openapi-spec": "*"
102
+ },
103
+ "publishConfig": {
104
+ "access": "public"
105
+ },
106
+ "repository": {
107
+ "type": "git",
108
+ "url": "git+https://github.com/remit-mail/reader.git",
109
+ "directory": "packages/backend"
110
+ },
111
+ "types": "dist/src/index.d.ts",
112
+ "files": [
113
+ "dist"
114
+ ]
100
115
  }
package/.env.test DELETED
@@ -1,4 +0,0 @@
1
- SERVER_PORT=5433
2
- SQS_QUEUE_URL=http://localhost:9324/queue/remit-local
3
- NODE_ENV=development
4
- LOG_LEVEL=debug
@@ -1,113 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import { createContentSigner } from "../src/derive/contentSignature.js";
4
- import { authorizeContentRequest } from "./content-auth.js";
5
-
6
- const SECRET = "test-master-secret-at-least-32-chars-long";
7
- const PATH = "accounts/cfg-alice/acc-alice/messages/msg-1/parts/1.2";
8
- const now = () => Math.floor(Date.now() / 1000);
9
-
10
- describe("authorizeContentRequest", () => {
11
- it("is a no-op on the AWS path (unsigned URLs)", () => {
12
- const result = authorizeContentRequest({
13
- dataBackend: undefined,
14
- secret: undefined,
15
- relativePath: PATH,
16
- exp: undefined,
17
- sig: undefined,
18
- nowSeconds: now(),
19
- });
20
- assert.deepEqual(result, { authorized: true });
21
- });
22
-
23
- it("authorizes a validly signed request on the self-host stack", () => {
24
- const { exp, sig } = createContentSigner(SECRET)(PATH);
25
- const result = authorizeContentRequest({
26
- dataBackend: "sqlite",
27
- secret: SECRET,
28
- relativePath: PATH,
29
- exp: String(exp),
30
- sig,
31
- nowSeconds: now(),
32
- });
33
- assert.deepEqual(result, { authorized: true });
34
- });
35
-
36
- it("returns 401 when the signature is absent", () => {
37
- const result = authorizeContentRequest({
38
- dataBackend: "sqlite",
39
- secret: SECRET,
40
- relativePath: PATH,
41
- exp: undefined,
42
- sig: undefined,
43
- nowSeconds: now(),
44
- });
45
- assert.equal(result.authorized, false);
46
- assert.equal(result.authorized === false && result.status, 401);
47
- });
48
-
49
- it("returns 403 for a signature minted for another account's path", () => {
50
- const { exp, sig } = createContentSigner(SECRET)(PATH);
51
- const result = authorizeContentRequest({
52
- dataBackend: "sqlite",
53
- secret: SECRET,
54
- relativePath: "accounts/cfg-bob/acc-bob/messages/msg-9/parts/1.2",
55
- exp: String(exp),
56
- sig,
57
- nowSeconds: now(),
58
- });
59
- assert.equal(result.authorized, false);
60
- assert.equal(result.authorized === false && result.status, 403);
61
- });
62
-
63
- it("returns 403 for an expired signature", () => {
64
- const { exp, sig } = createContentSigner(SECRET, -20)(PATH);
65
- const result = authorizeContentRequest({
66
- dataBackend: "sqlite",
67
- secret: SECRET,
68
- relativePath: PATH,
69
- exp: String(exp),
70
- sig,
71
- nowSeconds: now(),
72
- });
73
- assert.equal(result.authorized, false);
74
- assert.equal(result.authorized === false && result.status, 403);
75
- });
76
-
77
- it("fails closed with 500 when no signing secret is configured", () => {
78
- const result = authorizeContentRequest({
79
- dataBackend: "sqlite",
80
- secret: undefined,
81
- relativePath: PATH,
82
- exp: "123",
83
- sig: "whatever",
84
- nowSeconds: now(),
85
- });
86
- assert.equal(result.authorized, false);
87
- assert.equal(result.authorized === false && result.status, 500);
88
- });
89
-
90
- it("enforces signatures on every self-host request", () => {
91
- const { exp, sig } = createContentSigner(SECRET)(PATH);
92
- const valid = authorizeContentRequest({
93
- dataBackend: "sqlite",
94
- secret: SECRET,
95
- relativePath: PATH,
96
- exp: String(exp),
97
- sig,
98
- nowSeconds: now(),
99
- });
100
- assert.deepEqual(valid, { authorized: true });
101
-
102
- const unsigned = authorizeContentRequest({
103
- dataBackend: "sqlite",
104
- secret: SECRET,
105
- relativePath: PATH,
106
- exp: undefined,
107
- sig: undefined,
108
- nowSeconds: now(),
109
- });
110
- assert.equal(unsigned.authorized, false);
111
- assert.equal(unsigned.authorized === false && unsigned.status, 401);
112
- });
113
- });
@@ -1,54 +0,0 @@
1
- import { verifyContentSignature } from "../src/derive/contentSignature.js";
2
-
3
- /**
4
- * Decide whether a `/content` request is authorized. Enforcement applies on the
5
- * self-host SQL backend (sqlite), where this server is the
6
- * deployed backend container and content URLs are signed. On AWS-local dev
7
- * (`DATA_BACKEND` unset) `/content` is served straight from the filesystem
8
- * stand-in for CloudFront and URLs are unsigned, so the check is a no-op.
9
- *
10
- * Pure so the decision can be unit-tested without a live server. `relativePath`
11
- * is the decoded storage path (`accounts/{cfg}/{acc}/messages/{msg}/parts/{part}`)
12
- * the signature was minted over — the caller strips the `/content/` prefix.
13
- */
14
- export interface AuthorizeContentInput {
15
- dataBackend: string | undefined;
16
- secret: string | undefined;
17
- relativePath: string;
18
- exp: string | undefined;
19
- sig: string | undefined;
20
- nowSeconds: number;
21
- }
22
-
23
- export const authorizeContentRequest = (
24
- input: AuthorizeContentInput,
25
- ):
26
- | { authorized: true }
27
- | { authorized: false; status: number; reason: string } => {
28
- if (input.dataBackend !== "sqlite") {
29
- return { authorized: true };
30
- }
31
-
32
- if (!input.secret || input.secret.length === 0) {
33
- // A self-host SQL backend with no signing secret means every content URL
34
- // is unsigned and unverifiable. Fail closed rather than serve mail bytes
35
- // without auth.
36
- return {
37
- authorized: false,
38
- status: 500,
39
- reason: "content-signing-not-configured",
40
- };
41
- }
42
-
43
- const result = verifyContentSignature(
44
- input.relativePath,
45
- input.exp,
46
- input.sig,
47
- input.secret,
48
- input.nowSeconds,
49
- );
50
- if (result.valid) return { authorized: true };
51
-
52
- const status = result.reason === "missing" ? 401 : 403;
53
- return { authorized: false, status, reason: result.reason };
54
- };
@@ -1,105 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import { gzipSync } from "node:zlib";
4
- import {
5
- parseContentStorageKey,
6
- type ServeContentDeps,
7
- serveContent,
8
- } from "./content-handler.js";
9
-
10
- const PART_KEY = "accounts/cfg-1/acc-1/messages/msg-1/parts/1.2";
11
-
12
- const baseDeps = (
13
- over: Partial<ServeContentDeps> = {},
14
- ): { deps: ServeContentDeps; cues: Array<Record<string, unknown>> } => {
15
- const cues: Array<Record<string, unknown>> = [];
16
- const deps: ServeContentDeps = {
17
- readObject: async () => null,
18
- lookupMessage: async () => ({ mailboxId: "mbx-1", uid: 42 }),
19
- requestBodySync: async (input) => {
20
- cues.push(input);
21
- },
22
- ...over,
23
- };
24
- return { deps, cues };
25
- };
26
-
27
- describe("parseContentStorageKey", () => {
28
- it("extracts account + message ids from a part key", () => {
29
- assert.deepEqual(parseContentStorageKey(PART_KEY), {
30
- accountConfigId: "cfg-1",
31
- accountId: "acc-1",
32
- messageId: "msg-1",
33
- });
34
- });
35
-
36
- it("returns null for a non-matching key", () => {
37
- assert.equal(parseContentStorageKey("nope/whatever"), null);
38
- });
39
- });
40
-
41
- describe("serveContent", () => {
42
- it("returns 200 with decompressed bytes when the object is present", async () => {
43
- const { deps, cues } = baseDeps({
44
- readObject: async () => gzipSync(Buffer.from("hello body")),
45
- });
46
-
47
- const result = await serveContent(deps, {
48
- fullPath: "/x/parts/1.2",
49
- storageKey: PART_KEY,
50
- });
51
-
52
- assert.equal(result.status, 200);
53
- assert.equal(result.headers["content-type"], "application/octet-stream");
54
- assert.equal(result.body.toString(), "hello body");
55
- assert.equal(cues.length, 0);
56
- });
57
-
58
- it("returns 202 + Retry-After and re-arms the cue when the object is missing", async () => {
59
- const { deps, cues } = baseDeps({ readObject: async () => null });
60
-
61
- const result = await serveContent(deps, {
62
- fullPath: "/x/parts/1.2",
63
- storageKey: PART_KEY,
64
- });
65
-
66
- assert.equal(result.status, 202);
67
- assert.equal(result.headers["Retry-After"], "1");
68
- assert.equal(cues.length, 1, "cue re-armed exactly once");
69
- assert.deepEqual(cues[0], {
70
- accountId: "acc-1",
71
- mailboxId: "mbx-1",
72
- messageId: "msg-1",
73
- uid: 42,
74
- });
75
- });
76
-
77
- it("still answers 202 (no cue) when the message row is gone", async () => {
78
- const { deps, cues } = baseDeps({
79
- readObject: async () => null,
80
- lookupMessage: async () => null,
81
- });
82
-
83
- const result = await serveContent(deps, {
84
- fullPath: "/x/parts/1.2",
85
- storageKey: PART_KEY,
86
- });
87
-
88
- assert.equal(result.status, 202);
89
- assert.equal(cues.length, 0);
90
- });
91
-
92
- it("propagates a non-missing read error so the route 500s", async () => {
93
- const { deps } = baseDeps({
94
- readObject: async () => {
95
- throw new Error("EACCES");
96
- },
97
- });
98
-
99
- await assert.rejects(
100
- () =>
101
- serveContent(deps, { fullPath: "/x/parts/1.2", storageKey: PART_KEY }),
102
- /EACCES/,
103
- );
104
- });
105
- });