imapkit 4.3.0 → 5.0.0

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 (93) hide show
  1. package/README.md +167 -51
  2. package/bin/help.txt +17 -3
  3. package/bin/imapkit.js +53 -23
  4. package/dist/cjs/commands/append.js +1 -1
  5. package/dist/cjs/commands/create.js +2 -2
  6. package/dist/cjs/commands/delete.js +2 -14
  7. package/dist/cjs/commands/handlers/flags.d.ts +3 -3
  8. package/dist/cjs/commands/handlers/flags.js +3 -3
  9. package/dist/cjs/commands/handlers/store.js +3 -3
  10. package/dist/cjs/commands/rename.js +6 -46
  11. package/dist/cjs/commands/select.js +1 -0
  12. package/dist/cjs/commands/subscribe.js +7 -8
  13. package/dist/cjs/commands/unsubscribe.js +3 -9
  14. package/dist/cjs/control.d.ts +430 -0
  15. package/dist/cjs/control.js +637 -0
  16. package/dist/cjs/index.d.ts +8 -1
  17. package/dist/cjs/index.js +9 -2
  18. package/dist/cjs/load-plugins.d.ts +1 -1
  19. package/dist/cjs/load-plugins.js +7 -2
  20. package/dist/cjs/plugins/acl.js +109 -42
  21. package/dist/cjs/plugins/condstore.js +7 -0
  22. package/dist/cjs/plugins/index.js +1 -3
  23. package/dist/cjs/plugins/metadata.js +140 -57
  24. package/dist/cjs/plugins/objectid.js +10 -0
  25. package/dist/cjs/plugins/quota.js +50 -2
  26. package/dist/cjs/plugins/savedate.js +1 -1
  27. package/dist/cjs/plugins/special-use.d.ts +0 -8
  28. package/dist/cjs/plugins/special-use.js +23 -0
  29. package/dist/cjs/quirks.d.ts +28 -0
  30. package/dist/cjs/quirks.js +124 -0
  31. package/dist/cjs/random.d.ts +10 -0
  32. package/dist/cjs/random.js +22 -0
  33. package/dist/cjs/rest.d.ts +50 -0
  34. package/dist/cjs/rest.js +561 -0
  35. package/dist/cjs/script.d.ts +33 -4
  36. package/dist/cjs/script.js +64 -4
  37. package/dist/cjs/server.d.ts +149 -8
  38. package/dist/cjs/server.js +429 -152
  39. package/dist/cjs/smtp-listener.d.ts +3 -2
  40. package/dist/cjs/smtp-listener.js +10 -6
  41. package/dist/cjs/storage-schema.d.ts +139 -0
  42. package/dist/cjs/storage-schema.js +215 -0
  43. package/dist/cjs/store-operations.d.ts +168 -0
  44. package/dist/cjs/store-operations.js +422 -0
  45. package/dist/cjs/types.d.ts +22 -1
  46. package/dist/esm/commands/append.js +1 -1
  47. package/dist/esm/commands/create.js +2 -2
  48. package/dist/esm/commands/delete.js +2 -14
  49. package/dist/esm/commands/handlers/flags.d.ts +3 -3
  50. package/dist/esm/commands/handlers/flags.js +3 -3
  51. package/dist/esm/commands/handlers/store.js +3 -3
  52. package/dist/esm/commands/rename.js +6 -46
  53. package/dist/esm/commands/select.js +1 -0
  54. package/dist/esm/commands/subscribe.js +7 -8
  55. package/dist/esm/commands/unsubscribe.js +3 -9
  56. package/dist/esm/control.d.ts +430 -0
  57. package/dist/esm/control.js +630 -0
  58. package/dist/esm/index.d.ts +8 -1
  59. package/dist/esm/index.js +5 -2
  60. package/dist/esm/load-plugins.d.ts +1 -1
  61. package/dist/esm/load-plugins.js +7 -2
  62. package/dist/esm/plugins/acl.js +109 -42
  63. package/dist/esm/plugins/condstore.js +7 -0
  64. package/dist/esm/plugins/index.js +1 -3
  65. package/dist/esm/plugins/metadata.js +140 -57
  66. package/dist/esm/plugins/objectid.js +10 -0
  67. package/dist/esm/plugins/quota.js +50 -2
  68. package/dist/esm/plugins/savedate.js +1 -1
  69. package/dist/esm/plugins/special-use.d.ts +0 -8
  70. package/dist/esm/plugins/special-use.js +23 -0
  71. package/dist/esm/quirks.d.ts +28 -0
  72. package/dist/esm/quirks.js +120 -0
  73. package/dist/esm/random.d.ts +10 -0
  74. package/dist/esm/random.js +19 -0
  75. package/dist/esm/rest.d.ts +50 -0
  76. package/dist/esm/rest.js +553 -0
  77. package/dist/esm/script.d.ts +33 -4
  78. package/dist/esm/script.js +63 -4
  79. package/dist/esm/server.d.ts +149 -8
  80. package/dist/esm/server.js +429 -152
  81. package/dist/esm/smtp-listener.d.ts +3 -2
  82. package/dist/esm/smtp-listener.js +10 -6
  83. package/dist/esm/storage-schema.d.ts +139 -0
  84. package/dist/esm/storage-schema.js +211 -0
  85. package/dist/esm/store-operations.d.ts +168 -0
  86. package/dist/esm/store-operations.js +407 -0
  87. package/dist/esm/types.d.ts +22 -1
  88. package/dist/plugin-help.json +0 -14
  89. package/package.json +11 -3
  90. package/dist/cjs/plugins/xtoybird.d.ts +0 -2
  91. package/dist/cjs/plugins/xtoybird.js +0 -236
  92. package/dist/esm/plugins/xtoybird.d.ts +0 -2
  93. package/dist/esm/plugins/xtoybird.js +0 -218
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = renameCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function renameCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes ||
6
7
  parsed.attributes.length !== 2 ||
@@ -22,56 +23,15 @@ function renameCommand(connection, parsed, data, callback) {
22
23
  }
23
24
  const source = parsed.attributes[0].value;
24
25
  const destination = parsed.attributes[1].value;
25
- const server = connection.server;
26
- const mailbox = server.getMailbox(source);
27
- const target = server.getMailbox(destination);
28
- const sendError = (code, message) => {
29
- connection.sendStatus(parsed, data, 'NO', message, code, 'RENAME FAILED');
30
- return callback();
31
- };
32
- // check everything that can be checked up front, so that a failed RENAME never loses the source
33
- if (!mailbox || mailbox.flags.indexOf('\\Noselect') >= 0) {
34
- return sendError('NONEXISTENT', 'Mailbox does not exist');
35
- }
36
- if (target && target.flags.indexOf('\\Noselect') < 0) {
37
- return sendError('ALREADYEXISTS', 'Mailbox already exists');
38
- }
39
- const separator = server.getSeparator(mailbox);
40
- if (destination.substr(0, mailbox.path.length + separator.length) === mailbox.path + separator) {
41
- return sendError('CANNOT', 'Can not move a mailbox into itself');
42
- }
43
- const oldPath = mailbox.path;
44
- let newPath;
26
+ let renamed;
45
27
  try {
46
- if (source.toUpperCase() === 'INBOX') {
47
- // Renaming INBOX moves its messages to the new mailbox and leaves INBOX empty (RFC 3501 section 6.3.5)
48
- const newMailbox = server.createMailbox(destination);
49
- newPath = newMailbox.path;
50
- mailbox.messages.forEach((message) => {
51
- server.copyMessage(newMailbox, message);
52
- });
53
- connection.expungeSpecificMessages(mailbox, () => true);
54
- }
55
- else {
56
- server.deleteMailbox(source, true);
57
- try {
58
- server.createMailbox(destination, mailbox);
59
- }
60
- catch (E) {
61
- // put the source mailbox back where it was
62
- server.createMailbox(source, mailbox);
63
- server.indexFolders();
64
- throw E;
65
- }
66
- server.indexFolders();
67
- newPath = mailbox.path;
68
- }
28
+ renamed = (0, store_operations_js_1.renameMailbox)(connection.server, source, destination);
69
29
  }
70
30
  catch (err) {
71
31
  const E = err;
72
- return sendError(E.code, E.message);
32
+ connection.sendStatus(parsed, data, 'NO', E.message, E.code, 'RENAME FAILED');
33
+ return callback();
73
34
  }
74
- server.mailboxChanged('rename', newPath, { oldPath });
75
35
  connection.send({
76
36
  tag: parsed.tag,
77
37
  command: 'OK',
@@ -81,7 +41,7 @@ function renameCommand(connection, parsed, data, callback) {
81
41
  value: 'RENAME completed'
82
42
  }
83
43
  ]
84
- }, 'RENAME', parsed, data, mailbox);
44
+ }, 'RENAME', parsed, data, renamed.mailbox);
85
45
  return callback();
86
46
  }
87
47
  {
@@ -60,6 +60,7 @@ function selectMailbox(connection, parsed, data, callback, readOnly) {
60
60
  connection.state = 'Selected';
61
61
  connection.selectedMailbox = mailbox;
62
62
  connection.readOnly = readOnly;
63
+ connection.emitSession('select');
63
64
  connection.notificationQueue = [];
64
65
  // \Recent is a session flag: a read-write session takes over the messages
65
66
  // that no other session has seen yet (RFC 3501 section 2.3.2)
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = subscribeCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function subscribeCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes || parsed.attributes.length !== 1 || !parsed.attributes[0] || ['STRING', 'LITERAL', 'ATOM'].indexOf(parsed.attributes[0].type) < 0) {
6
7
  connection.send({
@@ -15,15 +16,13 @@ function subscribeCommand(connection, parsed, data, callback) {
15
16
  }, 'INVALID COMMAND', parsed, data);
16
17
  return callback();
17
18
  }
18
- const path = parsed.attributes[0].value;
19
- const mailbox = connection.server.getMailbox(path);
20
- if (!mailbox || mailbox.flags.indexOf('\\Noselect') >= 0) {
21
- connection.sendStatus(parsed, data, 'NO', 'Mailbox does not exist', 'NONEXISTENT', 'SUBSCRIBE FAILED');
22
- return callback();
19
+ try {
20
+ (0, store_operations_js_1.subscribeMailbox)(connection.server, parsed.attributes[0].value);
23
21
  }
24
- if (!mailbox.subscribed) {
25
- mailbox.subscribed = true;
26
- connection.server.mailboxChanged('subscribe', mailbox.path);
22
+ catch (err) {
23
+ const E = err;
24
+ connection.sendStatus(parsed, data, 'NO', E.message, E.code, 'SUBSCRIBE FAILED');
25
+ return callback();
27
26
  }
28
27
  connection.send({
29
28
  tag: parsed.tag,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = unsubscribeCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function unsubscribeCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes || parsed.attributes.length !== 1 || !parsed.attributes[0] || ['STRING', 'LITERAL', 'ATOM'].indexOf(parsed.attributes[0].type) < 0) {
6
7
  connection.send({
@@ -15,15 +16,8 @@ function unsubscribeCommand(connection, parsed, data, callback) {
15
16
  }, 'INVALID COMMAND', parsed, data);
16
17
  return callback();
17
18
  }
18
- let path = parsed.attributes[0].value;
19
- if (path.toUpperCase() === 'INBOX') {
20
- path = 'INBOX';
21
- }
22
- // The subscription list holds names, so a name stays removable after its mailbox is deleted
23
- // (RFC 3501 section 6.3.6). Removing a name that is not subscribed is not an error (RFC 9051 section 6.3.8)
24
- if (connection.server.subscriptions.delete(path)) {
25
- connection.server.mailboxChanged('unsubscribe', path);
26
- }
19
+ // removing a name that is not subscribed is not an error (RFC 9051 section 6.3.8)
20
+ (0, store_operations_js_1.unsubscribeMailbox)(connection.server, parsed.attributes[0].value);
27
21
  connection.send({
28
22
  tag: parsed.tag,
29
23
  command: 'OK',
@@ -0,0 +1,430 @@
1
+ /**
2
+ * The control API, `server.control`: inspects and changes the server state from a test, without an IMAP session.
3
+ * Every change reaches the connected sessions as if another session had made it (EXISTS, EXPUNGE or VANISHED,
4
+ * unsolicited FETCH, BYE, NOTIFY events, CONDSTORE mod-sequences), with no session as its origin. The API is the
5
+ * operator: ACL does not apply, but every argument is checked. Mailboxes are addressed by their storage name
6
+ * (modified UTF-7), messages by mailbox and UID. Methods return plain data and throw an ImapKitError with a `code`:
7
+ * NONEXISTENT, ALREADYEXISTS, INVALID, or a RFC 5530 code of a failed mailbox operation (CANNOT, HASCHILDREN ...)
8
+ */
9
+ import { ImapKitError } from './store-operations.js';
10
+ import type { FlagMode, UidValidityOptions } from './store-operations.js';
11
+ import type { IMAPConnection, IMAPServer, Mailbox, Message, StorageMailbox, StorageNamespace, UserData } from './types.js';
12
+ /** A mailbox as `getMailbox()` and `listMailboxes()` describe it */
13
+ interface MailboxInfo {
14
+ path: string;
15
+ delimiter: string;
16
+ /** mailbox attributes, e.g. \Noselect, \HasChildren */
17
+ flags: string[];
18
+ /** false for a \Noselect name that only holds child mailboxes */
19
+ selectable: boolean;
20
+ subscribed: boolean;
21
+ messages: number;
22
+ unseen: number;
23
+ uidnext: number;
24
+ uidvalidity: number;
25
+ /** flags that can be stored, \* when new keywords are allowed */
26
+ permanentFlags: string[];
27
+ /** only with CONDSTORE */
28
+ highestModseq?: number;
29
+ /** only with SPECIAL-USE */
30
+ specialUse?: string[];
31
+ /** only with OBJECTID */
32
+ mailboxId?: string;
33
+ }
34
+ /** A message as `getMessage()` and `listMessages()` describe it */
35
+ interface MessageInfo {
36
+ uid: number;
37
+ flags: string[];
38
+ internaldate: string;
39
+ /** size of the source in octets */
40
+ size: number;
41
+ /** only with CONDSTORE */
42
+ modseq?: number;
43
+ /** only with OBJECTID */
44
+ emailId?: string;
45
+ threadId?: string;
46
+ /** the message source, only when asked for */
47
+ raw?: Buffer;
48
+ }
49
+ /** A connected session as `sessions()` describes it */
50
+ interface SessionInfo {
51
+ session: number;
52
+ user: string | null;
53
+ state: string;
54
+ /** storage name of the selected mailbox */
55
+ mailbox: string | null;
56
+ readOnly: boolean;
57
+ /** extensions enabled with ENABLE */
58
+ enabled: string[];
59
+ secure: boolean;
60
+ compressed: boolean;
61
+ remoteAddress: string | null;
62
+ }
63
+ interface NewMessage {
64
+ /** the message source, a string is encoded as UTF-8 */
65
+ raw: string | Uint8Array;
66
+ flags?: string[] | undefined;
67
+ /** a Date or a RFC 3501 date-time string, the current time if not set */
68
+ internaldate?: Date | string | undefined;
69
+ }
70
+ interface UserOptions {
71
+ password?: string | undefined;
72
+ xoauth2?: {
73
+ accessToken?: string | undefined;
74
+ sessionTimeout?: number | undefined;
75
+ } | undefined;
76
+ }
77
+ /** What a REST route handler gets, see src/rest.ts */
78
+ interface RouteRequest {
79
+ params: Record<string, string>;
80
+ query: URLSearchParams;
81
+ body: any;
82
+ }
83
+ /** A REST route of the control API, see src/rest.ts */
84
+ interface ControlRoute {
85
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE';
86
+ /** URL path, `{name}` matches one URL encoded segment */
87
+ path: string;
88
+ summary: string;
89
+ /** returns the response body, `{ status, body }` for another status than 200 */
90
+ handler(request: RouteRequest): unknown;
91
+ }
92
+ /** Selects sessions: a session number, or the sessions of a user */
93
+ type SessionFilter = number | {
94
+ session?: number | undefined;
95
+ user?: string | undefined;
96
+ };
97
+ declare class Control {
98
+ [key: string]: any;
99
+ server: IMAPServer;
100
+ /** REST routes that plugins registered, the REST API serves them after its own */
101
+ routes: ControlRoute[];
102
+ /** `(mailbox, copy)` functions that put plugin data that is not JSON into the snapshot of a mailbox */
103
+ snapshotHandlers: ((mailbox: Mailbox, copy: StorageMailbox) => void)[];
104
+ /** `(mailbox, info)` and `(message, info)` functions that add plugin fields to getMailbox() and getMessage() */
105
+ mailboxInfoHandlers: ((mailbox: Mailbox, info: MailboxInfo) => void)[];
106
+ messageInfoHandlers: ((message: Message, info: MessageInfo) => void)[];
107
+ constructor(server: IMAPServer);
108
+ /**
109
+ * Adds an operation of a plugin, e.g. `server.control.setAcl()` of ACL, so that a plugin that is not loaded
110
+ * leaves no trace. The operation runs with no session as its origin, like the built-in ones, and throws
111
+ * ImapKitErrors (see storeError() in store-operations.ts)
112
+ *
113
+ * @param {String} name Method name on server.control
114
+ * @param {Function} fn Operation
115
+ * @param {Array} [routes] REST routes of the operation
116
+ */
117
+ register(name: string, fn: (...args: any[]) => unknown, routes?: ControlRoute[]): void;
118
+ /**
119
+ * Runs a change with no session as its origin, also when a listener of a command event calls the API
120
+ *
121
+ * @param {Function} fn Change
122
+ * @return {*} the result of fn
123
+ */
124
+ run<T>(fn: () => T): T;
125
+ /**
126
+ * Finds a mailbox by its storage name
127
+ *
128
+ * @param {String} path Storage name, INBOX in any case
129
+ * @param {Boolean} [selectable] true if the mailbox must hold messages (not \Noselect)
130
+ * @return {Object} mailbox
131
+ * @throws {ImapKitError} NONEXISTENT
132
+ */
133
+ requireMailbox(path: unknown, selectable?: boolean): Mailbox;
134
+ /**
135
+ * Checks a new mailbox name
136
+ *
137
+ * @param {String} path Storage name
138
+ * @throws {ImapKitError} INVALID for a name that is not valid modified UTF-7
139
+ */
140
+ checkName(path: unknown): asserts path is string;
141
+ /**
142
+ * Checks that a mailbox argument is a name, the store operations check the rest
143
+ *
144
+ * @param {String} path Storage name
145
+ * @throws {ImapKitError} INVALID
146
+ */
147
+ checkPath(path: unknown): asserts path is string;
148
+ /**
149
+ * Finds messages by UID
150
+ *
151
+ * @param {Object} mailbox Mailbox
152
+ * @param {Array} uids UIDs of the messages
153
+ * @return {Array} messages in the order of the UIDs, without duplicates
154
+ * @throws {ImapKitError} INVALID for a list that is not UIDs, NONEXISTENT for a UID that is not in the mailbox
155
+ */
156
+ requireMessages(mailbox: Mailbox, uids: unknown): Message[];
157
+ /**
158
+ * Finds the sessions a filter selects
159
+ *
160
+ * @param {Number|Object} filter A session number, or `{ session, user }`
161
+ * @return {Array} connections
162
+ */
163
+ findSessions(filter: SessionFilter): IMAPConnection[];
164
+ /**
165
+ * Returns the storage as JSON data in the shape of the `storage` option, so that `imapkit({ storage })` with it
166
+ * starts from the same mailboxes, messages, UIDs, flags and subscriptions. Plugin data on mailboxes and messages
167
+ * is included as far as it is JSON data. Subscriptions of names that are not mailboxes are not part of it
168
+ *
169
+ * @return {Object} storage
170
+ */
171
+ snapshot(): Record<string, StorageNamespace>;
172
+ /**
173
+ * Describes every mailbox, including \Noselect hierarchy levels
174
+ *
175
+ * @return {Array} mailboxes ordered by name
176
+ */
177
+ listMailboxes(): MailboxInfo[];
178
+ /**
179
+ * Describes a mailbox
180
+ *
181
+ * @param {String} path Storage name
182
+ * @return {Object} mailbox info
183
+ */
184
+ getMailbox(path: string): MailboxInfo;
185
+ describeMailbox(mailbox: Mailbox): MailboxInfo;
186
+ /**
187
+ * Describes the messages of a mailbox
188
+ *
189
+ * @param {String} path Storage name
190
+ * @param {Object} [options] `{ uids, raw }`: only these UIDs, include the message source
191
+ * @return {Array} messages ordered by UID
192
+ */
193
+ listMessages(path: string, options?: {
194
+ uids?: number[] | undefined;
195
+ raw?: boolean | undefined;
196
+ }): MessageInfo[];
197
+ /**
198
+ * Describes a message
199
+ *
200
+ * @param {String} path Storage name
201
+ * @param {Number} uid UID of the message
202
+ * @param {Object} [options] `{ raw }`: include the message source, true by default
203
+ * @return {Object} message info
204
+ */
205
+ getMessage(path: string, uid: number, options?: {
206
+ raw?: boolean | undefined;
207
+ }): MessageInfo;
208
+ describeMessage(message: Message, raw: boolean): MessageInfo;
209
+ /**
210
+ * Describes the connected sessions
211
+ *
212
+ * @return {Array} sessions ordered by number
213
+ */
214
+ sessions(): SessionInfo[];
215
+ /**
216
+ * Adds a message to a mailbox, like a delivery from outside: sessions that have the mailbox selected get EXISTS,
217
+ * the first read-write one sees it as \Recent
218
+ *
219
+ * @param {String} path Storage name
220
+ * @param {Object} message `{ raw, flags, internaldate }`
221
+ * @param {Object} [options] `{ checks }`: true runs the checks APPEND runs (QUOTA, APPENDLIMIT ...), a failed
222
+ * one throws an ImapKitError with its response code, e.g. OVERQUOTA
223
+ * @return {Object} `{ uid, uidvalidity }`
224
+ */
225
+ addMessage(path: string, message: NewMessage, options?: {
226
+ checks?: boolean | undefined;
227
+ }): {
228
+ uid: number;
229
+ uidvalidity: number;
230
+ };
231
+ /**
232
+ * Changes the flags of messages. Sessions that have the mailbox selected get unsolicited FETCH responses, with
233
+ * CONDSTORE the messages get new mod-sequences
234
+ *
235
+ * @param {String} path Storage name
236
+ * @param {Array} uids UIDs of the messages
237
+ * @param {Array} flags Flags
238
+ * @param {String} [mode] "set" (default), "add" or "remove"
239
+ * @return {Array} `{ uid, flags }` of every message
240
+ */
241
+ setFlags(path: string, uids: number[], flags: string[], mode?: FlagMode): {
242
+ uid: number;
243
+ flags: string[];
244
+ }[];
245
+ /**
246
+ * Removes messages. Sessions that have the mailbox selected get EXPUNGE (VANISHED with QRESYNC), following the
247
+ * RFC 2180 rules a session's EXPUNGE would
248
+ *
249
+ * @param {String} path Storage name
250
+ * @param {Array} uids UIDs of the messages
251
+ * @return {Array} the removed UIDs
252
+ */
253
+ expungeMessages(path: string, uids: number[]): number[];
254
+ /**
255
+ * Copies messages to another mailbox, the copies keep flags and internal date and get new UIDs
256
+ *
257
+ * @param {String} path Storage name of the source
258
+ * @param {Array} uids UIDs of the messages
259
+ * @param {String} target Storage name of the target
260
+ * @return {Object} `{ uidvalidity, uids }`: UIDVALIDITY of the target, `{ uid, targetUid }` for every message
261
+ */
262
+ copyMessages(path: string, uids: number[], target: string): {
263
+ uidvalidity: number;
264
+ uids: {
265
+ uid: number;
266
+ targetUid: number;
267
+ }[];
268
+ };
269
+ /**
270
+ * Moves messages to another mailbox: copies them and expunges them from the source
271
+ *
272
+ * @param {String} path Storage name of the source
273
+ * @param {Array} uids UIDs of the messages
274
+ * @param {String} target Storage name of the target
275
+ * @return {Object} `{ uidvalidity, uids }`: UIDVALIDITY of the target, `{ uid, targetUid }` for every message
276
+ */
277
+ moveMessages(path: string, uids: number[], target: string): {
278
+ uidvalidity: number;
279
+ uids: {
280
+ uid: number;
281
+ targetUid: number;
282
+ }[];
283
+ };
284
+ transfer(path: string, uids: number[], target: string, move: boolean): {
285
+ uidvalidity: number;
286
+ uids: {
287
+ uid: number;
288
+ targetUid: number;
289
+ }[];
290
+ };
291
+ /**
292
+ * Replaces a message. The content of a UID never changes (RFC 9051 section 2.3.1.1), so this adds the new
293
+ * message and expunges the old one, the new message gets a new UID. Flags and internal date of the old
294
+ * message are kept unless given
295
+ *
296
+ * @param {String} path Storage name
297
+ * @param {Number} uid UID of the message to replace
298
+ * @param {Object} message `{ raw, flags, internaldate }`
299
+ * @return {Object} `{ uid, uidvalidity }` of the new message
300
+ */
301
+ replaceMessage(path: string, uid: number, message: NewMessage): {
302
+ uid: number;
303
+ uidvalidity: number;
304
+ };
305
+ /**
306
+ * Creates a mailbox, with any missing superior levels
307
+ *
308
+ * @param {String} path Storage name
309
+ * @param {Object} [options] `{ subscribed }`
310
+ * @return {Object} mailbox info
311
+ */
312
+ createMailbox(path: string, options?: {
313
+ subscribed?: boolean | undefined;
314
+ }): MailboxInfo;
315
+ /**
316
+ * Deletes a mailbox. Sessions that have it selected are disconnected with BYE, like after DELETE in another session
317
+ *
318
+ * @param {String} path Storage name
319
+ */
320
+ deleteMailbox(path: string): void;
321
+ /**
322
+ * Renames a mailbox, with the RENAME rules (renaming INBOX moves its messages)
323
+ *
324
+ * @param {String} path Storage name
325
+ * @param {String} newPath New storage name
326
+ * @return {Object} info of the renamed mailbox
327
+ */
328
+ renameMailbox(path: string, newPath: string): MailboxInfo;
329
+ /**
330
+ * Gives a mailbox a new UIDVALIDITY, optionally with new UIDs ("keep", "renumber", "shuffle" or "offset"). Sessions
331
+ * that have the mailbox selected are disconnected with BYE, as a UID must not change during a session
332
+ *
333
+ * @param {String} path Storage name
334
+ * @param {Object} [options] `{ uidvalidity, uids, offset, seed }`
335
+ * @return {Object} `{ uidvalidity, uidnext, uids }`, `uids` lists `{ uid, newUid }` for every message
336
+ */
337
+ resetUidValidity(path: string, options?: UidValidityOptions): {
338
+ uidvalidity: number;
339
+ uidnext: number;
340
+ uids: {
341
+ uid: number;
342
+ newUid: number;
343
+ }[];
344
+ };
345
+ /**
346
+ * Subscribes a mailbox
347
+ *
348
+ * @param {String} path Storage name
349
+ * @return {Boolean} true if the subscription changed
350
+ */
351
+ subscribe(path: string): boolean;
352
+ /**
353
+ * Unsubscribes a name, it does not have to be a mailbox
354
+ *
355
+ * @param {String} path Storage name
356
+ * @return {Boolean} true if the subscription changed
357
+ */
358
+ unsubscribe(path: string): boolean;
359
+ /**
360
+ * Lists the users, without credentials
361
+ *
362
+ * @return {Array} `{ name, xoauth2 }`, xoauth2 is true for a user with an access token
363
+ */
364
+ listUsers(): {
365
+ name: string;
366
+ xoauth2: boolean;
367
+ }[];
368
+ /**
369
+ * Adds a user
370
+ *
371
+ * @param {String} name User name
372
+ * @param {Object} options `{ password, xoauth2: { accessToken, sessionTimeout } }`
373
+ */
374
+ addUser(name: string, options: UserOptions): void;
375
+ /**
376
+ * Changes the password or the XOAUTH2 token of a user
377
+ *
378
+ * @param {String} name User name
379
+ * @param {Object} options `{ password, xoauth2 }`, null xoauth2 removes the token
380
+ */
381
+ updateUser(name: string, options: UserOptions): void;
382
+ /**
383
+ * Deletes a user, its sessions are disconnected with BYE unless `disconnect` is false
384
+ *
385
+ * @param {String} name User name
386
+ * @param {Object} [options] `{ disconnect }`
387
+ */
388
+ deleteUser(name: string, options?: {
389
+ disconnect?: boolean | undefined;
390
+ }): void;
391
+ requireUser(name: unknown): UserData;
392
+ userData(user: UserData, options: UserOptions): UserData;
393
+ /**
394
+ * Disconnects sessions
395
+ *
396
+ * @param {Number|Object} filter A session number, or `{ session, user }`
397
+ * @param {Object} [options] `{ text, reset }`: the text of the untagged BYE, or reset the TCP connection without BYE
398
+ * @return {Number} how many sessions were disconnected
399
+ */
400
+ disconnect(filter: SessionFilter, options?: {
401
+ text?: string | undefined;
402
+ reset?: boolean | undefined;
403
+ }): number;
404
+ /**
405
+ * Writes bytes to a session as they are, e.g. an untagged response between commands (ALERT, BYE, EXISTS).
406
+ * Nothing checks that they are valid IMAP
407
+ *
408
+ * @param {Number} session Session number
409
+ * @param {String|Buffer} data Bytes to send, a string is encoded as UTF-8
410
+ */
411
+ inject(session: number, data: string | Uint8Array): void;
412
+ /**
413
+ * Restores the mailboxes and users of the server options and disconnects every session with BYE, so that a
414
+ * long running server (the imapkit command) can be reused between tests. Plugins restore their own state on
415
+ * the `reset` event (QUOTA limits, server annotations). Script rules stay, `server.script.clear()` removes them
416
+ */
417
+ reset(): void;
418
+ /**
419
+ * Stops the server. A graceful shutdown stops accepting connections and waits until the clients disconnect,
420
+ * otherwise the sessions are closed right away
421
+ *
422
+ * @param {Object} [options] `{ graceful }`, true by default
423
+ * @return {Promise} resolves when the server is closed
424
+ */
425
+ shutdown(options?: {
426
+ graceful?: boolean | undefined;
427
+ }): Promise<void>;
428
+ }
429
+ export { Control, ImapKitError };
430
+ export type { MailboxInfo, MessageInfo, SessionInfo, NewMessage, UserOptions, SessionFilter, ControlRoute, RouteRequest };