@remit/config-transfer 0.0.1

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.
@@ -0,0 +1,193 @@
1
+ import type { AccountSettingItem } from "@remit/data-ports";
2
+ import { SETTING_NAME_SEPARATOR } from "@remit/data-ports/account-settings";
3
+ import {
4
+ type CanonicalMailboxRoleValue,
5
+ parseFolderRoleAppointmentLabelName,
6
+ parseFolderRoleAppointmentName,
7
+ } from "@remit/data-ports/folder-role";
8
+ import { AccountSettingName } from "@remit/domain-enums";
9
+
10
+ /** One account's settings rows, already split out of the config-wide batch. */
11
+ export interface AccountSettingsView {
12
+ displayName: string;
13
+ muted: MutedValue | null;
14
+ composeLanguages: string[];
15
+ signaturePlainText: string;
16
+ signatureHtml: string;
17
+ /** role → the mailbox appointed to it, and the path it had when appointed. */
18
+ roles: Map<CanonicalMailboxRoleValue, RoleAppointmentView>;
19
+ }
20
+
21
+ export interface RoleAppointmentView {
22
+ mailboxId: string;
23
+ lastKnownPath: string | undefined;
24
+ }
25
+
26
+ export interface MailboxSettingsView {
27
+ displayName: string;
28
+ muted: MutedValue | null;
29
+ }
30
+
31
+ export type MutedValue = Extract<
32
+ AccountSettingItem["value"],
33
+ { kind: "MutedFlag" }
34
+ >["value"];
35
+
36
+ export interface SettingsView {
37
+ defaultComposerFormat: string | undefined;
38
+ /**
39
+ * The folder paths the user pinned. Stored once for the whole configuration,
40
+ * un-suffixed, which is the only per-target setting the registry does not
41
+ * spell per account — so it is read here rather than under an account, and
42
+ * the export resolves each path against the account that holds it.
43
+ */
44
+ pinnedFolders: string[];
45
+ byAccount: Map<string, AccountSettingsView>;
46
+ byMailbox: Map<string, MailboxSettingsView>;
47
+ }
48
+
49
+ /** An account nobody has set anything on. */
50
+ export const emptyAccountSettings = (): AccountSettingsView => ({
51
+ displayName: "",
52
+ muted: null,
53
+ composeLanguages: [],
54
+ signaturePlainText: "",
55
+ signatureHtml: "",
56
+ roles: new Map(),
57
+ });
58
+
59
+ const emptyMailboxView = (): MailboxSettingsView => ({
60
+ displayName: "",
61
+ muted: null,
62
+ });
63
+
64
+ const splitName = (name: string): { base: string; target: string } => {
65
+ const index = name.indexOf(SETTING_NAME_SEPARATOR);
66
+ if (index === -1) return { base: name, target: "" };
67
+ return {
68
+ base: name.slice(0, index),
69
+ target: name.slice(index + SETTING_NAME_SEPARATOR.length),
70
+ };
71
+ };
72
+
73
+ const stringOf = (item: AccountSettingItem): string | undefined =>
74
+ item.value.kind === "String" ? item.value.value : undefined;
75
+
76
+ const stringListOf = (item: AccountSettingItem): string[] | undefined =>
77
+ item.value.kind === "StringList" ? item.value.value : undefined;
78
+
79
+ const mutedOf = (item: AccountSettingItem): MutedValue | undefined =>
80
+ item.value.kind === "MutedFlag" ? item.value.value : undefined;
81
+
82
+ /**
83
+ * Split one configuration's whole settings batch into the shape the export
84
+ * reads. Every per-target setting is stored as a composite `<base>#<target>`
85
+ * row against the configuration, so a single `listByAccountConfig` holds every
86
+ * account's and every folder's overrides at once.
87
+ *
88
+ * A row whose base this build does not recognise is passed over rather than
89
+ * refused: an export written by a reader that has since learned a new setting
90
+ * still has to produce the settings it does know.
91
+ */
92
+ export const readSettings = (settings: AccountSettingItem[]): SettingsView => {
93
+ const view: SettingsView = {
94
+ defaultComposerFormat: undefined,
95
+ pinnedFolders: [],
96
+ byAccount: new Map(),
97
+ byMailbox: new Map(),
98
+ };
99
+
100
+ const account = (id: string): AccountSettingsView => {
101
+ const current = view.byAccount.get(id) ?? emptyAccountSettings();
102
+ view.byAccount.set(id, current);
103
+ return current;
104
+ };
105
+ const mailbox = (id: string): MailboxSettingsView => {
106
+ const current = view.byMailbox.get(id) ?? emptyMailboxView();
107
+ view.byMailbox.set(id, current);
108
+ return current;
109
+ };
110
+
111
+ for (const setting of settings) {
112
+ const appointment = parseFolderRoleAppointmentName(setting.name);
113
+ if (appointment) {
114
+ const mailboxId = stringOf(setting);
115
+ if (mailboxId === undefined) continue;
116
+ account(appointment.accountId).roles.set(appointment.role, {
117
+ mailboxId,
118
+ lastKnownPath: undefined,
119
+ });
120
+ continue;
121
+ }
122
+
123
+ const { base, target } = splitName(setting.name);
124
+
125
+ if (base === AccountSettingName.DefaultComposerFormat) {
126
+ view.defaultComposerFormat = stringOf(setting);
127
+ continue;
128
+ }
129
+
130
+ if (base === AccountSettingName.PinnedFolders) {
131
+ view.pinnedFolders = stringListOf(setting) ?? [];
132
+ continue;
133
+ }
134
+
135
+ if (target === "") continue;
136
+
137
+ switch (base) {
138
+ case AccountSettingName.AccountDisplayName: {
139
+ const value = stringOf(setting);
140
+ if (value !== undefined) account(target).displayName = value;
141
+ break;
142
+ }
143
+ case AccountSettingName.AccountMuted: {
144
+ const value = mutedOf(setting);
145
+ if (value !== undefined) account(target).muted = value;
146
+ break;
147
+ }
148
+ case AccountSettingName.AccountComposeLanguages: {
149
+ const value = stringListOf(setting);
150
+ if (value !== undefined) account(target).composeLanguages = value;
151
+ break;
152
+ }
153
+ case AccountSettingName.AccountSignaturePlainText: {
154
+ const value = stringOf(setting);
155
+ if (value !== undefined) account(target).signaturePlainText = value;
156
+ break;
157
+ }
158
+ case AccountSettingName.AccountSignatureHtml: {
159
+ const value = stringOf(setting);
160
+ if (value !== undefined) account(target).signatureHtml = value;
161
+ break;
162
+ }
163
+ case AccountSettingName.MailboxDisplayName: {
164
+ const value = stringOf(setting);
165
+ if (value !== undefined) mailbox(target).displayName = value;
166
+ break;
167
+ }
168
+ case AccountSettingName.MailboxMuted: {
169
+ const value = mutedOf(setting);
170
+ if (value !== undefined) mailbox(target).muted = value;
171
+ break;
172
+ }
173
+ default:
174
+ break;
175
+ }
176
+ }
177
+
178
+ // Second pass: the recorded path rides a sibling row, so it can only be
179
+ // attached once every appointment it belongs to has been read.
180
+ for (const setting of settings) {
181
+ const label = parseFolderRoleAppointmentLabelName(setting.name);
182
+ if (!label) continue;
183
+ const path = stringOf(setting);
184
+ if (path === undefined) continue;
185
+ const appointment = view.byAccount
186
+ .get(label.accountId)
187
+ ?.roles.get(label.role);
188
+ if (!appointment) continue;
189
+ appointment.lastKnownPath = path;
190
+ }
191
+
192
+ return view;
193
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src/**/*.ts"]
8
+ }