@remit/data-ports 0.0.82 → 0.0.84

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 (174) hide show
  1. package/dist/account-settings.d.ts +371 -0
  2. package/dist/account-settings.js +114 -0
  3. package/dist/conformance/account.d.ts +3 -0
  4. package/dist/conformance/account.js +118 -0
  5. package/dist/conformance/calendar-collection.d.ts +3 -0
  6. package/dist/conformance/calendar-collection.js +251 -0
  7. package/dist/conformance/calendar-event-index.d.ts +3 -0
  8. package/dist/conformance/calendar-event-index.js +114 -0
  9. package/dist/conformance/calendar-feed-token.d.ts +3 -0
  10. package/dist/conformance/calendar-feed-token.js +97 -0
  11. package/dist/conformance/calendar-object.d.ts +3 -0
  12. package/dist/conformance/calendar-object.js +156 -0
  13. package/dist/conformance/calendar-suggestion.d.ts +3 -0
  14. package/dist/conformance/calendar-suggestion.js +255 -0
  15. package/{src/conformance/harness.ts → dist/conformance/harness.d.ts} +4 -4
  16. package/dist/conformance/harness.js +1 -0
  17. package/dist/conformance/index.js +8 -0
  18. package/dist/conformance/label.d.ts +3 -0
  19. package/dist/conformance/label.js +92 -0
  20. package/dist/conformance/sender-signer-standing.d.ts +3 -0
  21. package/dist/conformance/sender-signer-standing.js +81 -0
  22. package/dist/display-name.d.ts +1 -0
  23. package/dist/display-name.js +26 -0
  24. package/dist/errors.d.ts +150 -0
  25. package/dist/errors.js +181 -0
  26. package/dist/filter-ttl.d.ts +7 -0
  27. package/{src/filter-ttl.ts → dist/filter-ttl.js} +8 -11
  28. package/dist/folder-role.d.ts +239 -0
  29. package/dist/folder-role.js +276 -0
  30. package/dist/id.d.ts +124 -0
  31. package/{src/id.ts → dist/id.js} +41 -127
  32. package/dist/index.d.ts +37 -0
  33. package/dist/index.js +5 -0
  34. package/dist/interfaces/account-config.d.ts +15 -0
  35. package/dist/interfaces/account-config.js +1 -0
  36. package/dist/interfaces/account-export-request.d.ts +10 -0
  37. package/dist/interfaces/account-export-request.js +1 -0
  38. package/dist/interfaces/account-setting.d.ts +7 -0
  39. package/dist/interfaces/account-setting.js +1 -0
  40. package/dist/interfaces/account.d.ts +29 -0
  41. package/dist/interfaces/account.js +1 -0
  42. package/dist/interfaces/address.d.ts +69 -0
  43. package/dist/interfaces/address.js +1 -0
  44. package/dist/interfaces/calendar-collection.d.ts +38 -0
  45. package/dist/interfaces/calendar-collection.js +1 -0
  46. package/dist/interfaces/calendar-event-index.d.ts +19 -0
  47. package/dist/interfaces/calendar-event-index.js +1 -0
  48. package/dist/interfaces/calendar-feed-token.d.ts +31 -0
  49. package/dist/interfaces/calendar-feed-token.js +1 -0
  50. package/dist/interfaces/calendar-object.d.ts +36 -0
  51. package/dist/interfaces/calendar-object.js +1 -0
  52. package/dist/interfaces/calendar-store.d.ts +93 -0
  53. package/dist/interfaces/calendar-store.js +1 -0
  54. package/dist/interfaces/calendar-suggestion.d.ts +62 -0
  55. package/dist/interfaces/calendar-suggestion.js +1 -0
  56. package/dist/interfaces/config-import.d.ts +8 -0
  57. package/dist/interfaces/config-import.js +1 -0
  58. package/dist/interfaces/envelope.d.ts +24 -0
  59. package/dist/interfaces/envelope.js +1 -0
  60. package/dist/interfaces/filter.d.ts +28 -0
  61. package/dist/interfaces/filter.js +1 -0
  62. package/dist/interfaces/label.d.ts +17 -0
  63. package/dist/interfaces/label.js +1 -0
  64. package/dist/interfaces/mailbox-lock.d.ts +12 -0
  65. package/dist/interfaces/mailbox-lock.js +1 -0
  66. package/dist/interfaces/mailbox-special-use.d.ts +54 -0
  67. package/dist/interfaces/mailbox-special-use.js +1 -0
  68. package/dist/interfaces/mailbox.d.ts +79 -0
  69. package/dist/interfaces/mailbox.js +1 -0
  70. package/dist/interfaces/message-flag-push.d.ts +9 -0
  71. package/dist/interfaces/message-flag-push.js +1 -0
  72. package/dist/interfaces/message-flag.d.ts +14 -0
  73. package/dist/interfaces/message-flag.js +1 -0
  74. package/dist/interfaces/message-label.d.ts +21 -0
  75. package/dist/interfaces/message-label.js +1 -0
  76. package/dist/interfaces/message-placement-move.d.ts +8 -0
  77. package/dist/interfaces/message-placement-move.js +1 -0
  78. package/dist/interfaces/message.d.ts +60 -0
  79. package/dist/interfaces/message.js +1 -0
  80. package/dist/interfaces/organize-job-request.d.ts +10 -0
  81. package/dist/interfaces/organize-job-request.js +1 -0
  82. package/dist/interfaces/outbox-attachment.d.ts +101 -0
  83. package/dist/interfaces/outbox-attachment.js +7 -0
  84. package/dist/interfaces/outbox-message.d.ts +70 -0
  85. package/dist/interfaces/outbox-message.js +20 -0
  86. package/dist/interfaces/quarantine.d.ts +20 -0
  87. package/dist/interfaces/quarantine.js +1 -0
  88. package/dist/interfaces/sender-signer-standing.d.ts +26 -0
  89. package/dist/interfaces/sender-signer-standing.js +1 -0
  90. package/dist/interfaces/sync-warning.d.ts +11 -0
  91. package/dist/interfaces/sync-warning.js +1 -0
  92. package/dist/interfaces/thread-message.d.ts +202 -0
  93. package/dist/interfaces/thread-message.js +1 -0
  94. package/dist/mailbox-name.d.ts +35 -0
  95. package/dist/mailbox-name.js +68 -0
  96. package/dist/message-parts.d.ts +22 -0
  97. package/dist/message-parts.js +18 -0
  98. package/dist/message-settlement.d.ts +35 -0
  99. package/{src/message-settlement.ts → dist/message-settlement.js} +5 -19
  100. package/dist/mutation-events.d.ts +19 -0
  101. package/{src/mutation-events.ts → dist/mutation-events.js} +2 -5
  102. package/dist/sender-mute.d.ts +26 -0
  103. package/{src/sender-mute.ts → dist/sender-mute.js} +12 -34
  104. package/dist/sync-warning-state.d.ts +3 -0
  105. package/dist/sync-warning-state.js +5 -0
  106. package/dist/types.d.ts +491 -0
  107. package/dist/types.js +1 -0
  108. package/dist/update-manifest.d.ts +24 -0
  109. package/dist/update-manifest.js +20 -0
  110. package/dist/virtual-copy.d.ts +8 -0
  111. package/{src/virtual-copy.ts → dist/virtual-copy.js} +10 -15
  112. package/dist/wellknown.d.ts +47 -0
  113. package/{src/wellknown.ts → dist/wellknown.js} +26 -49
  114. package/package.json +44 -79
  115. package/src/account-settings.ts +0 -194
  116. package/src/conformance/account.ts +0 -104
  117. package/src/conformance/calendar-collection.ts +0 -342
  118. package/src/conformance/calendar-event-index.ts +0 -203
  119. package/src/conformance/calendar-feed-token.ts +0 -148
  120. package/src/conformance/calendar-object.ts +0 -269
  121. package/src/conformance/calendar-suggestion.ts +0 -490
  122. package/src/conformance/label.ts +0 -136
  123. package/src/conformance/sender-signer-standing.ts +0 -117
  124. package/src/display-name.test.ts +0 -101
  125. package/src/display-name.ts +0 -40
  126. package/src/errors.ts +0 -226
  127. package/src/folder-role.test.ts +0 -422
  128. package/src/folder-role.ts +0 -447
  129. package/src/id.test.ts +0 -352
  130. package/src/index.ts +0 -170
  131. package/src/interfaces/account-config.ts +0 -25
  132. package/src/interfaces/account-export-request.ts +0 -21
  133. package/src/interfaces/account-setting.ts +0 -14
  134. package/src/interfaces/account.ts +0 -43
  135. package/src/interfaces/address.ts +0 -122
  136. package/src/interfaces/calendar-collection.ts +0 -60
  137. package/src/interfaces/calendar-event-index.ts +0 -34
  138. package/src/interfaces/calendar-feed-token.ts +0 -38
  139. package/src/interfaces/calendar-object.ts +0 -58
  140. package/src/interfaces/calendar-suggestion.ts +0 -93
  141. package/src/interfaces/calendar-unit-of-work.ts +0 -47
  142. package/src/interfaces/config-import.ts +0 -16
  143. package/src/interfaces/envelope.ts +0 -32
  144. package/src/interfaces/filter-anchor-transaction.ts +0 -23
  145. package/src/interfaces/filter-anchor.ts +0 -11
  146. package/src/interfaces/filter.ts +0 -41
  147. package/src/interfaces/label.ts +0 -30
  148. package/src/interfaces/mailbox-lock.ts +0 -32
  149. package/src/interfaces/mailbox-special-use.ts +0 -60
  150. package/src/interfaces/mailbox.ts +0 -115
  151. package/src/interfaces/message-flag-push.ts +0 -17
  152. package/src/interfaces/message-flag.ts +0 -15
  153. package/src/interfaces/message-label.ts +0 -25
  154. package/src/interfaces/message-placement-move.ts +0 -15
  155. package/src/interfaces/message.ts +0 -56
  156. package/src/interfaces/organize-job-request.ts +0 -19
  157. package/src/interfaces/outbox-attachment.ts +0 -132
  158. package/src/interfaces/outbox-message.ts +0 -106
  159. package/src/interfaces/quarantine.ts +0 -22
  160. package/src/interfaces/sender-signer-standing.ts +0 -37
  161. package/src/interfaces/thread-message.ts +0 -264
  162. package/src/interfaces/unit-of-work.ts +0 -28
  163. package/src/mailbox-name.test.ts +0 -106
  164. package/src/mailbox-name.ts +0 -90
  165. package/src/mailbox-state-doors.test.ts +0 -104
  166. package/src/message-placement-doors.test.ts +0 -100
  167. package/src/message-settlement.test.ts +0 -132
  168. package/src/sender-mute.test.ts +0 -76
  169. package/src/types.ts +0 -820
  170. package/src/update-manifest.test.ts +0 -130
  171. package/src/update-manifest.ts +0 -22
  172. package/src/virtual-copy.test.ts +0 -34
  173. package/tsconfig.json +0 -8
  174. /package/{src/conformance/index.ts → dist/conformance/index.d.ts} +0 -0
@@ -0,0 +1,371 @@
1
+ import { AccountSettingName } from "@remit/domain-enums";
2
+ import { z } from "zod";
3
+ export type AccountSettingNameValue = (typeof AccountSettingName)[keyof typeof AccountSettingName];
4
+ /**
5
+ * Per-`kind` value schemas, one per variant of the `AccountSettingValue`
6
+ * discriminated union in TypeSpec (`AccountSetting.tsp`). Authored here because
7
+ * the registry is the binding authority for what a value must satisfy — these
8
+ * are the contract every read and write is validated against.
9
+ */
10
+ export declare const BooleanSettingSchema: z.ZodObject<{
11
+ kind: z.ZodLiteral<"Boolean">;
12
+ value: z.ZodBoolean;
13
+ }, "strip", z.ZodTypeAny, {
14
+ kind: "Boolean";
15
+ value: boolean;
16
+ }, {
17
+ kind: "Boolean";
18
+ value: boolean;
19
+ }>;
20
+ export declare const StringSettingSchema: z.ZodObject<{
21
+ kind: z.ZodLiteral<"String">;
22
+ value: z.ZodString;
23
+ }, "strip", z.ZodTypeAny, {
24
+ kind: "String";
25
+ value: string;
26
+ }, {
27
+ kind: "String";
28
+ value: string;
29
+ }>;
30
+ export declare const NumberSettingSchema: z.ZodObject<{
31
+ kind: z.ZodLiteral<"Number">;
32
+ value: z.ZodNumber;
33
+ }, "strip", z.ZodTypeAny, {
34
+ kind: "Number";
35
+ value: number;
36
+ }, {
37
+ kind: "Number";
38
+ value: number;
39
+ }>;
40
+ export declare const StringListSettingSchema: z.ZodObject<{
41
+ kind: z.ZodLiteral<"StringList">;
42
+ value: z.ZodArray<z.ZodString, "many">;
43
+ }, "strip", z.ZodTypeAny, {
44
+ kind: "StringList";
45
+ value: string[];
46
+ }, {
47
+ kind: "StringList";
48
+ value: string[];
49
+ }>;
50
+ export declare const MapSettingSchema: z.ZodObject<{
51
+ kind: z.ZodLiteral<"Map">;
52
+ value: z.ZodRecord<z.ZodString, z.ZodString>;
53
+ }, "strip", z.ZodTypeAny, {
54
+ kind: "Map";
55
+ value: Record<string, string>;
56
+ }, {
57
+ kind: "Map";
58
+ value: Record<string, string>;
59
+ }>;
60
+ /**
61
+ * MutedFlag-valued setting. The value is a rich mute object (`MutedFlag` in
62
+ * TypeSpec / `@remit/api-openapi-types`): a boolean plus audit metadata. The
63
+ * schema is authored here — generated packages ship only `.d.ts` for this shape,
64
+ * so the registry owns the runtime contract a relocated mute flag is validated
65
+ * against (RFC 032 lesson from the signature slice).
66
+ */
67
+ export declare const MutedFlagSettingSchema: z.ZodObject<{
68
+ kind: z.ZodLiteral<"MutedFlag">;
69
+ value: z.ZodObject<{
70
+ value: z.ZodBoolean;
71
+ setAt: z.ZodNumber;
72
+ setBy: z.ZodOptional<z.ZodString>;
73
+ expiresAt: z.ZodOptional<z.ZodNumber>;
74
+ reason: z.ZodOptional<z.ZodString>;
75
+ }, "strip", z.ZodTypeAny, {
76
+ value: boolean;
77
+ setAt: number;
78
+ setBy?: string | undefined;
79
+ expiresAt?: number | undefined;
80
+ reason?: string | undefined;
81
+ }, {
82
+ value: boolean;
83
+ setAt: number;
84
+ setBy?: string | undefined;
85
+ expiresAt?: number | undefined;
86
+ reason?: string | undefined;
87
+ }>;
88
+ }, "strip", z.ZodTypeAny, {
89
+ kind: "MutedFlag";
90
+ value: {
91
+ value: boolean;
92
+ setAt: number;
93
+ setBy?: string | undefined;
94
+ expiresAt?: number | undefined;
95
+ reason?: string | undefined;
96
+ };
97
+ }, {
98
+ kind: "MutedFlag";
99
+ value: {
100
+ value: boolean;
101
+ setAt: number;
102
+ setBy?: string | undefined;
103
+ expiresAt?: number | undefined;
104
+ reason?: string | undefined;
105
+ };
106
+ }>;
107
+ /**
108
+ * RFC 032 Tier 1 registry: binds each setting `name` to the zod schema its
109
+ * `value` must satisfy. The discriminated union (`kind`) types the shape on the
110
+ * wire; this registry binds a name to one concrete shape, so a read or write of
111
+ * a given setting is validated against exactly the variant it requires.
112
+ *
113
+ * Adding a setting key is a deliberate change: extend the `AccountSettingName`
114
+ * enum in TypeSpec and add an entry here — never an ad-hoc, unvalidated value.
115
+ */
116
+ export declare const accountSettingRegistry: {
117
+ readonly Theme: z.ZodObject<{
118
+ kind: z.ZodLiteral<"String">;
119
+ value: z.ZodString;
120
+ }, "strip", z.ZodTypeAny, {
121
+ kind: "String";
122
+ value: string;
123
+ }, {
124
+ kind: "String";
125
+ value: string;
126
+ }>;
127
+ readonly Density: z.ZodObject<{
128
+ kind: z.ZodLiteral<"String">;
129
+ value: z.ZodString;
130
+ }, "strip", z.ZodTypeAny, {
131
+ kind: "String";
132
+ value: string;
133
+ }, {
134
+ kind: "String";
135
+ value: string;
136
+ }>;
137
+ readonly DefaultComposerFormat: z.ZodObject<{
138
+ kind: z.ZodLiteral<"String">;
139
+ value: z.ZodString;
140
+ }, "strip", z.ZodTypeAny, {
141
+ kind: "String";
142
+ value: string;
143
+ }, {
144
+ kind: "String";
145
+ value: string;
146
+ }>;
147
+ readonly PinnedFolders: z.ZodObject<{
148
+ kind: z.ZodLiteral<"StringList">;
149
+ value: z.ZodArray<z.ZodString, "many">;
150
+ }, "strip", z.ZodTypeAny, {
151
+ kind: "StringList";
152
+ value: string[];
153
+ }, {
154
+ kind: "StringList";
155
+ value: string[];
156
+ }>;
157
+ readonly AccountSignaturePlainText: z.ZodObject<{
158
+ kind: z.ZodLiteral<"String">;
159
+ value: z.ZodString;
160
+ }, "strip", z.ZodTypeAny, {
161
+ kind: "String";
162
+ value: string;
163
+ }, {
164
+ kind: "String";
165
+ value: string;
166
+ }>;
167
+ readonly AccountSignatureHtml: z.ZodObject<{
168
+ kind: z.ZodLiteral<"String">;
169
+ value: z.ZodString;
170
+ }, "strip", z.ZodTypeAny, {
171
+ kind: "String";
172
+ value: string;
173
+ }, {
174
+ kind: "String";
175
+ value: string;
176
+ }>;
177
+ readonly AccountDisplayName: z.ZodObject<{
178
+ kind: z.ZodLiteral<"String">;
179
+ value: z.ZodString;
180
+ }, "strip", z.ZodTypeAny, {
181
+ kind: "String";
182
+ value: string;
183
+ }, {
184
+ kind: "String";
185
+ value: string;
186
+ }>;
187
+ readonly AccountMuted: z.ZodObject<{
188
+ kind: z.ZodLiteral<"MutedFlag">;
189
+ value: z.ZodObject<{
190
+ value: z.ZodBoolean;
191
+ setAt: z.ZodNumber;
192
+ setBy: z.ZodOptional<z.ZodString>;
193
+ expiresAt: z.ZodOptional<z.ZodNumber>;
194
+ reason: z.ZodOptional<z.ZodString>;
195
+ }, "strip", z.ZodTypeAny, {
196
+ value: boolean;
197
+ setAt: number;
198
+ setBy?: string | undefined;
199
+ expiresAt?: number | undefined;
200
+ reason?: string | undefined;
201
+ }, {
202
+ value: boolean;
203
+ setAt: number;
204
+ setBy?: string | undefined;
205
+ expiresAt?: number | undefined;
206
+ reason?: string | undefined;
207
+ }>;
208
+ }, "strip", z.ZodTypeAny, {
209
+ kind: "MutedFlag";
210
+ value: {
211
+ value: boolean;
212
+ setAt: number;
213
+ setBy?: string | undefined;
214
+ expiresAt?: number | undefined;
215
+ reason?: string | undefined;
216
+ };
217
+ }, {
218
+ kind: "MutedFlag";
219
+ value: {
220
+ value: boolean;
221
+ setAt: number;
222
+ setBy?: string | undefined;
223
+ expiresAt?: number | undefined;
224
+ reason?: string | undefined;
225
+ };
226
+ }>;
227
+ readonly AccountComposeLanguages: z.ZodObject<{
228
+ kind: z.ZodLiteral<"StringList">;
229
+ value: z.ZodArray<z.ZodString, "many">;
230
+ }, "strip", z.ZodTypeAny, {
231
+ kind: "StringList";
232
+ value: string[];
233
+ }, {
234
+ kind: "StringList";
235
+ value: string[];
236
+ }>;
237
+ readonly MailboxDisplayName: z.ZodObject<{
238
+ kind: z.ZodLiteral<"String">;
239
+ value: z.ZodString;
240
+ }, "strip", z.ZodTypeAny, {
241
+ kind: "String";
242
+ value: string;
243
+ }, {
244
+ kind: "String";
245
+ value: string;
246
+ }>;
247
+ readonly MailboxMuted: z.ZodObject<{
248
+ kind: z.ZodLiteral<"MutedFlag">;
249
+ value: z.ZodObject<{
250
+ value: z.ZodBoolean;
251
+ setAt: z.ZodNumber;
252
+ setBy: z.ZodOptional<z.ZodString>;
253
+ expiresAt: z.ZodOptional<z.ZodNumber>;
254
+ reason: z.ZodOptional<z.ZodString>;
255
+ }, "strip", z.ZodTypeAny, {
256
+ value: boolean;
257
+ setAt: number;
258
+ setBy?: string | undefined;
259
+ expiresAt?: number | undefined;
260
+ reason?: string | undefined;
261
+ }, {
262
+ value: boolean;
263
+ setAt: number;
264
+ setBy?: string | undefined;
265
+ expiresAt?: number | undefined;
266
+ reason?: string | undefined;
267
+ }>;
268
+ }, "strip", z.ZodTypeAny, {
269
+ kind: "MutedFlag";
270
+ value: {
271
+ value: boolean;
272
+ setAt: number;
273
+ setBy?: string | undefined;
274
+ expiresAt?: number | undefined;
275
+ reason?: string | undefined;
276
+ };
277
+ }, {
278
+ kind: "MutedFlag";
279
+ value: {
280
+ value: boolean;
281
+ setAt: number;
282
+ setBy?: string | undefined;
283
+ expiresAt?: number | undefined;
284
+ reason?: string | undefined;
285
+ };
286
+ }>;
287
+ readonly FolderRoleAppointment: z.ZodObject<{
288
+ kind: z.ZodLiteral<"String">;
289
+ value: z.ZodString;
290
+ }, "strip", z.ZodTypeAny, {
291
+ kind: "String";
292
+ value: string;
293
+ }, {
294
+ kind: "String";
295
+ value: string;
296
+ }>;
297
+ readonly FolderRoleAppointmentLabel: z.ZodObject<{
298
+ kind: z.ZodLiteral<"String">;
299
+ value: z.ZodString;
300
+ }, "strip", z.ZodTypeAny, {
301
+ kind: "String";
302
+ value: string;
303
+ }, {
304
+ kind: "String";
305
+ value: string;
306
+ }>;
307
+ /**
308
+ * Deprecated tombstone: superseded by `FolderRoleAppointment` (RFC 032
309
+ * exclusive-folder-appointment, #976). Kept only so `baseSettingName`
310
+ * recognises leftover `MailboxRole#<mailboxId>` rows written by the #963/#964
311
+ * backfill instead of throwing `Unknown account setting name`; the read-side
312
+ * groupers (`groupAccountOverrides`, `groupMailboxOverrides`,
313
+ * `groupFolderAppointmentsByAccount`) already skip any base they don't
314
+ * explicitly handle, so a `MailboxRole` row is ignored, not surfaced. Do not
315
+ * write new rows under this name. Remove once the row-deletion migration ships.
316
+ */
317
+ readonly MailboxRole: z.ZodObject<{
318
+ kind: z.ZodLiteral<"String">;
319
+ value: z.ZodString;
320
+ }, "strip", z.ZodTypeAny, {
321
+ kind: "String";
322
+ value: string;
323
+ }, {
324
+ kind: "String";
325
+ value: string;
326
+ }>;
327
+ };
328
+ export type AccountSettingRegistry = typeof accountSettingRegistry;
329
+ export type AccountSettingValueFor<N extends AccountSettingNameValue> = z.infer<AccountSettingRegistry[N]>;
330
+ /**
331
+ * Separator between a setting's base enum member and its per-target suffix in a
332
+ * composite stored name (e.g. `AccountSignaturePlainText#<accountId>`). Some
333
+ * settings are per-account, but `AccountSetting` is keyed per accountConfigId, so
334
+ * the stored `name` encodes the target after this separator. The registry still
335
+ * validates by the base member; readers filter by the suffix.
336
+ */
337
+ export declare const SETTING_NAME_SEPARATOR = "#";
338
+ /**
339
+ * A persisted setting `name`. Either a plain closed-enum member or a composite
340
+ * `<base>#<suffix>` for a per-target setting. Always a `string` whose first
341
+ * segment is a known base member.
342
+ */
343
+ export type StoredSettingName = string;
344
+ /** Compose the stored name for a per-target setting (e.g. one signature per account). */
345
+ export declare function composeSettingName(base: AccountSettingNameValue, target: string): StoredSettingName;
346
+ /**
347
+ * Derive the base enum member from a stored name. Strips the `#<suffix>` from a
348
+ * composite name; returns a plain name unchanged. Throws when the base segment
349
+ * is not a registered setting (let-it-crash — an unknown key is a programmer
350
+ * error, never an ad-hoc column).
351
+ */
352
+ export declare function baseSettingName(name: StoredSettingName): AccountSettingNameValue;
353
+ /**
354
+ * Every value shape the registry can hold. Useful where a value is handled
355
+ * generically (e.g. the persisted `value` attribute) rather than per-name.
356
+ */
357
+ export type AnyAccountSettingValue = z.infer<typeof BooleanSettingSchema | typeof StringSettingSchema | typeof NumberSettingSchema | typeof StringListSettingSchema | typeof MapSettingSchema | typeof MutedFlagSettingSchema>;
358
+ /**
359
+ * Parse-and-narrow a value against the schema registered for `name`. Accepts a
360
+ * plain enum member or a composite `<base>#<suffix>` name; validation resolves
361
+ * to the base member's schema. Throws a ZodError on mismatch (let-it-crash);
362
+ * returns the value typed to the variant the name requires.
363
+ */
364
+ export declare function parseAccountSettingValue<N extends AccountSettingNameValue>(name: N, value: unknown): AccountSettingValueFor<N>;
365
+ export declare function parseAccountSettingValue(name: StoredSettingName, value: unknown): AnyAccountSettingValue;
366
+ /**
367
+ * Non-throwing variant: returns the zod SafeParse result so a caller with a
368
+ * recovery path can branch on `success`. Accepts composite names like above.
369
+ */
370
+ export declare function safeParseAccountSettingValue<N extends AccountSettingNameValue>(name: N, value: unknown): z.SafeParseReturnType<unknown, AccountSettingValueFor<N>>;
371
+ export declare function safeParseAccountSettingValue(name: StoredSettingName, value: unknown): z.SafeParseReturnType<unknown, AnyAccountSettingValue>;
@@ -0,0 +1,114 @@
1
+ import { AccountSettingName } from "@remit/domain-enums";
2
+ import { z } from "zod";
3
+ /**
4
+ * Per-`kind` value schemas, one per variant of the `AccountSettingValue`
5
+ * discriminated union in TypeSpec (`AccountSetting.tsp`). Authored here because
6
+ * the registry is the binding authority for what a value must satisfy — these
7
+ * are the contract every read and write is validated against.
8
+ */
9
+ export const BooleanSettingSchema = z.object({
10
+ kind: z.literal("Boolean"),
11
+ value: z.boolean(),
12
+ });
13
+ export const StringSettingSchema = z.object({
14
+ kind: z.literal("String"),
15
+ value: z.string(),
16
+ });
17
+ export const NumberSettingSchema = z.object({
18
+ kind: z.literal("Number"),
19
+ value: z.number(),
20
+ });
21
+ export const StringListSettingSchema = z.object({
22
+ kind: z.literal("StringList"),
23
+ value: z.array(z.string()),
24
+ });
25
+ export const MapSettingSchema = z.object({
26
+ kind: z.literal("Map"),
27
+ value: z.record(z.string(), z.string()),
28
+ });
29
+ /**
30
+ * MutedFlag-valued setting. The value is a rich mute object (`MutedFlag` in
31
+ * TypeSpec / `@remit/api-openapi-types`): a boolean plus audit metadata. The
32
+ * schema is authored here — generated packages ship only `.d.ts` for this shape,
33
+ * so the registry owns the runtime contract a relocated mute flag is validated
34
+ * against (RFC 032 lesson from the signature slice).
35
+ */
36
+ export const MutedFlagSettingSchema = z.object({
37
+ kind: z.literal("MutedFlag"),
38
+ value: z.object({
39
+ value: z.boolean(),
40
+ setAt: z.number(),
41
+ setBy: z.string().optional(),
42
+ expiresAt: z.number().optional(),
43
+ reason: z.string().optional(),
44
+ }),
45
+ });
46
+ /**
47
+ * RFC 032 Tier 1 registry: binds each setting `name` to the zod schema its
48
+ * `value` must satisfy. The discriminated union (`kind`) types the shape on the
49
+ * wire; this registry binds a name to one concrete shape, so a read or write of
50
+ * a given setting is validated against exactly the variant it requires.
51
+ *
52
+ * Adding a setting key is a deliberate change: extend the `AccountSettingName`
53
+ * enum in TypeSpec and add an entry here — never an ad-hoc, unvalidated value.
54
+ */
55
+ export const accountSettingRegistry = {
56
+ [AccountSettingName.Theme]: StringSettingSchema,
57
+ [AccountSettingName.Density]: StringSettingSchema,
58
+ [AccountSettingName.DefaultComposerFormat]: StringSettingSchema,
59
+ [AccountSettingName.PinnedFolders]: StringListSettingSchema,
60
+ [AccountSettingName.AccountSignaturePlainText]: StringSettingSchema,
61
+ [AccountSettingName.AccountSignatureHtml]: StringSettingSchema,
62
+ [AccountSettingName.AccountDisplayName]: StringSettingSchema,
63
+ [AccountSettingName.AccountMuted]: MutedFlagSettingSchema,
64
+ [AccountSettingName.AccountComposeLanguages]: StringListSettingSchema,
65
+ [AccountSettingName.MailboxDisplayName]: StringSettingSchema,
66
+ [AccountSettingName.MailboxMuted]: MutedFlagSettingSchema,
67
+ [AccountSettingName.FolderRoleAppointment]: StringSettingSchema,
68
+ [AccountSettingName.FolderRoleAppointmentLabel]: StringSettingSchema,
69
+ /**
70
+ * Deprecated tombstone: superseded by `FolderRoleAppointment` (RFC 032
71
+ * exclusive-folder-appointment, #976). Kept only so `baseSettingName`
72
+ * recognises leftover `MailboxRole#<mailboxId>` rows written by the #963/#964
73
+ * backfill instead of throwing `Unknown account setting name`; the read-side
74
+ * groupers (`groupAccountOverrides`, `groupMailboxOverrides`,
75
+ * `groupFolderAppointmentsByAccount`) already skip any base they don't
76
+ * explicitly handle, so a `MailboxRole` row is ignored, not surfaced. Do not
77
+ * write new rows under this name. Remove once the row-deletion migration ships.
78
+ */
79
+ [AccountSettingName.MailboxRole]: StringSettingSchema,
80
+ };
81
+ /**
82
+ * Separator between a setting's base enum member and its per-target suffix in a
83
+ * composite stored name (e.g. `AccountSignaturePlainText#<accountId>`). Some
84
+ * settings are per-account, but `AccountSetting` is keyed per accountConfigId, so
85
+ * the stored `name` encodes the target after this separator. The registry still
86
+ * validates by the base member; readers filter by the suffix.
87
+ */
88
+ export const SETTING_NAME_SEPARATOR = "#";
89
+ /** Compose the stored name for a per-target setting (e.g. one signature per account). */
90
+ export function composeSettingName(base, target) {
91
+ return `${base}${SETTING_NAME_SEPARATOR}${target}`;
92
+ }
93
+ const isAccountSettingNameValue = (value) => Object.hasOwn(accountSettingRegistry, value);
94
+ /**
95
+ * Derive the base enum member from a stored name. Strips the `#<suffix>` from a
96
+ * composite name; returns a plain name unchanged. Throws when the base segment
97
+ * is not a registered setting (let-it-crash — an unknown key is a programmer
98
+ * error, never an ad-hoc column).
99
+ */
100
+ export function baseSettingName(name) {
101
+ const base = name.split(SETTING_NAME_SEPARATOR)[0] ?? "";
102
+ if (!isAccountSettingNameValue(base)) {
103
+ throw new Error(`Unknown account setting name: ${name}`);
104
+ }
105
+ return base;
106
+ }
107
+ export function parseAccountSettingValue(name, value) {
108
+ const schema = accountSettingRegistry[baseSettingName(name)];
109
+ return schema.parse(value);
110
+ }
111
+ export function safeParseAccountSettingValue(name, value) {
112
+ const schema = accountSettingRegistry[baseSettingName(name)];
113
+ return schema.safeParse(value);
114
+ }
@@ -0,0 +1,3 @@
1
+ import type { IAccountRepository } from "../interfaces/account.js";
2
+ import type { RepositoryConformanceHarness } from "./harness.js";
3
+ export declare function accountRepositoryConformance(harness: RepositoryConformanceHarness<IAccountRepository>): void;
@@ -0,0 +1,118 @@
1
+ import assert from "node:assert/strict";
2
+ import { after, before, describe, test } from "node:test";
3
+ import { AccountAuthType, AccountService, ConnectionState, SyncPhase, } from "@remit/domain-enums";
4
+ const accountInput = (accountConfigId) => ({
5
+ accountConfigId,
6
+ username: "person@example.com",
7
+ email: "person@example.com",
8
+ imapHost: "imap.example.com",
9
+ imapPort: 993,
10
+ imapTls: true,
11
+ imapStartTls: false,
12
+ isActive: true,
13
+ connectionState: ConnectionState.NotAuthenticated,
14
+ });
15
+ const everyUpdatableField = {
16
+ username: "renamed@example.org",
17
+ email: "renamed@example.org",
18
+ authType: AccountAuthType.OauthMicrosoft,
19
+ syncedServices: [AccountService.Calendar],
20
+ grantedScopes: ["https://outlook.office.com/SMTP.Send"],
21
+ passwordHash: "password-ciphertext",
22
+ oauthRefreshTokenHash: "refresh-token-ciphertext",
23
+ oauthTokenUpdatedAt: 1_700_000_000_001,
24
+ imapHost: "imap.example.org",
25
+ imapPort: 143,
26
+ imapTls: false,
27
+ imapStartTls: true,
28
+ smtpEnabled: true,
29
+ smtpHost: "smtp.example.org",
30
+ smtpPort: 465,
31
+ smtpTls: true,
32
+ smtpStartTls: true,
33
+ smtpUsername: "smtp-person",
34
+ smtpPasswordHash: "smtp-password-ciphertext",
35
+ isActive: false,
36
+ connectionState: ConnectionState.Authenticated,
37
+ deletedAt: 1_700_000_000_002,
38
+ lastConnectedAt: 1_700_000_000_003,
39
+ lastSyncAt: 1_700_000_000_004,
40
+ lastError: "refused",
41
+ syncPhase: SyncPhase.complete,
42
+ mailboxCountTotal: 7,
43
+ mailboxCountSynced: 3,
44
+ };
45
+ export function accountRepositoryConformance(harness) {
46
+ describe("IAccountRepository conformance", () => {
47
+ let repo;
48
+ before(async () => {
49
+ repo = await harness.createRepository();
50
+ });
51
+ after(() => harness.teardown());
52
+ test("an account created without a service selection syncs mail", async () => {
53
+ const created = await repo.create(accountInput(harness.makeId()));
54
+ assert.deepEqual(created.syncedServices, [AccountService.Mail]);
55
+ assert.deepEqual((await repo.get(created.accountId)).syncedServices, [
56
+ AccountService.Mail,
57
+ ]);
58
+ });
59
+ test("an account keeps the selection it was created with", async () => {
60
+ const created = await repo.create({
61
+ ...accountInput(harness.makeId()),
62
+ syncedServices: [AccountService.Calendar],
63
+ });
64
+ assert.deepEqual((await repo.get(created.accountId)).syncedServices, [
65
+ AccountService.Calendar,
66
+ ]);
67
+ });
68
+ test("an update replaces the selection outright", async () => {
69
+ const created = await repo.create(accountInput(harness.makeId()));
70
+ const updated = await repo.update(created.accountId, {
71
+ syncedServices: [AccountService.Mail, AccountService.Calendar],
72
+ });
73
+ assert.deepEqual(updated.syncedServices, [
74
+ AccountService.Mail,
75
+ AccountService.Calendar,
76
+ ]);
77
+ assert.deepEqual((await repo.get(created.accountId)).syncedServices, [
78
+ AccountService.Mail,
79
+ AccountService.Calendar,
80
+ ]);
81
+ });
82
+ test("an update that leaves the selection out keeps it", async () => {
83
+ const created = await repo.create({
84
+ ...accountInput(harness.makeId()),
85
+ syncedServices: [AccountService.Calendar],
86
+ });
87
+ await repo.update(created.accountId, { lastError: "timeout" });
88
+ assert.deepEqual((await repo.get(created.accountId)).syncedServices, [
89
+ AccountService.Calendar,
90
+ ]);
91
+ });
92
+ test("the mail sources of a config leave out an account with mail off", async () => {
93
+ const accountConfigId = harness.makeId();
94
+ const mailOnly = await repo.create(accountInput(accountConfigId));
95
+ const both = await repo.create({
96
+ ...accountInput(accountConfigId),
97
+ syncedServices: [AccountService.Mail, AccountService.Calendar],
98
+ });
99
+ await repo.create({
100
+ ...accountInput(accountConfigId),
101
+ syncedServices: [AccountService.Calendar],
102
+ });
103
+ await repo.create(accountInput(harness.makeId()));
104
+ const sources = await repo.listMailSourcesByAccountConfig(accountConfigId);
105
+ assert.deepEqual(sources.map((account) => account.accountId).sort(), [mailOnly.accountId, both.accountId].sort());
106
+ });
107
+ test("an update stores every field it is handed", async () => {
108
+ const created = await repo.create(accountInput(harness.makeId()));
109
+ await repo.update(created.accountId, everyUpdatableField);
110
+ const stored = await repo.get(created.accountId);
111
+ assert.deepEqual(stored, {
112
+ ...created,
113
+ ...everyUpdatableField,
114
+ updatedAt: stored.updatedAt,
115
+ });
116
+ });
117
+ });
118
+ }
@@ -0,0 +1,3 @@
1
+ import type { ICalendarCollectionRepository } from "../interfaces/calendar-collection.js";
2
+ import type { RepositoryConformanceHarness } from "./harness.js";
3
+ export declare function calendarCollectionRepositoryConformance(harness: RepositoryConformanceHarness<ICalendarCollectionRepository>): void;