@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,53 @@
1
+ import type { MutedFlag } from "@remit/api-openapi-types";
2
+ import type { AccountSettingItem, IAccountSettingRepository } from "@remit/data-ports";
3
+ export interface AccountOverrides {
4
+ displayName?: string;
5
+ muted?: MutedFlag;
6
+ composeLanguages?: string[];
7
+ }
8
+ export interface MailboxOverrides {
9
+ displayNameOverride?: string;
10
+ muted?: MutedFlag;
11
+ }
12
+ export declare const groupAccountOverrides: (settings: AccountSettingItem[]) => Map<string, AccountOverrides>;
13
+ /**
14
+ * Resolve one account's display-name/mute overrides by reading just its two
15
+ * composite rows. Used by the account create/update handlers.
16
+ */
17
+ export declare const loadAccountOverrides: (accountSetting: IAccountSettingRepository, accountConfigId: string, accountId: string) => Promise<AccountOverrides>;
18
+ export declare const upsertAccountDisplayName: (accountSetting: IAccountSettingRepository, accountConfigId: string, accountId: string, displayName: string) => Promise<unknown>;
19
+ /**
20
+ * Replace the account's compose-language list. An empty array deletes the row:
21
+ * the client falls back to the browser's own languages when the setting is
22
+ * absent, and an account that offers no language at all has nothing to pick
23
+ * from (RFC 032: absence is "unset").
24
+ */
25
+ export declare const writeAccountComposeLanguages: (accountSetting: IAccountSettingRepository, accountConfigId: string, accountId: string, languages: string[]) => Promise<unknown>;
26
+ /**
27
+ * Set or clear the account mute flag. A `MutedFlag` upserts the row; `null`
28
+ * deletes it (RFC 032: absence is "unset"). Mirrors the address-flag
29
+ * null→remove, object→set semantics.
30
+ */
31
+ export declare const writeAccountMuted: (accountSetting: IAccountSettingRepository, accountConfigId: string, accountId: string, muted: MutedFlag | null) => Promise<unknown>;
32
+ export declare const groupMailboxOverrides: (settings: AccountSettingItem[]) => Map<string, MailboxOverrides>;
33
+ /**
34
+ * Load every mailbox's overrides for an account configuration in one query,
35
+ * grouped by mailboxId. listMailboxes uses this once to avoid an N+1 per mailbox.
36
+ */
37
+ export declare const loadMailboxOverridesForConfig: (accountSetting: IAccountSettingRepository, accountConfigId: string) => Promise<Map<string, MailboxOverrides>>;
38
+ /**
39
+ * Resolve one mailbox's overrides by reading just its two composite rows.
40
+ * Used by getMailbox and the rename/PATCH handler.
41
+ */
42
+ export declare const loadMailboxOverrides: (accountSetting: IAccountSettingRepository, accountConfigId: string, mailboxId: string) => Promise<MailboxOverrides>;
43
+ /**
44
+ * Apply a mailbox PATCH's override changes (displayNameOverride / muted) to
45
+ * AccountSetting rows. For each field: `null` deletes the row, a value upserts
46
+ * it, `undefined`/absent is a no-op — the same semantics the entity-backed
47
+ * version used. The canonical role a folder fills is written separately via
48
+ * `writeFolderRoleAppointment` (RFC 032 exclusive-folder-appointment, #976).
49
+ */
50
+ export declare const applyMailboxOverrideChanges: (accountSetting: Pick<IAccountSettingRepository, "upsert" | "delete">, accountConfigId: string, mailboxId: string, changes: {
51
+ displayNameOverride?: string | null;
52
+ muted?: MutedFlag | null;
53
+ }) => Promise<void>;
@@ -0,0 +1,233 @@
1
+ import { baseSettingName, composeSettingName, SETTING_NAME_SEPARATOR, } from "@remit/data-ports/account-settings";
2
+ /**
3
+ * RFC 032 Tier 1: the per-account display name and mute flag, and the
4
+ * per-mailbox display-name override and mute flag, live in AccountSetting
5
+ * rows — not on the Account/Mailbox entities. They are stored as composite-named
6
+ * rows keyed by the target id (`AccountDisplayName#<accountId>`,
7
+ * `MailboxMuted#<mailboxId>`, …), so one `listByAccountConfig` query yields every
8
+ * target's overrides; readers group them by the `#<targetId>` suffix. This mirrors
9
+ * the signature helper (`account-signature.ts`). The per-folder role override
10
+ * this module used to carry is superseded by the per-account role map — see
11
+ * `folder-role-appointments.ts` (RFC 032 exclusive-folder-appointment, #976).
12
+ */
13
+ const ACCOUNT_OVERRIDE_NAMES = {
14
+ displayName: "AccountDisplayName",
15
+ muted: "AccountMuted",
16
+ composeLanguages: "AccountComposeLanguages",
17
+ };
18
+ const MAILBOX_OVERRIDE_NAMES = {
19
+ displayName: "MailboxDisplayName",
20
+ muted: "MailboxMuted",
21
+ };
22
+ const stringValueOf = (item) => {
23
+ const { value } = item;
24
+ if (value.kind === "String")
25
+ return value.value;
26
+ return undefined;
27
+ };
28
+ const mutedValueOf = (item) => {
29
+ const { value } = item;
30
+ if (value.kind === "MutedFlag")
31
+ return value.value;
32
+ return undefined;
33
+ };
34
+ const stringListValueOf = (item) => {
35
+ const { value } = item;
36
+ if (value.kind === "StringList")
37
+ return value.value;
38
+ return undefined;
39
+ };
40
+ const targetIdOf = (name) => {
41
+ const idx = name.indexOf(SETTING_NAME_SEPARATOR);
42
+ if (idx === -1)
43
+ return undefined;
44
+ return name.slice(idx + SETTING_NAME_SEPARATOR.length) || undefined;
45
+ };
46
+ // ============================================
47
+ // Account overrides (displayName + muted)
48
+ // ============================================
49
+ export const groupAccountOverrides = (settings) => {
50
+ const byAccount = new Map();
51
+ for (const setting of settings) {
52
+ const accountId = targetIdOf(setting.name);
53
+ if (!accountId)
54
+ continue;
55
+ const base = baseSettingName(setting.name);
56
+ if (base === ACCOUNT_OVERRIDE_NAMES.displayName) {
57
+ const displayName = stringValueOf(setting);
58
+ if (displayName === undefined)
59
+ continue;
60
+ const current = byAccount.get(accountId) ?? {};
61
+ current.displayName = displayName;
62
+ byAccount.set(accountId, current);
63
+ continue;
64
+ }
65
+ if (base === ACCOUNT_OVERRIDE_NAMES.muted) {
66
+ const muted = mutedValueOf(setting);
67
+ if (muted === undefined)
68
+ continue;
69
+ const current = byAccount.get(accountId) ?? {};
70
+ current.muted = muted;
71
+ byAccount.set(accountId, current);
72
+ continue;
73
+ }
74
+ if (base === ACCOUNT_OVERRIDE_NAMES.composeLanguages) {
75
+ const composeLanguages = stringListValueOf(setting);
76
+ if (composeLanguages === undefined)
77
+ continue;
78
+ const current = byAccount.get(accountId) ?? {};
79
+ current.composeLanguages = composeLanguages;
80
+ byAccount.set(accountId, current);
81
+ }
82
+ }
83
+ return byAccount;
84
+ };
85
+ /**
86
+ * Resolve one account's display-name/mute overrides by reading just its two
87
+ * composite rows. Used by the account create/update handlers.
88
+ */
89
+ export const loadAccountOverrides = async (accountSetting, accountConfigId, accountId) => {
90
+ const [displayName, muted, composeLanguages] = await Promise.all([
91
+ accountSetting.get(accountConfigId, composeSettingName(ACCOUNT_OVERRIDE_NAMES.displayName, accountId)),
92
+ accountSetting.get(accountConfigId, composeSettingName(ACCOUNT_OVERRIDE_NAMES.muted, accountId)),
93
+ accountSetting.get(accountConfigId, composeSettingName(ACCOUNT_OVERRIDE_NAMES.composeLanguages, accountId)),
94
+ ]);
95
+ const overrides = {};
96
+ const displayNameValue = displayName ? stringValueOf(displayName) : undefined;
97
+ const mutedValue = muted ? mutedValueOf(muted) : undefined;
98
+ const languagesValue = composeLanguages
99
+ ? stringListValueOf(composeLanguages)
100
+ : undefined;
101
+ if (displayNameValue !== undefined)
102
+ overrides.displayName = displayNameValue;
103
+ if (mutedValue !== undefined)
104
+ overrides.muted = mutedValue;
105
+ if (languagesValue !== undefined)
106
+ overrides.composeLanguages = languagesValue;
107
+ return overrides;
108
+ };
109
+ export const upsertAccountDisplayName = (accountSetting, accountConfigId, accountId, displayName) => accountSetting.upsert({
110
+ accountConfigId,
111
+ name: composeSettingName(ACCOUNT_OVERRIDE_NAMES.displayName, accountId),
112
+ value: { kind: "String", value: displayName },
113
+ });
114
+ /**
115
+ * Replace the account's compose-language list. An empty array deletes the row:
116
+ * the client falls back to the browser's own languages when the setting is
117
+ * absent, and an account that offers no language at all has nothing to pick
118
+ * from (RFC 032: absence is "unset").
119
+ */
120
+ export const writeAccountComposeLanguages = (accountSetting, accountConfigId, accountId, languages) => {
121
+ const name = composeSettingName(ACCOUNT_OVERRIDE_NAMES.composeLanguages, accountId);
122
+ if (languages.length === 0) {
123
+ return accountSetting.delete(accountConfigId, name);
124
+ }
125
+ return accountSetting.upsert({
126
+ accountConfigId,
127
+ name,
128
+ value: { kind: "StringList", value: languages },
129
+ });
130
+ };
131
+ /**
132
+ * Set or clear the account mute flag. A `MutedFlag` upserts the row; `null`
133
+ * deletes it (RFC 032: absence is "unset"). Mirrors the address-flag
134
+ * null→remove, object→set semantics.
135
+ */
136
+ export const writeAccountMuted = (accountSetting, accountConfigId, accountId, muted) => {
137
+ const name = composeSettingName(ACCOUNT_OVERRIDE_NAMES.muted, accountId);
138
+ if (muted === null) {
139
+ return accountSetting.delete(accountConfigId, name);
140
+ }
141
+ return accountSetting.upsert({
142
+ accountConfigId,
143
+ name,
144
+ value: { kind: "MutedFlag", value: muted },
145
+ });
146
+ };
147
+ // ============================================
148
+ // Mailbox overrides (displayNameOverride + muted)
149
+ // ============================================
150
+ export const groupMailboxOverrides = (settings) => {
151
+ const byMailbox = new Map();
152
+ for (const setting of settings) {
153
+ const mailboxId = targetIdOf(setting.name);
154
+ if (!mailboxId)
155
+ continue;
156
+ const base = baseSettingName(setting.name);
157
+ if (base === MAILBOX_OVERRIDE_NAMES.displayName) {
158
+ const displayName = stringValueOf(setting);
159
+ if (displayName === undefined)
160
+ continue;
161
+ const current = byMailbox.get(mailboxId) ?? {};
162
+ current.displayNameOverride = displayName;
163
+ byMailbox.set(mailboxId, current);
164
+ continue;
165
+ }
166
+ if (base === MAILBOX_OVERRIDE_NAMES.muted) {
167
+ const muted = mutedValueOf(setting);
168
+ if (muted === undefined)
169
+ continue;
170
+ const current = byMailbox.get(mailboxId) ?? {};
171
+ current.muted = muted;
172
+ byMailbox.set(mailboxId, current);
173
+ }
174
+ }
175
+ return byMailbox;
176
+ };
177
+ /**
178
+ * Load every mailbox's overrides for an account configuration in one query,
179
+ * grouped by mailboxId. listMailboxes uses this once to avoid an N+1 per mailbox.
180
+ */
181
+ export const loadMailboxOverridesForConfig = async (accountSetting, accountConfigId) => {
182
+ const settings = await accountSetting.listByAccountConfig(accountConfigId);
183
+ return groupMailboxOverrides(settings);
184
+ };
185
+ /**
186
+ * Resolve one mailbox's overrides by reading just its two composite rows.
187
+ * Used by getMailbox and the rename/PATCH handler.
188
+ */
189
+ export const loadMailboxOverrides = async (accountSetting, accountConfigId, mailboxId) => {
190
+ const [displayName, muted] = await Promise.all([
191
+ accountSetting.get(accountConfigId, composeSettingName(MAILBOX_OVERRIDE_NAMES.displayName, mailboxId)),
192
+ accountSetting.get(accountConfigId, composeSettingName(MAILBOX_OVERRIDE_NAMES.muted, mailboxId)),
193
+ ]);
194
+ const overrides = {};
195
+ const displayNameValue = displayName ? stringValueOf(displayName) : undefined;
196
+ const mutedValue = muted ? mutedValueOf(muted) : undefined;
197
+ if (displayNameValue !== undefined)
198
+ overrides.displayNameOverride = displayNameValue;
199
+ if (mutedValue !== undefined)
200
+ overrides.muted = mutedValue;
201
+ return overrides;
202
+ };
203
+ /**
204
+ * Apply a mailbox PATCH's override changes (displayNameOverride / muted) to
205
+ * AccountSetting rows. For each field: `null` deletes the row, a value upserts
206
+ * it, `undefined`/absent is a no-op — the same semantics the entity-backed
207
+ * version used. The canonical role a folder fills is written separately via
208
+ * `writeFolderRoleAppointment` (RFC 032 exclusive-folder-appointment, #976).
209
+ */
210
+ export const applyMailboxOverrideChanges = async (accountSetting, accountConfigId, mailboxId, changes) => {
211
+ const writes = [];
212
+ if (changes.displayNameOverride !== undefined) {
213
+ const name = composeSettingName(MAILBOX_OVERRIDE_NAMES.displayName, mailboxId);
214
+ writes.push(changes.displayNameOverride === null
215
+ ? accountSetting.delete(accountConfigId, name)
216
+ : accountSetting.upsert({
217
+ accountConfigId,
218
+ name,
219
+ value: { kind: "String", value: changes.displayNameOverride },
220
+ }));
221
+ }
222
+ if (changes.muted !== undefined) {
223
+ const name = composeSettingName(MAILBOX_OVERRIDE_NAMES.muted, mailboxId);
224
+ writes.push(changes.muted === null
225
+ ? accountSetting.delete(accountConfigId, name)
226
+ : accountSetting.upsert({
227
+ accountConfigId,
228
+ name,
229
+ value: { kind: "MutedFlag", value: changes.muted },
230
+ }));
231
+ }
232
+ await Promise.all(writes);
233
+ };
@@ -0,0 +1,28 @@
1
+ import type { AccountSettingItem, IAccountSettingRepository } from "@remit/data-ports";
2
+ export interface AccountSignature {
3
+ plainText?: string;
4
+ html?: string;
5
+ }
6
+ /**
7
+ * Build a per-account signature lookup from the whole settings set of one
8
+ * account configuration. Signatures are stored as composite-named rows
9
+ * (`AccountSignaturePlainText#<accountId>` / `AccountSignatureHtml#<accountId>`),
10
+ * so one `listByAccountConfig` query yields every account's signature; this
11
+ * groups them by the `#<accountId>` suffix.
12
+ */
13
+ export declare const groupSignaturesByAccount: (settings: AccountSettingItem[]) => Map<string, AccountSignature>;
14
+ /**
15
+ * Resolve the signature for a single account by reading just its two composite
16
+ * rows. Used by the account create/update handlers, which act on one account.
17
+ */
18
+ export declare const loadSignatureForAccount: (accountSetting: IAccountSettingRepository, accountConfigId: string, accountId: string) => Promise<AccountSignature>;
19
+ /**
20
+ * Idempotent upsert of an account's signature fields. Only writes the parts the
21
+ * caller supplied — an empty string is a valid stored value (it clears the
22
+ * displayed signature without removing the row), matching the prior PATCH
23
+ * semantics where setting `""` stored `""`.
24
+ */
25
+ export declare const upsertAccountSignature: (accountSetting: IAccountSettingRepository, accountConfigId: string, accountId: string, signature: {
26
+ plainText?: string;
27
+ html?: string;
28
+ }) => Promise<void>;
@@ -0,0 +1,85 @@
1
+ import { baseSettingName, composeSettingName, SETTING_NAME_SEPARATOR, } from "@remit/data-ports/account-settings";
2
+ const SIGNATURE_NAMES = {
3
+ plainText: "AccountSignaturePlainText",
4
+ html: "AccountSignatureHtml",
5
+ };
6
+ const stringValueOf = (item) => {
7
+ const { value } = item;
8
+ if (value.kind === "String")
9
+ return value.value;
10
+ return undefined;
11
+ };
12
+ const accountIdOf = (name) => {
13
+ const idx = name.indexOf(SETTING_NAME_SEPARATOR);
14
+ if (idx === -1)
15
+ return undefined;
16
+ return name.slice(idx + SETTING_NAME_SEPARATOR.length) || undefined;
17
+ };
18
+ /**
19
+ * Build a per-account signature lookup from the whole settings set of one
20
+ * account configuration. Signatures are stored as composite-named rows
21
+ * (`AccountSignaturePlainText#<accountId>` / `AccountSignatureHtml#<accountId>`),
22
+ * so one `listByAccountConfig` query yields every account's signature; this
23
+ * groups them by the `#<accountId>` suffix.
24
+ */
25
+ export const groupSignaturesByAccount = (settings) => {
26
+ const byAccount = new Map();
27
+ for (const setting of settings) {
28
+ const accountId = accountIdOf(setting.name);
29
+ if (!accountId)
30
+ continue;
31
+ const base = baseSettingName(setting.name);
32
+ const text = stringValueOf(setting);
33
+ if (text === undefined)
34
+ continue;
35
+ const current = byAccount.get(accountId) ?? {};
36
+ if (base === SIGNATURE_NAMES.plainText)
37
+ current.plainText = text;
38
+ if (base === SIGNATURE_NAMES.html)
39
+ current.html = text;
40
+ byAccount.set(accountId, current);
41
+ }
42
+ return byAccount;
43
+ };
44
+ /**
45
+ * Resolve the signature for a single account by reading just its two composite
46
+ * rows. Used by the account create/update handlers, which act on one account.
47
+ */
48
+ export const loadSignatureForAccount = async (accountSetting, accountConfigId, accountId) => {
49
+ const [plain, html] = await Promise.all([
50
+ accountSetting.get(accountConfigId, composeSettingName(SIGNATURE_NAMES.plainText, accountId)),
51
+ accountSetting.get(accountConfigId, composeSettingName(SIGNATURE_NAMES.html, accountId)),
52
+ ]);
53
+ const signature = {};
54
+ const plainText = plain ? stringValueOf(plain) : undefined;
55
+ const htmlText = html ? stringValueOf(html) : undefined;
56
+ if (plainText !== undefined)
57
+ signature.plainText = plainText;
58
+ if (htmlText !== undefined)
59
+ signature.html = htmlText;
60
+ return signature;
61
+ };
62
+ /**
63
+ * Idempotent upsert of an account's signature fields. Only writes the parts the
64
+ * caller supplied — an empty string is a valid stored value (it clears the
65
+ * displayed signature without removing the row), matching the prior PATCH
66
+ * semantics where setting `""` stored `""`.
67
+ */
68
+ export const upsertAccountSignature = async (accountSetting, accountConfigId, accountId, signature) => {
69
+ const writes = [];
70
+ if (signature.plainText !== undefined) {
71
+ writes.push(accountSetting.upsert({
72
+ accountConfigId,
73
+ name: composeSettingName(SIGNATURE_NAMES.plainText, accountId),
74
+ value: { kind: "String", value: signature.plainText },
75
+ }));
76
+ }
77
+ if (signature.html !== undefined) {
78
+ writes.push(accountSetting.upsert({
79
+ accountConfigId,
80
+ name: composeSettingName(SIGNATURE_NAMES.html, accountId),
81
+ value: { kind: "String", value: signature.html },
82
+ }));
83
+ }
84
+ await Promise.all(writes);
85
+ };
@@ -0,0 +1,7 @@
1
+ import type { RemitClient } from "../service/data-client.js";
2
+ import type { AccountDetailOperationIds, AccountOperationIds, OperationHandler } from "../types.js";
3
+ import { assertNotOAuthCreate, assertPasswordProvided, toAccountResponse } from "./account-guards.js";
4
+ export { assertNotOAuthCreate, assertPasswordProvided, toAccountResponse };
5
+ export declare const triggerAccountSyncSafe: (client: RemitClient<"sync">, accountId: string) => Promise<void>;
6
+ export declare const AccountOperations: Record<AccountOperationIds, OperationHandler<AccountOperationIds, "sync" | "accountFanout">>;
7
+ export declare const AccountDetailOperations: Record<AccountDetailOperationIds, OperationHandler<AccountDetailOperationIds, "sync" | "accountFanout">>;