imapkit 4.3.1 → 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 +147 -8
  38. package/dist/cjs/server.js +398 -144
  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 +147 -8
  80. package/dist/esm/server.js +399 -145
  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,10 +1,13 @@
1
- const EVENTS = ['greeting', 'command', 'input', 'response', 'continuation'];
1
+ import { seededRandom } from './random.js';
2
+ import { MAX_NUMBER } from './numbers.js';
3
+ const EVENTS = ['greeting', 'command', 'input', 'response', 'continuation', 'quiet'];
2
4
  const OUTPUT_EVENTS = ['greeting', 'response', 'continuation'];
3
5
  // the events where a key is valid, every other key is a typo and refused
4
6
  const KEYS = {
5
7
  on: EVENTS,
6
- command: ['command', 'input', 'response', 'continuation'],
7
- tag: ['command', 'input', 'response', 'continuation'],
8
+ // a quiet event belongs to the command that waits for input (IDLE), if there is one
9
+ command: ['command', 'input', 'response', 'continuation', 'quiet'],
10
+ tag: ['command', 'input', 'response', 'continuation', 'quiet'],
8
11
  description: ['response', 'continuation'],
9
12
  session: EVENTS,
10
13
  user: EVENTS,
@@ -15,6 +18,8 @@ const KEYS = {
15
18
  when: EVENTS,
16
19
  nth: EVENTS,
17
20
  times: EVENTS,
21
+ chance: EVENTS,
22
+ quietFor: ['quiet'],
18
23
  mutate: ['response', 'continuation'],
19
24
  literals: ['response'],
20
25
  defer: ['response'],
@@ -22,7 +27,7 @@ const KEYS = {
22
27
  before: OUTPUT_EVENTS,
23
28
  after: OUTPUT_EVENTS,
24
29
  run: ['command', 'input'],
25
- drop: EVENTS,
30
+ drop: ['greeting', 'command', 'input', 'response', 'continuation'],
26
31
  delay: ['greeting', 'command', 'response', 'continuation'],
27
32
  chunk: EVENTS,
28
33
  chunkDelay: EVENTS,
@@ -70,6 +75,12 @@ function validateRule(rule) {
70
75
  throw new TypeError('Script rule option "' + key + '" can not be used with "on": "' + rule.on + '"');
71
76
  }
72
77
  }
78
+ if (rule.on === 'quiet' && !(Number.isInteger(rule.quietFor) && rule.quietFor > 0)) {
79
+ throw new TypeError('A quiet rule needs "quietFor", a positive number of milliseconds');
80
+ }
81
+ if (rule.chance !== undefined && !(typeof rule.chance === 'number' && rule.chance >= 0 && rule.chance <= 1)) {
82
+ throw new TypeError('Script rule option "chance" must be a number from 0 to 1');
83
+ }
73
84
  if (!ACTIONS.some(key => rule[key] !== undefined && rule[key] !== false)) {
74
85
  throw new TypeError('A script rule needs an action (' + ACTIONS.join(', ') + ')');
75
86
  }
@@ -92,6 +103,9 @@ function validateRule(rule) {
92
103
  }
93
104
  }
94
105
  for (const key of ['delay', 'chunkDelay', 'truncate']) {
106
+ if (key === 'chunkDelay' && rule[key] === 'tick') {
107
+ continue;
108
+ }
95
109
  if (rule[key] !== undefined && !(Number.isInteger(rule[key]) && rule[key] >= 0)) {
96
110
  throw new TypeError('Script rule option "' + key + '" must be a non-negative integer');
97
111
  }
@@ -150,6 +164,14 @@ export class ServerScript {
150
164
  constructor(server, rules) {
151
165
  this.server = server;
152
166
  this.entries = [];
167
+ this.ruleCounter = 0;
168
+ const seed = server.options.scriptSeed;
169
+ const seedRandom = () => {
170
+ this.random = seededRandom(typeof seed === 'number' ? seed : Math.floor(Math.random() * MAX_NUMBER));
171
+ };
172
+ seedRandom();
173
+ // control.reset() starts the same random sequence again
174
+ server.on('reset', seedRandom);
153
175
  this.watched = new Set();
154
176
  if (rules) {
155
177
  this.add(rules);
@@ -199,6 +221,26 @@ export class ServerScript {
199
221
  setEntries(entries) {
200
222
  this.entries = entries;
201
223
  this.watched = new Set(entries.map(entry => entry.rule.on));
224
+ if (this.watched.has('quiet')) {
225
+ // sessions that are quiet already start counting now
226
+ this.server.connections?.forEach(connection => connection.watchQuiet());
227
+ }
228
+ }
229
+ /**
230
+ * The next quiet time a rule waits for, after the quiet time the rules were checked for already
231
+ *
232
+ * @param {Number} checked Milliseconds of quiet the rules were checked for
233
+ * @return {Number|null} the smallest `quietFor` above it, null if there is none
234
+ */
235
+ nextQuiet(checked) {
236
+ let next = null;
237
+ for (const entry of this.entries) {
238
+ const quietFor = entry.rule.quietFor;
239
+ if (entry.rule.on === 'quiet' && quietFor !== undefined && quietFor > checked && (next === null || quietFor < next)) {
240
+ next = quietFor;
241
+ }
242
+ }
243
+ return next;
202
244
  }
203
245
  addEntry(rule, match) {
204
246
  // a copy, so that changing the caller's object later does not change the rule
@@ -212,6 +254,7 @@ export class ServerScript {
212
254
  }
213
255
  }
214
256
  const entry = {
257
+ id: ++this.ruleCounter,
215
258
  rule: copy,
216
259
  match,
217
260
  sets,
@@ -238,6 +281,9 @@ export class ServerScript {
238
281
  if (entry.matched < (rule.nth || 1) || (rule.times !== undefined && entry.hits >= rule.times)) {
239
282
  continue;
240
283
  }
284
+ if (rule.chance !== undefined && this.random() >= rule.chance) {
285
+ continue;
286
+ }
241
287
  entry.hits++;
242
288
  this.server.emit('script', { rule, event: context.event, session: context.session, tag: context.tag, command: context.command });
243
289
  return rule;
@@ -259,6 +305,9 @@ export class ServerScript {
259
305
  if (rule.untagged !== undefined && rule.untagged !== (!context.response || context.response.tag === '*')) {
260
306
  return false;
261
307
  }
308
+ if (rule.quietFor !== undefined && (context.quiet || 0) < rule.quietFor) {
309
+ return false;
310
+ }
262
311
  if (entry.match && !entry.match.test(context.data)) {
263
312
  return false;
264
313
  }
@@ -452,6 +501,16 @@ function sendBytes(connection, rule, data, delay) {
452
501
  close: rule.close || (rule.truncate === undefined ? undefined : true)
453
502
  });
454
503
  }
504
+ /**
505
+ * Handles a quiet event with the rule that matched it: sends the bytes of `send` and closes the connection with `close`
506
+ *
507
+ * @param {Object} connection IMAP connection
508
+ * @param {Object} rule Rule that matched
509
+ * @param {Object} context Event context
510
+ */
511
+ export function handleQuiet(connection, rule, context) {
512
+ sendBytes(connection, rule, resolveBytes(rule.send, context) || Buffer.alloc(0));
513
+ }
455
514
  /**
456
515
  * Handles a command or input line with the rule that matched it: sends the bytes of `send`, closes the connection,
457
516
  * and processes the line as usual with `run` (or when the rule only delays it)
@@ -1,12 +1,28 @@
1
1
  import { Stream } from 'node:stream';
2
2
  import net from 'node:net';
3
+ import http from 'node:http';
3
4
  import tls from 'node:tls';
4
5
  import type { CompilerOptions, ParserOptions } from 'imap-handler';
5
6
  import type { ResolvedCommandOptions } from './command-states.js';
7
+ import { Control } from './control.js';
8
+ import type { SessionInfo } from './control.js';
6
9
  import { ServerScript } from './script.js';
7
10
  import type { DeferredOutput, OutputOperation, ScriptContext, ScriptEvent, ScriptRule } from './script.js';
8
11
  import type { AppendCheck, AppendCheckOptions, AppendDataHandler, AppendMessage, CapabilityCheck, CheckResult, ClosedCheck, CommandCheck, CommandContext, CommandOptions, CommandHandler, ConnectionHandler, ConnectionState, CopyHandler, FetchHandler, IMAPResponse, IMAPServerOptions, ListedMailbox, LiteralFilter, Mailbox, MailboxHandler, MailboxStatus, Message, MessageFilter, MessageHandler, MessageRange, Namespace, Notification, NotifyEvent, NotifyFilter, OutputHandler, ParsedCommand, RangeLimit, Refusal, SearchAccessCheck, SearchHandler, SearchLimit, ServerStorage, StatusHandler, StorageMailbox, StorageMessage, StoreHandler, SubscriptionStandIn, Transport, UrlAccessCheck, UserData } from './types.js';
9
12
  declare const TAG_REGEX: RegExp;
13
+ /** The parts of a smtp-server SMTPServer that the server uses, smtp-server is an optional dependency */
14
+ interface SMTPListener {
15
+ close(callback: () => void): void;
16
+ once(event: 'error', listener: (err: Error) => void): unknown;
17
+ removeListener(event: 'error', listener: (err: Error) => void): unknown;
18
+ server: net.Server;
19
+ }
20
+ /** The command whose tagged response is pending, and the session state before it ran */
21
+ interface CommandStart {
22
+ tag: string;
23
+ command: string;
24
+ state: ConnectionState;
25
+ }
10
26
  /**
11
27
  * Creates a new IMAP server, call `listen()` on it to start accepting connections
12
28
  *
@@ -64,7 +80,58 @@ declare class IMAPServer extends Stream {
64
80
  script: ServerScript;
65
81
  /** connections accepted so far, the number of a connection is `connection.sessionNumber` */
66
82
  sessionCounter: number;
83
+ /** the control API, see src/control.ts */
84
+ control: Control;
85
+ /** the SMTP server that `start()` runs for the `smtp` option */
86
+ smtpServer: SMTPListener | null;
87
+ /** the HTTP server of the REST API that `start()` runs for the `rest` option */
88
+ restServer: http.Server | null;
67
89
  constructor(options?: IMAPServerOptions);
90
+ /**
91
+ * Sets the users from the `users` option, or the default user. The users are deep copied, so that runtime changes
92
+ * never leak into the caller's objects or into other servers built from the same fixture. Without a prototype,
93
+ * user names like "__proto__" or "toString" are plain keys
94
+ */
95
+ loadUsers(): void;
96
+ /**
97
+ * Builds the mailboxes from the `storage` option, a deep copy of it. Message and mailbox handlers of plugins run
98
+ * on every message and mailbox
99
+ */
100
+ loadStorage(): void;
101
+ /**
102
+ * Starts accepting connections
103
+ *
104
+ * @param {Number} [port] Port to listen on, a free port if not set
105
+ * @param {String} [host] Address to listen on, all addresses if not set
106
+ * @return {Promise<Number>} resolves with the port the server listens on
107
+ */
108
+ start(port?: number, host?: string): Promise<number>;
109
+ /**
110
+ * Starts the REST API of the `rest` option, see src/rest.ts
111
+ *
112
+ * @return {Promise<Number>} resolves with the HTTP port
113
+ */
114
+ startRest(): Promise<number>;
115
+ /**
116
+ * Starts the SMTP server of the `smtp` option. smtp-server is an optional dependency, loaded only here
117
+ *
118
+ * @return {Promise<Number>} resolves with the SMTP port
119
+ */
120
+ startSmtp(): Promise<number>;
121
+ /**
122
+ * Loads the SMTP listener, which needs the smtp-server package
123
+ *
124
+ * @return {Promise<Object>} the smtp-listener module
125
+ */
126
+ loadSmtpListener(): Promise<{
127
+ startSMTPServer(port: number, server: IMAPServer, callback: (port: number) => void, host?: string): SMTPListener;
128
+ }>;
129
+ /**
130
+ * Stops the server and closes every connection
131
+ *
132
+ * @return {Promise} resolves when the server is closed
133
+ */
134
+ stop(): Promise<void>;
68
135
  /**
69
136
  * Starts accepting connections, takes the arguments of `net.Server#listen()`
70
137
  */
@@ -72,6 +139,13 @@ declare class IMAPServer extends Stream {
72
139
  listen(port: number, hostname: string, listeningListener?: () => void): void;
73
140
  listen(options: net.ListenOptions, listeningListener?: () => void): void;
74
141
  close(callback?: (err?: Error) => void): void;
142
+ /**
143
+ * Stops the SMTP and REST listeners of `start()`
144
+ *
145
+ * @param {Boolean} force true closes the open REST connections too, otherwise only the idle ones (a request that
146
+ * is answered, like POST /v1/shutdown, finishes first)
147
+ */
148
+ closeListeners(force: boolean): void;
75
149
  /**
76
150
  * Returns TLS key and certificate. Without credentials in the options the
77
151
  * bundled self-signed certificate for localhost is used.
@@ -144,13 +218,23 @@ declare class IMAPServer extends Stream {
144
218
  *
145
219
  * @param {String} type Kind of change
146
220
  * @param {String} path Storage name of the mailbox
147
- * @param {Object} [details] `{ oldPath, mailbox }`: the earlier name of a renamed mailbox, the mailbox
148
- * object that a DELETE removed
221
+ * @param {Object} [details] `{ oldPath, mailbox, created }`: the earlier name of a renamed mailbox, the mailbox
222
+ * object that a DELETE removed, the mailboxes a CREATE made (superior levels first)
149
223
  */
150
224
  mailboxChanged(type: string, path: string, details?: {
151
225
  oldPath?: string | undefined;
152
226
  mailbox?: Mailbox | undefined;
227
+ created?: string[] | undefined;
153
228
  }): void;
229
+ /**
230
+ * Runs a change with `origin` as the session that caused it, the `origin` of the notifications and events it
231
+ * makes. Commands run with their session, the control API with null
232
+ *
233
+ * @param {Object|null} origin Session, or null for a change from outside
234
+ * @param {Function} fn Change
235
+ * @return {*} the result of fn
236
+ */
237
+ withOrigin<T>(origin: IMAPConnection | null, fn: () => T): T;
154
238
  /**
155
239
  * Retrieves the handler of an IMAP command
156
240
  *
@@ -286,6 +370,13 @@ declare class IMAPServer extends Stream {
286
370
  * @param {Array} flags Flags of a message
287
371
  */
288
372
  rememberFlags(mailbox: Mailbox, flags: string[]): void;
373
+ /**
374
+ * The current time for dates the server sets itself (INTERNALDATE of a message without one, SAVEDATE). The `now`
375
+ * option fixes it for repeatable tests: a Date, a timestamp, or a function that returns one
376
+ *
377
+ * @return {Date} current time
378
+ */
379
+ now(): Date;
289
380
  /**
290
381
  * Converts a date-time value from storage or a client to the form it is sent in
291
382
  *
@@ -510,11 +601,18 @@ declare class IMAPConnection {
510
601
  } | undefined;
511
602
  /** output that waits behind a delay of a script rule, null when output is written right away */
512
603
  _outputQueue: OutputOperation[] | null;
513
- _outputTimer: ReturnType<typeof setTimeout> | null;
604
+ _outputTimer: ReturnType<typeof setTimeout> | ReturnType<typeof setImmediate> | null;
605
+ /** a piece of a chunked write with chunkDelay 0 or "tick" waits until it was handed to the system */
606
+ _outputWaiting: boolean | undefined;
514
607
  /** untagged responses that `defer` rules hold back, see releaseDeferred in src/script.ts */
515
608
  _deferredOutput: DeferredOutput[] | null;
516
609
  /** a script rule closes the connection with a RST, see closeNow */
517
610
  _resetting: boolean | undefined;
611
+ /** time of the last input or output, and the timer of the quiet events of script rules, see watchQuiet */
612
+ _lastActivity: number | undefined;
613
+ _quietTimer: ReturnType<typeof setTimeout> | null | undefined;
614
+ /** the command whose tagged response is pending and the session before it ran, see commandCompleted */
615
+ _commandStart: CommandStart | null | undefined;
518
616
  constructor(server: IMAPServer, socket: net.Socket);
519
617
  /**
520
618
  * Writes protocol output to the client, through the transport layer if there is one
@@ -542,8 +640,9 @@ declare class IMAPConnection {
542
640
  *
543
641
  * @param {Buffer} data Output
544
642
  * @param {Object|null} transport Transport layer
643
+ * @param {Function} [callback] Called once the data was handed to the system (a transport: on the next turn)
545
644
  */
546
- writeLayer(data: Buffer, transport: Transport | null): void;
645
+ writeLayer(data: Buffer, transport: Transport | null, callback?: () => void): void;
547
646
  /**
548
647
  * Sends output through the script rule that handles its event, if there is one
549
648
  *
@@ -566,7 +665,19 @@ declare class IMAPConnection {
566
665
  *
567
666
  * @param {Buffer} data Data to send
568
667
  */
569
- writeRaw(data: Buffer | string): void;
668
+ writeRaw(data: Buffer | string, callback?: () => void): void;
669
+ /**
670
+ * Notes input or output for the quiet events of script rules (#88)
671
+ */
672
+ touch(): void;
673
+ /**
674
+ * Starts waiting for the next quiet event, if a rule waits for one and the session is not waiting already. When
675
+ * the time is up, the first rule that matches handles the event (`send`, `close`), the output it sends starts
676
+ * the next quiet time
677
+ *
678
+ * @param {Number} [checked] Quiet time the rules were checked for already, only longer ones are waited for
679
+ */
680
+ watchQuiet(checked?: number): void;
570
681
  /**
571
682
  * Handles data from the socket, through the transport layer if there is one
572
683
  *
@@ -949,6 +1060,34 @@ declare class IMAPConnection {
949
1060
  * @param {Boolean} [scripted] The line comes from a script rule with `run`, it is next in the queue
950
1061
  */
951
1062
  scheduleCommand(data: string, scripted?: boolean): void;
1063
+ /**
1064
+ * Emits the `command` event when the tagged response of a command goes out, and the `session` events "login"
1065
+ * (the command authenticated the session) and "logout" (back to Not Authenticated, UNAUTHENTICATE). Every command
1066
+ * that changes these does so before its tagged response. "select" and "unselect" come from SELECT and
1067
+ * closeMailbox(). Tests wait for these instead of polling
1068
+ *
1069
+ * @param {String} status OK, NO or BAD
1070
+ */
1071
+ commandCompleted(status: string): void;
1072
+ /**
1073
+ * Emits a `session` event, `{ type, session, ...fields }`
1074
+ *
1075
+ * @param {String} type Event type
1076
+ * @param {Object} [fields] More fields of the event
1077
+ */
1078
+ emitSession(type: string, fields?: Record<string, unknown>): void;
1079
+ /**
1080
+ * Describes the session without credentials or sockets, for the control API and the `session` events
1081
+ *
1082
+ * @return {Object} `{ session, user, state, mailbox, readOnly, enabled, secure, compressed, remoteAddress }`
1083
+ */
1084
+ describe(): SessionInfo;
1085
+ /**
1086
+ * Checks if output can still be sent: the socket is open and the connection is not closing (BYE, LOGOUT)
1087
+ *
1088
+ * @return {Boolean} true for a live session
1089
+ */
1090
+ isOpen(): boolean;
952
1091
  /**
953
1092
  * Handles a command line with the script rule that matched it, after the rule's delay. With `run` the line
954
1093
  * goes through the parser and the command handler as usual afterwards
@@ -963,19 +1102,19 @@ declare class IMAPConnection {
963
1102
  *
964
1103
  * @param {Object} mailbox Mailbox to check for
965
1104
  * @param {Boolean} [ignoreSelf] If set to true, does not send any notices to itself
966
- * @param {Boolean} [ignoreSelf] If set to true, does not send EXISTS notice to itself
1105
+ * @param {Boolean} [ignoreExists] If set to true, does not send EXISTS notice to itself
967
1106
  */
968
1107
  expungeDeleted(mailbox: Mailbox, ignoreSelf?: boolean, ignoreExists?: boolean): void;
969
1108
  /**
970
1109
  * Given a set of messages in a mailbox (possibly via getMessageRange), remove
971
- * them from the mailbox and generate EXPUNGE notifications.
1110
+ * them from the mailbox and generate EXPUNGE notifications, see expungeMessages() in store-operations.ts
972
1111
  *
973
1112
  * @param {Object} mailbox Mailbox to check for
974
1113
  * @param {Function|Array} messagesOrFilterFunc An Array of messages in the
975
1114
  * folder that should be removed or a filtering function that indicates
976
1115
  * messages to be removed by returning true.
977
1116
  * @param {Boolean} [ignoreSelf] If set to true, does not send any notices to itself
978
- * @param {Boolean} [ignoreSelf] If set to true, does not send EXISTS notice to itself
1117
+ * @param {Boolean} [ignoreExists] If set to true, does not send EXISTS notice to itself
979
1118
  * @param {Boolean} [highestFirst] If set to true, the EXPUNGE responses go from the highest UID to the lowest
980
1119
  * (MESSAGELIMIT, RFC 9738 section 3.1), otherwise from the lowest
981
1120
  */